Comparar commits
3 Commits
| Autor | SHA1 | Data | |
|---|---|---|---|
| ec5cd6a68e | |||
| 0fc94faf29 | |||
| de2d9739c8 |
@@ -1 +0,0 @@
|
||||
v0.10.21
|
||||
-11
@@ -25,17 +25,6 @@ You can also download a `.zip` file from the [releases page](https://github.com/
|
||||
The Windows version does not currently automatically update so you will need to
|
||||
manually upgrade to future releases by re-downloading the `.zip` file.
|
||||
|
||||
### Debian Linux (Ubuntu)
|
||||
|
||||
Currently only a 64-bit version is available.
|
||||
|
||||
1. Download `atom-amd64.deb` from the [Atom releases page](https://github.com/atom/atom/releases/latest).
|
||||
2. Run `sudo dpkg --install atom-amd64.deb` on the downloaded package.
|
||||
3. Launch Atom using the installed `atom` command.
|
||||
|
||||
The Linux version does not currently automatically update so you will need to
|
||||
repeat these steps to upgrade to future releases.
|
||||
|
||||
## Building
|
||||
|
||||
* [Linux](docs/build-instructions/linux.md)
|
||||
|
||||
+1
-1
@@ -6,6 +6,6 @@
|
||||
"url": "https://github.com/atom/atom.git"
|
||||
},
|
||||
"dependencies": {
|
||||
"atom-package-manager": "0.96.0"
|
||||
"atom-package-manager": "0.87.0"
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -50,7 +50,7 @@ if [ $OS == 'Mac' ]; then
|
||||
|
||||
# If ATOM_PATH isn't a executable file, use spotlight to search for Atom
|
||||
if [ ! -x "$ATOM_PATH/$ATOM_APP_NAME" ]; then
|
||||
ATOM_PATH=$(mdfind "kMDItemCFBundleIdentifier == 'com.github.atom'" | grep -v ShipIt | head -1 | xargs dirname)
|
||||
ATOM_PATH=$(mdfind "kMDItemCFBundleIdentifier == 'com.github.atom'" | head -1 | xargs dirname)
|
||||
fi
|
||||
|
||||
# Exit if Atom can't be found
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
require '../src/window'
|
||||
Atom = require '../src/atom'
|
||||
window.atom = Atom.loadOrCreate('spec')
|
||||
atom = new Atom()
|
||||
atom.show() unless atom.getLoadSettings().exitWhenDone
|
||||
window.atom = atom
|
||||
|
||||
@@ -9,4 +9,5 @@ window.atom = atom
|
||||
atom.openDevTools()
|
||||
|
||||
document.title = "Benchmark Suite"
|
||||
runSpecSuite('../benchmark/benchmark-suite', atom.getLoadSettings().logFile)
|
||||
benchmarkSuite = require.resolve('./benchmark-suite')
|
||||
runSpecSuite(benchmarkSuite, true)
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
require '../spec/spec-helper'
|
||||
|
||||
path = require 'path'
|
||||
{$, Point} = require 'atom'
|
||||
_ = require 'underscore-plus'
|
||||
fs = require 'fs-plus'
|
||||
{$, _, Point, fs} = require 'atom'
|
||||
Project = require '../src/project'
|
||||
TokenizedBuffer = require '../src/tokenized-buffer'
|
||||
|
||||
@@ -103,7 +101,7 @@ $.fn.resultOfTrigger = (type) ->
|
||||
event.result
|
||||
|
||||
$.fn.enableKeymap = ->
|
||||
@on 'keydown', (e) -> window.keymap.handleKeyEvent(e)
|
||||
@on 'keydown', (e) => window.keymap.handleKeyEvent(e)
|
||||
|
||||
$.fn.attachToDom = ->
|
||||
$('#jasmine-content').append(this)
|
||||
|
||||
@@ -53,7 +53,7 @@ describe "editorView.", ->
|
||||
|
||||
describe "at-end.", ->
|
||||
beforeEach ->
|
||||
editorView.moveToBottom()
|
||||
editorView.moveCursorToBottom()
|
||||
|
||||
benchmark "insert-delete", ->
|
||||
editorView.insertText('"')
|
||||
@@ -62,8 +62,8 @@ describe "editorView.", ->
|
||||
describe "empty-vs-set-innerHTML.", ->
|
||||
[firstRow, lastRow] = []
|
||||
beforeEach ->
|
||||
firstRow = editorView.getModel().getFirstVisibleScreenRow()
|
||||
lastRow = editorView.getModel().getLastVisibleScreenRow()
|
||||
firstRow = editorView.getFirstVisibleScreenRow()
|
||||
lastRow = editorView.getLastVisibleScreenRow()
|
||||
|
||||
benchmark "build-gutter-html.", 1000, ->
|
||||
editorView.gutter.renderLineNumbers(null, firstRow, lastRow)
|
||||
@@ -97,13 +97,13 @@ describe "editorView.", ->
|
||||
describe "multiple-lines.", ->
|
||||
[firstRow, lastRow] = []
|
||||
beforeEach ->
|
||||
firstRow = editorView.getModel().getFirstVisibleScreenRow()
|
||||
lastRow = editorView.getModel().getLastVisibleScreenRow()
|
||||
firstRow = editorView.getFirstVisibleScreenRow()
|
||||
lastRow = editorView.getLastVisibleScreenRow()
|
||||
|
||||
benchmark "cache-entire-visible-area", 100, ->
|
||||
for i in [firstRow..lastRow]
|
||||
line = editorView.lineElementForScreenRow(i)[0]
|
||||
editorView.positionLeftForLineAndColumn(line, i, Math.max(0, editorView.getModel().lineTextForBufferRow(i).length))
|
||||
editorView.positionLeftForLineAndColumn(line, i, Math.max(0, editorView.lineLengthForBufferRow(i)))
|
||||
|
||||
describe "text-rendering.", ->
|
||||
beforeEach ->
|
||||
@@ -178,7 +178,7 @@ describe "editorView.", ->
|
||||
atom.workspaceView.openSync('huge.js')
|
||||
|
||||
benchmark "moving-to-eof.", 1, ->
|
||||
editorView.moveToBottom()
|
||||
editorView.moveCursorToBottom()
|
||||
|
||||
describe "on-first-line.", ->
|
||||
benchmark "inserting-newline", 5, ->
|
||||
@@ -195,11 +195,11 @@ describe "editorView.", ->
|
||||
endPosition = null
|
||||
|
||||
beforeEach ->
|
||||
editorView.moveToBottom()
|
||||
editorView.moveCursorToBottom()
|
||||
endPosition = editorView.getCursorScreenPosition()
|
||||
|
||||
benchmark "move-to-beginning-of-word", ->
|
||||
editorView.moveToBeginningOfWord()
|
||||
editorView.moveCursorToBeginningOfWord()
|
||||
editorView.setCursorScreenPosition(endPosition)
|
||||
|
||||
benchmark "insert", ->
|
||||
|
||||
@@ -1,56 +0,0 @@
|
||||
#!/usr/bin/env coffee
|
||||
|
||||
{spawn, exec} = require 'child_process'
|
||||
fs = require 'fs'
|
||||
os = require 'os'
|
||||
path = require 'path'
|
||||
_ = require 'underscore-plus'
|
||||
temp = require 'temp'
|
||||
|
||||
directoryToOpen = temp.mkdirSync('browser-process-startup-')
|
||||
socketPath = path.join(os.tmpdir(), 'atom.sock')
|
||||
numberOfRuns = 10
|
||||
|
||||
deleteSocketFile = ->
|
||||
try
|
||||
fs.unlinkSync(socketPath) if fs.existsSync(socketPath)
|
||||
catch error
|
||||
console.error(error)
|
||||
|
||||
launchAtom = (callback) ->
|
||||
deleteSocketFile()
|
||||
|
||||
cmd = 'atom'
|
||||
args = ['--safe', '--new-window', '--foreground', directoryToOpen]
|
||||
atomProcess = spawn(cmd, args)
|
||||
|
||||
output = ''
|
||||
startupTimes = []
|
||||
dataListener = (data) ->
|
||||
output += data
|
||||
if match = /App load time: (\d+)/.exec(output)
|
||||
startupTime = parseInt(match[1])
|
||||
atomProcess.stderr.removeListener 'data', dataListener
|
||||
atomProcess.kill()
|
||||
exec 'pkill -9 Atom', (error) ->
|
||||
console.error(error) if error?
|
||||
callback(startupTime)
|
||||
|
||||
atomProcess.stderr.on 'data', dataListener
|
||||
|
||||
startupTimes = []
|
||||
collector = (startupTime) ->
|
||||
startupTimes.push(startupTime)
|
||||
if startupTimes.length < numberOfRuns
|
||||
launchAtom(collector)
|
||||
else
|
||||
maxTime = _.max(startupTimes)
|
||||
minTime = _.min(startupTimes)
|
||||
totalTime = startupTimes.reduce (previousValue=0, currentValue) -> previousValue + currentValue
|
||||
console.log "Startup Runs: #{startupTimes.length}"
|
||||
console.log "First run time: #{startupTimes[0]}ms"
|
||||
console.log "Max time: #{maxTime}ms"
|
||||
console.log "Min time: #{minTime}ms"
|
||||
console.log "Average time: #{Math.round(totalTime/startupTimes.length)}ms"
|
||||
|
||||
launchAtom(collector)
|
||||
@@ -60,7 +60,7 @@ module.exports = (grunt) ->
|
||||
contentsDir = shellAppDir
|
||||
appDir = path.join(shellAppDir, 'resources', 'app')
|
||||
installDir ?= process.env.INSTALL_PREFIX ? '/usr/local'
|
||||
killCommand ='pkill -9 atom'
|
||||
killCommand ='pkill -9 Atom'
|
||||
|
||||
coffeeConfig =
|
||||
glob_to_multiple:
|
||||
@@ -130,7 +130,7 @@ module.exports = (grunt) ->
|
||||
|
||||
atom: {appDir, appName, symbolsDir, buildDir, contentsDir, installDir, shellAppDir}
|
||||
|
||||
docsOutputDir: 'docs/output'
|
||||
docsOutputDir: 'docs/output/api'
|
||||
|
||||
coffee: coffeeConfig
|
||||
|
||||
@@ -144,7 +144,12 @@ module.exports = (grunt) ->
|
||||
|
||||
coffeelint:
|
||||
options:
|
||||
configFile: 'coffeelint.json'
|
||||
no_empty_param_list:
|
||||
level: 'error'
|
||||
max_line_length:
|
||||
level: 'ignore'
|
||||
indentation:
|
||||
level: 'ignore'
|
||||
src: [
|
||||
'dot-atom/**/*.coffee'
|
||||
'exports/**/*.coffee'
|
||||
@@ -225,16 +230,9 @@ module.exports = (grunt) ->
|
||||
grunt.registerTask('compile', ['coffee', 'prebuild-less', 'cson', 'peg'])
|
||||
grunt.registerTask('lint', ['coffeelint', 'csslint', 'lesslint'])
|
||||
grunt.registerTask('test', ['shell:kill-atom', 'run-specs'])
|
||||
grunt.registerTask('ci', ['output-disk-space', 'download-atom-shell', 'build', 'dump-symbols', 'set-version', 'check-licenses', 'lint', 'test', 'codesign', 'publish-build'])
|
||||
grunt.registerTask('docs', ['markdown:guides', 'build-docs'])
|
||||
|
||||
ciTasks = ['output-disk-space', 'download-atom-shell', 'build']
|
||||
ciTasks.push('dump-symbols') if process.platform isnt 'win32'
|
||||
ciTasks.push('set-version', 'check-licenses', 'lint')
|
||||
ciTasks.push('mkdeb') if process.platform is 'linux'
|
||||
ciTasks.push('test') if process.platform isnt 'linux'
|
||||
ciTasks.push('codesign', 'publish-build')
|
||||
grunt.registerTask('ci', ciTasks)
|
||||
|
||||
defaultTasks = ['download-atom-shell', 'build', 'set-version']
|
||||
defaultTasks.push 'install' unless process.platform is 'linux'
|
||||
grunt.registerTask('default', defaultTasks)
|
||||
|
||||
@@ -7,14 +7,13 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"async": "~0.2.9",
|
||||
"donna": "1.0.1",
|
||||
"tello": "1.0.3",
|
||||
"biscotto": ">=2.1.1 <3.0",
|
||||
"formidable": "~1.0.14",
|
||||
"fs-plus": "2.x",
|
||||
"github-releases": "~0.2.0",
|
||||
"grunt": "~0.4.1",
|
||||
"grunt-cli": "~0.1.9",
|
||||
"grunt-coffeelint": "git+https://github.com/atom/grunt-coffeelint.git#cfb99aa99811d52687969532bd5a98011ed95bfe",
|
||||
"grunt-coffeelint": "git+https://github.com/atom/grunt-coffeelint.git",
|
||||
"grunt-contrib-coffee": "~0.9.0",
|
||||
"grunt-contrib-csslint": "~0.1.2",
|
||||
"grunt-contrib-less": "~0.8.0",
|
||||
@@ -27,7 +26,7 @@
|
||||
"harmony-collections": "~0.3.8",
|
||||
"json-front-matter": "~0.1.3",
|
||||
"legal-eagle": "~0.4.0",
|
||||
"minidump": "~0.8",
|
||||
"minidump": "~0.7",
|
||||
"normalize-package-data": "0.2.12",
|
||||
"npm": "~1.4.5",
|
||||
"rcedit": "~0.1.2",
|
||||
|
||||
@@ -148,6 +148,9 @@ module.exports = (grunt) ->
|
||||
grunt.file.copy(sourcePath, path.resolve(appDir, '..', subDirectory, filename))
|
||||
|
||||
if process.platform is 'win32'
|
||||
cp path.join('resources', 'win', 'msvcp100.dll'), path.join(shellAppDir, 'msvcp100.dll')
|
||||
cp path.join('resources', 'win', 'msvcr100.dll'), path.join(shellAppDir, 'msvcr100.dll')
|
||||
|
||||
# Set up chocolatey ignore and gui files
|
||||
fs.writeFileSync path.join(appDir, 'apm', 'node_modules', 'atom-package-manager', 'bin', 'node.exe.ignore'), ''
|
||||
fs.writeFileSync path.join(appDir, 'node_modules', 'symbols-view', 'vendor', 'ctags-win32.exe.ignore'), ''
|
||||
|
||||
+151
-29
@@ -1,40 +1,162 @@
|
||||
path = require 'path'
|
||||
|
||||
async = require 'async'
|
||||
fs = require 'fs-plus'
|
||||
_ = require 'underscore-plus'
|
||||
|
||||
donna = require 'donna'
|
||||
tello = require 'tello'
|
||||
request = require 'request'
|
||||
|
||||
module.exports = (grunt) ->
|
||||
getClassesToInclude = ->
|
||||
modulesPath = path.resolve(__dirname, '..', '..', 'node_modules')
|
||||
classes = {}
|
||||
fs.traverseTreeSync modulesPath, (modulePath) ->
|
||||
return false if modulePath.match(/node_modules/g).length > 1 # dont need the dependencies of the dependencies
|
||||
return true unless path.basename(modulePath) is 'package.json'
|
||||
return true unless fs.isFileSync(modulePath)
|
||||
{rm} = require('./task-helpers')(grunt)
|
||||
|
||||
apiPath = path.join(path.dirname(modulePath), 'api.json')
|
||||
if fs.isFileSync(apiPath)
|
||||
_.extend(classes, grunt.file.readJSON(apiPath).classes)
|
||||
true
|
||||
classes
|
||||
|
||||
sortClasses = (classes) ->
|
||||
sortedClasses = {}
|
||||
for className in Object.keys(classes).sort()
|
||||
sortedClasses[className] = classes[className]
|
||||
sortedClasses
|
||||
cmd = path.join('node_modules', '.bin', 'coffee')
|
||||
commonArgs = [path.join('build', 'node_modules', '.bin', 'biscotto'), '--']
|
||||
opts =
|
||||
stdio: 'inherit'
|
||||
|
||||
grunt.registerTask 'build-docs', 'Builds the API docs in src', ->
|
||||
done = @async()
|
||||
|
||||
docsOutputDir = grunt.config.get('docsOutputDir')
|
||||
|
||||
metadata = donna.generateMetadata(['.'])
|
||||
api = tello.digest(metadata)
|
||||
_.extend(api.classes, getClassesToInclude())
|
||||
api.classes = sortClasses(api.classes)
|
||||
downloadIncludes (error, includePaths) ->
|
||||
if error?
|
||||
done(error)
|
||||
else
|
||||
rm(docsOutputDir)
|
||||
args = [
|
||||
commonArgs...
|
||||
'--title', 'Atom API Documentation'
|
||||
'-o', docsOutputDir
|
||||
'-r', 'docs/README.md'
|
||||
'--stability', '1'
|
||||
'src/'
|
||||
includePaths...
|
||||
]
|
||||
grunt.util.spawn({cmd, args, opts}, done)
|
||||
|
||||
apiJson = JSON.stringify(api, null, 2)
|
||||
apiJsonPath = path.join(docsOutputDir, 'api.json')
|
||||
grunt.file.write(apiJsonPath, apiJson)
|
||||
grunt.registerTask 'lint-docs', 'Generate stats about the doc coverage', ->
|
||||
done = @async()
|
||||
downloadIncludes (error, includePaths) ->
|
||||
if error?
|
||||
done(error)
|
||||
else
|
||||
args = [
|
||||
commonArgs...
|
||||
'--noOutput'
|
||||
'src/'
|
||||
includePaths...
|
||||
]
|
||||
grunt.util.spawn({cmd, args, opts}, done)
|
||||
|
||||
grunt.registerTask 'missing-docs', 'Generate stats about the doc coverage', ->
|
||||
done = @async()
|
||||
downloadIncludes (error, includePaths) ->
|
||||
if error?
|
||||
done(error)
|
||||
else
|
||||
args = [
|
||||
commonArgs...
|
||||
'--noOutput'
|
||||
'--missing'
|
||||
'src/'
|
||||
includePaths...
|
||||
]
|
||||
grunt.util.spawn({cmd, args, opts}, done)
|
||||
|
||||
grunt.registerTask 'copy-docs', 'Copies over latest API docs to atom-docs', ->
|
||||
done = @async()
|
||||
|
||||
fetchTag = (args..., callback) ->
|
||||
cmd = 'git'
|
||||
args = ['describe', '--abbrev=0', '--tags']
|
||||
grunt.util.spawn {cmd, args}, (error, result) ->
|
||||
if error?
|
||||
callback(error)
|
||||
else
|
||||
callback(null, String(result).trim())
|
||||
|
||||
copyDocs = (tag, callback) ->
|
||||
cmd = 'cp'
|
||||
args = ['-r', 'docs/output/', "../atom.io/public/docs/api/#{tag}/"]
|
||||
|
||||
fs.exists "../atom.io/public/docs/api/", (exists) ->
|
||||
if exists
|
||||
grunt.util.spawn {cmd, args}, (error, result) ->
|
||||
if error?
|
||||
callback(error)
|
||||
else
|
||||
callback(null, tag)
|
||||
else
|
||||
grunt.log.error "../atom.io/public/docs/api/ doesn't exist"
|
||||
return false
|
||||
|
||||
grunt.util.async.waterfall [fetchTag, copyDocs], done
|
||||
|
||||
grunt.registerTask 'deploy-docs', 'Publishes latest API docs to atom-docs.githubapp.com', ->
|
||||
done = @async()
|
||||
docsRepoArgs = ['--work-tree=../atom-docs/', '--git-dir=../atom-docs/.git/']
|
||||
|
||||
fetchTag = (args..., callback) ->
|
||||
cmd = 'git'
|
||||
args = ['describe', '--abbrev=0', '--tags']
|
||||
grunt.util.spawn {cmd, args}, (error, result) ->
|
||||
if error?
|
||||
callback(error)
|
||||
else
|
||||
callback(null, String(result).trim().split('.')[0..1].join('.'))
|
||||
|
||||
stageDocs = (tag, callback) ->
|
||||
cmd = 'git'
|
||||
args = [docsRepoArgs..., 'add', "public/#{tag}"]
|
||||
grunt.util.spawn({cmd, args, opts}, callback)
|
||||
|
||||
fetchSha = (args..., callback) ->
|
||||
cmd = 'git'
|
||||
args = ['rev-parse', 'HEAD']
|
||||
grunt.util.spawn {cmd, args}, (error, result) ->
|
||||
if error?
|
||||
callback(error)
|
||||
else
|
||||
callback(null, String(result).trim())
|
||||
|
||||
commitChanges = (sha, callback) ->
|
||||
cmd = 'git'
|
||||
args = [docsRepoArgs..., 'commit', "-m Update API docs to #{sha}"]
|
||||
grunt.util.spawn({cmd, args, opts}, callback)
|
||||
|
||||
pushOrigin = (args..., callback) ->
|
||||
cmd = 'git'
|
||||
args = [docsRepoArgs..., 'push', 'origin', 'master']
|
||||
grunt.util.spawn({cmd, args, opts}, callback)
|
||||
|
||||
pushHeroku = (args..., callback) ->
|
||||
cmd = 'git'
|
||||
args = [docsRepoArgs..., 'push', 'heroku', 'master']
|
||||
grunt.util.spawn({cmd, args, opts}, callback)
|
||||
|
||||
grunt.util.async.waterfall [fetchTag, stageDocs, fetchSha, commitChanges, pushOrigin, pushHeroku], done
|
||||
|
||||
downloadFileFromRepo = ({repo, file}, callback) ->
|
||||
uri = "https://raw.github.com/atom/#{repo}/master/#{file}"
|
||||
request uri, (error, response, contents) ->
|
||||
return callback(error) if error?
|
||||
downloadPath = path.join('docs', 'includes', repo, file)
|
||||
fs.writeFile downloadPath, contents, (error) ->
|
||||
callback(error, downloadPath)
|
||||
|
||||
downloadIncludes = (callback) ->
|
||||
includes = [
|
||||
{repo: 'atom-keymap', file: 'src/keymap-manager.coffee'}
|
||||
{repo: 'atom-keymap', file: 'src/key-binding.coffee'}
|
||||
{repo: 'first-mate', file: 'src/grammar.coffee'}
|
||||
{repo: 'first-mate', file: 'src/grammar-registry.coffee'}
|
||||
{repo: 'node-pathwatcher', file: 'src/directory.coffee'}
|
||||
{repo: 'node-pathwatcher', file: 'src/file.coffee'}
|
||||
{repo: 'space-pen', file: 'src/space-pen.coffee'}
|
||||
{repo: 'text-buffer', file: 'src/marker.coffee'}
|
||||
{repo: 'text-buffer', file: 'src/point.coffee'}
|
||||
{repo: 'text-buffer', file: 'src/range.coffee'}
|
||||
{repo: 'text-buffer', file: 'src/text-buffer.coffee'}
|
||||
{repo: 'theorist', file: 'src/model.coffee'}
|
||||
]
|
||||
|
||||
async.map(includes, downloadFileFromRepo, callback)
|
||||
|
||||
@@ -13,16 +13,8 @@ module.exports = (grunt) ->
|
||||
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)
|
||||
|
||||
grunt.registerTask 'mkdeb', 'Create debian package', ->
|
||||
done = @async()
|
||||
buildDir = grunt.config.get('atom.buildDir')
|
||||
|
||||
if process.arch is 'ia32'
|
||||
arch = 'i386'
|
||||
@@ -36,17 +28,13 @@ module.exports = (grunt) ->
|
||||
maintainer = 'GitHub <atom@github.com>'
|
||||
installDir = '/usr'
|
||||
iconName = 'atom'
|
||||
getInstalledSize buildDir, (error, installedSize) ->
|
||||
data = {name, version, description, section, arch, maintainer, installDir, iconName, installedSize}
|
||||
controlFilePath = fillTemplate(path.join('resources', 'linux', 'debian', 'control'), data)
|
||||
desktopFilePath = fillTemplate(path.join('resources', 'linux', 'Atom.desktop'), data)
|
||||
icon = path.join('resources', 'atom.png')
|
||||
data = {name, version, description, section, arch, maintainer, installDir, iconName}
|
||||
|
||||
cmd = path.join('script', 'mkdeb')
|
||||
args = [version, arch, controlFilePath, desktopFilePath, icon, buildDir]
|
||||
spawn {cmd, args}, (error) ->
|
||||
if error?
|
||||
done(error)
|
||||
else
|
||||
grunt.log.ok "Created #{buildDir}/atom-#{version}-#{arch}.deb"
|
||||
done()
|
||||
controlFilePath = fillTemplate(path.join('resources', 'linux', 'debian', 'control'), data)
|
||||
desktopFilePath = fillTemplate(path.join('resources', 'linux', 'Atom.desktop'), data)
|
||||
icon = path.join('resources', 'atom.png')
|
||||
buildDir = grunt.config.get('atom.buildDir')
|
||||
|
||||
cmd = path.join('script', 'mkdeb')
|
||||
args = [version, arch, controlFilePath, desktopFilePath, icon, buildDir]
|
||||
spawn({cmd, args}, done)
|
||||
|
||||
@@ -17,7 +17,6 @@ defaultHeaders =
|
||||
|
||||
module.exports = (gruntObject) ->
|
||||
grunt = gruntObject
|
||||
{cp} = require('./task-helpers')(grunt)
|
||||
|
||||
grunt.registerTask 'publish-build', 'Publish the built app', ->
|
||||
return if process.env.JANKY_SHA1 and process.env.JANKY_BRANCH isnt 'master'
|
||||
@@ -25,10 +24,8 @@ module.exports = (gruntObject) ->
|
||||
tasks.unshift('build-docs', 'prepare-docs') if process.platform is 'darwin'
|
||||
grunt.task.run(tasks)
|
||||
|
||||
grunt.registerTask 'prepare-docs', 'Move api.json to atom-api.json', ->
|
||||
docsOutputDir = grunt.config.get('docsOutputDir')
|
||||
buildDir = grunt.config.get('atom.buildDir')
|
||||
cp path.join(docsOutputDir, 'api.json'), path.join(buildDir, 'atom-api.json')
|
||||
grunt.registerTask 'prepare-docs', 'Move the build docs to the build dir', ->
|
||||
fs.copySync(grunt.config.get('docsOutputDir'), path.join(grunt.config.get('atom.buildDir'), 'atom-docs'))
|
||||
|
||||
grunt.registerTask 'upload-assets', 'Upload the assets to a GitHub release', ->
|
||||
done = @async()
|
||||
@@ -45,33 +42,16 @@ module.exports = (gruntObject) ->
|
||||
uploadAssets(release, buildDir, assets, done)
|
||||
|
||||
getAssets = ->
|
||||
switch process.platform
|
||||
when 'darwin'
|
||||
[
|
||||
{assetName: 'atom-mac.zip', sourcePath: 'Atom.app'}
|
||||
{assetName: 'atom-mac-symbols.zip', sourcePath: 'Atom.breakpad.syms'}
|
||||
{assetName: 'atom-api.json', sourcePath: 'atom-api.json'}
|
||||
]
|
||||
when 'win32'
|
||||
[
|
||||
{assetName: 'atom-windows.zip', sourcePath: 'Atom'}
|
||||
]
|
||||
when 'linux'
|
||||
buildDir = grunt.config.get('atom.buildDir')
|
||||
if process.arch is 'ia32'
|
||||
arch = 'i386'
|
||||
else
|
||||
arch = 'amd64'
|
||||
{version} = grunt.file.readJSON('package.json')
|
||||
sourcePath = "#{buildDir}/atom-#{version}-#{arch}.deb"
|
||||
assetName = "atom-#{arch}.deb"
|
||||
|
||||
{cp} = require('./task-helpers')(grunt)
|
||||
cp sourcePath, path.join(buildDir, assetName)
|
||||
|
||||
[
|
||||
{assetName, sourcePath}
|
||||
]
|
||||
if process.platform is 'darwin'
|
||||
[
|
||||
{assetName: 'atom-mac.zip', sourcePath: 'Atom.app'}
|
||||
{assetName: 'atom-mac-symbols.zip', sourcePath: 'Atom.breakpad.syms'}
|
||||
{assetName: 'atom-docs.zip', sourcePath: 'atom-docs'}
|
||||
]
|
||||
else
|
||||
[
|
||||
{assetName: 'atom-windows.zip', sourcePath: 'Atom'}
|
||||
]
|
||||
|
||||
logError = (message, error, details) ->
|
||||
grunt.log.error(message)
|
||||
@@ -90,7 +70,7 @@ zipAssets = (buildDir, assets, callback) ->
|
||||
callback(error)
|
||||
|
||||
tasks = []
|
||||
for {assetName, sourcePath} in assets when path.extname(assetName) is '.zip'
|
||||
for {assetName, sourcePath} in assets
|
||||
fs.removeSync(path.join(buildDir, assetName))
|
||||
tasks.push(zip.bind(this, buildDir, sourcePath, assetName))
|
||||
async.parallel(tasks, callback)
|
||||
|
||||
@@ -45,8 +45,8 @@ module.exports = (grunt) ->
|
||||
|
||||
strings =
|
||||
CompanyName: 'GitHub, Inc.'
|
||||
FileDescription: 'Atom'
|
||||
LegalCopyright: 'Copyright (C) 2014 GitHub, Inc. All rights reserved'
|
||||
FileDescription: 'The hackable editor'
|
||||
LegalCopyright: 'Copyright (C) 2013 GitHub, Inc. All rights reserved'
|
||||
ProductName: 'Atom'
|
||||
ProductVersion: version
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ fs = require 'fs'
|
||||
path = require 'path'
|
||||
|
||||
_ = require 'underscore-plus'
|
||||
|
||||
async = require 'async'
|
||||
|
||||
module.exports = (grunt) ->
|
||||
@@ -9,27 +10,18 @@ module.exports = (grunt) ->
|
||||
|
||||
packageSpecQueue = null
|
||||
|
||||
getAppPath = ->
|
||||
contentsDir = grunt.config.get('atom.contentsDir')
|
||||
switch process.platform
|
||||
when 'darwin'
|
||||
path.join(contentsDir, 'MacOS', 'Atom')
|
||||
when 'linux'
|
||||
path.join(contentsDir, 'atom')
|
||||
when 'win32'
|
||||
path.join(contentsDir, 'atom.exe')
|
||||
|
||||
runPackageSpecs = (callback) ->
|
||||
failedPackages = []
|
||||
rootDir = grunt.config.get('atom.shellAppDir')
|
||||
contentsDir = grunt.config.get('atom.contentsDir')
|
||||
resourcePath = process.cwd()
|
||||
appPath = getAppPath()
|
||||
|
||||
# Ensure application is executable on Linux
|
||||
fs.chmodSync(appPath, '755') if process.platform is 'linux'
|
||||
if process.platform is 'darwin'
|
||||
appPath = path.join(contentsDir, 'MacOS', 'Atom')
|
||||
else if process.platform is 'win32'
|
||||
appPath = path.join(contentsDir, 'atom.exe')
|
||||
|
||||
packageSpecQueue = async.queue (packagePath, callback) ->
|
||||
if process.platform in ['darwin', 'linux']
|
||||
if process.platform is 'darwin'
|
||||
options =
|
||||
cmd: appPath
|
||||
args: ['--test', "--resource-path=#{resourcePath}", "--spec-directory=#{path.join(packagePath, 'spec')}"]
|
||||
@@ -65,11 +57,15 @@ module.exports = (grunt) ->
|
||||
packageSpecQueue.drain = -> callback(null, failedPackages)
|
||||
|
||||
runCoreSpecs = (callback) ->
|
||||
appPath = getAppPath()
|
||||
contentsDir = grunt.config.get('atom.contentsDir')
|
||||
if process.platform is 'darwin'
|
||||
appPath = path.join(contentsDir, 'MacOS', 'Atom')
|
||||
else if process.platform is 'win32'
|
||||
appPath = path.join(contentsDir, 'atom.exe')
|
||||
resourcePath = process.cwd()
|
||||
coreSpecsPath = path.resolve('spec')
|
||||
|
||||
if process.platform in ['darwin', 'linux']
|
||||
if process.platform is 'darwin'
|
||||
options =
|
||||
cmd: appPath
|
||||
args: ['--test', "--resource-path=#{resourcePath}", "--spec-directory=#{coreSpecsPath}"]
|
||||
@@ -94,7 +90,7 @@ module.exports = (grunt) ->
|
||||
|
||||
# TODO: This should really be parallel on both platforms, however our
|
||||
# fixtures step on each others toes currently.
|
||||
if process.platform in ['darwin', 'linux']
|
||||
if process.platform is 'darwin'
|
||||
method = async.parallel
|
||||
else if process.platform is 'win32'
|
||||
method = async.series
|
||||
@@ -109,6 +105,7 @@ module.exports = (grunt) ->
|
||||
grunt.log.error("[Error]".red + " #{failures.join(', ')} spec(s) failed") if failures.length > 0
|
||||
|
||||
if process.platform is 'win32' and process.env.JANKY_SHA1
|
||||
done()
|
||||
# Package specs are still flaky on Windows CI
|
||||
done(!coreSpecFailed)
|
||||
else
|
||||
done(!coreSpecFailed and failedPackages.length == 0)
|
||||
|
||||
@@ -53,9 +53,8 @@ module.exports = (grunt) ->
|
||||
proc = childProcess.spawn(options.cmd, options.args, options.opts)
|
||||
proc.stdout.on 'data', (data) -> stdout.push(data.toString())
|
||||
proc.stderr.on 'data', (data) -> stderr.push(data.toString())
|
||||
proc.on 'error', (processError) -> error ?= processError
|
||||
proc.on 'close', (exitCode, signal) ->
|
||||
error ?= new Error(signal) if exitCode != 0
|
||||
error = new Error(signal) if exitCode != 0
|
||||
results = {stderr: stderr.join(''), stdout: stdout.join(''), code: exitCode}
|
||||
grunt.log.error results.stderr if exitCode != 0
|
||||
callback(error, results, exitCode)
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
{
|
||||
"max_line_length": {
|
||||
"level": "ignore"
|
||||
},
|
||||
"no_empty_param_list": {
|
||||
"level": "error"
|
||||
},
|
||||
"arrow_spacing": {
|
||||
"level": "error"
|
||||
},
|
||||
"no_interpolation_in_single_quotes": {
|
||||
"level": "error"
|
||||
}
|
||||
}
|
||||
@@ -56,7 +56,7 @@ character of the current line:
|
||||
class EditorView
|
||||
listenForEvents: ->
|
||||
@command 'editor:move-to-first-character-of-line', =>
|
||||
@editor.moveToFirstCharacterOfLine()
|
||||
@editor.moveCursorToFirstCharacterOfLine()
|
||||
```
|
||||
|
||||
The `::command` method is basically an enhanced version of jQuery's `::on`
|
||||
|
||||
@@ -0,0 +1,112 @@
|
||||
# API Guidelines
|
||||
|
||||
__Note__: These are not all in practice yet. We are still sorting this out, and plan to move the entire API this way.
|
||||
|
||||
## General Guidelines
|
||||
|
||||
We should strive to have only one way to do something, and make it clear what that way is.
|
||||
|
||||
__Bad__
|
||||
* Several ways to get the `Editor` model from the view.
|
||||
* `EditorView.editor`
|
||||
* `EditorView.getModel()`
|
||||
* `EditorView.getEditor()`
|
||||
* Delegated methods on the views when there is a model counterpart
|
||||
* `Editor.toggleSoftTabs()`
|
||||
* `EditorView.toggleSoftTabs()`
|
||||
|
||||
__Good__
|
||||
* One way to get the `Editor` model from the view
|
||||
* `EditorView.getModel()`
|
||||
* Use the method on the model
|
||||
* `Editor.toggleSoftTabs()`
|
||||
* One clear way to subscribe to events
|
||||
* `subscription = @subscribe thing, 'event', -> ...`
|
||||
* One clear way to unsubscribe to events
|
||||
* `subscription.off()` only; not `thing.off 'event', -> ...`
|
||||
|
||||
## Essential vs Extended
|
||||
|
||||
There are two groups of classes / methods. We break them up to facilitate a gentle introduction into the API and help authors build a knowledge foundation more quickly.
|
||||
|
||||
### Essential
|
||||
|
||||
These are classes, methods, and concepts nearly every package author will need to know about. Need to create commands? Subscribe to atom events? Get a reference to all the editors? Highlight a line in the editor? Patterns and methods for these things will be explained in the essential API.
|
||||
|
||||
We want to keep the essential API minimal and focused.
|
||||
|
||||
### Extended
|
||||
|
||||
The extended API contains The Power. Need to move one cursor independent of the others? Want to do some processing on the markers? You can do it with the extended API.
|
||||
|
||||
## View / Model
|
||||
|
||||
Operations on Views should be limited to DOM manipulation only. A package author should only need access to, say, the `EditorView` when it needs to directly modify the `EditorView`'s DOM.
|
||||
|
||||
## Properties
|
||||
|
||||
No public properties. Use methods instead.
|
||||
|
||||
## Methods
|
||||
|
||||
### Naming
|
||||
|
||||
We strive to fit the [Objective C][naming] naming conventions for the sake of readability.
|
||||
|
||||
* Be descriptive, always write out the whole word. `selection` not `sel`, `cursor` not `cur`.
|
||||
* Describe the arguments names in the method name eg. `decorationsForMarker(marker)`, `objectAtIndex(index)`
|
||||
* Use `get` prefix only when there are no arguments `getCursors()`, `getLastCursor()`, `cursorForMarker(marker)`
|
||||
* Prefix bool methods with `is` eg. `isDefault()`
|
||||
|
||||
Array accessor methods would be written as follows
|
||||
|
||||
```coffee
|
||||
getObjects()
|
||||
addObject(object)
|
||||
removeObject(object)
|
||||
removeObjectAtIndex(index)
|
||||
objectAtIndex(index)
|
||||
objectsForThing(thing)
|
||||
objectForThing(thing)
|
||||
```
|
||||
|
||||
## Events
|
||||
|
||||
There will be no `off()` method on objects. `on()` will return a subscription object which contains the `off()` method.
|
||||
|
||||
* Events should be emitted with one event object as an argument, rather than a bunch of arguments.
|
||||
* If an event is cancelable, it will provide a `cancel` function in the event object.
|
||||
|
||||
### Naming
|
||||
|
||||
Past tense. ???
|
||||
|
||||
## Documentation
|
||||
|
||||
Comment doc strings on methods, events, etc
|
||||
|
||||
* how to doc sections?
|
||||
* events?
|
||||
* props?
|
||||
* callback args?
|
||||
* option hashes?
|
||||
|
||||
### Method organization
|
||||
|
||||
* All methods in classes should be grouped into sections by usage pattern.
|
||||
* Methods within a section should be ordered with the most commonly used methods at the top. `Essential` methods always go above `Extended` methods.
|
||||
* Sections should be ordered in the class with the most commonly used sections at the top.
|
||||
|
||||
A section:
|
||||
|
||||
```coffee
|
||||
###
|
||||
Section: Reading Text
|
||||
###
|
||||
|
||||
# Essential: Returns a {String} representing the entire contents of the editor.
|
||||
getText: -> @buffer.getText()
|
||||
```
|
||||
|
||||
|
||||
[naming]:https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/CodingGuidelines/Articles/NamingMethods.html
|
||||
@@ -6,26 +6,23 @@ Ubuntu LTS 12.04 64-bit is the recommended platform.
|
||||
|
||||
* OS with 64-bit or 32-bit architecture
|
||||
* C++ toolchain
|
||||
* [Git](http://git-scm.com/)
|
||||
* [Node.js](http://nodejs.org/download/) v0.10.x
|
||||
* [npm](http://www.npmjs.org/) v1.4.x (bundled with Node.js)
|
||||
* git
|
||||
* [node.js](http://nodejs.org/download/) v0.10.x
|
||||
* [npm](http://www.npmjs.org/) v1.4.x (bundled with node.js)
|
||||
* `npm -v` to check the version.
|
||||
* `npm config set python /usr/bin/python2 -g` to ensure that gyp uses python2.
|
||||
* You might need to run this command as `sudo`, depending on how you have set up [npm](https://github.com/joyent/node/wiki/Installing-Node.js-via-package-manager#ubuntu-mint-elementary-os).
|
||||
* development headers for [GNOME Keyring](https://wiki.gnome.org/Projects/GnomeKeyring)
|
||||
* libgnome-keyring-dev
|
||||
|
||||
### Ubuntu / Debian
|
||||
|
||||
* `sudo apt-get install build-essential git libgnome-keyring-dev`
|
||||
* Instructions for [Node.js](https://github.com/joyent/node/wiki/Installing-Node.js-via-package-manager#ubuntu-mint-elementary-os).
|
||||
* Instructions for [node.js](https://github.com/joyent/node/wiki/Installing-Node.js-via-package-manager#ubuntu-mint-elementary-os).
|
||||
|
||||
### Fedora
|
||||
|
||||
* `sudo yum --assumeyes install make gcc gcc-c++ glibc-devel git-core libgnome-keyring-devel`
|
||||
* Instructions for [Node.js](https://github.com/joyent/node/wiki/Installing-Node.js-via-package-manager#fedora).
|
||||
* Instructions for [node.js](https://github.com/joyent/node/wiki/Installing-Node.js-via-package-manager#fedora).
|
||||
|
||||
### Arch
|
||||
|
||||
* `sudo pacman -S base-devel git nodejs libgnome-keyring`
|
||||
* `export PYTHON=/usr/bin/python2` before building Atom.
|
||||
|
||||
@@ -33,56 +30,26 @@ Ubuntu LTS 12.04 64-bit is the recommended platform.
|
||||
|
||||
If you have problems with permissions don't forget to prefix with `sudo`
|
||||
|
||||
1. Clone the Atom repository:
|
||||
|
||||
```sh
|
||||
git clone https://github.com/atom/atom
|
||||
cd atom
|
||||
```
|
||||
|
||||
2. Checkout the latest Atom release:
|
||||
|
||||
```sh
|
||||
git fetch
|
||||
git checkout $(git describe --tags `git rev-list --tags --max-count=1`)
|
||||
```
|
||||
|
||||
3. Build Atom:
|
||||
|
||||
```sh
|
||||
script/build
|
||||
```
|
||||
|
||||
This will create the atom application at `$TMPDIR/atom-build/Atom`.
|
||||
|
||||
4. Install the `atom` and `apm` commands to `/usr/local/bin` by executing:
|
||||
|
||||
```sh
|
||||
sudo script/grunt install
|
||||
```
|
||||
|
||||
5. *Optionally*, you may generate a `.deb` package at `$TMPDIR/atom-build`:
|
||||
|
||||
```sh
|
||||
script/grunt mkdeb
|
||||
```
|
||||
|
||||
Use the newly installed Atom by fully quitting Atom and then reopening.
|
||||
|
||||
## Advanced Options
|
||||
|
||||
### Custom install directory
|
||||
Create the atom application at `$TMPDIR/atom-build/Atom`:
|
||||
|
||||
```sh
|
||||
sudo script/grunt install --install-dir /install/atom/here
|
||||
script/build
|
||||
```
|
||||
|
||||
### Custom build directory
|
||||
Install the `atom` and `apm` commands to `/usr/local/bin`:
|
||||
|
||||
```sh
|
||||
script/build --build-dir /build/atom/here
|
||||
sudo script/grunt install
|
||||
```
|
||||
|
||||
Generate a `.deb` package at `$TMPDIR/atom-build`: (*optional*)
|
||||
|
||||
```sh
|
||||
script/grunt mkdeb
|
||||
```
|
||||
|
||||
Use the newly installed atom by restarting any running atom instances.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Exception: "TypeError: Unable to watch path"
|
||||
@@ -103,7 +70,7 @@ this is the reason for this error you can issue
|
||||
and restart Atom. If Atom now works fine, you can make this setting permanent:
|
||||
|
||||
```sh
|
||||
echo 32768 | sudo tee -a /proc/sys/fs/inotify/max_user_watches
|
||||
echo 32768 > /proc/sys/fs/inotify/max_user_watches
|
||||
```
|
||||
|
||||
See also https://github.com/atom/atom/issues/2082.
|
||||
@@ -111,19 +78,10 @@ See also https://github.com/atom/atom/issues/2082.
|
||||
### /usr/bin/env: node: No such file or directory
|
||||
|
||||
If you get this notice when attempting to `script/build`, you either do not
|
||||
have Node.js installed, or node isn't identified as Node.js on your machine.
|
||||
have nodejs installed, or node isn't identified as nodejs on your machine.
|
||||
If it's the latter, entering `sudo ln -s /usr/bin/nodejs /usr/bin/node` into
|
||||
your terminal may fix the issue.
|
||||
|
||||
#### You can also use Alternatives
|
||||
|
||||
On some variants (mostly Debian based distros) it's preferable for you to use
|
||||
Alternatives so that changes to the binary paths can be fixed or altered easily:
|
||||
|
||||
```sh
|
||||
sudo update-alternatives --install /usr/bin/node node /usr/bin/nodejs 1 --slave /usr/bin/js js /usr/bin/nodejs
|
||||
```
|
||||
|
||||
### Linux build error reports in atom/atom
|
||||
* Use [this search](https://github.com/atom/atom/search?q=label%3Abuild-error+label%3Alinux&type=Issues)
|
||||
to get a list of reports about build errors on Linux.
|
||||
|
||||
@@ -66,19 +66,6 @@ If none of this works, do install Github for Windows and use its Git shell. Make
|
||||
* https://github.com/TooTallNate/node-gyp/issues/297
|
||||
* https://code.google.com/p/gyp/issues/detail?id=393
|
||||
|
||||
* `script/build` stops at installing runas with 'Failed at the runas@0.5.4 install script.'
|
||||
|
||||
See the next item.
|
||||
|
||||
* `error MSB8020: The build tools for Visual Studio 2010 (Platform Toolset = 'v100') cannot be found.`
|
||||
|
||||
* If you're building atom with Visual Studio 2013 try executing the following
|
||||
command in your Git shell and then re-run `script/build`:
|
||||
|
||||
```
|
||||
$env:GYP_MSVS_VERSION=2013
|
||||
```
|
||||
|
||||
* Other `node-gyp` errors on first build attempt, even though the right node and python versions are installed.
|
||||
* Do try the build command one more time, as experience shows it often works on second try in many of these cases.
|
||||
|
||||
|
||||
@@ -53,7 +53,7 @@ module.exports =
|
||||
|
||||
convert: ->
|
||||
# This assumes the active pane item is an editor
|
||||
editor = atom.workspace.getActivePaneItem()
|
||||
editor = atom.workspace.activePaneItem
|
||||
editor.insertText('Hello, World!')
|
||||
```
|
||||
|
||||
@@ -131,8 +131,8 @@ inserting 'Hello, World!' convert the selected text to ASCII art.
|
||||
```coffeescript
|
||||
convert: ->
|
||||
# This assumes the active pane item is an editor
|
||||
editor = atom.workspace.getActivePaneItem()
|
||||
selection = editor.getLastSelection()
|
||||
editor = atom.workspace.activePaneItem
|
||||
selection = editor.getSelection()
|
||||
|
||||
figlet = require 'figlet'
|
||||
figlet selection.getText(), {font: "Larry 3D 2"}, (error, asciiArt) ->
|
||||
|
||||
@@ -11,4 +11,4 @@
|
||||
# atom.workspaceView.eachEditorView (editorView) ->
|
||||
# editor = editorView.getEditor()
|
||||
# if path.extname(editor.getPath()) is '.md'
|
||||
# editor.setSoftWrapped(true)
|
||||
# editor.setSoftWrap(true)
|
||||
|
||||
@@ -13,9 +13,6 @@
|
||||
# 'enter': 'editor:newline'
|
||||
#
|
||||
# '.workspace':
|
||||
# 'ctrl-shift-p': 'core:move-up'
|
||||
# 'ctrl-P': 'core:move-up'
|
||||
# 'ctrl-p': 'core:move-down'
|
||||
#
|
||||
# You can find more information about keymaps in these guides:
|
||||
# * https://atom.io/docs/latest/customizing-atom#customizing-key-bindings
|
||||
# * https://atom.io/docs/latest/advanced/keymaps
|
||||
|
||||
@@ -1,17 +1,12 @@
|
||||
{Point, Range} = require 'text-buffer'
|
||||
{deprecate} = require 'grim'
|
||||
|
||||
module.exports =
|
||||
BufferedNodeProcess: require '../src/buffered-node-process'
|
||||
BufferedProcess: require '../src/buffered-process'
|
||||
GitRepository: require '../src/git-repository'
|
||||
Git: require '../src/git'
|
||||
Point: Point
|
||||
Range: Range
|
||||
|
||||
Object.defineProperty module.exports, 'Git', get: ->
|
||||
deprecate "Please require `GitRepository` instead of `Git`: `{GitRepository} = require 'atom'`"
|
||||
module.exports.GitRepository
|
||||
|
||||
# The following classes can't be used from a Task handler and should therefore
|
||||
# only be exported when not running as a child node process
|
||||
unless process.env.ATOM_SHELL_INTERNAL_RUN_AS_NODE
|
||||
@@ -20,7 +15,10 @@ unless process.env.ATOM_SHELL_INTERNAL_RUN_AS_NODE
|
||||
module.exports.$ = $
|
||||
module.exports.$$ = $$
|
||||
module.exports.$$$ = $$$
|
||||
module.exports.EditorView = require '../src/editor-view'
|
||||
if atom.config.get('core.useReactMiniEditors')
|
||||
module.exports.EditorView = require '../src/react-editor-view'
|
||||
else
|
||||
module.exports.EditorView = require '../src/editor-view'
|
||||
module.exports.ScrollView = require '../src/scroll-view'
|
||||
module.exports.SelectListView = require '../src/select-list-view'
|
||||
module.exports.Task = require '../src/task'
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
# Sublime Parity
|
||||
'tab': 'editor:indent'
|
||||
'enter': 'editor:newline'
|
||||
'num-enter': 'editor:newline'
|
||||
'shift-tab': 'editor:outdent-selected-rows'
|
||||
'ctrl-K': 'editor:delete-line'
|
||||
|
||||
@@ -26,6 +27,7 @@
|
||||
'tab': 'core:focus-next'
|
||||
'shift-tab': 'core:focus-previous'
|
||||
'enter': 'native!'
|
||||
'num-enter': 'native!'
|
||||
'backspace': 'native!'
|
||||
'shift-backspace': 'native!'
|
||||
'delete': 'native!'
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
'cmd-O': 'application:open-dev'
|
||||
'cmd-alt-ctrl-s': 'application:run-all-specs'
|
||||
'enter': 'core:confirm'
|
||||
'num-enter': 'core:confirm'
|
||||
'escape': 'core:cancel'
|
||||
'up': 'core:move-up'
|
||||
'down': 'core:move-down'
|
||||
@@ -119,7 +120,7 @@
|
||||
'cmd-shift-right': 'editor:select-to-end-of-line'
|
||||
'alt-backspace': 'editor:delete-to-beginning-of-word'
|
||||
'alt-delete': 'editor:delete-to-end-of-word'
|
||||
'ctrl-a': 'editor:move-to-first-character-of-line'
|
||||
'ctrl-a': 'editor:move-to-beginning-of-line'
|
||||
'ctrl-e': 'editor:move-to-end-of-line'
|
||||
'ctrl-k': 'editor:cut-to-end-of-line'
|
||||
|
||||
@@ -143,8 +144,6 @@
|
||||
# Sublime Parity
|
||||
'cmd-enter': 'editor:newline-below'
|
||||
'cmd-shift-enter': 'editor:newline-above'
|
||||
'alt-enter': 'editor:newline'
|
||||
'shift-enter': 'editor:newline'
|
||||
'cmd-]': 'editor:indent-selected-rows'
|
||||
'cmd-[': 'editor:outdent-selected-rows'
|
||||
'ctrl-cmd-up': 'editor:move-line-up'
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
'body':
|
||||
# Atom Specific
|
||||
'enter': 'core:confirm'
|
||||
'num-enter': 'core:confirm'
|
||||
'escape': 'core:cancel'
|
||||
'up': 'core:move-up'
|
||||
'down': 'core:move-down'
|
||||
@@ -10,12 +11,10 @@
|
||||
'ctrl-shift-i': 'window:toggle-dev-tools'
|
||||
'ctrl-alt-p': 'window:run-package-specs'
|
||||
'ctrl-alt-s': 'application:run-all-specs'
|
||||
'ctrl-alt-o': 'application:open-dev'
|
||||
'ctrl-shift-o': 'application:open-folder'
|
||||
'ctrl-shift-o': 'application:open-dev'
|
||||
'F11': 'window:toggle-full-screen'
|
||||
|
||||
# Sublime Parity
|
||||
'ctrl-,': 'application:show-settings'
|
||||
'ctrl-N': 'application:new-window'
|
||||
'ctrl-W': 'window:close'
|
||||
'ctrl-o': 'application:open-file'
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
# Atom Specific
|
||||
'enter': 'core:confirm'
|
||||
'num-enter': 'core:confirm'
|
||||
'escape': 'core:cancel'
|
||||
'up': 'core:move-up'
|
||||
'down': 'core:move-down'
|
||||
@@ -25,8 +26,8 @@
|
||||
'ctrl-n': 'application:new-file'
|
||||
'ctrl-s': 'core:save'
|
||||
'ctrl-S': 'core:save-as'
|
||||
'ctrl-f4': 'core:close'
|
||||
'ctrl-w': 'core:close'
|
||||
'ctrl-f4': 'core:close'
|
||||
'ctrl-z': 'core:undo'
|
||||
'ctrl-shift-z': 'core:redo'
|
||||
'ctrl-y': 'core:redo'
|
||||
|
||||
@@ -18,8 +18,6 @@
|
||||
{ type: 'separator' }
|
||||
{ label: 'Install Shell Commands', command: 'window:install-shell-commands' }
|
||||
{ type: 'separator' }
|
||||
{ label: 'Services', submenu: [] }
|
||||
{ type: 'separator' }
|
||||
{ label: 'Hide Atom', command: 'application:hide' }
|
||||
{ label: 'Hide Others', command: 'application:hide-other-applications' }
|
||||
{ label: 'Show All', command: 'application:unhide-all-applications' }
|
||||
@@ -194,13 +192,3 @@
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
'context-menu':
|
||||
'.overlayer':
|
||||
'Undo': 'core:undo'
|
||||
'Redo': 'core:redo'
|
||||
'Cut': 'core:cut'
|
||||
'Copy': 'core:copy'
|
||||
'Paste': 'core:paste'
|
||||
'Delete': 'core:delete'
|
||||
'Select All': 'core:select-all'
|
||||
|
||||
+12
-18
@@ -8,6 +8,8 @@
|
||||
{ label: 'Open Folder...', command: 'application:open-folder' }
|
||||
{ label: 'Reopen Last &Item', command: 'pane:reopen-closed-item' }
|
||||
{ type: 'separator' }
|
||||
{ label: '&Preferences...', command: 'application:show-settings' }
|
||||
{ type: 'separator' }
|
||||
{ label: '&Save', command: 'core:save' }
|
||||
{ label: 'Save &As...', command: 'core:save-as' }
|
||||
{ label: 'Save A&ll', command: 'window:save-all' }
|
||||
@@ -78,14 +80,6 @@
|
||||
{ label: 'Fold Level 9', command: 'editor:fold-at-indent-level-9' }
|
||||
]
|
||||
}
|
||||
{ type: 'separator' }
|
||||
{ label: '&Preferences', command: 'application:show-settings' }
|
||||
{ label: 'Open Your Config', command: 'application:open-your-config' }
|
||||
{ label: 'Open Your Init Script', command: 'application:open-your-init-script' }
|
||||
{ label: 'Open Your Keymap', command: 'application:open-your-keymap' }
|
||||
{ label: 'Open Your Snippets', command: 'application:open-your-snippets' }
|
||||
{ label: 'Open Your Stylesheet', command: 'application:open-your-stylesheet' }
|
||||
{ type: 'separator' }
|
||||
]
|
||||
}
|
||||
|
||||
@@ -139,6 +133,16 @@
|
||||
submenu: []
|
||||
}
|
||||
|
||||
{
|
||||
label: '&Window'
|
||||
submenu: [
|
||||
{ label: 'Mi&nimize', command: 'application:minimize' }
|
||||
{ label: 'Ma&ximize', command: 'application:zoom' }
|
||||
{ type: 'separator' }
|
||||
{ label: 'Bring &All to Front', command: 'application:bring-all-windows-to-front' }
|
||||
]
|
||||
}
|
||||
|
||||
{
|
||||
label: '&Help'
|
||||
submenu: [
|
||||
@@ -151,13 +155,3 @@
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
'context-menu':
|
||||
'.overlayer':
|
||||
'Undo': 'core:undo'
|
||||
'Redo': 'core:redo'
|
||||
'Cut': 'core:cut'
|
||||
'Copy': 'core:copy'
|
||||
'Paste': 'core:paste'
|
||||
'Delete': 'core:delete'
|
||||
'Select All': 'core:select-all'
|
||||
|
||||
+10
-10
@@ -157,6 +157,16 @@
|
||||
submenu: []
|
||||
}
|
||||
|
||||
{
|
||||
label: '&Window'
|
||||
submenu: [
|
||||
{ label: 'Mi&nimize', command: 'application:minimize' }
|
||||
{ label: 'Ma&ximize', command: 'application:zoom' }
|
||||
{ type: 'separator' }
|
||||
{ label: 'Bring &All to Front', command: 'application:bring-all-windows-to-front' }
|
||||
]
|
||||
}
|
||||
|
||||
{
|
||||
label: '&Help'
|
||||
submenu: [
|
||||
@@ -169,13 +179,3 @@
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
'context-menu':
|
||||
'.overlayer':
|
||||
'Undo': 'core:undo'
|
||||
'Redo': 'core:redo'
|
||||
'Cut': 'core:cut'
|
||||
'Copy': 'core:copy'
|
||||
'Paste': 'core:paste'
|
||||
'Delete': 'core:delete'
|
||||
'Select All': 'core:select-all'
|
||||
|
||||
+69
-70
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "atom",
|
||||
"productName": "Atom",
|
||||
"version": "0.130.0",
|
||||
"version": "0.121.0",
|
||||
"description": "A hackable text editor for the 21st Century.",
|
||||
"main": "./src/browser/main.js",
|
||||
"repository": {
|
||||
@@ -17,129 +17,128 @@
|
||||
"url": "http://github.com/atom/atom/raw/master/LICENSE.md"
|
||||
}
|
||||
],
|
||||
"atomShellVersion": "0.16.2",
|
||||
"atomShellVersion": "0.15.0",
|
||||
"dependencies": {
|
||||
"async": "0.2.6",
|
||||
"atom-keymap": "^2.1.1",
|
||||
"atom-keymap": "^1.0.0",
|
||||
"bootstrap": "git+https://github.com/atom/bootstrap.git#6af81906189f1747fd6c93479e3d998ebe041372",
|
||||
"clear-cut": "0.4.0",
|
||||
"coffee-script": "1.7.0",
|
||||
"coffeestack": "0.7.0",
|
||||
"delegato": "^1",
|
||||
"emissary": "^1.3.1",
|
||||
"event-kit": "0.7.2",
|
||||
"first-mate": "^2.1.2",
|
||||
"emissary": "^1.2.1",
|
||||
"first-mate": "^2.0.1",
|
||||
"fs-plus": "^2.2.6",
|
||||
"fstream": "0.1.24",
|
||||
"fuzzaldrin": "^2.1",
|
||||
"git-utils": "^2.1.4",
|
||||
"grim": "0.12.0",
|
||||
"fuzzaldrin": "^1.1",
|
||||
"git-utils": "^2.1.3",
|
||||
"grim": "0.11.0",
|
||||
"guid": "0.0.10",
|
||||
"jasmine-tagged": "^1.1.2",
|
||||
"less-cache": "0.15.0",
|
||||
"less-cache": "0.13.0",
|
||||
"mixto": "^1",
|
||||
"mkdirp": "0.3.5",
|
||||
"nslog": "^1.0.1",
|
||||
"oniguruma": "^3.0.4",
|
||||
"oniguruma": "^3.0.3",
|
||||
"optimist": "0.4.0",
|
||||
"pathwatcher": "^2.1.3",
|
||||
"pathwatcher": "^2.0.6",
|
||||
"property-accessors": "^1",
|
||||
"q": "^1.0.1",
|
||||
"random-words": "0.0.1",
|
||||
"react-atom-fork": "^0.11.1",
|
||||
"reactionary-atom-fork": "^1.0.0",
|
||||
"runas": "1.0.1",
|
||||
"scandal": "1.0.2",
|
||||
"scandal": "1.0.0",
|
||||
"scoped-property-store": "^0.9.0",
|
||||
"scrollbar-style": "^1.0.2",
|
||||
"scrollbar-style": "^1.0.1",
|
||||
"season": "^1.0.2",
|
||||
"semver": "1.1.4",
|
||||
"serializable": "^1",
|
||||
"space-pen": "3.4.7",
|
||||
"space-pen": "3.2.0",
|
||||
"temp": "0.7.0",
|
||||
"text-buffer": "^3.2.6",
|
||||
"theorist": "^1.0.2",
|
||||
"text-buffer": "^3.0.0",
|
||||
"theorist": "^1",
|
||||
"underscore-plus": "^1.5.1",
|
||||
"vm-compatibility-layer": "0.1.0"
|
||||
},
|
||||
"packageDependencies": {
|
||||
"atom-dark-syntax": "0.19.0",
|
||||
"atom-dark-ui": "0.35.0",
|
||||
"atom-dark-ui": "0.33.0",
|
||||
"atom-light-syntax": "0.20.0",
|
||||
"atom-light-ui": "0.30.0",
|
||||
"base16-tomorrow-dark-theme": "0.21.0",
|
||||
"atom-light-ui": "0.29.0",
|
||||
"base16-tomorrow-dark-theme": "0.20.0",
|
||||
"base16-tomorrow-light-theme": "0.4.0",
|
||||
"solarized-dark-syntax": "0.22.0",
|
||||
"solarized-light-syntax": "0.12.0",
|
||||
"archive-view": "0.37.0",
|
||||
"autocomplete": "0.32.0",
|
||||
"autoflow": "0.18.0",
|
||||
"autosave": "0.17.0",
|
||||
"background-tips": "0.16.0",
|
||||
"bookmarks": "0.28.0",
|
||||
"bracket-matcher": "0.58.0",
|
||||
"command-palette": "0.25.0",
|
||||
"deprecation-cop": "0.10.0",
|
||||
"dev-live-reload": "0.34.0",
|
||||
"exception-reporting": "0.20.0",
|
||||
"archive-view": "0.35.0",
|
||||
"autocomplete": "0.29.0",
|
||||
"autoflow": "0.17.0",
|
||||
"autosave": "0.14.0",
|
||||
"background-tips": "0.15.0",
|
||||
"bookmarks": "0.27.0",
|
||||
"bracket-matcher": "0.51.0",
|
||||
"command-palette": "0.24.0",
|
||||
"deprecation-cop": "0.7.0",
|
||||
"dev-live-reload": "0.33.0",
|
||||
"exception-reporting": "0.19.0",
|
||||
"feedback": "0.33.0",
|
||||
"find-and-replace": "0.138.0",
|
||||
"fuzzy-finder": "0.58.0",
|
||||
"git-diff": "0.39.0",
|
||||
"go-to-line": "0.25.0",
|
||||
"grammar-selector": "0.34.0",
|
||||
"find-and-replace": "0.127.0",
|
||||
"fuzzy-finder": "0.57.0",
|
||||
"git-diff": "0.37.0",
|
||||
"go-to-line": "0.23.0",
|
||||
"grammar-selector": "0.27.0",
|
||||
"image-view": "0.36.0",
|
||||
"incompatible-packages": "0.9.0",
|
||||
"keybinding-resolver": "0.20.0",
|
||||
"incompatible-packages": "0.3.0",
|
||||
"keybinding-resolver": "0.18.0",
|
||||
"link": "0.25.0",
|
||||
"markdown-preview": "0.103.0",
|
||||
"metrics": "0.34.0",
|
||||
"open-on-github": "0.30.0",
|
||||
"markdown-preview": "0.94.0",
|
||||
"metrics": "0.33.0",
|
||||
"open-on-github": "0.29.0",
|
||||
"package-generator": "0.31.0",
|
||||
"release-notes": "0.36.0",
|
||||
"settings-view": "0.145.0",
|
||||
"snippets": "0.52.0",
|
||||
"spell-check": "0.42.0",
|
||||
"status-bar": "0.45.0",
|
||||
"styleguide": "0.30.0",
|
||||
"symbols-view": "0.66.0",
|
||||
"tabs": "0.52.0",
|
||||
"settings-view": "0.137.0",
|
||||
"snippets": "0.50.0",
|
||||
"spell-check": "0.40.0",
|
||||
"status-bar": "0.41.0",
|
||||
"styleguide": "0.29.0",
|
||||
"symbols-view": "0.63.0",
|
||||
"tabs": "0.48.0",
|
||||
"timecop": "0.22.0",
|
||||
"tree-view": "0.126.0",
|
||||
"tree-view": "0.112.0",
|
||||
"update-package-dependencies": "0.6.0",
|
||||
"welcome": "0.18.0",
|
||||
"welcome": "0.17.0",
|
||||
"whitespace": "0.25.0",
|
||||
"wrap-guide": "0.22.0",
|
||||
"language-c": "0.28.0",
|
||||
"language-coffee-script": "0.34.0",
|
||||
"wrap-guide": "0.21.0",
|
||||
|
||||
"language-c": "0.26.0",
|
||||
"language-coffee-script": "0.28.0",
|
||||
"language-css": "0.17.0",
|
||||
"language-gfm": "0.50.0",
|
||||
"language-gfm": "0.44.0",
|
||||
"language-git": "0.9.0",
|
||||
"language-go": "0.17.0",
|
||||
"language-html": "0.26.0",
|
||||
"language-hyperlink": "0.12.0",
|
||||
"language-go": "0.16.0",
|
||||
"language-html": "0.22.0",
|
||||
"language-hyperlink": "0.10.0",
|
||||
"language-java": "0.11.0",
|
||||
"language-javascript": "0.40.0",
|
||||
"language-javascript": "0.39.0",
|
||||
"language-json": "0.8.0",
|
||||
"language-less": "0.15.0",
|
||||
"language-make": "0.12.0",
|
||||
"language-mustache": "0.10.0",
|
||||
"language-less": "0.13.0",
|
||||
"language-make": "0.10.0",
|
||||
"language-objective-c": "0.11.0",
|
||||
"language-perl": "0.9.0",
|
||||
"language-php": "0.16.0",
|
||||
"language-php": "0.15.0",
|
||||
"language-property-list": "0.7.0",
|
||||
"language-python": "0.19.0",
|
||||
"language-ruby": "0.38.0",
|
||||
"language-ruby-on-rails": "0.18.0",
|
||||
"language-sass": "0.21.0",
|
||||
"language-python": "0.18.0",
|
||||
"language-ruby": "0.33.0",
|
||||
"language-ruby-on-rails": "0.15.0",
|
||||
"language-sass": "0.14.0",
|
||||
"language-shellscript": "0.8.0",
|
||||
"language-source": "0.8.0",
|
||||
"language-sql": "0.11.0",
|
||||
"language-sql": "0.9.0",
|
||||
"language-text": "0.6.0",
|
||||
"language-todo": "0.11.0",
|
||||
"language-todo": "0.10.0",
|
||||
"language-toml": "0.12.0",
|
||||
"language-xml": "0.21.0",
|
||||
"language-yaml": "0.17.0"
|
||||
"language-xml": "0.17.0",
|
||||
"language-yaml": "0.13.0"
|
||||
},
|
||||
"private": true,
|
||||
"scripts": {
|
||||
|
||||
@@ -5,5 +5,4 @@ Exec=<%= installDir %>/share/atom/atom %U
|
||||
Icon=<%= iconName %>
|
||||
Type=Application
|
||||
StartupNotify=true
|
||||
Categories=GNOME;GTK;Utility;TextEditor;Development;
|
||||
MimeType=text/plain;
|
||||
Categories=GNOME;GTK;Utility;TextEditor;
|
||||
|
||||
@@ -3,6 +3,6 @@ Version: <%= version %>
|
||||
Section: <%= section %>
|
||||
Priority: optional
|
||||
Architecture: <%= arch %>
|
||||
Installed-Size: <%= installedSize %>
|
||||
Installed-Size: `du -ks usr|cut -f 1`
|
||||
Maintainer: <%= maintainer %>
|
||||
Description: <%= description %>
|
||||
|
||||
Arquivo binário não exibido.
Arquivo binário não exibido.
+4
-1
@@ -5,6 +5,9 @@ var path = require('path');
|
||||
|
||||
process.chdir(path.dirname(__dirname));
|
||||
|
||||
if (process.platform == 'linux')
|
||||
throw new Error('cibuild can not run on linux yet!');
|
||||
|
||||
var homeDir = process.platform == 'win32' ? process.env.USERPROFILE : process.env.HOME;
|
||||
|
||||
function loadEnvironmentVariables(filePath) {
|
||||
@@ -24,7 +27,7 @@ function loadEnvironmentVariables(filePath) {
|
||||
function readEnvironmentVariables() {
|
||||
if (process.platform === 'win32')
|
||||
loadEnvironmentVariables(path.resolve('/jenkins/config/atomcredentials'));
|
||||
else if (process.platform === 'darwin') {
|
||||
else {
|
||||
loadEnvironmentVariables('/var/lib/jenkins/config/atomcredentials');
|
||||
loadEnvironmentVariables('/var/lib/jenkins/config/xcodekeychain');
|
||||
}
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
export ATOM_ACCESS_TOKEN=$BUILD_ATOM_LINUX_ACCESS_TOKEN
|
||||
|
||||
if [ -d /usr/local/share/nodenv ]; then
|
||||
export NODENV_ROOT=/usr/local/share/nodenv
|
||||
export PATH=/usr/local/share/nodenv/bin:/usr/local/share/nodenv/shims:$PATH
|
||||
export NODENV_VERSION="v0.10.21"
|
||||
fi
|
||||
|
||||
script/cibuild
|
||||
+4
-5
@@ -13,22 +13,21 @@ CONTROL_FILE="$3"
|
||||
DESKTOP_FILE="$4"
|
||||
ICON_FILE="$5"
|
||||
DEB_PATH="$6"
|
||||
FILE_MODE=755
|
||||
|
||||
TARGET_ROOT="`mktemp -d`"
|
||||
chmod 755 "$TARGET_ROOT"
|
||||
TARGET="$TARGET_ROOT/atom-$VERSION-$ARCH"
|
||||
|
||||
mkdir -m $FILE_MODE -p "$TARGET/usr"
|
||||
mkdir -p "$TARGET/usr"
|
||||
env INSTALL_PREFIX="$TARGET/usr" script/grunt install
|
||||
|
||||
mkdir -m $FILE_MODE -p "$TARGET/DEBIAN"
|
||||
mkdir -p "$TARGET/DEBIAN"
|
||||
cp "$CONTROL_FILE" "$TARGET/DEBIAN/control"
|
||||
|
||||
mkdir -m $FILE_MODE -p "$TARGET/usr/share/applications"
|
||||
mkdir -p "$TARGET/usr/share/applications"
|
||||
cp "$DESKTOP_FILE" "$TARGET/usr/share/applications"
|
||||
|
||||
mkdir -m $FILE_MODE -p "$TARGET/usr/share/pixmaps"
|
||||
mkdir -p "$TARGET/usr/share/pixmaps"
|
||||
cp "$ICON_FILE" "$TARGET/usr/share/pixmaps"
|
||||
|
||||
dpkg-deb -b "$TARGET"
|
||||
|
||||
@@ -31,7 +31,7 @@ function verifyNode(cb) {
|
||||
var nodeMajorVersion = +versionArray[0];
|
||||
var nodeMinorVersion = +versionArray[1];
|
||||
if (nodeMajorVersion === 0 && nodeMinorVersion < 10) {
|
||||
error = "node v0.10 is required to build Atom, node " + nodeVersion + " is installed.";
|
||||
error = "node v0.10 is required to build Atom.";
|
||||
cb(error);
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -119,14 +119,14 @@ class AtomReporter extends View
|
||||
grim.clearDeprecations()
|
||||
|
||||
handleEvents: ->
|
||||
$(document).on "click", ".spec-toggle", ({currentTarget}) ->
|
||||
$(document).on "click", ".spec-toggle", ({currentTarget}) =>
|
||||
element = $(currentTarget)
|
||||
specFailures = element.parent().find('.spec-failures')
|
||||
specFailures.toggle()
|
||||
element.toggleClass('folded')
|
||||
false
|
||||
|
||||
$(document).on "click", ".deprecation-toggle", ({currentTarget}) ->
|
||||
$(document).on "click", ".deprecation-toggle", ({currentTarget}) =>
|
||||
element = $(currentTarget)
|
||||
deprecationList = $(document).find('.deprecation-list')
|
||||
deprecationList.toggle()
|
||||
|
||||
+16
-48
@@ -8,23 +8,6 @@ describe "the `atom` global", ->
|
||||
beforeEach ->
|
||||
atom.workspaceView = new WorkspaceView
|
||||
|
||||
describe 'window sizing methods', ->
|
||||
describe '::getPosition and ::setPosition', ->
|
||||
it 'sets the position of the window, and can retrieve the position just set', ->
|
||||
atom.setPosition(22, 45)
|
||||
expect(atom.getPosition()).toEqual x: 22, y: 45
|
||||
|
||||
describe '::getSize and ::setSize', ->
|
||||
originalSize = null
|
||||
beforeEach ->
|
||||
originalSize = atom.getSize()
|
||||
afterEach ->
|
||||
atom.setSize(originalSize.width, originalSize.height)
|
||||
|
||||
it 'sets the size of the window, and can retrieve the size just set', ->
|
||||
atom.setSize(100, 400)
|
||||
expect(atom.getSize()).toEqual width: 100, height: 400
|
||||
|
||||
describe "package lifecycle methods", ->
|
||||
describe ".loadPackage(name)", ->
|
||||
it "continues if the package has an invalid package.json", ->
|
||||
@@ -258,15 +241,15 @@ describe "the `atom` global", ->
|
||||
two = atom.themes.stringToId(two)
|
||||
three = atom.themes.stringToId(three)
|
||||
|
||||
expect(atom.themes.stylesheetElementForId(one)).toBeNull()
|
||||
expect(atom.themes.stylesheetElementForId(two)).toBeNull()
|
||||
expect(atom.themes.stylesheetElementForId(three)).toBeNull()
|
||||
expect(atom.themes.stylesheetElementForId(one)).not.toExist()
|
||||
expect(atom.themes.stylesheetElementForId(two)).not.toExist()
|
||||
expect(atom.themes.stylesheetElementForId(three)).not.toExist()
|
||||
|
||||
atom.packages.activatePackage("package-with-stylesheets-manifest")
|
||||
|
||||
expect(atom.themes.stylesheetElementForId(one)).not.toBeNull()
|
||||
expect(atom.themes.stylesheetElementForId(two)).not.toBeNull()
|
||||
expect(atom.themes.stylesheetElementForId(three)).toBeNull()
|
||||
expect(atom.themes.stylesheetElementForId(one)).toExist()
|
||||
expect(atom.themes.stylesheetElementForId(two)).toExist()
|
||||
expect(atom.themes.stylesheetElementForId(three)).not.toExist()
|
||||
expect($('#jasmine-content').css('font-size')).toBe '1px'
|
||||
|
||||
describe "when the metadata does not contain a 'stylesheets' manifest", ->
|
||||
@@ -280,14 +263,14 @@ describe "the `atom` global", ->
|
||||
two = atom.themes.stringToId(two)
|
||||
three = atom.themes.stringToId(three)
|
||||
|
||||
expect(atom.themes.stylesheetElementForId(one)).toBeNull()
|
||||
expect(atom.themes.stylesheetElementForId(two)).toBeNull()
|
||||
expect(atom.themes.stylesheetElementForId(three)).toBeNull()
|
||||
expect(atom.themes.stylesheetElementForId(one)).not.toExist()
|
||||
expect(atom.themes.stylesheetElementForId(two)).not.toExist()
|
||||
expect(atom.themes.stylesheetElementForId(three)).not.toExist()
|
||||
|
||||
atom.packages.activatePackage("package-with-stylesheets")
|
||||
expect(atom.themes.stylesheetElementForId(one)).not.toBeNull()
|
||||
expect(atom.themes.stylesheetElementForId(two)).not.toBeNull()
|
||||
expect(atom.themes.stylesheetElementForId(three)).not.toBeNull()
|
||||
expect(atom.themes.stylesheetElementForId(one)).toExist()
|
||||
expect(atom.themes.stylesheetElementForId(two)).toExist()
|
||||
expect(atom.themes.stylesheetElementForId(three)).toExist()
|
||||
expect($('#jasmine-content').css('font-size')).toBe '3px'
|
||||
|
||||
describe "grammar loading", ->
|
||||
@@ -367,7 +350,7 @@ describe "the `atom` global", ->
|
||||
atom.packages.deactivatePackage("package-that-throws-on-activate")
|
||||
expect(badPack.mainModule.serialize).not.toHaveBeenCalled()
|
||||
|
||||
it "absorbs exceptions that are thrown by the package module's serialize method", ->
|
||||
it "absorbs exceptions that are thrown by the package module's serialize methods", ->
|
||||
spyOn(console, 'error')
|
||||
|
||||
waitsForPromise ->
|
||||
@@ -382,16 +365,6 @@ describe "the `atom` global", ->
|
||||
expect(atom.packages.packageStates['package-with-serialization']).toEqual someNumber: 1
|
||||
expect(console.error).toHaveBeenCalled()
|
||||
|
||||
it "absorbs exceptions that are thrown by the package module's deactivate method", ->
|
||||
spyOn(console, 'error')
|
||||
|
||||
waitsForPromise ->
|
||||
atom.packages.activatePackage("package-that-throws-on-deactivate")
|
||||
|
||||
runs ->
|
||||
expect(-> atom.packages.deactivatePackage("package-that-throws-on-deactivate")).not.toThrow()
|
||||
expect(console.error).toHaveBeenCalled()
|
||||
|
||||
it "removes the package's grammars", ->
|
||||
waitsForPromise ->
|
||||
atom.packages.activatePackage('package-with-grammars')
|
||||
@@ -520,14 +493,13 @@ describe "the `atom` global", ->
|
||||
expect(atom.config.get('core.disabledPackages')).toContain packageName
|
||||
|
||||
describe "with themes", ->
|
||||
reloadedHandler = null
|
||||
|
||||
beforeEach ->
|
||||
waitsForPromise ->
|
||||
atom.themes.activateThemes()
|
||||
|
||||
afterEach ->
|
||||
atom.themes.deactivateThemes()
|
||||
atom.config.unobserve('core.themes')
|
||||
|
||||
it ".enablePackage() and .disablePackage() enables and disables a theme", ->
|
||||
packageName = 'theme-with-package-file'
|
||||
@@ -545,17 +517,13 @@ describe "the `atom` global", ->
|
||||
expect(atom.config.get('core.themes')).toContain packageName
|
||||
expect(atom.config.get('core.disabledPackages')).not.toContain packageName
|
||||
|
||||
reloadedHandler = jasmine.createSpy('reloadedHandler')
|
||||
reloadedHandler.reset()
|
||||
atom.themes.onDidReloadAll reloadedHandler
|
||||
|
||||
# disabling of theme
|
||||
pack = atom.packages.disablePackage(packageName)
|
||||
|
||||
waitsFor ->
|
||||
reloadedHandler.callCount is 1
|
||||
not (pack in atom.packages.getActivePackages())
|
||||
|
||||
runs ->
|
||||
expect(atom.packages.getActivePackages()).not.toContain pack
|
||||
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
|
||||
|
||||
@@ -1,126 +0,0 @@
|
||||
CommandRegistry = require '../src/command-registry'
|
||||
|
||||
describe "CommandRegistry", ->
|
||||
[registry, parent, child, grandchild] = []
|
||||
|
||||
beforeEach ->
|
||||
parent = document.createElement("div")
|
||||
child = document.createElement("div")
|
||||
grandchild = document.createElement("div")
|
||||
parent.classList.add('parent')
|
||||
child.classList.add('child')
|
||||
grandchild.classList.add('grandchild')
|
||||
child.appendChild(grandchild)
|
||||
parent.appendChild(child)
|
||||
document.querySelector('#jasmine-content').appendChild(parent)
|
||||
|
||||
registry = new CommandRegistry(parent)
|
||||
|
||||
describe "command dispatch", ->
|
||||
it "invokes callbacks with selectors matching the target", ->
|
||||
called = false
|
||||
registry.add '.grandchild', 'command', (event) ->
|
||||
expect(this).toBe grandchild
|
||||
expect(event.type).toBe 'command'
|
||||
expect(event.eventPhase).toBe Event.BUBBLING_PHASE
|
||||
expect(event.target).toBe grandchild
|
||||
expect(event.currentTarget).toBe grandchild
|
||||
called = true
|
||||
|
||||
grandchild.dispatchEvent(new CustomEvent('command', bubbles: true))
|
||||
expect(called).toBe true
|
||||
|
||||
it "invokes callbacks with selectors matching ancestors of the target", ->
|
||||
calls = []
|
||||
|
||||
registry.add '.child', 'command', (event) ->
|
||||
expect(this).toBe child
|
||||
expect(event.target).toBe grandchild
|
||||
expect(event.currentTarget).toBe child
|
||||
calls.push('child')
|
||||
|
||||
registry.add '.parent', 'command', (event) ->
|
||||
expect(this).toBe parent
|
||||
expect(event.target).toBe grandchild
|
||||
expect(event.currentTarget).toBe parent
|
||||
calls.push('parent')
|
||||
|
||||
grandchild.dispatchEvent(new CustomEvent('command', bubbles: true))
|
||||
expect(calls).toEqual ['child', 'parent']
|
||||
|
||||
it "orders multiple matching listeners for an element by selector specificity", ->
|
||||
child.classList.add('foo', 'bar')
|
||||
calls = []
|
||||
|
||||
registry.add '.foo.bar', 'command', -> calls.push('.foo.bar')
|
||||
registry.add '.foo', 'command', -> calls.push('.foo')
|
||||
registry.add '.bar', 'command', -> calls.push('.bar') # specificity ties favor commands added later, like CSS
|
||||
|
||||
grandchild.dispatchEvent(new CustomEvent('command', bubbles: true))
|
||||
expect(calls).toEqual ['.foo.bar', '.bar', '.foo']
|
||||
|
||||
it "stops bubbling through ancestors when .stopPropagation() is called on the event", ->
|
||||
calls = []
|
||||
|
||||
registry.add '.parent', 'command', -> calls.push('parent')
|
||||
registry.add '.child', 'command', -> calls.push('child-2')
|
||||
registry.add '.child', 'command', (event) -> calls.push('child-1'); event.stopPropagation()
|
||||
|
||||
grandchild.dispatchEvent(new CustomEvent('command', bubbles: true))
|
||||
expect(calls).toEqual ['child-1', 'child-2']
|
||||
|
||||
it "stops invoking callbacks when .stopImmediatePropagation() is called on the event", ->
|
||||
calls = []
|
||||
|
||||
registry.add '.parent', 'command', -> calls.push('parent')
|
||||
registry.add '.child', 'command', -> calls.push('child-2')
|
||||
registry.add '.child', 'command', (event) -> calls.push('child-1'); event.stopImmediatePropagation()
|
||||
|
||||
grandchild.dispatchEvent(new CustomEvent('command', bubbles: true))
|
||||
expect(calls).toEqual ['child-1']
|
||||
|
||||
it "allows listeners to be removed via a disposable returned by ::add", ->
|
||||
calls = []
|
||||
|
||||
disposable1 = registry.add '.parent', 'command', -> calls.push('parent')
|
||||
disposable2 = registry.add '.child', 'command', -> calls.push('child')
|
||||
|
||||
disposable1.dispose()
|
||||
grandchild.dispatchEvent(new CustomEvent('command', bubbles: true))
|
||||
expect(calls).toEqual ['child']
|
||||
|
||||
calls = []
|
||||
disposable2.dispose()
|
||||
grandchild.dispatchEvent(new CustomEvent('command', bubbles: true))
|
||||
expect(calls).toEqual []
|
||||
|
||||
it "allows multiple commands to be registered under one selector when called with an object", ->
|
||||
calls = []
|
||||
|
||||
disposable = registry.add '.child',
|
||||
'command-1': -> calls.push('command-1')
|
||||
'command-2': -> calls.push('command-2')
|
||||
|
||||
grandchild.dispatchEvent(new CustomEvent('command-1', bubbles: true))
|
||||
grandchild.dispatchEvent(new CustomEvent('command-2', bubbles: true))
|
||||
|
||||
expect(calls).toEqual ['command-1', 'command-2']
|
||||
|
||||
calls = []
|
||||
disposable.dispose()
|
||||
grandchild.dispatchEvent(new CustomEvent('command-1', bubbles: true))
|
||||
grandchild.dispatchEvent(new CustomEvent('command-2', bubbles: true))
|
||||
expect(calls).toEqual []
|
||||
|
||||
describe "::findCommands({target})", ->
|
||||
it "returns commands that can be invoked on the target or its ancestors", ->
|
||||
registry.add '.parent', 'namespace:command-1', ->
|
||||
registry.add '.child', 'namespace:command-2', ->
|
||||
registry.add '.grandchild', 'namespace:command-3', ->
|
||||
registry.add '.grandchild.no-match', 'namespace:command-4', ->
|
||||
|
||||
expect(registry.findCommands(target: grandchild)[0..2]).toEqual [
|
||||
{name: 'namespace:command-3', displayName: 'Namespace: Command 3'}
|
||||
{name: 'namespace:command-2', displayName: 'Namespace: Command 2'}
|
||||
{name: 'namespace:command-1', displayName: 'Namespace: Command 1'}
|
||||
]
|
||||
@@ -23,18 +23,6 @@ describe "Config", ->
|
||||
retrievedValue.array[1].b = 2.1
|
||||
expect(atom.config.get('value')).toEqual(array: [1, b: 2, 3])
|
||||
|
||||
it "merges defaults into the returned value if both the assigned value and the default value are objects", ->
|
||||
atom.config.setDefaults("foo", a: 1, b: 2)
|
||||
atom.config.set("foo", a: 3)
|
||||
expect(atom.config.get("foo")).toEqual {a: 3, b: 2}
|
||||
|
||||
atom.config.set("foo", 7)
|
||||
expect(atom.config.get("foo")).toBe 7
|
||||
|
||||
atom.config.set("bar.baz", a: 3)
|
||||
atom.config.setDefaults("bar", baz: 7)
|
||||
expect(atom.config.get("bar.baz")).toEqual {a: 3}
|
||||
|
||||
describe ".set(keyPath, value)", ->
|
||||
it "allows a key path's value to be written", ->
|
||||
expect(atom.config.set("foo.bar.baz", 42)).toBe 42
|
||||
|
||||
@@ -6,8 +6,7 @@ describe "ContextMenuManager", ->
|
||||
[contextMenu] = []
|
||||
|
||||
beforeEach ->
|
||||
{resourcePath} = atom.getLoadSettings()
|
||||
contextMenu = new ContextMenuManager({resourcePath})
|
||||
contextMenu = new ContextMenuManager
|
||||
|
||||
describe "adding definitions", ->
|
||||
it 'loads', ->
|
||||
|
||||
+136
-148
@@ -9,7 +9,7 @@ describe "DisplayBuffer", ->
|
||||
buffer = atom.project.bufferForPathSync('sample.js')
|
||||
displayBuffer = new DisplayBuffer({buffer, tabLength})
|
||||
changeHandler = jasmine.createSpy 'changeHandler'
|
||||
displayBuffer.onDidChange changeHandler
|
||||
displayBuffer.on 'changed', changeHandler
|
||||
|
||||
waitsForPromise ->
|
||||
atom.packages.activatePackage('language-javascript')
|
||||
@@ -58,7 +58,7 @@ describe "DisplayBuffer", ->
|
||||
|
||||
describe "soft wrapping", ->
|
||||
beforeEach ->
|
||||
displayBuffer.setSoftWrapped(true)
|
||||
displayBuffer.setSoftWrap(true)
|
||||
displayBuffer.setEditorWidthInChars(50)
|
||||
changeHandler.reset()
|
||||
|
||||
@@ -67,48 +67,48 @@ describe "DisplayBuffer", ->
|
||||
it "uses the preferred line length as the soft wrap column when it is less than the configured soft wrap column", ->
|
||||
atom.config.set('editor.preferredLineLength', 100)
|
||||
atom.config.set('editor.softWrapAtPreferredLineLength', true)
|
||||
expect(displayBuffer.tokenizedLineForScreenRow(10).text).toBe ' return '
|
||||
expect(displayBuffer.lineForRow(10).text).toBe ' return '
|
||||
|
||||
atom.config.set('editor.preferredLineLength', 5)
|
||||
expect(displayBuffer.tokenizedLineForScreenRow(10).text).toBe 'funct'
|
||||
expect(displayBuffer.lineForRow(10).text).toBe 'funct'
|
||||
|
||||
atom.config.set('editor.softWrapAtPreferredLineLength', false)
|
||||
expect(displayBuffer.tokenizedLineForScreenRow(10).text).toBe ' return '
|
||||
expect(displayBuffer.lineForRow(10).text).toBe ' return '
|
||||
|
||||
describe "when the line is shorter than the max line length", ->
|
||||
it "renders the line unchanged", ->
|
||||
expect(displayBuffer.tokenizedLineForScreenRow(0).text).toBe buffer.lineForRow(0)
|
||||
expect(displayBuffer.lineForRow(0).text).toBe buffer.lineForRow(0)
|
||||
|
||||
describe "when the line is empty", ->
|
||||
it "renders the empty line", ->
|
||||
expect(displayBuffer.tokenizedLineForScreenRow(13).text).toBe ''
|
||||
expect(displayBuffer.lineForRow(13).text).toBe ''
|
||||
|
||||
describe "when there is a non-whitespace character at the max length boundary", ->
|
||||
describe "when there is whitespace before the boundary", ->
|
||||
it "wraps the line at the end of the first whitespace preceding the boundary", ->
|
||||
expect(displayBuffer.tokenizedLineForScreenRow(10).text).toBe ' return '
|
||||
expect(displayBuffer.tokenizedLineForScreenRow(11).text).toBe 'sort(left).concat(pivot).concat(sort(right));'
|
||||
expect(displayBuffer.lineForRow(10).text).toBe ' return '
|
||||
expect(displayBuffer.lineForRow(11).text).toBe 'sort(left).concat(pivot).concat(sort(right));'
|
||||
|
||||
describe "when there is no whitespace before the boundary", ->
|
||||
it "wraps the line exactly at the boundary since there's no more graceful place to wrap it", ->
|
||||
buffer.setTextInRange([[0, 0], [1, 0]], 'abcdefghijklmnopqrstuvwxyz\n')
|
||||
displayBuffer.setEditorWidthInChars(10)
|
||||
expect(displayBuffer.tokenizedLineForScreenRow(0).text).toBe 'abcdefghij'
|
||||
expect(displayBuffer.tokenizedLineForScreenRow(1).text).toBe 'klmnopqrst'
|
||||
expect(displayBuffer.tokenizedLineForScreenRow(2).text).toBe 'uvwxyz'
|
||||
expect(displayBuffer.lineForRow(0).text).toBe 'abcdefghij'
|
||||
expect(displayBuffer.lineForRow(1).text).toBe 'klmnopqrst'
|
||||
expect(displayBuffer.lineForRow(2).text).toBe 'uvwxyz'
|
||||
|
||||
describe "when there is a whitespace character at the max length boundary", ->
|
||||
it "wraps the line at the first non-whitespace character following the boundary", ->
|
||||
expect(displayBuffer.tokenizedLineForScreenRow(3).text).toBe ' var pivot = items.shift(), current, left = [], '
|
||||
expect(displayBuffer.tokenizedLineForScreenRow(4).text).toBe 'right = [];'
|
||||
expect(displayBuffer.lineForRow(3).text).toBe ' var pivot = items.shift(), current, left = [], '
|
||||
expect(displayBuffer.lineForRow(4).text).toBe 'right = [];'
|
||||
|
||||
describe "when there are hard tabs", ->
|
||||
beforeEach ->
|
||||
buffer.setText(buffer.getText().replace(new RegExp(' ', 'g'), '\t'))
|
||||
|
||||
it "correctly tokenizes the hard tabs", ->
|
||||
expect(displayBuffer.tokenizedLineForScreenRow(3).tokens[0].isHardTab).toBeTruthy()
|
||||
expect(displayBuffer.tokenizedLineForScreenRow(3).tokens[1].isHardTab).toBeTruthy()
|
||||
expect(displayBuffer.lineForRow(3).tokens[0].isHardTab).toBeTruthy()
|
||||
expect(displayBuffer.lineForRow(3).tokens[1].isHardTab).toBeTruthy()
|
||||
|
||||
describe "when the buffer changes", ->
|
||||
describe "when buffer lines are updated", ->
|
||||
@@ -121,57 +121,57 @@ describe "DisplayBuffer", ->
|
||||
describe "when the update makes a soft-wrapped line shorter than the max line length", ->
|
||||
it "rewraps the line and emits a change event", ->
|
||||
buffer.delete([[6, 24], [6, 42]])
|
||||
expect(displayBuffer.tokenizedLineForScreenRow(7).text).toBe ' current < pivot ? : right.push(current);'
|
||||
expect(displayBuffer.tokenizedLineForScreenRow(8).text).toBe ' }'
|
||||
expect(displayBuffer.lineForRow(7).text).toBe ' current < pivot ? : right.push(current);'
|
||||
expect(displayBuffer.lineForRow(8).text).toBe ' }'
|
||||
|
||||
expect(changeHandler).toHaveBeenCalled()
|
||||
[[event]]= changeHandler.argsForCall
|
||||
|
||||
expect(event).toEqual(start: 7, end: 8, screenDelta: -1, bufferDelta: 0)
|
||||
|
||||
describe "when the update causes a line to soft wrap an additional time", ->
|
||||
describe "when the update causes a line to softwrap an additional time", ->
|
||||
it "rewraps the line and emits a change event", ->
|
||||
buffer.insert([6, 28], '1234567890')
|
||||
expect(displayBuffer.tokenizedLineForScreenRow(7).text).toBe ' current < pivot ? '
|
||||
expect(displayBuffer.tokenizedLineForScreenRow(8).text).toBe 'left1234567890.push(current) : '
|
||||
expect(displayBuffer.tokenizedLineForScreenRow(9).text).toBe 'right.push(current);'
|
||||
expect(displayBuffer.tokenizedLineForScreenRow(10).text).toBe ' }'
|
||||
expect(displayBuffer.lineForRow(7).text).toBe ' current < pivot ? '
|
||||
expect(displayBuffer.lineForRow(8).text).toBe 'left1234567890.push(current) : '
|
||||
expect(displayBuffer.lineForRow(9).text).toBe 'right.push(current);'
|
||||
expect(displayBuffer.lineForRow(10).text).toBe ' }'
|
||||
|
||||
expect(changeHandler).toHaveBeenCalledWith(start: 7, end: 8, screenDelta: 1, bufferDelta: 0)
|
||||
|
||||
describe "when buffer lines are inserted", ->
|
||||
it "inserts / updates wrapped lines and emits a change event", ->
|
||||
buffer.insert([6, 21], '1234567890 abcdefghij 1234567890\nabcdefghij')
|
||||
expect(displayBuffer.tokenizedLineForScreenRow(7).text).toBe ' current < pivot1234567890 abcdefghij '
|
||||
expect(displayBuffer.tokenizedLineForScreenRow(8).text).toBe '1234567890'
|
||||
expect(displayBuffer.tokenizedLineForScreenRow(9).text).toBe 'abcdefghij ? left.push(current) : '
|
||||
expect(displayBuffer.tokenizedLineForScreenRow(10).text).toBe 'right.push(current);'
|
||||
expect(displayBuffer.lineForRow(7).text).toBe ' current < pivot1234567890 abcdefghij '
|
||||
expect(displayBuffer.lineForRow(8).text).toBe '1234567890'
|
||||
expect(displayBuffer.lineForRow(9).text).toBe 'abcdefghij ? left.push(current) : '
|
||||
expect(displayBuffer.lineForRow(10).text).toBe 'right.push(current);'
|
||||
|
||||
expect(changeHandler).toHaveBeenCalledWith(start: 7, end: 8, screenDelta: 2, bufferDelta: 1)
|
||||
|
||||
describe "when buffer lines are removed", ->
|
||||
it "removes lines and emits a change event", ->
|
||||
buffer.setTextInRange([[3, 21], [7, 5]], ';')
|
||||
expect(displayBuffer.tokenizedLineForScreenRow(3).text).toBe ' var pivot = items;'
|
||||
expect(displayBuffer.tokenizedLineForScreenRow(4).text).toBe ' return '
|
||||
expect(displayBuffer.tokenizedLineForScreenRow(5).text).toBe 'sort(left).concat(pivot).concat(sort(right));'
|
||||
expect(displayBuffer.tokenizedLineForScreenRow(6).text).toBe ' };'
|
||||
expect(displayBuffer.lineForRow(3).text).toBe ' var pivot = items;'
|
||||
expect(displayBuffer.lineForRow(4).text).toBe ' return '
|
||||
expect(displayBuffer.lineForRow(5).text).toBe 'sort(left).concat(pivot).concat(sort(right));'
|
||||
expect(displayBuffer.lineForRow(6).text).toBe ' };'
|
||||
|
||||
expect(changeHandler).toHaveBeenCalledWith(start: 3, end: 9, screenDelta: -6, bufferDelta: -4)
|
||||
|
||||
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, softWrap: true})
|
||||
|
||||
buffer.insert([0, 0], "the quick brown fox jumps over the lazy dog.")
|
||||
buffer.insert([0, Infinity], '\n')
|
||||
buffer.delete([[0, Infinity], [1, 0]])
|
||||
buffer.insert([0, Infinity], '\n')
|
||||
|
||||
expect(displayBuffer.tokenizedLineForScreenRow(0).text).toBe "the quick brown fox jumps over "
|
||||
expect(displayBuffer.tokenizedLineForScreenRow(1).text).toBe "the lazy dog."
|
||||
expect(displayBuffer.tokenizedLineForScreenRow(2).text).toBe ""
|
||||
expect(displayBuffer.lineForRow(0).text).toBe "the quick brown fox jumps over "
|
||||
expect(displayBuffer.lineForRow(1).text).toBe "the lazy dog."
|
||||
expect(displayBuffer.lineForRow(2).text).toBe ""
|
||||
|
||||
describe "position translation", ->
|
||||
it "translates positions accounting for wrapped lines", ->
|
||||
@@ -204,9 +204,9 @@ describe "DisplayBuffer", ->
|
||||
describe ".setEditorWidthInChars(length)", ->
|
||||
it "changes the length at which lines are wrapped and emits a change event for all screen lines", ->
|
||||
displayBuffer.setEditorWidthInChars(40)
|
||||
expect(tokensText displayBuffer.tokenizedLineForScreenRow(4).tokens).toBe 'left = [], right = [];'
|
||||
expect(tokensText displayBuffer.tokenizedLineForScreenRow(5).tokens).toBe ' while(items.length > 0) {'
|
||||
expect(tokensText displayBuffer.tokenizedLineForScreenRow(12).tokens).toBe 'sort(left).concat(pivot).concat(sort(rig'
|
||||
expect(tokensText displayBuffer.lineForRow(4).tokens).toBe 'left = [], right = [];'
|
||||
expect(tokensText displayBuffer.lineForRow(5).tokens).toBe ' while(items.length > 0) {'
|
||||
expect(tokensText displayBuffer.lineForRow(12).tokens).toBe 'sort(left).concat(pivot).concat(sort(rig'
|
||||
expect(changeHandler).toHaveBeenCalledWith(start: 0, end: 15, screenDelta: 3, bufferDelta: 0)
|
||||
|
||||
it "only allows positive widths to be assigned", ->
|
||||
@@ -220,10 +220,10 @@ describe "DisplayBuffer", ->
|
||||
displayBuffer.setWidth(50)
|
||||
displayBuffer.manageScrollPosition = true
|
||||
|
||||
displayBuffer.setSoftWrapped(false)
|
||||
displayBuffer.setSoftWrap(false)
|
||||
displayBuffer.setScrollLeft(Infinity)
|
||||
expect(displayBuffer.getScrollLeft()).toBeGreaterThan 0
|
||||
displayBuffer.setSoftWrapped(true)
|
||||
displayBuffer.setSoftWrap(true)
|
||||
expect(displayBuffer.getScrollLeft()).toBe 0
|
||||
displayBuffer.setScrollLeft(10)
|
||||
expect(displayBuffer.getScrollLeft()).toBe 0
|
||||
@@ -234,7 +234,7 @@ describe "DisplayBuffer", ->
|
||||
buffer.release()
|
||||
buffer = atom.project.bufferForPathSync('two-hundred.txt')
|
||||
displayBuffer = new DisplayBuffer({buffer, tabLength})
|
||||
displayBuffer.onDidChange changeHandler
|
||||
displayBuffer.on 'changed', changeHandler
|
||||
|
||||
describe "when folds are created and destroyed", ->
|
||||
describe "when a fold spans multiple lines", ->
|
||||
@@ -242,7 +242,7 @@ describe "DisplayBuffer", ->
|
||||
fold = displayBuffer.createFold(4, 7)
|
||||
expect(fold).toBeDefined()
|
||||
|
||||
[line4, line5] = displayBuffer.tokenizedLinesForScreenRows(4, 5)
|
||||
[line4, line5] = displayBuffer.linesForRows(4, 5)
|
||||
expect(line4.fold).toBe fold
|
||||
expect(line4.text).toMatch /^4-+/
|
||||
expect(line5.text).toBe '8'
|
||||
@@ -251,7 +251,7 @@ describe "DisplayBuffer", ->
|
||||
changeHandler.reset()
|
||||
|
||||
fold.destroy()
|
||||
[line4, line5] = displayBuffer.tokenizedLinesForScreenRows(4, 5)
|
||||
[line4, line5] = displayBuffer.linesForRows(4, 5)
|
||||
expect(line4.fold).toBeUndefined()
|
||||
expect(line4.text).toMatch /^4-+/
|
||||
expect(line5.text).toBe '5'
|
||||
@@ -262,7 +262,7 @@ describe "DisplayBuffer", ->
|
||||
it "renders a fold placeholder for the folded line but does not skip any lines", ->
|
||||
fold = displayBuffer.createFold(4, 4)
|
||||
|
||||
[line4, line5] = displayBuffer.tokenizedLinesForScreenRows(4, 5)
|
||||
[line4, line5] = displayBuffer.linesForRows(4, 5)
|
||||
expect(line4.fold).toBe fold
|
||||
expect(line4.text).toMatch /^4-+/
|
||||
expect(line5.text).toBe '5'
|
||||
@@ -275,7 +275,7 @@ describe "DisplayBuffer", ->
|
||||
|
||||
fold.destroy()
|
||||
|
||||
[line4, line5] = displayBuffer.tokenizedLinesForScreenRows(4, 5)
|
||||
[line4, line5] = displayBuffer.linesForRows(4, 5)
|
||||
expect(line4.fold).toBeUndefined()
|
||||
expect(line4.text).toMatch /^4-+/
|
||||
expect(line5.text).toBe '5'
|
||||
@@ -287,13 +287,13 @@ describe "DisplayBuffer", ->
|
||||
innerFold = displayBuffer.createFold(6, 7)
|
||||
outerFold = displayBuffer.createFold(4, 8)
|
||||
|
||||
[line4, line5] = displayBuffer.tokenizedLinesForScreenRows(4, 5)
|
||||
[line4, line5] = displayBuffer.linesForRows(4, 5)
|
||||
expect(line4.fold).toBe outerFold
|
||||
expect(line4.text).toMatch /4-+/
|
||||
expect(line5.text).toMatch /9-+/
|
||||
|
||||
outerFold.destroy()
|
||||
[line4, line5, line6, line7] = displayBuffer.tokenizedLinesForScreenRows(4, 7)
|
||||
[line4, line5, line6, line7] = displayBuffer.linesForRows(4, 7)
|
||||
expect(line4.fold).toBeUndefined()
|
||||
expect(line4.text).toMatch /^4-+/
|
||||
expect(line5.text).toBe '5'
|
||||
@@ -305,7 +305,7 @@ describe "DisplayBuffer", ->
|
||||
innerFold = displayBuffer.createFold(4, 6)
|
||||
outerFold = displayBuffer.createFold(4, 8)
|
||||
|
||||
[line4, line5] = displayBuffer.tokenizedLinesForScreenRows(4, 5)
|
||||
[line4, line5] = displayBuffer.linesForRows(4, 5)
|
||||
expect(line4.fold).toBe outerFold
|
||||
expect(line4.text).toMatch /4-+/
|
||||
expect(line5.text).toMatch /9-+/
|
||||
@@ -326,14 +326,14 @@ describe "DisplayBuffer", ->
|
||||
|
||||
innerFold = displayBuffer.createFold(2, 5)
|
||||
expect(changeHandler).not.toHaveBeenCalled()
|
||||
[line0, line1] = displayBuffer.tokenizedLinesForScreenRows(0, 1)
|
||||
[line0, line1] = displayBuffer.linesForRows(0, 1)
|
||||
expect(line0.fold).toBe outerFold
|
||||
expect(line1.fold).toBeUndefined()
|
||||
|
||||
changeHandler.reset()
|
||||
innerFold.destroy()
|
||||
expect(changeHandler).not.toHaveBeenCalled()
|
||||
[line0, line1] = displayBuffer.tokenizedLinesForScreenRows(0, 1)
|
||||
[line0, line1] = displayBuffer.linesForRows(0, 1)
|
||||
expect(line0.fold).toBe outerFold
|
||||
expect(line1.fold).toBeUndefined()
|
||||
|
||||
@@ -342,8 +342,8 @@ describe "DisplayBuffer", ->
|
||||
fold2 = displayBuffer.createFold(4, 9)
|
||||
fold1 = displayBuffer.createFold(0, 4)
|
||||
|
||||
expect(displayBuffer.tokenizedLineForScreenRow(0).text).toMatch /^0/
|
||||
expect(displayBuffer.tokenizedLineForScreenRow(1).text).toMatch /^10/
|
||||
expect(displayBuffer.lineForRow(0).text).toMatch /^0/
|
||||
expect(displayBuffer.lineForRow(1).text).toMatch /^10/
|
||||
|
||||
describe "when there is another display buffer pointing to the same buffer", ->
|
||||
it "does not create folds in the other display buffer", ->
|
||||
@@ -363,19 +363,19 @@ describe "DisplayBuffer", ->
|
||||
buffer.setTextInRange([[1, 0], [5, 1]], 'party!')
|
||||
|
||||
it "removes the fold and replaces the selection with the new text", ->
|
||||
expect(displayBuffer.tokenizedLineForScreenRow(0).text).toBe "0"
|
||||
expect(displayBuffer.tokenizedLineForScreenRow(1).text).toBe "party!"
|
||||
expect(displayBuffer.tokenizedLineForScreenRow(2).fold).toBe fold2
|
||||
expect(displayBuffer.tokenizedLineForScreenRow(3).text).toMatch /^9-+/
|
||||
expect(displayBuffer.lineForRow(0).text).toBe "0"
|
||||
expect(displayBuffer.lineForRow(1).text).toBe "party!"
|
||||
expect(displayBuffer.lineForRow(2).fold).toBe fold2
|
||||
expect(displayBuffer.lineForRow(3).text).toMatch /^9-+/
|
||||
|
||||
expect(changeHandler).toHaveBeenCalledWith(start: 1, end: 3, screenDelta: -2, bufferDelta: -4)
|
||||
|
||||
describe "when the changes is subsequently undone", ->
|
||||
xit "restores destroyed folds", ->
|
||||
buffer.undo()
|
||||
expect(displayBuffer.tokenizedLineForScreenRow(2).text).toBe '2'
|
||||
expect(displayBuffer.tokenizedLineForScreenRow(2).fold).toBe fold1
|
||||
expect(displayBuffer.tokenizedLineForScreenRow(3).text).toBe '5'
|
||||
expect(displayBuffer.lineForRow(2).text).toBe '2'
|
||||
expect(displayBuffer.lineForRow(2).fold).toBe fold1
|
||||
expect(displayBuffer.lineForRow(3).text).toBe '5'
|
||||
|
||||
describe "when the old range surrounds two nested folds", ->
|
||||
it "removes both folds and replaces the selection with the new text", ->
|
||||
@@ -384,9 +384,9 @@ describe "DisplayBuffer", ->
|
||||
|
||||
buffer.setTextInRange([[1, 0], [10, 0]], 'goodbye')
|
||||
|
||||
expect(displayBuffer.tokenizedLineForScreenRow(0).text).toBe "0"
|
||||
expect(displayBuffer.tokenizedLineForScreenRow(1).text).toBe "goodbye10"
|
||||
expect(displayBuffer.tokenizedLineForScreenRow(2).text).toBe "11"
|
||||
expect(displayBuffer.lineForRow(0).text).toBe "0"
|
||||
expect(displayBuffer.lineForRow(1).text).toBe "goodbye10"
|
||||
expect(displayBuffer.lineForRow(2).text).toBe "11"
|
||||
|
||||
expect(changeHandler).toHaveBeenCalledWith(start: 1, end: 3, screenDelta: -2, bufferDelta: -9)
|
||||
|
||||
@@ -403,42 +403,42 @@ describe "DisplayBuffer", ->
|
||||
it "updates the buffer and re-positions subsequent folds", ->
|
||||
buffer.setTextInRange([[0, 0], [1, 1]], 'abc')
|
||||
|
||||
expect(displayBuffer.tokenizedLineForScreenRow(0).text).toBe "abc"
|
||||
expect(displayBuffer.tokenizedLineForScreenRow(1).fold).toBe fold1
|
||||
expect(displayBuffer.tokenizedLineForScreenRow(2).text).toBe "5"
|
||||
expect(displayBuffer.tokenizedLineForScreenRow(3).fold).toBe fold2
|
||||
expect(displayBuffer.tokenizedLineForScreenRow(4).text).toMatch /^9-+/
|
||||
expect(displayBuffer.lineForRow(0).text).toBe "abc"
|
||||
expect(displayBuffer.lineForRow(1).fold).toBe fold1
|
||||
expect(displayBuffer.lineForRow(2).text).toBe "5"
|
||||
expect(displayBuffer.lineForRow(3).fold).toBe fold2
|
||||
expect(displayBuffer.lineForRow(4).text).toMatch /^9-+/
|
||||
|
||||
expect(changeHandler).toHaveBeenCalledWith(start: 0, end: 1, screenDelta: -1, bufferDelta: -1)
|
||||
changeHandler.reset()
|
||||
|
||||
fold1.destroy()
|
||||
expect(displayBuffer.tokenizedLineForScreenRow(0).text).toBe "abc"
|
||||
expect(displayBuffer.tokenizedLineForScreenRow(1).text).toBe "2"
|
||||
expect(displayBuffer.tokenizedLineForScreenRow(3).text).toMatch /^4-+/
|
||||
expect(displayBuffer.tokenizedLineForScreenRow(4).text).toBe "5"
|
||||
expect(displayBuffer.tokenizedLineForScreenRow(5).fold).toBe fold2
|
||||
expect(displayBuffer.tokenizedLineForScreenRow(6).text).toMatch /^9-+/
|
||||
expect(displayBuffer.lineForRow(0).text).toBe "abc"
|
||||
expect(displayBuffer.lineForRow(1).text).toBe "2"
|
||||
expect(displayBuffer.lineForRow(3).text).toMatch /^4-+/
|
||||
expect(displayBuffer.lineForRow(4).text).toBe "5"
|
||||
expect(displayBuffer.lineForRow(5).fold).toBe fold2
|
||||
expect(displayBuffer.lineForRow(6).text).toMatch /^9-+/
|
||||
|
||||
expect(changeHandler).toHaveBeenCalledWith(start: 1, end: 1, screenDelta: 2, bufferDelta: 0)
|
||||
|
||||
describe "when the old range straddles the beginning of a fold", ->
|
||||
it "destroys the fold", ->
|
||||
buffer.setTextInRange([[1, 1], [3, 0]], "a\nb\nc\nd\n")
|
||||
expect(displayBuffer.tokenizedLineForScreenRow(1).text).toBe '1a'
|
||||
expect(displayBuffer.tokenizedLineForScreenRow(2).text).toBe 'b'
|
||||
expect(displayBuffer.tokenizedLineForScreenRow(2).fold).toBeUndefined()
|
||||
expect(displayBuffer.tokenizedLineForScreenRow(3).text).toBe 'c'
|
||||
expect(displayBuffer.lineForRow(1).text).toBe '1a'
|
||||
expect(displayBuffer.lineForRow(2).text).toBe 'b'
|
||||
expect(displayBuffer.lineForRow(2).fold).toBeUndefined()
|
||||
expect(displayBuffer.lineForRow(3).text).toBe 'c'
|
||||
|
||||
describe "when the old range follows a fold", ->
|
||||
it "re-positions the screen ranges for the change event based on the preceding fold", ->
|
||||
buffer.setTextInRange([[10, 0], [11, 0]], 'abc')
|
||||
|
||||
expect(displayBuffer.tokenizedLineForScreenRow(1).text).toBe "1"
|
||||
expect(displayBuffer.tokenizedLineForScreenRow(2).fold).toBe fold1
|
||||
expect(displayBuffer.tokenizedLineForScreenRow(3).text).toBe "5"
|
||||
expect(displayBuffer.tokenizedLineForScreenRow(4).fold).toBe fold2
|
||||
expect(displayBuffer.tokenizedLineForScreenRow(5).text).toMatch /^9-+/
|
||||
expect(displayBuffer.lineForRow(1).text).toBe "1"
|
||||
expect(displayBuffer.lineForRow(2).fold).toBe fold1
|
||||
expect(displayBuffer.lineForRow(3).text).toBe "5"
|
||||
expect(displayBuffer.lineForRow(4).fold).toBe fold2
|
||||
expect(displayBuffer.lineForRow(5).text).toMatch /^9-+/
|
||||
|
||||
expect(changeHandler).toHaveBeenCalledWith(start: 6, end: 7, screenDelta: -1, bufferDelta: -1)
|
||||
|
||||
@@ -449,12 +449,12 @@ describe "DisplayBuffer", ->
|
||||
expect(fold1.getStartRow()).toBe 2
|
||||
expect(fold1.getEndRow()).toBe 5
|
||||
|
||||
expect(displayBuffer.tokenizedLineForScreenRow(1).text).toBe "1"
|
||||
expect(displayBuffer.tokenizedLineForScreenRow(2).text).toBe "2"
|
||||
expect(displayBuffer.tokenizedLineForScreenRow(2).fold).toBe fold1
|
||||
expect(displayBuffer.tokenizedLineForScreenRow(3).text).toMatch "5"
|
||||
expect(displayBuffer.tokenizedLineForScreenRow(4).fold).toBe fold2
|
||||
expect(displayBuffer.tokenizedLineForScreenRow(5).text).toMatch /^9-+/
|
||||
expect(displayBuffer.lineForRow(1).text).toBe "1"
|
||||
expect(displayBuffer.lineForRow(2).text).toBe "2"
|
||||
expect(displayBuffer.lineForRow(2).fold).toBe fold1
|
||||
expect(displayBuffer.lineForRow(3).text).toMatch "5"
|
||||
expect(displayBuffer.lineForRow(4).fold).toBe fold2
|
||||
expect(displayBuffer.lineForRow(5).text).toMatch /^9-+/
|
||||
|
||||
expect(changeHandler).toHaveBeenCalledWith(start: 2, end: 2, screenDelta: 0, bufferDelta: 1)
|
||||
|
||||
@@ -464,12 +464,12 @@ describe "DisplayBuffer", ->
|
||||
expect(fold1.getStartRow()).toBe 2
|
||||
expect(fold1.getEndRow()).toBe 7
|
||||
|
||||
expect(displayBuffer.tokenizedLineForScreenRow(1).text).toBe "1"
|
||||
expect(displayBuffer.tokenizedLineForScreenRow(2).text).toBe "2"
|
||||
expect(displayBuffer.tokenizedLineForScreenRow(2).fold).toBe fold1
|
||||
expect(displayBuffer.tokenizedLineForScreenRow(3).text).toMatch "5"
|
||||
expect(displayBuffer.tokenizedLineForScreenRow(4).fold).toBe fold2
|
||||
expect(displayBuffer.tokenizedLineForScreenRow(5).text).toMatch /^9-+/
|
||||
expect(displayBuffer.lineForRow(1).text).toBe "1"
|
||||
expect(displayBuffer.lineForRow(2).text).toBe "2"
|
||||
expect(displayBuffer.lineForRow(2).fold).toBe fold1
|
||||
expect(displayBuffer.lineForRow(3).text).toMatch "5"
|
||||
expect(displayBuffer.lineForRow(4).fold).toBe fold2
|
||||
expect(displayBuffer.lineForRow(5).text).toMatch /^9-+/
|
||||
|
||||
expect(changeHandler).toHaveBeenCalledWith(start: 2, end: 2, screenDelta: 0, bufferDelta: 3)
|
||||
|
||||
@@ -478,21 +478,21 @@ describe "DisplayBuffer", ->
|
||||
it "destroys the fold", ->
|
||||
fold2.destroy()
|
||||
buffer.setTextInRange([[3, 0], [6, 0]], 'a\n')
|
||||
expect(displayBuffer.tokenizedLineForScreenRow(2).text).toBe '2'
|
||||
expect(displayBuffer.tokenizedLineForScreenRow(2).fold).toBeUndefined()
|
||||
expect(displayBuffer.tokenizedLineForScreenRow(3).text).toBe 'a'
|
||||
expect(displayBuffer.tokenizedLineForScreenRow(4).text).toBe '6'
|
||||
expect(displayBuffer.lineForRow(2).text).toBe '2'
|
||||
expect(displayBuffer.lineForRow(2).fold).toBeUndefined()
|
||||
expect(displayBuffer.lineForRow(3).text).toBe 'a'
|
||||
expect(displayBuffer.lineForRow(4).text).toBe '6'
|
||||
|
||||
describe "when the old range is contained to a single line in-between two folds", ->
|
||||
it "re-renders the line with the placeholder and re-positions the second fold", ->
|
||||
buffer.insert([5, 0], 'abc\n')
|
||||
|
||||
expect(displayBuffer.tokenizedLineForScreenRow(1).text).toBe "1"
|
||||
expect(displayBuffer.tokenizedLineForScreenRow(2).fold).toBe fold1
|
||||
expect(displayBuffer.tokenizedLineForScreenRow(3).text).toMatch "abc"
|
||||
expect(displayBuffer.tokenizedLineForScreenRow(4).text).toBe "5"
|
||||
expect(displayBuffer.tokenizedLineForScreenRow(5).fold).toBe fold2
|
||||
expect(displayBuffer.tokenizedLineForScreenRow(6).text).toMatch /^9-+/
|
||||
expect(displayBuffer.lineForRow(1).text).toBe "1"
|
||||
expect(displayBuffer.lineForRow(2).fold).toBe fold1
|
||||
expect(displayBuffer.lineForRow(3).text).toMatch "abc"
|
||||
expect(displayBuffer.lineForRow(4).text).toBe "5"
|
||||
expect(displayBuffer.lineForRow(5).fold).toBe fold2
|
||||
expect(displayBuffer.lineForRow(6).text).toMatch /^9-+/
|
||||
|
||||
expect(changeHandler).toHaveBeenCalledWith(start: 3, end: 3, screenDelta: 1, bufferDelta: 1)
|
||||
|
||||
@@ -545,15 +545,15 @@ describe "DisplayBuffer", ->
|
||||
displayBuffer.createFold(1, 9)
|
||||
displayBuffer.createFold(11, 12)
|
||||
|
||||
expect(displayBuffer.tokenizedLineForScreenRow(1).text).toBe '1'
|
||||
expect(displayBuffer.tokenizedLineForScreenRow(2).text).toBe '10'
|
||||
expect(displayBuffer.lineForRow(1).text).toBe '1'
|
||||
expect(displayBuffer.lineForRow(2).text).toBe '10'
|
||||
|
||||
displayBuffer.unfoldBufferRow(2)
|
||||
expect(displayBuffer.tokenizedLineForScreenRow(1).text).toBe '1'
|
||||
expect(displayBuffer.tokenizedLineForScreenRow(2).text).toBe '2'
|
||||
expect(displayBuffer.tokenizedLineForScreenRow(7).fold).toBeDefined()
|
||||
expect(displayBuffer.tokenizedLineForScreenRow(8).text).toMatch /^9-+/
|
||||
expect(displayBuffer.tokenizedLineForScreenRow(10).fold).toBeDefined()
|
||||
expect(displayBuffer.lineForRow(1).text).toBe '1'
|
||||
expect(displayBuffer.lineForRow(2).text).toBe '2'
|
||||
expect(displayBuffer.lineForRow(7).fold).toBeDefined()
|
||||
expect(displayBuffer.lineForRow(8).text).toMatch /^9-+/
|
||||
expect(displayBuffer.lineForRow(10).fold).toBeDefined()
|
||||
|
||||
describe ".outermostFoldsInBufferRowRange(startRow, endRow)", ->
|
||||
it "returns the outermost folds entirely contained in the given row range, exclusive of end row", ->
|
||||
@@ -568,7 +568,7 @@ describe "DisplayBuffer", ->
|
||||
|
||||
describe "::clipScreenPosition(screenPosition, wrapBeyondNewlines: false, wrapAtSoftNewlines: false, skipAtomicTokens: false)", ->
|
||||
beforeEach ->
|
||||
displayBuffer.setSoftWrapped(true)
|
||||
displayBuffer.setSoftWrap(true)
|
||||
displayBuffer.setEditorWidthInChars(50)
|
||||
|
||||
it "allows valid positions", ->
|
||||
@@ -643,7 +643,7 @@ describe "DisplayBuffer", ->
|
||||
|
||||
it "correctly translates positions on soft wrapped lines containing tabs", ->
|
||||
buffer.setText('\t\taa bb cc dd ee ff gg')
|
||||
displayBuffer.setSoftWrapped(true)
|
||||
displayBuffer.setSoftWrap(true)
|
||||
displayBuffer.setEditorWidthInChars(10)
|
||||
expect(displayBuffer.screenPositionForBufferPosition([0, 10], wrapAtSoftNewlines: true)).toEqual [1, 0]
|
||||
expect(displayBuffer.bufferPositionForScreenPosition([1, 0])).toEqual [0, 10]
|
||||
@@ -686,7 +686,7 @@ describe "DisplayBuffer", ->
|
||||
expect(marker2.getScreenRange()).toEqual [[5, 4], [5, 10]]
|
||||
|
||||
it "emits a 'marker-created' event on the DisplayBuffer whenever a marker is created", ->
|
||||
displayBuffer.onDidCreateMarker markerCreatedHandler = jasmine.createSpy("markerCreatedHandler")
|
||||
displayBuffer.on 'marker-created', markerCreatedHandler = jasmine.createSpy("markerCreatedHandler")
|
||||
|
||||
marker1 = displayBuffer.markScreenRange([[5, 4], [5, 10]])
|
||||
expect(markerCreatedHandler).toHaveBeenCalledWith(marker1)
|
||||
@@ -722,7 +722,7 @@ describe "DisplayBuffer", ->
|
||||
|
||||
beforeEach ->
|
||||
marker = displayBuffer.markScreenRange([[5, 4], [5, 10]])
|
||||
marker.onDidChange markerChangedHandler = jasmine.createSpy("markerChangedHandler")
|
||||
marker.on 'changed', markerChangedHandler = jasmine.createSpy("markerChangedHandler")
|
||||
|
||||
it "triggers the 'changed' event whenever the markers head's screen position changes in the buffer or on screen", ->
|
||||
marker.setHeadScreenPosition([8, 20])
|
||||
@@ -859,8 +859,8 @@ describe "DisplayBuffer", ->
|
||||
|
||||
it "updates markers before emitting buffer change events, but does not notify their observers until the change event", ->
|
||||
marker2 = displayBuffer.markBufferRange([[8, 1], [8, 1]])
|
||||
marker2.onDidChange marker2ChangedHandler = jasmine.createSpy("marker2ChangedHandler")
|
||||
displayBuffer.onDidChange changeHandler = jasmine.createSpy("changeHandler").andCallFake -> onDisplayBufferChange()
|
||||
marker2.on 'changed', marker2ChangedHandler = jasmine.createSpy("marker2ChangedHandler")
|
||||
displayBuffer.on 'changed', changeHandler = jasmine.createSpy("changeHandler").andCallFake -> onDisplayBufferChange()
|
||||
|
||||
# New change ----
|
||||
|
||||
@@ -886,7 +886,7 @@ describe "DisplayBuffer", ->
|
||||
marker2ChangedHandler.reset()
|
||||
|
||||
marker3 = displayBuffer.markBufferRange([[8, 1], [8, 2]])
|
||||
marker3.onDidChange marker3ChangedHandler = jasmine.createSpy("marker3ChangedHandler")
|
||||
marker3.on 'changed', marker3ChangedHandler = jasmine.createSpy("marker3ChangedHandler")
|
||||
|
||||
onDisplayBufferChange = ->
|
||||
# calls change handler first
|
||||
@@ -932,7 +932,7 @@ describe "DisplayBuffer", ->
|
||||
expect(marker3ChangedHandler).toHaveBeenCalled()
|
||||
|
||||
it "updates the position of markers before emitting change events that aren't caused by a buffer change", ->
|
||||
displayBuffer.onDidChange changeHandler = jasmine.createSpy("changeHandler").andCallFake ->
|
||||
displayBuffer.on 'changed', changeHandler = jasmine.createSpy("changeHandler").andCallFake ->
|
||||
# calls change handler first
|
||||
expect(markerChangedHandler).not.toHaveBeenCalled()
|
||||
# but still updates the markers
|
||||
@@ -998,20 +998,20 @@ describe "DisplayBuffer", ->
|
||||
expect(marker.isValid()).toBeFalsy()
|
||||
expect(displayBuffer.getMarker(marker.id)).toBeUndefined()
|
||||
|
||||
it "notifies ::onDidDestroy observers when markers are destroyed", ->
|
||||
it "emits 'destroyed' events when markers are destroyed", ->
|
||||
destroyedHandler = jasmine.createSpy("destroyedHandler")
|
||||
marker = displayBuffer.markScreenRange([[5, 4], [5, 10]])
|
||||
marker.onDidDestroy destroyedHandler
|
||||
marker.on 'destroyed', destroyedHandler
|
||||
marker.destroy()
|
||||
expect(destroyedHandler).toHaveBeenCalled()
|
||||
destroyedHandler.reset()
|
||||
|
||||
marker2 = displayBuffer.markScreenRange([[5, 4], [5, 10]])
|
||||
marker2.onDidDestroy destroyedHandler
|
||||
marker2.on 'destroyed', destroyedHandler
|
||||
buffer.getMarker(marker2.id).destroy()
|
||||
expect(destroyedHandler).toHaveBeenCalled()
|
||||
|
||||
describe "Marker::copy(attributes)", ->
|
||||
describe "DisplayBufferMarker::copy(attributes)", ->
|
||||
it "creates a copy of the marker with the given attributes merged in", ->
|
||||
initialMarkerCount = displayBuffer.getMarkerCount()
|
||||
marker1 = displayBuffer.markScreenRange([[5, 4], [5, 10]], a: 1, b: 2)
|
||||
@@ -1020,10 +1020,10 @@ describe "DisplayBuffer", ->
|
||||
marker2 = marker1.copy(b: 3)
|
||||
expect(marker2.getBufferRange()).toEqual marker1.getBufferRange()
|
||||
expect(displayBuffer.getMarkerCount()).toBe initialMarkerCount + 2
|
||||
expect(marker1.getProperties()).toEqual a: 1, b: 2
|
||||
expect(marker2.getProperties()).toEqual a: 1, b: 3
|
||||
expect(marker1.getAttributes()).toEqual a: 1, b: 2
|
||||
expect(marker2.getAttributes()).toEqual a: 1, b: 3
|
||||
|
||||
describe "Marker::getPixelRange()", ->
|
||||
describe "DisplayBufferMarker::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]])
|
||||
|
||||
@@ -1037,28 +1037,16 @@ describe "DisplayBuffer", ->
|
||||
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', ->
|
||||
displayBuffer2 = new DisplayBuffer({buffer, tabLength})
|
||||
displayBuffer.onDidCreateMarker markerCreated1 = jasmine.createSpy().andCallFake (marker) -> marker.destroy()
|
||||
displayBuffer2.onDidCreateMarker markerCreated2 = jasmine.createSpy()
|
||||
|
||||
displayBuffer.markBufferRange([[0, 0], [1, 5]], {})
|
||||
|
||||
expect(markerCreated1).toHaveBeenCalled()
|
||||
expect(markerCreated2).not.toHaveBeenCalled()
|
||||
|
||||
describe "decorations", ->
|
||||
[marker, decoration, decorationProperties] = []
|
||||
[marker, decoration, decorationParams] = []
|
||||
beforeEach ->
|
||||
marker = displayBuffer.markBufferRange([[2, 13], [3, 15]])
|
||||
decorationProperties = {type: 'gutter', class: 'one'}
|
||||
decoration = displayBuffer.decorateMarker(marker, decorationProperties)
|
||||
decorationParams = {type: 'gutter', class: 'one'}
|
||||
decoration = displayBuffer.decorateMarker(marker, decorationParams)
|
||||
|
||||
it "can add decorations associated with markers and remove them", ->
|
||||
expect(decoration).toBeDefined()
|
||||
expect(decoration.getProperties()).toBe decorationProperties
|
||||
expect(decoration.getParams()).toBe decorationParams
|
||||
expect(displayBuffer.decorationForId(decoration.id)).toBe decoration
|
||||
expect(displayBuffer.decorationsForScreenRowRange(2, 3)[marker.id][0]).toBe decoration
|
||||
|
||||
@@ -1073,12 +1061,12 @@ describe "DisplayBuffer", ->
|
||||
|
||||
describe "when a decoration is updated via Decoration::update()", ->
|
||||
it "emits an 'updated' event containing the new and old params", ->
|
||||
decoration.onDidChangeProperties updatedSpy = jasmine.createSpy()
|
||||
decoration.setProperties type: 'gutter', class: 'two'
|
||||
decoration.on 'updated', updatedSpy = jasmine.createSpy()
|
||||
decoration.update type: 'gutter', class: 'two'
|
||||
|
||||
{oldProperties, newProperties} = updatedSpy.mostRecentCall.args[0]
|
||||
expect(oldProperties).toEqual decorationProperties
|
||||
expect(newProperties).toEqual type: 'gutter', class: 'two', id: decoration.id
|
||||
{oldParams, newParams} = updatedSpy.mostRecentCall.args[0]
|
||||
expect(oldParams).toEqual decorationParams
|
||||
expect(newParams).toEqual type: 'gutter', class: 'two', id: decoration.id
|
||||
|
||||
describe "::setScrollTop", ->
|
||||
beforeEach ->
|
||||
@@ -1171,7 +1159,7 @@ describe "DisplayBuffer", ->
|
||||
it "recomputes the scroll width when the scoped character widths change in a batch", ->
|
||||
operatorWidth = 20
|
||||
|
||||
displayBuffer.onDidChangeCharacterWidths changedSpy = jasmine.createSpy()
|
||||
displayBuffer.on 'character-widths-changed', changedSpy = jasmine.createSpy()
|
||||
|
||||
displayBuffer.batchCharacterMeasurement ->
|
||||
displayBuffer.setScopedCharWidth(['source.js', 'keyword.operator.js'], '<', operatorWidth)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
_ = require 'underscore-plus'
|
||||
{extend, flatten, toArray, last} = _
|
||||
|
||||
EditorView = require '../src/editor-view'
|
||||
ReactEditorView = require '../src/react-editor-view'
|
||||
EditorComponent = require '../src/editor-component'
|
||||
nbsp = String.fromCharCode(160)
|
||||
|
||||
@@ -34,7 +34,7 @@ describe "EditorComponent", ->
|
||||
contentNode = document.querySelector('#jasmine-content')
|
||||
contentNode.style.width = '1000px'
|
||||
|
||||
wrapperView = new EditorView(editor, {lineOverdrawMargin})
|
||||
wrapperView = new ReactEditorView(editor, {lineOverdrawMargin})
|
||||
wrapperView.attachToDom()
|
||||
wrapperNode = wrapperView.element
|
||||
|
||||
@@ -65,9 +65,9 @@ describe "EditorComponent", ->
|
||||
linesNode = componentNode.querySelector('.lines')
|
||||
expect(linesNode.style['-webkit-transform']).toBe "translate3d(0px, 0px, 0px)"
|
||||
expect(componentNode.querySelectorAll('.line').length).toBe 6 + 2 # no margin above
|
||||
expect(component.lineNodeForScreenRow(0).textContent).toBe editor.tokenizedLineForScreenRow(0).text
|
||||
expect(component.lineNodeForScreenRow(0).textContent).toBe editor.lineForScreenRow(0).text
|
||||
expect(component.lineNodeForScreenRow(0).offsetTop).toBe 0
|
||||
expect(component.lineNodeForScreenRow(5).textContent).toBe editor.tokenizedLineForScreenRow(5).text
|
||||
expect(component.lineNodeForScreenRow(5).textContent).toBe editor.lineForScreenRow(5).text
|
||||
expect(component.lineNodeForScreenRow(5).offsetTop).toBe 5 * lineHeightInPixels
|
||||
|
||||
verticalScrollbarNode.scrollTop = 4.5 * lineHeightInPixels
|
||||
@@ -77,9 +77,9 @@ describe "EditorComponent", ->
|
||||
expect(linesNode.style['-webkit-transform']).toBe "translate3d(0px, #{-4.5 * lineHeightInPixels}px, 0px)"
|
||||
expect(componentNode.querySelectorAll('.line').length).toBe 6 + 4 # margin above and below
|
||||
expect(component.lineNodeForScreenRow(2).offsetTop).toBe 2 * lineHeightInPixels
|
||||
expect(component.lineNodeForScreenRow(2).textContent).toBe editor.tokenizedLineForScreenRow(2).text
|
||||
expect(component.lineNodeForScreenRow(2).textContent).toBe editor.lineForScreenRow(2).text
|
||||
expect(component.lineNodeForScreenRow(9).offsetTop).toBe 9 * lineHeightInPixels
|
||||
expect(component.lineNodeForScreenRow(9).textContent).toBe editor.tokenizedLineForScreenRow(9).text
|
||||
expect(component.lineNodeForScreenRow(9).textContent).toBe editor.lineForScreenRow(9).text
|
||||
|
||||
it "updates the top position of subsequent lines when lines are inserted or removed", ->
|
||||
editor.getBuffer().deleteRows(0, 1)
|
||||
@@ -111,11 +111,11 @@ describe "EditorComponent", ->
|
||||
|
||||
buffer.insert([0, 0], '\n\n')
|
||||
nextAnimationFrame()
|
||||
expect(component.lineNodeForScreenRow(3).textContent).toBe editor.tokenizedLineForScreenRow(3).text
|
||||
expect(component.lineNodeForScreenRow(3).textContent).toBe editor.lineForScreenRow(3).text
|
||||
|
||||
buffer.delete([[0, 0], [3, 0]])
|
||||
nextAnimationFrame()
|
||||
expect(component.lineNodeForScreenRow(3).textContent).toBe editor.tokenizedLineForScreenRow(3).text
|
||||
expect(component.lineNodeForScreenRow(3).textContent).toBe editor.lineForScreenRow(3).text
|
||||
|
||||
it "updates the top position of lines when the line height changes", ->
|
||||
initialLineHeightInPixels = editor.getLineHeightInPixels()
|
||||
@@ -197,50 +197,6 @@ describe "EditorComponent", ->
|
||||
nextAnimationFrame()
|
||||
expect(linesNode.style.backgroundColor).toBe 'rgb(255, 0, 0)'
|
||||
|
||||
it "applies .leading-whitespace for lines with leading spaces and/or tabs", ->
|
||||
editor.setText(' a')
|
||||
nextAnimationFrame()
|
||||
|
||||
leafNodes = getLeafNodes(component.lineNodeForScreenRow(0))
|
||||
expect(leafNodes[0].classList.contains('leading-whitespace')).toBe true
|
||||
expect(leafNodes[0].classList.contains('trailing-whitespace')).toBe false
|
||||
|
||||
editor.setText('\ta')
|
||||
nextAnimationFrame()
|
||||
|
||||
leafNodes = getLeafNodes(component.lineNodeForScreenRow(0))
|
||||
expect(leafNodes[0].classList.contains('leading-whitespace')).toBe true
|
||||
expect(leafNodes[0].classList.contains('trailing-whitespace')).toBe false
|
||||
|
||||
it "applies .trailing-whitespace for lines with trailing spaces and/or tabs", ->
|
||||
editor.setText(' ')
|
||||
nextAnimationFrame()
|
||||
|
||||
leafNodes = getLeafNodes(component.lineNodeForScreenRow(0))
|
||||
expect(leafNodes[0].classList.contains('trailing-whitespace')).toBe true
|
||||
expect(leafNodes[0].classList.contains('leading-whitespace')).toBe false
|
||||
|
||||
editor.setText('\t')
|
||||
nextAnimationFrame()
|
||||
|
||||
leafNodes = getLeafNodes(component.lineNodeForScreenRow(0))
|
||||
expect(leafNodes[0].classList.contains('trailing-whitespace')).toBe true
|
||||
expect(leafNodes[0].classList.contains('leading-whitespace')).toBe false
|
||||
|
||||
editor.setText('a ')
|
||||
nextAnimationFrame()
|
||||
|
||||
leafNodes = getLeafNodes(component.lineNodeForScreenRow(0))
|
||||
expect(leafNodes[0].classList.contains('trailing-whitespace')).toBe true
|
||||
expect(leafNodes[0].classList.contains('leading-whitespace')).toBe false
|
||||
|
||||
editor.setText('a\t')
|
||||
nextAnimationFrame()
|
||||
|
||||
leafNodes = getLeafNodes(component.lineNodeForScreenRow(0))
|
||||
expect(leafNodes[0].classList.contains('trailing-whitespace')).toBe true
|
||||
expect(leafNodes[0].classList.contains('leading-whitespace')).toBe false
|
||||
|
||||
describe "when showInvisibles is enabled", ->
|
||||
invisibles = null
|
||||
|
||||
@@ -253,32 +209,25 @@ describe "EditorComponent", ->
|
||||
|
||||
atom.config.set("editor.showInvisibles", true)
|
||||
atom.config.set("editor.invisibles", invisibles)
|
||||
nextAnimationFrame()
|
||||
|
||||
it "re-renders the lines when the showInvisibles config option changes", ->
|
||||
editor.setText " a line with tabs\tand spaces \n"
|
||||
editor.setText " a line with tabs\tand spaces "
|
||||
nextAnimationFrame()
|
||||
expect(component.lineNodeForScreenRow(0).textContent).toBe "#{invisibles.space}a line with tabs#{invisibles.tab}and spaces#{invisibles.space}#{invisibles.eol}"
|
||||
|
||||
atom.config.set("editor.showInvisibles", false)
|
||||
nextAnimationFrame()
|
||||
expect(component.lineNodeForScreenRow(0).textContent).toBe " a line with tabs and spaces "
|
||||
|
||||
atom.config.set("editor.showInvisibles", true)
|
||||
nextAnimationFrame()
|
||||
expect(component.lineNodeForScreenRow(0).textContent).toBe "#{invisibles.space}a line with tabs#{invisibles.tab}and spaces#{invisibles.space}#{invisibles.eol}"
|
||||
|
||||
it "displays leading/trailing spaces, tabs, and newlines as visible characters", ->
|
||||
editor.setText " a line with tabs\tand spaces \n"
|
||||
it "displays spaces, tabs, and newlines as visible characters", ->
|
||||
editor.setText " a line with tabs\tand spaces "
|
||||
nextAnimationFrame()
|
||||
expect(component.lineNodeForScreenRow(0).textContent).toBe "#{invisibles.space}a line with tabs#{invisibles.tab}and spaces#{invisibles.space}#{invisibles.eol}"
|
||||
|
||||
leafNodes = getLeafNodes(component.lineNodeForScreenRow(0))
|
||||
expect(leafNodes[0].classList.contains('invisible-character')).toBe true
|
||||
expect(leafNodes[leafNodes.length - 1].classList.contains('invisible-character')).toBe true
|
||||
|
||||
it "displays newlines as their own token outside of the other tokens' scopes", ->
|
||||
editor.setText "var\n"
|
||||
editor.setText "var"
|
||||
nextAnimationFrame()
|
||||
expect(component.lineNodeForScreenRow(0).innerHTML).toBe "<span class=\"source js\"><span class=\"storage modifier js\">var</span></span><span class=\"invisible-character\">#{invisibles.eol}</span>"
|
||||
|
||||
@@ -292,12 +241,10 @@ describe "EditorComponent", ->
|
||||
|
||||
it "renders an nbsp on empty lines when the line-ending character is an empty string", ->
|
||||
atom.config.set("editor.invisibles", eol: '')
|
||||
nextAnimationFrame()
|
||||
expect(component.lineNodeForScreenRow(10).textContent).toBe nbsp
|
||||
|
||||
it "renders an nbsp on empty lines when the line-ending character is false", ->
|
||||
atom.config.set("editor.invisibles", eol: false)
|
||||
nextAnimationFrame()
|
||||
it "renders an nbsp on empty lines when no line-ending character is defined", ->
|
||||
atom.config.set("editor.invisibles", eol: null)
|
||||
expect(component.lineNodeForScreenRow(10).textContent).toBe nbsp
|
||||
|
||||
it "interleaves invisible line-ending characters with indent guides on empty lines", ->
|
||||
@@ -321,8 +268,8 @@ describe "EditorComponent", ->
|
||||
|
||||
describe "when soft wrapping is enabled", ->
|
||||
beforeEach ->
|
||||
editor.setText "a line that wraps \n"
|
||||
editor.setSoftWrapped(true)
|
||||
editor.setText "a line that wraps "
|
||||
editor.setSoftWrap(true)
|
||||
nextAnimationFrame()
|
||||
componentNode.style.width = 16 * charWidth + editor.getVerticalScrollbarWidth() + 'px'
|
||||
component.measureHeightAndWidth()
|
||||
@@ -501,7 +448,7 @@ describe "EditorComponent", ->
|
||||
expect(component.lineNumberNodeForScreenRow(6).offsetTop).toBe 6 * lineHeightInPixels
|
||||
|
||||
it "renders • characters for soft-wrapped lines", ->
|
||||
editor.setSoftWrapped(true)
|
||||
editor.setSoftWrap(true)
|
||||
wrapperNode.style.height = 4.5 * lineHeightInPixels + 'px'
|
||||
wrapperNode.style.width = 30 * charWidth + 'px'
|
||||
component.measureHeightAndWidth()
|
||||
@@ -645,7 +592,7 @@ describe "EditorComponent", ->
|
||||
|
||||
describe "cursor rendering", ->
|
||||
it "renders the currently visible cursors, translated relative to the scroll position", ->
|
||||
cursor1 = editor.getLastCursor()
|
||||
cursor1 = editor.getCursor()
|
||||
cursor1.setScreenPosition([0, 5])
|
||||
|
||||
wrapperNode.style.height = 4.5 * lineHeightInPixels + 'px'
|
||||
@@ -754,7 +701,7 @@ describe "EditorComponent", ->
|
||||
expect(cursorsNode.classList.contains('blink-off')).toBe false
|
||||
|
||||
# Stop blinking after moving the cursor
|
||||
editor.moveRight()
|
||||
editor.moveCursorRight()
|
||||
expect(cursorsNode.classList.contains('blink-off')).toBe false
|
||||
|
||||
advanceClock(component.props.cursorBlinkResumeDelay)
|
||||
@@ -858,8 +805,8 @@ describe "EditorComponent", ->
|
||||
it "does not render empty selections", ->
|
||||
editor.addSelectionForBufferRange([[2, 2], [2, 2]])
|
||||
nextAnimationFrame()
|
||||
expect(editor.getSelections()[0].isEmpty()).toBe true
|
||||
expect(editor.getSelections()[1].isEmpty()).toBe true
|
||||
expect(editor.getSelection(0).isEmpty()).toBe true
|
||||
expect(editor.getSelection(1).isEmpty()).toBe true
|
||||
|
||||
expect(componentNode.querySelectorAll('.selection').length).toBe 0
|
||||
|
||||
@@ -937,7 +884,7 @@ describe "EditorComponent", ->
|
||||
|
||||
it "only applies decorations to screen rows that are spanned by their marker when lines are soft-wrapped", ->
|
||||
editor.setText("a line that wraps, ok")
|
||||
editor.setSoftWrapped(true)
|
||||
editor.setSoftWrap(true)
|
||||
componentNode.style.width = 16 * charWidth + 'px'
|
||||
component.measureHeightAndWidth()
|
||||
nextAnimationFrame()
|
||||
@@ -1178,7 +1125,7 @@ describe "EditorComponent", ->
|
||||
it "renders the decoration's new params", ->
|
||||
expect(componentNode.querySelector('.test-highlight')).toBeTruthy()
|
||||
|
||||
decoration.setProperties(type: 'highlight', class: 'new-test-highlight')
|
||||
decoration.update(type: 'highlight', class: 'new-test-highlight')
|
||||
nextAnimationFrame()
|
||||
|
||||
expect(componentNode.querySelector('.test-highlight')).toBeFalsy()
|
||||
@@ -1362,12 +1309,6 @@ describe "EditorComponent", ->
|
||||
linesNode.dispatchEvent(buildMouseEvent('mousedown', clientCoordinatesForScreenPosition([4, 8]), {target}))
|
||||
expect(editor.isFoldedAtBufferRow 4).toBe false
|
||||
|
||||
describe "when the horizontal scrollbar is interacted with", ->
|
||||
it "clicking on the scrollbar does not move the cursor", ->
|
||||
target = horizontalScrollbarNode
|
||||
linesNode.dispatchEvent(buildMouseEvent('mousedown', clientCoordinatesForScreenPosition([4, 8]), {target}))
|
||||
expect(editor.getCursorScreenPosition()).toEqual [0, 0]
|
||||
|
||||
describe "mouse interactions on the gutter", ->
|
||||
gutterNode = null
|
||||
|
||||
@@ -1375,19 +1316,9 @@ describe "EditorComponent", ->
|
||||
gutterNode = componentNode.querySelector('.gutter')
|
||||
|
||||
describe "when the gutter is clicked", ->
|
||||
it "selects the clicked row", ->
|
||||
it "moves the cursor to the beginning of the clicked row", ->
|
||||
gutterNode.dispatchEvent(buildMouseEvent('mousedown', clientCoordinatesForScreenRowInGutter(4)))
|
||||
expect(editor.getSelectedScreenRange()).toEqual [[4, 0], [5, 0]]
|
||||
|
||||
describe "when the gutter is meta-clicked", ->
|
||||
it "creates a new selection for the clicked row", ->
|
||||
editor.setSelectedScreenRange([[3, 0], [3, 2]])
|
||||
|
||||
gutterNode.dispatchEvent(buildMouseEvent('mousedown', clientCoordinatesForScreenRowInGutter(4), metaKey: true))
|
||||
expect(editor.getSelectedScreenRanges()).toEqual [[[3, 0], [3, 2]], [[4, 0], [5, 0]]]
|
||||
|
||||
gutterNode.dispatchEvent(buildMouseEvent('mousedown', clientCoordinatesForScreenRowInGutter(6), metaKey: true))
|
||||
expect(editor.getSelectedScreenRanges()).toEqual [[[3, 0], [3, 2]], [[4, 0], [5, 0]], [[6, 0], [7, 0]]]
|
||||
expect(editor.getCursorScreenPosition()).toEqual [4, 0]
|
||||
|
||||
describe "when the gutter is shift-clicked", ->
|
||||
beforeEach ->
|
||||
@@ -1420,40 +1351,6 @@ describe "EditorComponent", ->
|
||||
gutterNode.dispatchEvent(buildMouseEvent('mouseup', clientCoordinatesForScreenRowInGutter(2)))
|
||||
expect(editor.getSelectedScreenRange()).toEqual [[2, 0], [7, 0]]
|
||||
|
||||
describe "when the gutter is meta-clicked and dragged", ->
|
||||
beforeEach ->
|
||||
editor.setSelectedScreenRange([[3, 0], [3, 2]])
|
||||
|
||||
describe "when dragging downward", ->
|
||||
it "selects the rows between the start and end of the drag", ->
|
||||
gutterNode.dispatchEvent(buildMouseEvent('mousedown', clientCoordinatesForScreenRowInGutter(4), metaKey: true))
|
||||
gutterNode.dispatchEvent(buildMouseEvent('mousemove', clientCoordinatesForScreenRowInGutter(6), metaKey: true))
|
||||
nextAnimationFrame()
|
||||
gutterNode.dispatchEvent(buildMouseEvent('mouseup', clientCoordinatesForScreenRowInGutter(6), metaKey: true))
|
||||
expect(editor.getSelectedScreenRanges()).toEqual [[[3, 0], [3, 2]], [[4, 0], [7, 0]]]
|
||||
|
||||
it "merges overlapping selections", ->
|
||||
gutterNode.dispatchEvent(buildMouseEvent('mousedown', clientCoordinatesForScreenRowInGutter(2), metaKey: true))
|
||||
gutterNode.dispatchEvent(buildMouseEvent('mousemove', clientCoordinatesForScreenRowInGutter(6), metaKey: true))
|
||||
nextAnimationFrame()
|
||||
gutterNode.dispatchEvent(buildMouseEvent('mouseup', clientCoordinatesForScreenRowInGutter(6), metaKey: true))
|
||||
expect(editor.getSelectedScreenRanges()).toEqual [[[2, 0], [7, 0]]]
|
||||
|
||||
describe "when dragging upward", ->
|
||||
it "selects the rows between the start and end of the drag", ->
|
||||
gutterNode.dispatchEvent(buildMouseEvent('mousedown', clientCoordinatesForScreenRowInGutter(6), metaKey: true))
|
||||
gutterNode.dispatchEvent(buildMouseEvent('mousemove', clientCoordinatesForScreenRowInGutter(4), metaKey: true))
|
||||
nextAnimationFrame()
|
||||
gutterNode.dispatchEvent(buildMouseEvent('mouseup', clientCoordinatesForScreenRowInGutter(4), metaKey: true))
|
||||
expect(editor.getSelectedScreenRanges()).toEqual [[[3, 0], [3, 2]], [[4, 0], [7, 0]]]
|
||||
|
||||
it "merges overlapping selections", ->
|
||||
gutterNode.dispatchEvent(buildMouseEvent('mousedown', clientCoordinatesForScreenRowInGutter(6), metaKey: true))
|
||||
gutterNode.dispatchEvent(buildMouseEvent('mousemove', clientCoordinatesForScreenRowInGutter(2), metaKey: true))
|
||||
nextAnimationFrame()
|
||||
gutterNode.dispatchEvent(buildMouseEvent('mouseup', clientCoordinatesForScreenRowInGutter(2), metaKey: true))
|
||||
expect(editor.getSelectedScreenRanges()).toEqual [[[2, 0], [7, 0]]]
|
||||
|
||||
describe "when the gutter is shift-clicked and dragged", ->
|
||||
describe "when the shift-click is below the existing selection's tail", ->
|
||||
describe "when dragging downward", ->
|
||||
@@ -1525,7 +1422,7 @@ describe "EditorComponent", ->
|
||||
cursor = null
|
||||
|
||||
beforeEach ->
|
||||
cursor = editor.getLastCursor()
|
||||
cursor = editor.getCursor()
|
||||
cursor.setScreenPosition([0, 0])
|
||||
|
||||
it "adds the 'has-selection' class to the editor when there is a selection", ->
|
||||
@@ -1645,7 +1542,6 @@ describe "EditorComponent", ->
|
||||
height: 8px;
|
||||
}
|
||||
"""
|
||||
nextAnimationFrame()
|
||||
|
||||
scrollbarCornerNode = componentNode.querySelector('.scrollbar-corner')
|
||||
expect(verticalScrollbarNode.offsetWidth).toBe 8
|
||||
@@ -1871,28 +1767,28 @@ describe "EditorComponent", ->
|
||||
it "inserts the newest character in the input's value into the buffer", ->
|
||||
componentNode.dispatchEvent(buildTextInputEvent(data: 'x', target: inputNode))
|
||||
nextAnimationFrame()
|
||||
expect(editor.lineTextForBufferRow(0)).toBe 'xvar quicksort = function () {'
|
||||
expect(editor.lineForBufferRow(0)).toBe 'xvar quicksort = function () {'
|
||||
|
||||
componentNode.dispatchEvent(buildTextInputEvent(data: 'y', target: inputNode))
|
||||
nextAnimationFrame()
|
||||
expect(editor.lineTextForBufferRow(0)).toBe 'xyvar quicksort = function () {'
|
||||
expect(editor.lineForBufferRow(0)).toBe 'xyvar quicksort = function () {'
|
||||
|
||||
it "replaces the last character if the length of the input's value doesn't increase, as occurs with the accented character menu", ->
|
||||
componentNode.dispatchEvent(buildTextInputEvent(data: 'u', target: inputNode))
|
||||
nextAnimationFrame()
|
||||
expect(editor.lineTextForBufferRow(0)).toBe 'uvar quicksort = function () {'
|
||||
expect(editor.lineForBufferRow(0)).toBe 'uvar quicksort = function () {'
|
||||
|
||||
# simulate the accented character suggestion's selection of the previous character
|
||||
inputNode.setSelectionRange(0, 1)
|
||||
componentNode.dispatchEvent(buildTextInputEvent(data: 'ü', target: inputNode))
|
||||
nextAnimationFrame()
|
||||
expect(editor.lineTextForBufferRow(0)).toBe 'üvar quicksort = function () {'
|
||||
expect(editor.lineForBufferRow(0)).toBe 'üvar quicksort = function () {'
|
||||
|
||||
it "does not handle input events when input is disabled", ->
|
||||
component.setInputEnabled(false)
|
||||
componentNode.dispatchEvent(buildTextInputEvent(data: 'x', target: inputNode))
|
||||
expect(nextAnimationFrame).toBe noAnimationFrame
|
||||
expect(editor.lineTextForBufferRow(0)).toBe 'var quicksort = function () {'
|
||||
expect(editor.lineForBufferRow(0)).toBe 'var quicksort = function () {'
|
||||
|
||||
describe "when IME composition is used to insert international characters", ->
|
||||
inputNode = null
|
||||
@@ -1910,46 +1806,46 @@ describe "EditorComponent", ->
|
||||
it "inserts the chosen completion", ->
|
||||
componentNode.dispatchEvent(buildIMECompositionEvent('compositionstart', target: inputNode))
|
||||
componentNode.dispatchEvent(buildIMECompositionEvent('compositionupdate', data: 's', target: inputNode))
|
||||
expect(editor.lineTextForBufferRow(0)).toBe 'svar quicksort = function () {'
|
||||
expect(editor.lineForBufferRow(0)).toBe 'svar quicksort = function () {'
|
||||
|
||||
componentNode.dispatchEvent(buildIMECompositionEvent('compositionupdate', data: 'sd', target: inputNode))
|
||||
expect(editor.lineTextForBufferRow(0)).toBe 'sdvar quicksort = function () {'
|
||||
expect(editor.lineForBufferRow(0)).toBe 'sdvar quicksort = function () {'
|
||||
|
||||
componentNode.dispatchEvent(buildIMECompositionEvent('compositionend', target: inputNode))
|
||||
componentNode.dispatchEvent(buildTextInputEvent(data: '速度', target: inputNode))
|
||||
expect(editor.lineTextForBufferRow(0)).toBe '速度var quicksort = function () {'
|
||||
expect(editor.lineForBufferRow(0)).toBe '速度var quicksort = function () {'
|
||||
|
||||
it "reverts back to the original text when the completion helper is dismissed", ->
|
||||
componentNode.dispatchEvent(buildIMECompositionEvent('compositionstart', target: inputNode))
|
||||
componentNode.dispatchEvent(buildIMECompositionEvent('compositionupdate', data: 's', target: inputNode))
|
||||
expect(editor.lineTextForBufferRow(0)).toBe 'svar quicksort = function () {'
|
||||
expect(editor.lineForBufferRow(0)).toBe 'svar quicksort = function () {'
|
||||
|
||||
componentNode.dispatchEvent(buildIMECompositionEvent('compositionupdate', data: 'sd', target: inputNode))
|
||||
expect(editor.lineTextForBufferRow(0)).toBe 'sdvar quicksort = function () {'
|
||||
expect(editor.lineForBufferRow(0)).toBe 'sdvar quicksort = function () {'
|
||||
|
||||
componentNode.dispatchEvent(buildIMECompositionEvent('compositionend', target: inputNode))
|
||||
expect(editor.lineTextForBufferRow(0)).toBe 'var quicksort = function () {'
|
||||
expect(editor.lineForBufferRow(0)).toBe 'var quicksort = function () {'
|
||||
|
||||
it "allows multiple accented character to be inserted with the ' on a US international layout", ->
|
||||
inputNode.value = "'"
|
||||
inputNode.setSelectionRange(0, 1)
|
||||
componentNode.dispatchEvent(buildIMECompositionEvent('compositionstart', target: inputNode))
|
||||
componentNode.dispatchEvent(buildIMECompositionEvent('compositionupdate', data: "'", target: inputNode))
|
||||
expect(editor.lineTextForBufferRow(0)).toBe "'var quicksort = function () {"
|
||||
expect(editor.lineForBufferRow(0)).toBe "'var quicksort = function () {"
|
||||
|
||||
componentNode.dispatchEvent(buildIMECompositionEvent('compositionend', target: inputNode))
|
||||
componentNode.dispatchEvent(buildTextInputEvent(data: 'á', target: inputNode))
|
||||
expect(editor.lineTextForBufferRow(0)).toBe "ávar quicksort = function () {"
|
||||
expect(editor.lineForBufferRow(0)).toBe "ávar quicksort = function () {"
|
||||
|
||||
inputNode.value = "'"
|
||||
inputNode.setSelectionRange(0, 1)
|
||||
componentNode.dispatchEvent(buildIMECompositionEvent('compositionstart', target: inputNode))
|
||||
componentNode.dispatchEvent(buildIMECompositionEvent('compositionupdate', data: "'", target: inputNode))
|
||||
expect(editor.lineTextForBufferRow(0)).toBe "á'var quicksort = function () {"
|
||||
expect(editor.lineForBufferRow(0)).toBe "á'var quicksort = function () {"
|
||||
|
||||
componentNode.dispatchEvent(buildIMECompositionEvent('compositionend', target: inputNode))
|
||||
componentNode.dispatchEvent(buildTextInputEvent(data: 'á', target: inputNode))
|
||||
expect(editor.lineTextForBufferRow(0)).toBe "áávar quicksort = function () {"
|
||||
expect(editor.lineForBufferRow(0)).toBe "áávar quicksort = function () {"
|
||||
|
||||
describe "when a string is selected", ->
|
||||
beforeEach ->
|
||||
@@ -1958,25 +1854,25 @@ describe "EditorComponent", ->
|
||||
it "inserts the chosen completion", ->
|
||||
componentNode.dispatchEvent(buildIMECompositionEvent('compositionstart', target: inputNode))
|
||||
componentNode.dispatchEvent(buildIMECompositionEvent('compositionupdate', data: 's', target: inputNode))
|
||||
expect(editor.lineTextForBufferRow(0)).toBe 'var ssort = function () {'
|
||||
expect(editor.lineForBufferRow(0)).toBe 'var ssort = function () {'
|
||||
|
||||
componentNode.dispatchEvent(buildIMECompositionEvent('compositionupdate', data: 'sd', target: inputNode))
|
||||
expect(editor.lineTextForBufferRow(0)).toBe 'var sdsort = function () {'
|
||||
expect(editor.lineForBufferRow(0)).toBe 'var sdsort = function () {'
|
||||
|
||||
componentNode.dispatchEvent(buildIMECompositionEvent('compositionend', target: inputNode))
|
||||
componentNode.dispatchEvent(buildTextInputEvent(data: '速度', target: inputNode))
|
||||
expect(editor.lineTextForBufferRow(0)).toBe 'var 速度sort = function () {'
|
||||
expect(editor.lineForBufferRow(0)).toBe 'var 速度sort = function () {'
|
||||
|
||||
it "reverts back to the original text when the completion helper is dismissed", ->
|
||||
componentNode.dispatchEvent(buildIMECompositionEvent('compositionstart', target: inputNode))
|
||||
componentNode.dispatchEvent(buildIMECompositionEvent('compositionupdate', data: 's', target: inputNode))
|
||||
expect(editor.lineTextForBufferRow(0)).toBe 'var ssort = function () {'
|
||||
expect(editor.lineForBufferRow(0)).toBe 'var ssort = function () {'
|
||||
|
||||
componentNode.dispatchEvent(buildIMECompositionEvent('compositionupdate', data: 'sd', target: inputNode))
|
||||
expect(editor.lineTextForBufferRow(0)).toBe 'var sdsort = function () {'
|
||||
expect(editor.lineForBufferRow(0)).toBe 'var sdsort = function () {'
|
||||
|
||||
componentNode.dispatchEvent(buildIMECompositionEvent('compositionend', target: inputNode))
|
||||
expect(editor.lineTextForBufferRow(0)).toBe 'var quicksort = function () {'
|
||||
expect(editor.lineForBufferRow(0)).toBe 'var quicksort = function () {'
|
||||
|
||||
describe "commands", ->
|
||||
describe "editor:consolidate-selections", ->
|
||||
@@ -1999,7 +1895,7 @@ describe "EditorComponent", ->
|
||||
hiddenParent.style.display = 'none'
|
||||
contentNode.appendChild(hiddenParent)
|
||||
|
||||
wrapperView = new EditorView(editor, {lineOverdrawMargin})
|
||||
wrapperView = new ReactEditorView(editor, {lineOverdrawMargin})
|
||||
wrapperNode = wrapperView.element
|
||||
wrapperView.appendTo(hiddenParent)
|
||||
|
||||
@@ -2041,7 +1937,6 @@ describe "EditorComponent", ->
|
||||
wrapperView.hide()
|
||||
|
||||
component.setFontSize(22)
|
||||
editor.getBuffer().insert([0, 0], 'a') # regression test against atom/atom#3318
|
||||
|
||||
wrapperView.show()
|
||||
editor.setCursorBufferPosition([0, Infinity])
|
||||
@@ -2110,7 +2005,7 @@ describe "EditorComponent", ->
|
||||
|
||||
describe "soft wrapping", ->
|
||||
beforeEach ->
|
||||
editor.setSoftWrapped(true)
|
||||
editor.setSoftWrap(true)
|
||||
nextAnimationFrame()
|
||||
|
||||
it "updates the wrap location when the editor is resized", ->
|
||||
@@ -2208,13 +2103,9 @@ describe "EditorComponent", ->
|
||||
it "adds the 'mini' class to the wrapper view", ->
|
||||
expect(wrapperNode.classList.contains('mini')).toBe true
|
||||
|
||||
it "does not have an opaque background on lines", ->
|
||||
expect(component.refs.lines.getDOMNode().getAttribute('style')).not.toContain 'background-color'
|
||||
|
||||
it "does not render invisible characters", ->
|
||||
atom.config.set('editor.invisibles', eol: 'E')
|
||||
atom.config.set('editor.showInvisibles', true)
|
||||
nextAnimationFrame()
|
||||
component.setInvisibles(eol: 'E')
|
||||
component.setShowInvisibles(true)
|
||||
expect(component.lineNodeForScreenRow(0).textContent).toBe 'var quicksort = function () {'
|
||||
|
||||
it "does not assign an explicit line-height on the editor contents", ->
|
||||
@@ -2236,10 +2127,10 @@ describe "EditorComponent", ->
|
||||
|
||||
describe "legacy editor compatibility", ->
|
||||
it "triggers the screen-lines-changed event before the editor:display-update event", ->
|
||||
editor.setSoftWrapped(true)
|
||||
editor.setSoftWrap(true)
|
||||
|
||||
callingOrder = []
|
||||
editor.onDidChange -> callingOrder.push 'screen-lines-changed'
|
||||
editor.on 'screen-lines-changed', -> callingOrder.push 'screen-lines-changed'
|
||||
wrapperView.on 'editor:display-updated', -> callingOrder.push 'editor:display-updated'
|
||||
editor.insertText("HELLO! HELLO!\n HELLO! HELLO! HELLO! HELLO! HELLO! HELLO! HELLO! HELLO! HELLO! HELLO! HELLO! HELLO! HELLO! HELLO! HELLO! HELLO! HELLO! HELLO! ")
|
||||
nextAnimationFrame()
|
||||
@@ -2253,12 +2144,6 @@ describe "EditorComponent", ->
|
||||
setEditorWidthInChars(wrapperView, 10)
|
||||
expect(componentNode.querySelector('.scroll-view').offsetWidth).toBe charWidth * 10
|
||||
|
||||
describe "grammar data attributes", ->
|
||||
it "adds and updates the grammar data attribute based on the current grammar", ->
|
||||
expect(wrapperNode.dataset.grammar).toBe 'source js'
|
||||
editor.setGrammar(atom.syntax.nullGrammar)
|
||||
expect(wrapperNode.dataset.grammar).toBe 'text plain null-grammar'
|
||||
|
||||
buildMouseEvent = (type, properties...) ->
|
||||
properties = extend({bubbles: true, cancelable: true}, properties...)
|
||||
properties.detail ?= 1
|
||||
|
||||
+344
-636
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
@@ -1,4 +0,0 @@
|
||||
module.exports =
|
||||
activate: ->
|
||||
deactivate: -> throw new Error('Top that')
|
||||
serialize: ->
|
||||
+31
-63
@@ -1,5 +1,5 @@
|
||||
temp = require 'temp'
|
||||
GitRepository = require '../src/git-repository'
|
||||
Git = require '../src/git'
|
||||
fs = require 'fs-plus'
|
||||
path = require 'path'
|
||||
Task = require '../src/task'
|
||||
@@ -10,7 +10,7 @@ copyRepository = ->
|
||||
fs.renameSync(path.join(workingDirPath, 'git.git'), path.join(workingDirPath, '.git'))
|
||||
workingDirPath
|
||||
|
||||
describe "GitRepository", ->
|
||||
describe "Git", ->
|
||||
repo = null
|
||||
|
||||
beforeEach ->
|
||||
@@ -22,28 +22,28 @@ describe "GitRepository", ->
|
||||
|
||||
describe "@open(path)", ->
|
||||
it "returns null when no repository is found", ->
|
||||
expect(GitRepository.open(path.join(temp.dir, 'nogit.txt'))).toBeNull()
|
||||
expect(Git.open(path.join(temp.dir, 'nogit.txt'))).toBeNull()
|
||||
|
||||
describe "new GitRepository(path)", ->
|
||||
describe "new Git(path)", ->
|
||||
it "throws an exception when no repository is found", ->
|
||||
expect(-> new GitRepository(path.join(temp.dir, 'nogit.txt'))).toThrow()
|
||||
expect(-> new Git(path.join(temp.dir, 'nogit.txt'))).toThrow()
|
||||
|
||||
describe ".getPath()", ->
|
||||
it "returns the repository path for a .git directory path", ->
|
||||
repo = new GitRepository(path.join(__dirname, 'fixtures', 'git', 'master.git', 'HEAD'))
|
||||
repo = new Git(path.join(__dirname, 'fixtures', 'git', 'master.git', 'HEAD'))
|
||||
expect(repo.getPath()).toBe path.join(__dirname, 'fixtures', 'git', 'master.git')
|
||||
|
||||
it "returns the repository path for a repository path", ->
|
||||
repo = new GitRepository(path.join(__dirname, 'fixtures', 'git', 'master.git'))
|
||||
repo = new Git(path.join(__dirname, 'fixtures', 'git', 'master.git'))
|
||||
expect(repo.getPath()).toBe path.join(__dirname, 'fixtures', 'git', 'master.git')
|
||||
|
||||
describe ".isPathIgnored(path)", ->
|
||||
it "returns true for an ignored path", ->
|
||||
repo = new GitRepository(path.join(__dirname, 'fixtures', 'git', 'ignore.git'))
|
||||
repo = new Git(path.join(__dirname, 'fixtures', 'git', 'ignore.git'))
|
||||
expect(repo.isPathIgnored('a.txt')).toBeTruthy()
|
||||
|
||||
it "returns false for a non-ignored path", ->
|
||||
repo = new GitRepository(path.join(__dirname, 'fixtures', 'git', 'ignore.git'))
|
||||
repo = new Git(path.join(__dirname, 'fixtures', 'git', 'ignore.git'))
|
||||
expect(repo.isPathIgnored('b.txt')).toBeFalsy()
|
||||
|
||||
describe ".isPathModified(path)", ->
|
||||
@@ -51,7 +51,7 @@ describe "GitRepository", ->
|
||||
|
||||
beforeEach ->
|
||||
workingDirPath = copyRepository()
|
||||
repo = new GitRepository(workingDirPath)
|
||||
repo = new Git(workingDirPath)
|
||||
filePath = path.join(workingDirPath, 'a.txt')
|
||||
newPath = path.join(workingDirPath, 'new-path.txt')
|
||||
|
||||
@@ -75,7 +75,7 @@ describe "GitRepository", ->
|
||||
|
||||
beforeEach ->
|
||||
workingDirPath = copyRepository()
|
||||
repo = new GitRepository(workingDirPath)
|
||||
repo = new Git(workingDirPath)
|
||||
filePath = path.join(workingDirPath, 'a.txt')
|
||||
newPath = path.join(workingDirPath, 'new-path.txt')
|
||||
fs.writeFileSync(newPath, "i'm new here")
|
||||
@@ -92,7 +92,7 @@ describe "GitRepository", ->
|
||||
|
||||
beforeEach ->
|
||||
workingDirPath = copyRepository()
|
||||
repo = new GitRepository(workingDirPath)
|
||||
repo = new Git(workingDirPath)
|
||||
filePath = path.join(workingDirPath, 'a.txt')
|
||||
|
||||
it "no longer reports a path as modified after checkout", ->
|
||||
@@ -111,49 +111,17 @@ describe "GitRepository", ->
|
||||
fs.writeFileSync(filePath, 'ch ch changes')
|
||||
repo.getPathStatus(filePath)
|
||||
statusHandler = jasmine.createSpy('statusHandler')
|
||||
repo.onDidChangeStatus statusHandler
|
||||
repo.on 'status-changed', statusHandler
|
||||
repo.checkoutHead(filePath)
|
||||
expect(statusHandler.callCount).toBe 1
|
||||
expect(statusHandler.argsForCall[0][0]).toEqual {path: filePath, pathStatus: 0}
|
||||
expect(statusHandler.argsForCall[0][0..1]).toEqual [filePath, 0]
|
||||
|
||||
repo.checkoutHead(filePath)
|
||||
expect(statusHandler.callCount).toBe 1
|
||||
|
||||
describe ".checkoutHeadForEditor(editor)", ->
|
||||
[filePath, editor] = []
|
||||
|
||||
beforeEach ->
|
||||
workingDirPath = copyRepository()
|
||||
repo = new GitRepository(workingDirPath)
|
||||
filePath = path.join(workingDirPath, 'a.txt')
|
||||
fs.writeFileSync(filePath, 'ch ch changes')
|
||||
|
||||
waitsForPromise ->
|
||||
atom.workspace.open(filePath)
|
||||
|
||||
runs ->
|
||||
editor = atom.workspace.getActiveEditor()
|
||||
|
||||
it "displays a confirmation dialog by default", ->
|
||||
spyOn(atom, 'confirm').andCallFake ({buttons}) -> buttons.OK()
|
||||
atom.config.set('editor.confirmCheckoutHeadRevision', true)
|
||||
|
||||
repo.checkoutHeadForEditor(editor)
|
||||
|
||||
expect(fs.readFileSync(filePath, 'utf8')).toBe ''
|
||||
|
||||
it "does not display a dialog when confirmation is disabled", ->
|
||||
spyOn(atom, 'confirm')
|
||||
atom.config.set('editor.confirmCheckoutHeadRevision', false)
|
||||
|
||||
repo.checkoutHeadForEditor(editor)
|
||||
|
||||
expect(fs.readFileSync(filePath, 'utf8')).toBe ''
|
||||
expect(atom.confirm).not.toHaveBeenCalled()
|
||||
|
||||
describe ".destroy()", ->
|
||||
it "throws an exception when any method is called after it is called", ->
|
||||
repo = new GitRepository(require.resolve('./fixtures/git/master.git/HEAD'))
|
||||
repo = new Git(require.resolve('./fixtures/git/master.git/HEAD'))
|
||||
repo.destroy()
|
||||
expect(-> repo.getShortHead()).toThrow()
|
||||
|
||||
@@ -162,16 +130,16 @@ describe "GitRepository", ->
|
||||
|
||||
beforeEach ->
|
||||
workingDirectory = copyRepository()
|
||||
repo = new GitRepository(workingDirectory)
|
||||
repo = new Git(workingDirectory)
|
||||
filePath = path.join(workingDirectory, 'file.txt')
|
||||
|
||||
it "trigger a status-changed event when the new status differs from the last cached one", ->
|
||||
statusHandler = jasmine.createSpy("statusHandler")
|
||||
repo.onDidChangeStatus statusHandler
|
||||
repo.on 'status-changed', statusHandler
|
||||
fs.writeFileSync(filePath, '')
|
||||
status = repo.getPathStatus(filePath)
|
||||
expect(statusHandler.callCount).toBe 1
|
||||
expect(statusHandler.argsForCall[0][0]).toEqual {path: filePath, pathStatus: status}
|
||||
expect(statusHandler.argsForCall[0][0..1]).toEqual [filePath, status]
|
||||
|
||||
fs.writeFileSync(filePath, 'abc')
|
||||
status = repo.getPathStatus(filePath)
|
||||
@@ -182,7 +150,7 @@ describe "GitRepository", ->
|
||||
|
||||
beforeEach ->
|
||||
workingDirectory = copyRepository()
|
||||
repo = new GitRepository(workingDirectory)
|
||||
repo = new Git(workingDirectory)
|
||||
directoryPath = path.join(workingDirectory, 'dir')
|
||||
filePath = path.join(directoryPath, 'b.txt')
|
||||
|
||||
@@ -197,7 +165,7 @@ describe "GitRepository", ->
|
||||
|
||||
beforeEach ->
|
||||
workingDirectory = copyRepository()
|
||||
repo = new GitRepository(workingDirectory)
|
||||
repo = new Git(workingDirectory)
|
||||
modifiedPath = path.join(workingDirectory, 'file.txt')
|
||||
newPath = path.join(workingDirectory, 'untracked.txt')
|
||||
cleanPath = path.join(workingDirectory, 'other.txt')
|
||||
@@ -208,7 +176,7 @@ describe "GitRepository", ->
|
||||
it "returns status information for all new and modified files", ->
|
||||
fs.writeFileSync(modifiedPath, 'making this path modified')
|
||||
statusHandler = jasmine.createSpy('statusHandler')
|
||||
repo.onDidChangeStatuses statusHandler
|
||||
repo.on 'statuses-changed', statusHandler
|
||||
repo.refreshStatus()
|
||||
|
||||
waitsFor ->
|
||||
@@ -232,19 +200,19 @@ describe "GitRepository", ->
|
||||
editor.insertNewline()
|
||||
|
||||
statusHandler = jasmine.createSpy('statusHandler')
|
||||
atom.project.getRepo().onDidChangeStatus statusHandler
|
||||
atom.project.getRepo().on 'status-changed', statusHandler
|
||||
editor.save()
|
||||
expect(statusHandler.callCount).toBe 1
|
||||
expect(statusHandler).toHaveBeenCalledWith {path: editor.getPath(), pathStatus: 256}
|
||||
expect(statusHandler).toHaveBeenCalledWith editor.getPath(), 256
|
||||
|
||||
it "emits a status-changed event when a buffer is reloaded", ->
|
||||
fs.writeFileSync(editor.getPath(), 'changed')
|
||||
|
||||
statusHandler = jasmine.createSpy('statusHandler')
|
||||
atom.project.getRepo().onDidChangeStatus statusHandler
|
||||
atom.project.getRepo().on 'status-changed', statusHandler
|
||||
editor.getBuffer().reload()
|
||||
expect(statusHandler.callCount).toBe 1
|
||||
expect(statusHandler).toHaveBeenCalledWith {path: editor.getPath(), pathStatus: 256}
|
||||
expect(statusHandler).toHaveBeenCalledWith editor.getPath(), 256
|
||||
editor.getBuffer().reload()
|
||||
expect(statusHandler.callCount).toBe 1
|
||||
|
||||
@@ -252,11 +220,11 @@ describe "GitRepository", ->
|
||||
fs.writeFileSync(editor.getPath(), 'changed')
|
||||
|
||||
statusHandler = jasmine.createSpy('statusHandler')
|
||||
atom.project.getRepo().onDidChangeStatus statusHandler
|
||||
editor.getBuffer().emitter.emit 'did-change-path'
|
||||
atom.project.getRepo().on 'status-changed', statusHandler
|
||||
editor.getBuffer().emit 'path-changed'
|
||||
expect(statusHandler.callCount).toBe 1
|
||||
expect(statusHandler).toHaveBeenCalledWith {path: editor.getPath(), pathStatus: 256}
|
||||
editor.getBuffer().emitter.emit 'did-change-path'
|
||||
expect(statusHandler).toHaveBeenCalledWith editor.getPath(), 256
|
||||
editor.getBuffer().emit 'path-changed'
|
||||
expect(statusHandler.callCount).toBe 1
|
||||
|
||||
describe "when a project is deserialized", ->
|
||||
@@ -283,7 +251,7 @@ describe "GitRepository", ->
|
||||
buffer.append('changes')
|
||||
|
||||
statusHandler = jasmine.createSpy('statusHandler')
|
||||
project2.getRepo().onDidChangeStatus statusHandler
|
||||
project2.getRepo().on 'status-changed', statusHandler
|
||||
buffer.save()
|
||||
expect(statusHandler.callCount).toBe 1
|
||||
expect(statusHandler).toHaveBeenCalledWith {path: buffer.getPath(), pathStatus: 256}
|
||||
expect(statusHandler).toHaveBeenCalledWith buffer.getPath(), 256
|
||||
|
||||
@@ -7,8 +7,6 @@ module.exports.runSpecSuite = (specSuite, logFile, logErrors=true) ->
|
||||
|
||||
{TerminalReporter} = require 'jasmine-tagged'
|
||||
|
||||
disableFocusMethods() if process.env.JANKY_SHA1
|
||||
|
||||
TimeReporter = require './time-reporter'
|
||||
timeReporter = new TimeReporter()
|
||||
|
||||
@@ -40,10 +38,3 @@ module.exports.runSpecSuite = (specSuite, logFile, logErrors=true) ->
|
||||
$('body').append $$ -> @div id: 'jasmine-content'
|
||||
|
||||
jasmineEnv.execute()
|
||||
|
||||
disableFocusMethods = ->
|
||||
['fdescribe', 'ffdescribe', 'fffdescribe', 'fit', 'ffit', 'fffit'].forEach (methodName) ->
|
||||
focusMethod = window[methodName]
|
||||
window[methodName] = (description) ->
|
||||
error = new Error('Focused spec is running on CI')
|
||||
focusMethod description, -> throw error
|
||||
|
||||
@@ -70,28 +70,6 @@ describe "LanguageMode", ->
|
||||
expect(languageMode.rowRangeForCodeFoldAtBufferRow(2)).toBeNull()
|
||||
expect(languageMode.rowRangeForCodeFoldAtBufferRow(4)).toEqual [4, 7]
|
||||
|
||||
describe ".rowRangeForCommentAtBufferRow(bufferRow)", ->
|
||||
it "returns the start/end rows of the foldable comment starting at the given row", ->
|
||||
buffer.setText("//this is a multi line comment\n//another line")
|
||||
expect(languageMode.rowRangeForCommentAtBufferRow(0)).toEqual [0, 1]
|
||||
expect(languageMode.rowRangeForCommentAtBufferRow(1)).toEqual [0, 1]
|
||||
|
||||
buffer.setText("//this is a multi line comment\n//another line\n//and one more")
|
||||
expect(languageMode.rowRangeForCommentAtBufferRow(0)).toEqual [0, 2]
|
||||
expect(languageMode.rowRangeForCommentAtBufferRow(1)).toEqual [0, 2]
|
||||
|
||||
buffer.setText("//this is a multi line comment\n\n//with an empty line")
|
||||
expect(languageMode.rowRangeForCommentAtBufferRow(0)).toBeUndefined()
|
||||
expect(languageMode.rowRangeForCommentAtBufferRow(1)).toBeUndefined()
|
||||
expect(languageMode.rowRangeForCommentAtBufferRow(2)).toBeUndefined()
|
||||
|
||||
buffer.setText("//this is a single line comment\n")
|
||||
expect(languageMode.rowRangeForCommentAtBufferRow(0)).toBeUndefined()
|
||||
expect(languageMode.rowRangeForCommentAtBufferRow(1)).toBeUndefined()
|
||||
|
||||
buffer.setText("//this is a single line comment")
|
||||
expect(languageMode.rowRangeForCommentAtBufferRow(0)).toBeUndefined()
|
||||
|
||||
describe "suggestedIndentForBufferRow", ->
|
||||
it "returns the suggested indentation based on auto-indent/outdent rules", ->
|
||||
expect(languageMode.suggestedIndentForBufferRow(0)).toBe 0
|
||||
@@ -297,46 +275,46 @@ describe "LanguageMode", ->
|
||||
it "folds every foldable line", ->
|
||||
languageMode.foldAll()
|
||||
|
||||
fold1 = editor.tokenizedLineForScreenRow(0).fold
|
||||
fold1 = editor.lineForScreenRow(0).fold
|
||||
expect([fold1.getStartRow(), fold1.getEndRow()]).toEqual [0, 12]
|
||||
fold1.destroy()
|
||||
|
||||
fold2 = editor.tokenizedLineForScreenRow(1).fold
|
||||
fold2 = editor.lineForScreenRow(1).fold
|
||||
expect([fold2.getStartRow(), fold2.getEndRow()]).toEqual [1, 9]
|
||||
fold2.destroy()
|
||||
|
||||
fold3 = editor.tokenizedLineForScreenRow(4).fold
|
||||
fold3 = editor.lineForScreenRow(4).fold
|
||||
expect([fold3.getStartRow(), fold3.getEndRow()]).toEqual [4, 7]
|
||||
|
||||
describe ".foldBufferRow(bufferRow)", ->
|
||||
describe "when bufferRow can be folded", ->
|
||||
it "creates a fold based on the syntactic region starting at the given row", ->
|
||||
languageMode.foldBufferRow(1)
|
||||
fold = editor.tokenizedLineForScreenRow(1).fold
|
||||
fold = editor.lineForScreenRow(1).fold
|
||||
expect(fold.getStartRow()).toBe 1
|
||||
expect(fold.getEndRow()).toBe 9
|
||||
|
||||
describe "when bufferRow can't be folded", ->
|
||||
it "searches upward for the first row that begins a syntatic region containing the given buffer row (and folds it)", ->
|
||||
languageMode.foldBufferRow(8)
|
||||
fold = editor.tokenizedLineForScreenRow(1).fold
|
||||
fold = editor.lineForScreenRow(1).fold
|
||||
expect(fold.getStartRow()).toBe 1
|
||||
expect(fold.getEndRow()).toBe 9
|
||||
|
||||
describe "when the bufferRow is already folded", ->
|
||||
it "searches upward for the first row that begins a syntatic region containing the folded row (and folds it)", ->
|
||||
languageMode.foldBufferRow(2)
|
||||
expect(editor.tokenizedLineForScreenRow(1).fold).toBeDefined()
|
||||
expect(editor.tokenizedLineForScreenRow(0).fold).not.toBeDefined()
|
||||
expect(editor.lineForScreenRow(1).fold).toBeDefined()
|
||||
expect(editor.lineForScreenRow(0).fold).not.toBeDefined()
|
||||
|
||||
languageMode.foldBufferRow(1)
|
||||
expect(editor.tokenizedLineForScreenRow(0).fold).toBeDefined()
|
||||
expect(editor.lineForScreenRow(0).fold).toBeDefined()
|
||||
|
||||
describe "when the bufferRow is in a multi-line comment", ->
|
||||
it "searches upward and downward for surrounding comment lines and folds them as a single fold", ->
|
||||
buffer.insert([1,0], " //this is a comment\n // and\n //more docs\n\n//second comment")
|
||||
languageMode.foldBufferRow(1)
|
||||
fold = editor.tokenizedLineForScreenRow(1).fold
|
||||
fold = editor.lineForScreenRow(1).fold
|
||||
expect(fold.getStartRow()).toBe 1
|
||||
expect(fold.getEndRow()).toBe 3
|
||||
|
||||
@@ -344,7 +322,7 @@ describe "LanguageMode", ->
|
||||
it "searches upward for the first row that begins a syntatic region containing the folded row (and folds it)", ->
|
||||
buffer.insert([1,0], " //this is a single line comment\n")
|
||||
languageMode.foldBufferRow(1)
|
||||
fold = editor.tokenizedLineForScreenRow(0).fold
|
||||
fold = editor.lineForScreenRow(0).fold
|
||||
expect(fold.getStartRow()).toBe 0
|
||||
expect(fold.getEndRow()).toBe 13
|
||||
|
||||
@@ -379,38 +357,38 @@ describe "LanguageMode", ->
|
||||
it "folds every foldable line", ->
|
||||
languageMode.foldAll()
|
||||
|
||||
fold1 = editor.tokenizedLineForScreenRow(0).fold
|
||||
fold1 = editor.lineForScreenRow(0).fold
|
||||
expect([fold1.getStartRow(), fold1.getEndRow()]).toEqual [0, 19]
|
||||
fold1.destroy()
|
||||
|
||||
fold2 = editor.tokenizedLineForScreenRow(1).fold
|
||||
fold2 = editor.lineForScreenRow(1).fold
|
||||
expect([fold2.getStartRow(), fold2.getEndRow()]).toEqual [1, 4]
|
||||
|
||||
fold3 = editor.tokenizedLineForScreenRow(2).fold.destroy()
|
||||
fold3 = editor.lineForScreenRow(2).fold.destroy()
|
||||
|
||||
fold4 = editor.tokenizedLineForScreenRow(3).fold
|
||||
fold4 = editor.lineForScreenRow(3).fold
|
||||
expect([fold4.getStartRow(), fold4.getEndRow()]).toEqual [6, 8]
|
||||
|
||||
describe ".foldAllAtIndentLevel()", ->
|
||||
it "folds every foldable range at a given indentLevel", ->
|
||||
languageMode.foldAllAtIndentLevel(2)
|
||||
|
||||
fold1 = editor.tokenizedLineForScreenRow(6).fold
|
||||
fold1 = editor.lineForScreenRow(6).fold
|
||||
expect([fold1.getStartRow(), fold1.getEndRow()]).toEqual [6, 8]
|
||||
fold1.destroy()
|
||||
|
||||
fold2 = editor.tokenizedLineForScreenRow(11).fold
|
||||
fold2 = editor.lineForScreenRow(11).fold
|
||||
expect([fold2.getStartRow(), fold2.getEndRow()]).toEqual [11, 14]
|
||||
fold2.destroy()
|
||||
|
||||
it "does not fold anything but the indentLevel", ->
|
||||
languageMode.foldAllAtIndentLevel(0)
|
||||
|
||||
fold1 = editor.tokenizedLineForScreenRow(0).fold
|
||||
fold1 = editor.lineForScreenRow(0).fold
|
||||
expect([fold1.getStartRow(), fold1.getEndRow()]).toEqual [0, 19]
|
||||
fold1.destroy()
|
||||
|
||||
fold2 = editor.tokenizedLineForScreenRow(5).fold
|
||||
fold2 = editor.lineForScreenRow(5).fold
|
||||
expect(fold2).toBeFalsy()
|
||||
|
||||
describe ".isFoldableAtBufferRow(bufferRow)", ->
|
||||
|
||||
@@ -5,9 +5,6 @@ ThemePackage = require '../src/theme-package'
|
||||
|
||||
describe "Package", ->
|
||||
describe "when the package contains incompatible native modules", ->
|
||||
beforeEach ->
|
||||
spyOn(atom, 'inDevMode').andReturn(false)
|
||||
|
||||
it "does not activate it", ->
|
||||
packagePath = atom.project.resolve('packages/package-with-incompatible-native-module')
|
||||
pack = new Package(packagePath)
|
||||
@@ -102,6 +99,6 @@ describe "Package", ->
|
||||
theme.activate()
|
||||
|
||||
it "deactivated event fires on .deactivate()", ->
|
||||
theme.onDidDeactivate spy = jasmine.createSpy()
|
||||
theme.on 'deactivated', spy = jasmine.createSpy()
|
||||
theme.deactivate()
|
||||
expect(spy).toHaveBeenCalled()
|
||||
|
||||
@@ -27,91 +27,42 @@ describe "PaneContainer", ->
|
||||
|
||||
it "preserves the active pane across serialization, independent of focus", ->
|
||||
pane3A.activate()
|
||||
expect(containerA.getActivePane()).toBe pane3A
|
||||
expect(containerA.activePane).toBe pane3A
|
||||
|
||||
containerB = containerA.testSerialization()
|
||||
[pane1B, pane2B, pane3B] = containerB.getPanes()
|
||||
expect(containerB.getActivePane()).toBe pane3B
|
||||
expect(containerB.activePane).toBe pane3B
|
||||
|
||||
it "does not allow the root pane to be destroyed", ->
|
||||
container = new PaneContainer
|
||||
container.getRoot().destroy()
|
||||
expect(container.getRoot()).toBeDefined()
|
||||
expect(container.getRoot().isDestroyed()).toBe false
|
||||
|
||||
describe "::getActivePane()", ->
|
||||
describe "::activePane", ->
|
||||
[container, pane1, pane2] = []
|
||||
|
||||
beforeEach ->
|
||||
container = new PaneContainer
|
||||
pane1 = container.getRoot()
|
||||
pane1 = container.root
|
||||
|
||||
it "returns the first pane if no pane has been made active", ->
|
||||
expect(container.getActivePane()).toBe pane1
|
||||
expect(pane1.isActive()).toBe true
|
||||
it "references the first pane if no pane has been made active", ->
|
||||
expect(container.activePane).toBe pane1
|
||||
expect(pane1.active).toBe true
|
||||
|
||||
it "returns the most pane on which ::activate() was most recently called", ->
|
||||
it "references the most pane on which ::activate was most recently called", ->
|
||||
pane2 = pane1.splitRight()
|
||||
pane2.activate()
|
||||
expect(container.getActivePane()).toBe pane2
|
||||
expect(pane1.isActive()).toBe false
|
||||
expect(pane2.isActive()).toBe true
|
||||
expect(container.activePane).toBe pane2
|
||||
expect(pane1.active).toBe false
|
||||
expect(pane2.active).toBe true
|
||||
pane1.activate()
|
||||
expect(container.getActivePane()).toBe pane1
|
||||
expect(pane1.isActive()).toBe true
|
||||
expect(pane2.isActive()).toBe false
|
||||
expect(container.activePane).toBe pane1
|
||||
expect(pane1.active).toBe true
|
||||
expect(pane2.active).toBe false
|
||||
|
||||
it "returns the next pane if the current active pane is destroyed", ->
|
||||
it "is reassigned to the next pane if the current active pane is destroyed", ->
|
||||
pane2 = pane1.splitRight()
|
||||
pane2.activate()
|
||||
pane2.destroy()
|
||||
expect(container.getActivePane()).toBe pane1
|
||||
expect(pane1.isActive()).toBe true
|
||||
expect(container.activePane).toBe pane1
|
||||
expect(pane1.active).toBe true
|
||||
|
||||
describe "::onDidChangeActivePaneItem()", ->
|
||||
[container, pane1, pane2, observed] = []
|
||||
|
||||
beforeEach ->
|
||||
container = new PaneContainer(root: new Pane(items: [new Object, new Object]))
|
||||
container.getRoot().splitRight(items: [new Object, new Object])
|
||||
[pane1, pane2] = container.getPanes()
|
||||
|
||||
observed = []
|
||||
container.onDidChangeActivePaneItem (item) -> observed.push(item)
|
||||
|
||||
it "invokes observers when the active item of the active pane changes", ->
|
||||
pane2.activateNextItem()
|
||||
pane2.activateNextItem()
|
||||
expect(observed).toEqual [pane2.itemAtIndex(1), pane2.itemAtIndex(0)]
|
||||
|
||||
it "invokes observers when the active pane changes", ->
|
||||
pane1.activate()
|
||||
pane2.activate()
|
||||
expect(observed).toEqual [pane1.itemAtIndex(0), pane2.itemAtIndex(0)]
|
||||
|
||||
describe "::observePanes()", ->
|
||||
it "invokes observers with all current and future panes", ->
|
||||
container = new PaneContainer
|
||||
container.getRoot().splitRight()
|
||||
[pane1, pane2] = container.getPanes()
|
||||
|
||||
observed = []
|
||||
container.observePanes (pane) -> observed.push(pane)
|
||||
|
||||
pane3 = pane2.splitDown()
|
||||
pane4 = pane2.splitRight()
|
||||
|
||||
expect(observed).toEqual [pane1, pane2, pane3, pane4]
|
||||
|
||||
describe "::observePaneItems()", ->
|
||||
it "invokes observers with all current and future pane items", ->
|
||||
container = new PaneContainer(root: new Pane(items: [new Object, new Object]))
|
||||
container.getRoot().splitRight(items: [new Object])
|
||||
[pane1, pane2] = container.getPanes()
|
||||
observed = []
|
||||
container.observePaneItems (pane) -> observed.push(pane)
|
||||
|
||||
pane3 = pane2.splitDown(items: [new Object])
|
||||
pane3.addItems([new Object, new Object])
|
||||
|
||||
expect(observed).toEqual container.getPaneItems()
|
||||
it "does not allow the root pane to be destroyed", ->
|
||||
pane1.destroy()
|
||||
expect(container.root).toBe pane1
|
||||
expect(pane1.isDestroyed()).toBe false
|
||||
|
||||
+105
-216
@@ -21,83 +21,39 @@ describe "Pane", ->
|
||||
describe "construction", ->
|
||||
it "sets the active item to the first item", ->
|
||||
pane = new Pane(items: [new Item("A"), new Item("B")])
|
||||
expect(pane.getActiveItem()).toBe pane.itemAtIndex(0)
|
||||
expect(pane.activeItem).toBe pane.items[0]
|
||||
|
||||
it "compacts the items array", ->
|
||||
pane = new Pane(items: [undefined, new Item("A"), null, new Item("B")])
|
||||
expect(pane.getItems().length).toBe 2
|
||||
expect(pane.getActiveItem()).toBe pane.itemAtIndex(0)
|
||||
|
||||
describe "::activate()", ->
|
||||
[container, pane1, pane2] = []
|
||||
|
||||
beforeEach ->
|
||||
container = new PaneContainer(root: new Pane)
|
||||
container.getRoot().splitRight()
|
||||
[pane1, pane2] = container.getPanes()
|
||||
|
||||
it "changes the active pane on the container", ->
|
||||
expect(container.getActivePane()).toBe pane2
|
||||
pane1.activate()
|
||||
expect(container.getActivePane()).toBe pane1
|
||||
pane2.activate()
|
||||
expect(container.getActivePane()).toBe pane2
|
||||
|
||||
it "invokes ::onDidChangeActivePane observers on the container", ->
|
||||
observed = []
|
||||
container.onDidChangeActivePane (activePane) -> observed.push(activePane)
|
||||
|
||||
pane1.activate()
|
||||
pane1.activate()
|
||||
pane2.activate()
|
||||
pane1.activate()
|
||||
expect(observed).toEqual [pane1, pane2, pane1]
|
||||
|
||||
it "invokes ::onDidChangeActive observers on the relevant panes", ->
|
||||
observed = []
|
||||
pane1.onDidChangeActive (active) -> observed.push(active)
|
||||
pane1.activate()
|
||||
pane2.activate()
|
||||
expect(observed).toEqual [true, false]
|
||||
|
||||
it "invokes ::onDidActivate() observers", ->
|
||||
eventCount = 0
|
||||
pane1.onDidActivate -> eventCount++
|
||||
pane1.activate()
|
||||
pane1.activate()
|
||||
pane2.activate()
|
||||
expect(eventCount).toBe 2
|
||||
expect(pane.items.length).toBe 2
|
||||
expect(pane.activeItem).toBe pane.items[0]
|
||||
|
||||
describe "::addItem(item, index)", ->
|
||||
it "adds the item at the given index", ->
|
||||
pane = new Pane(items: [new Item("A"), new Item("B")])
|
||||
[item1, item2] = pane.getItems()
|
||||
[item1, item2] = pane.items
|
||||
item3 = new Item("C")
|
||||
pane.addItem(item3, 1)
|
||||
expect(pane.getItems()).toEqual [item1, item3, item2]
|
||||
expect(pane.items).toEqual [item1, item3, item2]
|
||||
|
||||
it "adds the item after the active item if no index is provided", ->
|
||||
it "adds the item after the active item ", ->
|
||||
pane = new Pane(items: [new Item("A"), new Item("B"), new Item("C")])
|
||||
[item1, item2, item3] = pane.getItems()
|
||||
[item1, item2, item3] = pane.items
|
||||
pane.activateItem(item2)
|
||||
item4 = new Item("D")
|
||||
pane.addItem(item4)
|
||||
expect(pane.getItems()).toEqual [item1, item2, item4, item3]
|
||||
expect(pane.items).toEqual [item1, item2, item4, item3]
|
||||
|
||||
it "sets the active item after adding the first item", ->
|
||||
pane = new Pane
|
||||
item = new Item("A")
|
||||
pane.addItem(item)
|
||||
expect(pane.getActiveItem()).toBe item
|
||||
|
||||
it "invokes ::onDidAddItem() observers", ->
|
||||
pane = new Pane(items: [new Item("A"), new Item("B")])
|
||||
events = []
|
||||
pane.onDidAddItem (event) -> events.push(event)
|
||||
pane.on 'item-added', -> events.push('item-added')
|
||||
pane.$activeItem.changes.onValue -> events.push('active-item-changed')
|
||||
|
||||
item = new Item("C")
|
||||
pane.addItem(item, 1)
|
||||
expect(events).toEqual [{item, index: 1}]
|
||||
pane.addItem(item)
|
||||
expect(pane.activeItem).toBe item
|
||||
expect(events).toEqual ['item-added', 'active-item-changed']
|
||||
|
||||
describe "::activateItem(item)", ->
|
||||
pane = null
|
||||
@@ -106,102 +62,83 @@ describe "Pane", ->
|
||||
pane = new Pane(items: [new Item("A"), new Item("B")])
|
||||
|
||||
it "changes the active item to the current item", ->
|
||||
expect(pane.getActiveItem()).toBe pane.itemAtIndex(0)
|
||||
pane.activateItem(pane.itemAtIndex(1))
|
||||
expect(pane.getActiveItem()).toBe pane.itemAtIndex(1)
|
||||
expect(pane.activeItem).toBe pane.items[0]
|
||||
pane.activateItem(pane.items[1])
|
||||
expect(pane.activeItem).toBe pane.items[1]
|
||||
|
||||
it "adds the given item if it isn't present in ::items", ->
|
||||
item = new Item("C")
|
||||
pane.activateItem(item)
|
||||
expect(item in pane.getItems()).toBe true
|
||||
expect(pane.getActiveItem()).toBe item
|
||||
|
||||
it "invokes ::onDidChangeActiveItem() observers", ->
|
||||
observed = []
|
||||
pane.onDidChangeActiveItem (item) -> observed.push(item)
|
||||
pane.activateItem(pane.itemAtIndex(1))
|
||||
expect(observed).toEqual [pane.itemAtIndex(1)]
|
||||
expect(item in pane.items).toBe true
|
||||
expect(pane.activeItem).toBe item
|
||||
|
||||
describe "::activateNextItem() and ::activatePreviousItem()", ->
|
||||
it "sets the active item to the next/previous item, looping around at either end", ->
|
||||
pane = new Pane(items: [new Item("A"), new Item("B"), new Item("C")])
|
||||
[item1, item2, item3] = pane.getItems()
|
||||
[item1, item2, item3] = pane.items
|
||||
|
||||
expect(pane.getActiveItem()).toBe item1
|
||||
expect(pane.activeItem).toBe item1
|
||||
pane.activatePreviousItem()
|
||||
expect(pane.getActiveItem()).toBe item3
|
||||
expect(pane.activeItem).toBe item3
|
||||
pane.activatePreviousItem()
|
||||
expect(pane.getActiveItem()).toBe item2
|
||||
expect(pane.activeItem).toBe item2
|
||||
pane.activateNextItem()
|
||||
expect(pane.getActiveItem()).toBe item3
|
||||
expect(pane.activeItem).toBe item3
|
||||
pane.activateNextItem()
|
||||
expect(pane.getActiveItem()).toBe item1
|
||||
expect(pane.activeItem).toBe item1
|
||||
|
||||
describe "::activateItemAtIndex(index)", ->
|
||||
it "activates the item at the given index", ->
|
||||
pane = new Pane(items: [new Item("A"), new Item("B"), new Item("C")])
|
||||
[item1, item2, item3] = pane.getItems()
|
||||
[item1, item2, item3] = pane.items
|
||||
pane.activateItemAtIndex(2)
|
||||
expect(pane.getActiveItem()).toBe item3
|
||||
expect(pane.activeItem).toBe item3
|
||||
pane.activateItemAtIndex(1)
|
||||
expect(pane.getActiveItem()).toBe item2
|
||||
expect(pane.activeItem).toBe item2
|
||||
pane.activateItemAtIndex(0)
|
||||
expect(pane.getActiveItem()).toBe item1
|
||||
expect(pane.activeItem).toBe item1
|
||||
|
||||
# Doesn't fail with out-of-bounds indices
|
||||
pane.activateItemAtIndex(100)
|
||||
expect(pane.getActiveItem()).toBe item1
|
||||
expect(pane.activeItem).toBe item1
|
||||
pane.activateItemAtIndex(-1)
|
||||
expect(pane.getActiveItem()).toBe item1
|
||||
expect(pane.activeItem).toBe item1
|
||||
|
||||
describe "::destroyItem(item)", ->
|
||||
[pane, item1, item2, item3] = []
|
||||
|
||||
beforeEach ->
|
||||
pane = new Pane(items: [new Item("A"), new Item("B"), new Item("C")])
|
||||
[item1, item2, item3] = pane.getItems()
|
||||
[item1, item2, item3] = pane.items
|
||||
|
||||
it "removes the item from the items list and destroyes it", ->
|
||||
expect(pane.getActiveItem()).toBe item1
|
||||
it "removes the item from the items list", ->
|
||||
expect(pane.activeItem).toBe item1
|
||||
pane.destroyItem(item2)
|
||||
expect(item2 in pane.getItems()).toBe false
|
||||
expect(item2.isDestroyed()).toBe true
|
||||
expect(pane.getActiveItem()).toBe item1
|
||||
expect(item2 in pane.items).toBe false
|
||||
expect(pane.activeItem).toBe item1
|
||||
|
||||
pane.destroyItem(item1)
|
||||
expect(item1 in pane.getItems()).toBe false
|
||||
expect(item1.isDestroyed()).toBe true
|
||||
|
||||
it "invokes ::onWillDestroyItem() observers before destroying the item", ->
|
||||
events = []
|
||||
pane.onWillDestroyItem (event) ->
|
||||
expect(item2.isDestroyed()).toBe false
|
||||
events.push(event)
|
||||
|
||||
pane.destroyItem(item2)
|
||||
expect(item2.isDestroyed()).toBe true
|
||||
expect(events).toEqual [{item: item2, index: 1}]
|
||||
|
||||
it "invokes ::onDidRemoveItem() observers", ->
|
||||
events = []
|
||||
pane.onDidRemoveItem (event) -> events.push(event)
|
||||
pane.destroyItem(item2)
|
||||
expect(events).toEqual [{item: item2, index: 1, destroyed: true}]
|
||||
expect(item1 in pane.items).toBe false
|
||||
|
||||
describe "when the destroyed item is the active item and is the first item", ->
|
||||
it "activates the next item", ->
|
||||
expect(pane.getActiveItem()).toBe item1
|
||||
expect(pane.activeItem).toBe item1
|
||||
pane.destroyItem(item1)
|
||||
expect(pane.getActiveItem()).toBe item2
|
||||
expect(pane.activeItem).toBe item2
|
||||
|
||||
describe "when the destroyed item is the active item and is not the first item", ->
|
||||
beforeEach ->
|
||||
pane.activateItem(item2)
|
||||
|
||||
it "activates the previous item", ->
|
||||
expect(pane.getActiveItem()).toBe item2
|
||||
expect(pane.activeItem).toBe item2
|
||||
pane.destroyItem(item2)
|
||||
expect(pane.getActiveItem()).toBe item1
|
||||
expect(pane.activeItem).toBe item1
|
||||
|
||||
it "emits 'item-removed' with the item, its index, and true indicating the item is being destroyed", ->
|
||||
pane.on 'item-removed', itemRemovedHandler = jasmine.createSpy("itemRemovedHandler")
|
||||
pane.destroyItem(item2)
|
||||
expect(itemRemovedHandler).toHaveBeenCalledWith(item2, 1, true)
|
||||
|
||||
describe "if the item is modified", ->
|
||||
itemUri = null
|
||||
@@ -220,7 +157,7 @@ describe "Pane", ->
|
||||
pane.destroyItem(item1)
|
||||
|
||||
expect(item1.save).toHaveBeenCalled()
|
||||
expect(item1 in pane.getItems()).toBe false
|
||||
expect(item1 in pane.items).toBe false
|
||||
expect(item1.isDestroyed()).toBe true
|
||||
|
||||
describe "when the item has no uri", ->
|
||||
@@ -233,7 +170,7 @@ describe "Pane", ->
|
||||
|
||||
expect(atom.showSaveDialogSync).toHaveBeenCalled()
|
||||
expect(item1.saveAs).toHaveBeenCalledWith("/selected/path")
|
||||
expect(item1 in pane.getItems()).toBe false
|
||||
expect(item1 in pane.items).toBe false
|
||||
expect(item1.isDestroyed()).toBe true
|
||||
|
||||
describe "if the [Don't Save] option is selected", ->
|
||||
@@ -242,7 +179,7 @@ describe "Pane", ->
|
||||
pane.destroyItem(item1)
|
||||
|
||||
expect(item1.save).not.toHaveBeenCalled()
|
||||
expect(item1 in pane.getItems()).toBe false
|
||||
expect(item1 in pane.items).toBe false
|
||||
expect(item1.isDestroyed()).toBe true
|
||||
|
||||
describe "if the [Cancel] option is selected", ->
|
||||
@@ -251,7 +188,7 @@ describe "Pane", ->
|
||||
pane.destroyItem(item1)
|
||||
|
||||
expect(item1.save).not.toHaveBeenCalled()
|
||||
expect(item1 in pane.getItems()).toBe true
|
||||
expect(item1 in pane.items).toBe true
|
||||
expect(item1.isDestroyed()).toBe false
|
||||
|
||||
describe "when the last item is destroyed", ->
|
||||
@@ -260,7 +197,7 @@ describe "Pane", ->
|
||||
expect(atom.config.get('core.destroyEmptyPanes')).toBe false
|
||||
pane.destroyItem(item) for item in pane.getItems()
|
||||
expect(pane.isDestroyed()).toBe false
|
||||
expect(pane.getActiveItem()).toBeUndefined()
|
||||
expect(pane.activeItem).toBeUndefined()
|
||||
expect(-> pane.saveActiveItem()).not.toThrow()
|
||||
expect(-> pane.saveActiveItemAs()).not.toThrow()
|
||||
|
||||
@@ -273,10 +210,10 @@ describe "Pane", ->
|
||||
describe "::destroyActiveItem()", ->
|
||||
it "destroys the active item", ->
|
||||
pane = new Pane(items: [new Item("A"), new Item("B")])
|
||||
activeItem = pane.getActiveItem()
|
||||
activeItem = pane.activeItem
|
||||
pane.destroyActiveItem()
|
||||
expect(activeItem.isDestroyed()).toBe true
|
||||
expect(activeItem in pane.getItems()).toBe false
|
||||
expect(activeItem in pane.items).toBe false
|
||||
|
||||
it "does not throw an exception if there are no more items", ->
|
||||
pane = new Pane
|
||||
@@ -285,40 +222,27 @@ describe "Pane", ->
|
||||
describe "::destroyItems()", ->
|
||||
it "destroys all items", ->
|
||||
pane = new Pane(items: [new Item("A"), new Item("B"), new Item("C")])
|
||||
[item1, item2, item3] = pane.getItems()
|
||||
[item1, item2, item3] = pane.items
|
||||
pane.destroyItems()
|
||||
expect(item1.isDestroyed()).toBe true
|
||||
expect(item2.isDestroyed()).toBe true
|
||||
expect(item3.isDestroyed()).toBe true
|
||||
expect(pane.getItems()).toEqual []
|
||||
|
||||
describe "::observeItems()", ->
|
||||
it "invokes the observer with all current and future items", ->
|
||||
pane = new Pane(items: [new Item, new Item])
|
||||
[item1, item2] = pane.getItems()
|
||||
|
||||
observed = []
|
||||
pane.observeItems (item) -> observed.push(item)
|
||||
|
||||
item3 = new Item
|
||||
pane.addItem(item3)
|
||||
|
||||
expect(observed).toEqual [item1, item2, item3]
|
||||
expect(pane.items).toEqual []
|
||||
|
||||
describe "when an item emits a destroyed event", ->
|
||||
it "removes it from the list of items", ->
|
||||
pane = new Pane(items: [new Item("A"), new Item("B"), new Item("C")])
|
||||
[item1, item2, item3] = pane.getItems()
|
||||
pane.itemAtIndex(1).destroy()
|
||||
expect(pane.getItems()).toEqual [item1, item3]
|
||||
[item1, item2, item3] = pane.items
|
||||
pane.items[1].destroy()
|
||||
expect(pane.items).toEqual [item1, item3]
|
||||
|
||||
describe "::destroyInactiveItems()", ->
|
||||
it "destroys all items but the active item", ->
|
||||
pane = new Pane(items: [new Item("A"), new Item("B"), new Item("C")])
|
||||
[item1, item2, item3] = pane.getItems()
|
||||
[item1, item2, item3] = pane.items
|
||||
pane.activateItem(item2)
|
||||
pane.destroyInactiveItems()
|
||||
expect(pane.getItems()).toEqual [item2]
|
||||
expect(pane.items).toEqual [item2]
|
||||
|
||||
describe "::saveActiveItem()", ->
|
||||
pane = null
|
||||
@@ -329,30 +253,30 @@ describe "Pane", ->
|
||||
|
||||
describe "when the active item has a uri", ->
|
||||
beforeEach ->
|
||||
pane.getActiveItem().uri = "test"
|
||||
pane.activeItem.uri = "test"
|
||||
|
||||
describe "when the active item has a save method", ->
|
||||
it "saves the current item", ->
|
||||
pane.getActiveItem().save = jasmine.createSpy("save")
|
||||
pane.activeItem.save = jasmine.createSpy("save")
|
||||
pane.saveActiveItem()
|
||||
expect(pane.getActiveItem().save).toHaveBeenCalled()
|
||||
expect(pane.activeItem.save).toHaveBeenCalled()
|
||||
|
||||
describe "when the current item has no save method", ->
|
||||
it "does nothing", ->
|
||||
expect(pane.getActiveItem().save).toBeUndefined()
|
||||
expect(pane.activeItem.save).toBeUndefined()
|
||||
pane.saveActiveItem()
|
||||
|
||||
describe "when the current item has no uri", ->
|
||||
describe "when the current item has a saveAs method", ->
|
||||
it "opens a save dialog and saves the current item as the selected path", ->
|
||||
pane.getActiveItem().saveAs = jasmine.createSpy("saveAs")
|
||||
pane.activeItem.saveAs = jasmine.createSpy("saveAs")
|
||||
pane.saveActiveItem()
|
||||
expect(atom.showSaveDialogSync).toHaveBeenCalled()
|
||||
expect(pane.getActiveItem().saveAs).toHaveBeenCalledWith('/selected/path')
|
||||
expect(pane.activeItem.saveAs).toHaveBeenCalledWith('/selected/path')
|
||||
|
||||
describe "when the current item has no saveAs method", ->
|
||||
it "does nothing", ->
|
||||
expect(pane.getActiveItem().saveAs).toBeUndefined()
|
||||
expect(pane.activeItem.saveAs).toBeUndefined()
|
||||
pane.saveActiveItem()
|
||||
expect(atom.showSaveDialogSync).not.toHaveBeenCalled()
|
||||
|
||||
@@ -365,22 +289,22 @@ describe "Pane", ->
|
||||
|
||||
describe "when the current item has a saveAs method", ->
|
||||
it "opens the save dialog and calls saveAs on the item with the selected path", ->
|
||||
pane.getActiveItem().path = __filename
|
||||
pane.getActiveItem().saveAs = jasmine.createSpy("saveAs")
|
||||
pane.activeItem.path = __filename
|
||||
pane.activeItem.saveAs = jasmine.createSpy("saveAs")
|
||||
pane.saveActiveItemAs()
|
||||
expect(atom.showSaveDialogSync).toHaveBeenCalledWith(__filename)
|
||||
expect(pane.getActiveItem().saveAs).toHaveBeenCalledWith('/selected/path')
|
||||
expect(pane.activeItem.saveAs).toHaveBeenCalledWith('/selected/path')
|
||||
|
||||
describe "when the current item does not have a saveAs method", ->
|
||||
it "does nothing", ->
|
||||
expect(pane.getActiveItem().saveAs).toBeUndefined()
|
||||
expect(pane.activeItem.saveAs).toBeUndefined()
|
||||
pane.saveActiveItemAs()
|
||||
expect(atom.showSaveDialogSync).not.toHaveBeenCalled()
|
||||
|
||||
describe "::itemForUri(uri)", ->
|
||||
it "returns the item for which a call to .getUri() returns the given uri", ->
|
||||
pane = new Pane(items: [new Item("A"), new Item("B"), new Item("C"), new Item("D")])
|
||||
[item1, item2, item3] = pane.getItems()
|
||||
[item1, item2, item3] = pane.items
|
||||
item1.uri = "a"
|
||||
item2.uri = "b"
|
||||
expect(pane.itemForUri("a")).toBe item1
|
||||
@@ -388,32 +312,24 @@ describe "Pane", ->
|
||||
expect(pane.itemForUri("bogus")).toBeUndefined()
|
||||
|
||||
describe "::moveItem(item, index)", ->
|
||||
[pane, item1, item2, item3, item4] = []
|
||||
|
||||
beforeEach ->
|
||||
it "moves the item to the given index and emits an 'item-moved' event with the item and its new index", ->
|
||||
pane = new Pane(items: [new Item("A"), new Item("B"), new Item("C"), new Item("D")])
|
||||
[item1, item2, item3, item4] = pane.getItems()
|
||||
[item1, item2, item3, item4] = pane.items
|
||||
pane.on 'item-moved', itemMovedHandler = jasmine.createSpy("itemMovedHandler")
|
||||
|
||||
it "moves the item to the given index and invokes ::onDidMoveItem observers", ->
|
||||
pane.moveItem(item1, 2)
|
||||
expect(pane.getItems()).toEqual [item2, item3, item1, item4]
|
||||
expect(itemMovedHandler).toHaveBeenCalledWith(item1, 2)
|
||||
itemMovedHandler.reset()
|
||||
|
||||
pane.moveItem(item2, 3)
|
||||
expect(pane.getItems()).toEqual [item3, item1, item4, item2]
|
||||
expect(itemMovedHandler).toHaveBeenCalledWith(item2, 3)
|
||||
itemMovedHandler.reset()
|
||||
|
||||
pane.moveItem(item2, 1)
|
||||
expect(pane.getItems()).toEqual [item3, item2, item1, item4]
|
||||
|
||||
it "invokes ::onDidMoveItem() observers", ->
|
||||
events = []
|
||||
pane.onDidMoveItem (event) -> events.push(event)
|
||||
|
||||
pane.moveItem(item1, 2)
|
||||
pane.moveItem(item2, 3)
|
||||
expect(events).toEqual [
|
||||
{item: item1, oldIndex: 0, newIndex: 2}
|
||||
{item: item2, oldIndex: 0, newIndex: 3}
|
||||
]
|
||||
expect(itemMovedHandler).toHaveBeenCalledWith(item2, 1)
|
||||
|
||||
describe "::moveItemToPane(item, pane, index)", ->
|
||||
[container, pane1, pane2] = []
|
||||
@@ -423,20 +339,13 @@ describe "Pane", ->
|
||||
pane1 = new Pane(items: [new Item("A"), new Item("B"), new Item("C")])
|
||||
container = new PaneContainer(root: pane1)
|
||||
pane2 = pane1.splitRight(items: [new Item("D"), new Item("E")])
|
||||
[item1, item2, item3] = pane1.getItems()
|
||||
[item4, item5] = pane2.getItems()
|
||||
[item1, item2, item3] = pane1.items
|
||||
[item4, item5] = pane2.items
|
||||
|
||||
it "moves the item to the given pane at the given index", ->
|
||||
pane1.moveItemToPane(item2, pane2, 1)
|
||||
expect(pane1.getItems()).toEqual [item1, item3]
|
||||
expect(pane2.getItems()).toEqual [item4, item2, item5]
|
||||
|
||||
it "invokes ::onDidRemoveItem() observers", ->
|
||||
events = []
|
||||
pane1.onDidRemoveItem (event) -> events.push(event)
|
||||
pane1.moveItemToPane(item2, pane2, 1)
|
||||
|
||||
expect(events).toEqual [{item: item2, index: 1, destroyed: false}]
|
||||
expect(pane1.items).toEqual [item1, item3]
|
||||
expect(pane2.items).toEqual [item4, item2, item5]
|
||||
|
||||
describe "when the moved item the last item in the source pane", ->
|
||||
beforeEach ->
|
||||
@@ -459,27 +368,22 @@ describe "Pane", ->
|
||||
[pane1, container] = []
|
||||
|
||||
beforeEach ->
|
||||
pane1 = new Pane(items: [new Item("A")])
|
||||
pane1 = new Pane(items: ["A"])
|
||||
container = new PaneContainer(root: pane1)
|
||||
|
||||
describe "::splitLeft(params)", ->
|
||||
describe "when the parent is the container root", ->
|
||||
it "replaces itself with a row and inserts a new pane to the left of itself", ->
|
||||
pane2 = pane1.splitLeft(items: [new Item("B")])
|
||||
pane3 = pane1.splitLeft(items: [new Item("C")])
|
||||
pane2 = pane1.splitLeft(items: ["B"])
|
||||
pane3 = pane1.splitLeft(items: ["C"])
|
||||
expect(container.root.orientation).toBe 'horizontal'
|
||||
expect(container.root.children).toEqual [pane2, pane3, pane1]
|
||||
|
||||
describe "when `copyActiveItem: true` is passed in the params", ->
|
||||
it "duplicates the active item", ->
|
||||
pane2 = pane1.splitLeft(copyActiveItem: true)
|
||||
expect(pane2.getActiveItem()).toEqual pane1.getActiveItem()
|
||||
|
||||
describe "when the parent is a column", ->
|
||||
it "replaces itself with a row and inserts a new pane to the left of itself", ->
|
||||
pane1.splitDown()
|
||||
pane2 = pane1.splitLeft(items: [new Item("B")])
|
||||
pane3 = pane1.splitLeft(items: [new Item("C")])
|
||||
pane2 = pane1.splitLeft(items: ["B"])
|
||||
pane3 = pane1.splitLeft(items: ["C"])
|
||||
row = container.root.children[0]
|
||||
expect(row.orientation).toBe 'horizontal'
|
||||
expect(row.children).toEqual [pane2, pane3, pane1]
|
||||
@@ -487,21 +391,16 @@ describe "Pane", ->
|
||||
describe "::splitRight(params)", ->
|
||||
describe "when the parent is the container root", ->
|
||||
it "replaces itself with a row and inserts a new pane to the right of itself", ->
|
||||
pane2 = pane1.splitRight(items: [new Item("B")])
|
||||
pane3 = pane1.splitRight(items: [new Item("C")])
|
||||
pane2 = pane1.splitRight(items: ["B"])
|
||||
pane3 = pane1.splitRight(items: ["C"])
|
||||
expect(container.root.orientation).toBe 'horizontal'
|
||||
expect(container.root.children).toEqual [pane1, pane3, pane2]
|
||||
|
||||
describe "when `copyActiveItem: true` is passed in the params", ->
|
||||
it "duplicates the active item", ->
|
||||
pane2 = pane1.splitRight(copyActiveItem: true)
|
||||
expect(pane2.getActiveItem()).toEqual pane1.getActiveItem()
|
||||
|
||||
describe "when the parent is a column", ->
|
||||
it "replaces itself with a row and inserts a new pane to the right of itself", ->
|
||||
pane1.splitDown()
|
||||
pane2 = pane1.splitRight(items: [new Item("B")])
|
||||
pane3 = pane1.splitRight(items: [new Item("C")])
|
||||
pane2 = pane1.splitRight(items: ["B"])
|
||||
pane3 = pane1.splitRight(items: ["C"])
|
||||
row = container.root.children[0]
|
||||
expect(row.orientation).toBe 'horizontal'
|
||||
expect(row.children).toEqual [pane1, pane3, pane2]
|
||||
@@ -509,21 +408,16 @@ describe "Pane", ->
|
||||
describe "::splitUp(params)", ->
|
||||
describe "when the parent is the container root", ->
|
||||
it "replaces itself with a column and inserts a new pane above itself", ->
|
||||
pane2 = pane1.splitUp(items: [new Item("B")])
|
||||
pane3 = pane1.splitUp(items: [new Item("C")])
|
||||
pane2 = pane1.splitUp(items: ["B"])
|
||||
pane3 = pane1.splitUp(items: ["C"])
|
||||
expect(container.root.orientation).toBe 'vertical'
|
||||
expect(container.root.children).toEqual [pane2, pane3, pane1]
|
||||
|
||||
describe "when `copyActiveItem: true` is passed in the params", ->
|
||||
it "duplicates the active item", ->
|
||||
pane2 = pane1.splitUp(copyActiveItem: true)
|
||||
expect(pane2.getActiveItem()).toEqual pane1.getActiveItem()
|
||||
|
||||
describe "when the parent is a row", ->
|
||||
it "replaces itself with a column and inserts a new pane above itself", ->
|
||||
pane1.splitRight()
|
||||
pane2 = pane1.splitUp(items: [new Item("B")])
|
||||
pane3 = pane1.splitUp(items: [new Item("C")])
|
||||
pane2 = pane1.splitUp(items: ["B"])
|
||||
pane3 = pane1.splitUp(items: ["C"])
|
||||
column = container.root.children[0]
|
||||
expect(column.orientation).toBe 'vertical'
|
||||
expect(column.children).toEqual [pane2, pane3, pane1]
|
||||
@@ -531,21 +425,16 @@ describe "Pane", ->
|
||||
describe "::splitDown(params)", ->
|
||||
describe "when the parent is the container root", ->
|
||||
it "replaces itself with a column and inserts a new pane below itself", ->
|
||||
pane2 = pane1.splitDown(items: [new Item("B")])
|
||||
pane3 = pane1.splitDown(items: [new Item("C")])
|
||||
pane2 = pane1.splitDown(items: ["B"])
|
||||
pane3 = pane1.splitDown(items: ["C"])
|
||||
expect(container.root.orientation).toBe 'vertical'
|
||||
expect(container.root.children).toEqual [pane1, pane3, pane2]
|
||||
|
||||
describe "when `copyActiveItem: true` is passed in the params", ->
|
||||
it "duplicates the active item", ->
|
||||
pane2 = pane1.splitDown(copyActiveItem: true)
|
||||
expect(pane2.getActiveItem()).toEqual pane1.getActiveItem()
|
||||
|
||||
describe "when the parent is a row", ->
|
||||
it "replaces itself with a column and inserts a new pane below itself", ->
|
||||
pane1.splitRight()
|
||||
pane2 = pane1.splitDown(items: [new Item("B")])
|
||||
pane3 = pane1.splitDown(items: [new Item("C")])
|
||||
pane2 = pane1.splitDown(items: ["B"])
|
||||
pane3 = pane1.splitDown(items: ["C"])
|
||||
column = container.root.children[0]
|
||||
expect(column.orientation).toBe 'vertical'
|
||||
expect(column.children).toEqual [pane1, pane3, pane2]
|
||||
@@ -566,7 +455,7 @@ describe "Pane", ->
|
||||
pane2 = pane1.splitRight()
|
||||
|
||||
it "destroys the pane's destroyable items", ->
|
||||
[item1, item2] = pane1.getItems()
|
||||
[item1, item2] = pane1.items
|
||||
pane1.destroy()
|
||||
expect(item1.isDestroyed()).toBe true
|
||||
expect(item2.isDestroyed()).toBe true
|
||||
@@ -604,12 +493,12 @@ describe "Pane", ->
|
||||
|
||||
it "can serialize and deserialize the pane and all its items", ->
|
||||
newPane = pane.testSerialization()
|
||||
expect(newPane.getItems()).toEqual pane.getItems()
|
||||
expect(newPane.items).toEqual pane.items
|
||||
|
||||
it "restores the active item on deserialization", ->
|
||||
pane.activateItemAtIndex(1)
|
||||
newPane = pane.testSerialization()
|
||||
expect(newPane.getActiveItem()).toEqual newPane.itemAtIndex(1)
|
||||
expect(newPane.activeItem).toEqual newPane.items[1]
|
||||
|
||||
it "does not include items that cannot be deserialized", ->
|
||||
spyOn(console, 'warn')
|
||||
@@ -617,8 +506,8 @@ describe "Pane", ->
|
||||
pane.activateItem(unserializable)
|
||||
|
||||
newPane = pane.testSerialization()
|
||||
expect(newPane.getActiveItem()).toEqual pane.itemAtIndex(0)
|
||||
expect(newPane.getItems().length).toBe pane.getItems().length - 1
|
||||
expect(newPane.activeItem).toEqual pane.items[0]
|
||||
expect(newPane.items.length).toBe pane.items.length - 1
|
||||
|
||||
it "includes the pane's focus state in the serialized state", ->
|
||||
pane.focus()
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
PaneContainerView = require '../src/pane-container-view'
|
||||
PaneView = require '../src/pane-view'
|
||||
fs = require 'fs-plus'
|
||||
{Emitter} = require 'event-kit'
|
||||
{$, View} = require 'atom'
|
||||
path = require 'path'
|
||||
temp = require 'temp'
|
||||
@@ -13,14 +12,9 @@ describe "PaneView", ->
|
||||
@deserialize: ({id, text}) -> new TestView({id, text})
|
||||
@content: ({id, text}) -> @div class: 'test-view', id: id, tabindex: -1, text
|
||||
initialize: ({@id, @text}) ->
|
||||
@emitter = new Emitter
|
||||
serialize: -> { deserializer: 'TestView', @id, @text }
|
||||
getUri: -> @id
|
||||
isEqual: (other) -> other? and @id == other.id and @text == other.text
|
||||
changeTitle: ->
|
||||
@emitter.emit 'did-change-title', 'title'
|
||||
onDidChangeTitle: (callback) ->
|
||||
@emitter.on 'did-change-title', callback
|
||||
|
||||
beforeEach ->
|
||||
atom.deserializers.add(TestView)
|
||||
@@ -35,7 +29,7 @@ describe "PaneView", ->
|
||||
|
||||
runs ->
|
||||
pane = container.getRoot()
|
||||
paneModel = pane.getModel()
|
||||
paneModel = pane.model
|
||||
paneModel.addItems([view1, editor1, view2, editor2])
|
||||
|
||||
afterEach ->
|
||||
@@ -43,7 +37,7 @@ describe "PaneView", ->
|
||||
|
||||
describe "when the active pane item changes", ->
|
||||
it "hides all item views except the active one", ->
|
||||
expect(pane.getActiveItem()).toBe view1
|
||||
expect(pane.activeItem).toBe view1
|
||||
expect(view1.css('display')).not.toBe 'none'
|
||||
|
||||
pane.activateItem(view2)
|
||||
@@ -54,7 +48,7 @@ describe "PaneView", ->
|
||||
itemChangedHandler = jasmine.createSpy("itemChangedHandler")
|
||||
container.on 'pane:active-item-changed', itemChangedHandler
|
||||
|
||||
expect(pane.getActiveItem()).toBe view1
|
||||
expect(pane.activeItem).toBe view1
|
||||
paneModel.activateItem(view2)
|
||||
paneModel.activateItem(view2)
|
||||
|
||||
@@ -151,47 +145,22 @@ describe "PaneView", ->
|
||||
expect(view1.data('preservative')).toBe 1234
|
||||
|
||||
describe "when the title of the active item changes", ->
|
||||
describe 'when there is no onDidChangeTitle method', ->
|
||||
beforeEach ->
|
||||
view1.onDidChangeTitle = null
|
||||
view2.onDidChangeTitle = null
|
||||
it "emits pane:active-item-title-changed", ->
|
||||
activeItemTitleChangedHandler = jasmine.createSpy("activeItemTitleChangedHandler")
|
||||
pane.on 'pane:active-item-title-changed', activeItemTitleChangedHandler
|
||||
|
||||
pane.activateItem(view2)
|
||||
pane.activateItem(view1)
|
||||
expect(pane.activeItem).toBe view1
|
||||
|
||||
it "emits pane:active-item-title-changed", ->
|
||||
activeItemTitleChangedHandler = jasmine.createSpy("activeItemTitleChangedHandler")
|
||||
pane.on 'pane:active-item-title-changed', activeItemTitleChangedHandler
|
||||
view2.trigger 'title-changed'
|
||||
expect(activeItemTitleChangedHandler).not.toHaveBeenCalled()
|
||||
|
||||
expect(pane.getActiveItem()).toBe view1
|
||||
view1.trigger 'title-changed'
|
||||
expect(activeItemTitleChangedHandler).toHaveBeenCalled()
|
||||
activeItemTitleChangedHandler.reset()
|
||||
|
||||
view2.trigger 'title-changed'
|
||||
expect(activeItemTitleChangedHandler).not.toHaveBeenCalled()
|
||||
|
||||
view1.trigger 'title-changed'
|
||||
expect(activeItemTitleChangedHandler).toHaveBeenCalled()
|
||||
activeItemTitleChangedHandler.reset()
|
||||
|
||||
pane.activateItem(view2)
|
||||
view2.trigger 'title-changed'
|
||||
expect(activeItemTitleChangedHandler).toHaveBeenCalled()
|
||||
|
||||
describe 'when there is a onDidChangeTitle method', ->
|
||||
it "emits pane:active-item-title-changed", ->
|
||||
activeItemTitleChangedHandler = jasmine.createSpy("activeItemTitleChangedHandler")
|
||||
pane.on 'pane:active-item-title-changed', activeItemTitleChangedHandler
|
||||
|
||||
expect(pane.getActiveItem()).toBe view1
|
||||
view2.changeTitle()
|
||||
expect(activeItemTitleChangedHandler).not.toHaveBeenCalled()
|
||||
|
||||
view1.changeTitle()
|
||||
expect(activeItemTitleChangedHandler).toHaveBeenCalled()
|
||||
activeItemTitleChangedHandler.reset()
|
||||
|
||||
pane.activateItem(view2)
|
||||
view2.changeTitle()
|
||||
expect(activeItemTitleChangedHandler).toHaveBeenCalled()
|
||||
pane.activateItem(view2)
|
||||
view2.trigger 'title-changed'
|
||||
expect(activeItemTitleChangedHandler).toHaveBeenCalled()
|
||||
|
||||
describe "when an unmodifed buffer's path is deleted", ->
|
||||
it "removes the pane item", ->
|
||||
@@ -277,7 +246,7 @@ describe "PaneView", ->
|
||||
|
||||
it "transfers focus to the active view", ->
|
||||
focusHandler = jasmine.createSpy("focusHandler")
|
||||
pane.getActiveItem().on 'focus', focusHandler
|
||||
pane.activeItem.on 'focus', focusHandler
|
||||
pane.focus()
|
||||
expect(focusHandler).toHaveBeenCalled()
|
||||
|
||||
@@ -290,7 +259,7 @@ describe "PaneView", ->
|
||||
describe "when a pane is split", ->
|
||||
it "builds the appropriate pane-row and pane-column views", ->
|
||||
pane1 = pane
|
||||
pane1Model = pane.getModel()
|
||||
pane1Model = pane.model
|
||||
pane.activateItem(editor1)
|
||||
|
||||
pane2Model = pane1Model.splitRight(items: [pane1Model.copyActiveItem()])
|
||||
|
||||
@@ -190,11 +190,6 @@ describe "Project", ->
|
||||
expect(atom.project.getPath()?).toBeFalsy()
|
||||
expect(atom.project.getRootDirectory()?).toBeFalsy()
|
||||
|
||||
it "normalizes the path to remove consecutive slashes, ., and .. segments", ->
|
||||
atom.project.setPath("#{require.resolve('./fixtures/dir/a')}#{path.sep}b#{path.sep}#{path.sep}..")
|
||||
expect(atom.project.getPath()).toEqual path.dirname(require.resolve('./fixtures/dir/a'))
|
||||
expect(atom.project.getRootDirectory().path).toEqual path.dirname(require.resolve('./fixtures/dir/a'))
|
||||
|
||||
describe ".replace()", ->
|
||||
[filePath, commentFilePath, sampleContent, sampleCommentContent] = []
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ describe "Editor", ->
|
||||
logLines()
|
||||
throw new Error("Invalid buffer row #{actualBufferRow} for screen row #{screenRow}", )
|
||||
|
||||
actualScreenLine = editor.tokenizedLineForScreenRow(screenRow)
|
||||
actualScreenLine = editor.lineForScreenRow(screenRow)
|
||||
unless actualScreenLine.text is referenceScreenLine.text
|
||||
logLines()
|
||||
throw new Error("Invalid line text at screen row #{screenRow}")
|
||||
@@ -50,9 +50,9 @@ describe "Editor", ->
|
||||
|
||||
randomlyMutateEditor = ->
|
||||
if Math.random() < .2
|
||||
softWrapped = not editor.isSoftWrapped()
|
||||
steps.push(['setSoftWrapped', softWrapped])
|
||||
editor.setSoftWrapped(softWrapped)
|
||||
softWrap = not editor.getSoftWrap()
|
||||
steps.push(['setSoftWrap', softWrap])
|
||||
editor.setSoftWrap(softWrap)
|
||||
else
|
||||
range = getRandomRange()
|
||||
text = getRandomText()
|
||||
@@ -79,11 +79,11 @@ describe "Editor", ->
|
||||
text
|
||||
|
||||
getReferenceScreenLines = ->
|
||||
if editor.isSoftWrapped()
|
||||
if editor.getSoftWrap()
|
||||
screenLines = []
|
||||
bufferRows = []
|
||||
for bufferRow in [0..tokenizedBuffer.getLastRow()]
|
||||
for screenLine in softWrapLine(tokenizedBuffer.tokenizedLineForRow(bufferRow))
|
||||
for screenLine in softWrapLine(tokenizedBuffer.lineForScreenRow(bufferRow))
|
||||
screenLines.push(screenLine)
|
||||
bufferRows.push(bufferRow)
|
||||
else
|
||||
|
||||
@@ -6,7 +6,7 @@ describe "Selection", ->
|
||||
beforeEach ->
|
||||
buffer = atom.project.bufferForPathSync('sample.js')
|
||||
editor = new Editor(buffer: buffer, tabLength: 2)
|
||||
selection = editor.getLastSelection()
|
||||
selection = editor.getSelection()
|
||||
|
||||
afterEach ->
|
||||
buffer.destroy()
|
||||
@@ -57,10 +57,10 @@ describe "Selection", ->
|
||||
expect(selection.isReversed()).toBeFalsy()
|
||||
|
||||
describe "when only the selection's tail is moved (regression)", ->
|
||||
it "notifies ::onDidChangeRange observers", ->
|
||||
it "emits the 'screen-range-changed' event", ->
|
||||
selection.setBufferRange([[2, 0], [2, 10]], reversed: true)
|
||||
changeScreenRangeHandler = jasmine.createSpy('changeScreenRangeHandler')
|
||||
selection.onDidChangeRange changeScreenRangeHandler
|
||||
selection.on 'screen-range-changed', changeScreenRangeHandler
|
||||
|
||||
buffer.insert([2, 5], 'abc')
|
||||
expect(changeScreenRangeHandler).toHaveBeenCalled()
|
||||
|
||||
@@ -21,7 +21,6 @@ clipboard = require 'clipboard'
|
||||
|
||||
atom.themes.loadBaseStylesheets()
|
||||
atom.themes.requireStylesheet '../static/jasmine'
|
||||
atom.themes.initialLoadComplete = true
|
||||
|
||||
fixturePackagesPath = path.resolve(__dirname, './fixtures/packages')
|
||||
atom.packages.packageDirPaths.unshift(fixturePackagesPath)
|
||||
@@ -29,7 +28,7 @@ atom.keymaps.loadBundledKeymaps()
|
||||
keyBindingsToRestore = atom.keymaps.getKeyBindings()
|
||||
|
||||
$(window).on 'core:close', -> window.close()
|
||||
$(window).on 'beforeunload', ->
|
||||
$(window).on 'unload', ->
|
||||
atom.storeWindowDimensions()
|
||||
atom.saveSync()
|
||||
$('html,body').css('overflow', 'auto')
|
||||
@@ -64,7 +63,6 @@ beforeEach ->
|
||||
atom.project = new Project(path: projectPath)
|
||||
atom.workspace = new Workspace()
|
||||
atom.keymaps.keyBindings = _.clone(keyBindingsToRestore)
|
||||
atom.commands.setRootNode(document.body)
|
||||
|
||||
window.resetTimeouts()
|
||||
atom.packages.packageStates = {}
|
||||
@@ -97,6 +95,7 @@ beforeEach ->
|
||||
config.set "editor.autoIndent", false
|
||||
config.set "core.disabledPackages", ["package-that-throws-an-exception",
|
||||
"package-with-broken-package-json", "package-with-broken-keymap"]
|
||||
config.set "core.useReactEditor", true
|
||||
config.save.reset()
|
||||
atom.config = config
|
||||
|
||||
@@ -121,8 +120,6 @@ beforeEach ->
|
||||
addCustomMatchers(this)
|
||||
|
||||
afterEach ->
|
||||
atom.commands.clear()
|
||||
|
||||
atom.packages.deactivatePackages()
|
||||
atom.menu.template = []
|
||||
|
||||
@@ -141,7 +138,8 @@ afterEach ->
|
||||
|
||||
jasmine.unspy(atom, 'saveSync')
|
||||
ensureNoPathSubscriptions()
|
||||
atom.syntax.clearObservers()
|
||||
atom.syntax.off()
|
||||
ensureNoDeprecatedFunctionsCalled() if isCoreSpec
|
||||
waits(0) # yield to ui thread to make screen update more frequently
|
||||
|
||||
ensureNoPathSubscriptions = ->
|
||||
@@ -213,13 +211,6 @@ addCustomMatchers = (spec) ->
|
||||
element = element.get(0) if element.jquery
|
||||
element.webkitMatchesSelector(":focus") or element.querySelector(":focus")
|
||||
|
||||
toShow: ->
|
||||
notText = if @isNot then " not" else ""
|
||||
element = @actual
|
||||
element = element.get(0) if element.jquery
|
||||
@message = -> return "Expected element '#{element}' or its descendants #{notText} to show."
|
||||
element.style.display in ['block', 'inline-block', 'static', 'fixed']
|
||||
|
||||
window.keyIdentifierForKey = (key) ->
|
||||
if key.length > 1 # named key
|
||||
key
|
||||
|
||||
@@ -1,32 +1,30 @@
|
||||
textUtils = require '../src/text-utils'
|
||||
|
||||
describe 'text utilities', ->
|
||||
describe '.hasPairedCharacter(string)', ->
|
||||
it 'returns true when the string contains a surrogate pair or variation sequence', ->
|
||||
expect(textUtils.hasPairedCharacter('abc')).toBe false
|
||||
expect(textUtils.hasPairedCharacter('a\uD835\uDF97b\uD835\uDF97c')).toBe true
|
||||
expect(textUtils.hasPairedCharacter('\uD835\uDF97')).toBe true
|
||||
expect(textUtils.hasPairedCharacter('\u2714\uFE0E')).toBe true
|
||||
expect(textUtils.hasPairedCharacter('\uD835')).toBe false
|
||||
expect(textUtils.hasPairedCharacter('\uDF97')).toBe false
|
||||
expect(textUtils.hasPairedCharacter('\uFE0E')).toBe false
|
||||
expect(textUtils.hasPairedCharacter('\uFE0E\uFE0E')).toBe false
|
||||
describe '.getCharacterCount(string)', ->
|
||||
it 'returns the number of full characters in the string', ->
|
||||
expect(textUtils.getCharacterCount('abc')).toBe 3
|
||||
expect(textUtils.getCharacterCount('a\uD835\uDF97b\uD835\uDF97c')).toBe 5
|
||||
expect(textUtils.getCharacterCount('\uD835\uDF97')).toBe 1
|
||||
expect(textUtils.getCharacterCount('\uD835')).toBe 1
|
||||
expect(textUtils.getCharacterCount('\uDF97')).toBe 1
|
||||
|
||||
describe '.isPairedCharacter(string, index)', ->
|
||||
it 'returns true when the index is the start of a high/low surrogate pair or variation sequence', ->
|
||||
expect(textUtils.isPairedCharacter('a\uD835\uDF97b\uD835\uDF97c', 0)).toBe false
|
||||
expect(textUtils.isPairedCharacter('a\uD835\uDF97b\uD835\uDF97c', 1)).toBe true
|
||||
expect(textUtils.isPairedCharacter('a\uD835\uDF97b\uD835\uDF97c', 2)).toBe false
|
||||
expect(textUtils.isPairedCharacter('a\uD835\uDF97b\uD835\uDF97c', 3)).toBe false
|
||||
expect(textUtils.isPairedCharacter('a\uD835\uDF97b\uD835\uDF97c', 4)).toBe true
|
||||
expect(textUtils.isPairedCharacter('a\uD835\uDF97b\uD835\uDF97c', 5)).toBe false
|
||||
expect(textUtils.isPairedCharacter('a\uD835\uDF97b\uD835\uDF97c', 6)).toBe false
|
||||
expect(textUtils.isPairedCharacter('a\u2714\uFE0E', 0)).toBe false
|
||||
expect(textUtils.isPairedCharacter('a\u2714\uFE0E', 1)).toBe true
|
||||
expect(textUtils.isPairedCharacter('a\u2714\uFE0E', 2)).toBe false
|
||||
expect(textUtils.isPairedCharacter('a\u2714\uFE0E', 3)).toBe false
|
||||
expect(textUtils.isPairedCharacter('\uD835')).toBe false
|
||||
expect(textUtils.isPairedCharacter('\uDF97')).toBe false
|
||||
expect(textUtils.isPairedCharacter('\uFE0E')).toBe false
|
||||
expect(textUtils.isPairedCharacter('\uFE0E')).toBe false
|
||||
expect(textUtils.isPairedCharacter('\uFE0E\uFE0E')).toBe false
|
||||
describe '.hasSurrogatePair(string)', ->
|
||||
it 'returns true when the string contains a surrogate pair', ->
|
||||
expect(textUtils.hasSurrogatePair('abc')).toBe false
|
||||
expect(textUtils.hasSurrogatePair('a\uD835\uDF97b\uD835\uDF97c')).toBe true
|
||||
expect(textUtils.hasSurrogatePair('\uD835\uDF97')).toBe true
|
||||
expect(textUtils.hasSurrogatePair('\uD835')).toBe false
|
||||
expect(textUtils.hasSurrogatePair('\uDF97')).toBe false
|
||||
|
||||
describe '.isSurrogatePair(string, index)', ->
|
||||
it 'returns true when the index is the start of a high/low surrogate pair', ->
|
||||
expect(textUtils.isSurrogatePair('a\uD835\uDF97b\uD835\uDF97c', 0)).toBe false
|
||||
expect(textUtils.isSurrogatePair('a\uD835\uDF97b\uD835\uDF97c', 1)).toBe true
|
||||
expect(textUtils.isSurrogatePair('a\uD835\uDF97b\uD835\uDF97c', 2)).toBe false
|
||||
expect(textUtils.isSurrogatePair('a\uD835\uDF97b\uD835\uDF97c', 3)).toBe false
|
||||
expect(textUtils.isSurrogatePair('a\uD835\uDF97b\uD835\uDF97c', 4)).toBe true
|
||||
expect(textUtils.isSurrogatePair('a\uD835\uDF97b\uD835\uDF97c', 5)).toBe false
|
||||
expect(textUtils.isSurrogatePair('a\uD835\uDF97b\uD835\uDF97c', 6)).toBe false
|
||||
expect(textUtils.isSurrogatePair('\uD835')).toBe false
|
||||
expect(textUtils.isSurrogatePair('\uDF97')).toBe false
|
||||
|
||||
@@ -69,7 +69,7 @@ describe "ThemeManager", ->
|
||||
|
||||
describe "when the core.themes config value changes", ->
|
||||
it "add/removes stylesheets to reflect the new config value", ->
|
||||
themeManager.onDidReloadAll reloadHandler = jasmine.createSpy()
|
||||
themeManager.on 'reloaded', reloadHandler = jasmine.createSpy()
|
||||
spyOn(themeManager, 'getUserStylesheetPath').andCallFake -> null
|
||||
|
||||
waitsForPromise ->
|
||||
@@ -131,8 +131,8 @@ describe "ThemeManager", ->
|
||||
|
||||
describe "requireStylesheet(path)", ->
|
||||
it "synchronously loads css at the given path and installs a style tag for it in the head", ->
|
||||
themeManager.onDidChangeStylesheets stylesheetsChangedHandler = jasmine.createSpy("stylesheetsChangedHandler")
|
||||
themeManager.onDidAddStylesheet stylesheetAddedHandler = jasmine.createSpy("stylesheetAddedHandler")
|
||||
themeManager.on 'stylesheets-changed', stylesheetsChangedHandler = jasmine.createSpy("stylesheetsChangedHandler")
|
||||
themeManager.on 'stylesheet-added', stylesheetAddedHandler = jasmine.createSpy("stylesheetAddedHandler")
|
||||
cssPath = atom.project.resolve('css.css')
|
||||
lengthBefore = $('head style').length
|
||||
|
||||
@@ -193,8 +193,8 @@ describe "ThemeManager", ->
|
||||
themeManager.requireStylesheet(cssPath)
|
||||
expect($(document.body).css('font-weight')).toBe("bold")
|
||||
|
||||
themeManager.onDidRemoveStylesheet stylesheetRemovedHandler = jasmine.createSpy("stylesheetRemovedHandler")
|
||||
themeManager.onDidChangeStylesheets stylesheetsChangedHandler = jasmine.createSpy("stylesheetsChangedHandler")
|
||||
themeManager.on 'stylesheet-removed', stylesheetRemovedHandler = jasmine.createSpy("stylesheetRemovedHandler")
|
||||
themeManager.on 'stylesheets-changed', stylesheetsChangedHandler = jasmine.createSpy("stylesheetsChangedHandler")
|
||||
|
||||
themeManager.removeStylesheet(cssPath)
|
||||
|
||||
@@ -217,7 +217,7 @@ describe "ThemeManager", ->
|
||||
themeManager.activateThemes()
|
||||
|
||||
it "loads the correct values from the theme's ui-variables file", ->
|
||||
themeManager.onDidReloadAll reloadHandler = jasmine.createSpy()
|
||||
themeManager.on 'reloaded', reloadHandler = jasmine.createSpy()
|
||||
atom.config.set('core.themes', ['theme-with-ui-variables'])
|
||||
|
||||
waitsFor ->
|
||||
@@ -234,7 +234,7 @@ describe "ThemeManager", ->
|
||||
|
||||
describe "when there is a theme with incomplete variables", ->
|
||||
it "loads the correct values from the fallback ui-variables", ->
|
||||
themeManager.onDidReloadAll reloadHandler = jasmine.createSpy()
|
||||
themeManager.on 'reloaded', reloadHandler = jasmine.createSpy()
|
||||
atom.config.set('core.themes', ['theme-with-incomplete-ui-variables'])
|
||||
|
||||
waitsFor ->
|
||||
@@ -247,21 +247,6 @@ describe "ThemeManager", ->
|
||||
# from within the theme itself
|
||||
expect($(".editor").css("background-color")).toBe "rgb(0, 152, 255)"
|
||||
|
||||
describe "theme classes on the workspace", ->
|
||||
it 'adds theme-* classes to the workspace for each active theme', ->
|
||||
expect(atom.workspaceView).toHaveClass 'theme-atom-dark-ui'
|
||||
|
||||
themeManager.onDidReloadAll reloadHandler = jasmine.createSpy()
|
||||
atom.config.set('core.themes', ['theme-with-ui-variables'])
|
||||
|
||||
waitsFor ->
|
||||
reloadHandler.callCount > 0
|
||||
|
||||
runs ->
|
||||
# `theme-` twice as it prefixes the name with `theme-`
|
||||
expect(atom.workspaceView).toHaveClass 'theme-theme-with-ui-variables'
|
||||
expect(atom.workspaceView).not.toHaveClass 'theme-atom-dark-ui'
|
||||
|
||||
describe "when the user stylesheet changes", ->
|
||||
it "reloads it", ->
|
||||
[stylesheetRemovedHandler, stylesheetAddedHandler, stylesheetsChangedHandler] = []
|
||||
@@ -273,9 +258,9 @@ describe "ThemeManager", ->
|
||||
themeManager.activateThemes()
|
||||
|
||||
runs ->
|
||||
themeManager.onDidChangeStylesheets stylesheetsChangedHandler = jasmine.createSpy("stylesheetsChangedHandler")
|
||||
themeManager.onDidRemoveStylesheet stylesheetRemovedHandler = jasmine.createSpy("stylesheetRemovedHandler")
|
||||
themeManager.onDidAddStylesheet stylesheetAddedHandler = jasmine.createSpy("stylesheetAddedHandler")
|
||||
themeManager.on 'stylesheets-changed', stylesheetsChangedHandler = jasmine.createSpy("stylesheetsChangedHandler")
|
||||
themeManager.on 'stylesheet-removed', stylesheetRemovedHandler = jasmine.createSpy("stylesheetRemovedHandler")
|
||||
themeManager.on 'stylesheet-added', stylesheetAddedHandler = jasmine.createSpy("stylesheetAddedHandler")
|
||||
spyOn(themeManager, 'loadUserStylesheet').andCallThrough()
|
||||
|
||||
expect($(document.body).css('border-style')).toBe 'dotted'
|
||||
@@ -316,9 +301,7 @@ describe "ThemeManager", ->
|
||||
themeManager.activateThemes()
|
||||
|
||||
runs ->
|
||||
disposable = themeManager.onDidReloadAll ->
|
||||
disposable.dispose()
|
||||
reloaded = true
|
||||
themeManager.once 'reloaded', -> reloaded = true
|
||||
spyOn(console, 'warn')
|
||||
expect(-> atom.config.set('core.themes', ['atom-light-ui', 'theme-really-does-not-exist'])).not.toThrow()
|
||||
|
||||
@@ -327,59 +310,3 @@ describe "ThemeManager", ->
|
||||
runs ->
|
||||
expect(console.warn.callCount).toBe 1
|
||||
expect(console.warn.argsForCall[0][0].length).toBeGreaterThan 0
|
||||
|
||||
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()
|
||||
|
||||
it 'uses the enabled themes', ->
|
||||
activeThemeNames = themeManager.getActiveNames()
|
||||
expect(activeThemeNames.length).toBe(2)
|
||||
expect(activeThemeNames).toContain('atom-light-ui')
|
||||
expect(activeThemeNames).toContain('atom-dark-syntax')
|
||||
|
||||
describe 'when the enabled UI and syntax themes are not bundled with Atom', ->
|
||||
beforeEach ->
|
||||
atom.config.set('core.themes', ['installed-dark-ui', 'installed-dark-syntax'])
|
||||
|
||||
waitsForPromise ->
|
||||
themeManager.activateThemes()
|
||||
|
||||
it 'uses the default dark UI and syntax themes', ->
|
||||
activeThemeNames = themeManager.getActiveNames()
|
||||
expect(activeThemeNames.length).toBe(2)
|
||||
expect(activeThemeNames).toContain('atom-dark-ui')
|
||||
expect(activeThemeNames).toContain('atom-dark-syntax')
|
||||
|
||||
describe 'when the enabled UI theme is not bundled with Atom', ->
|
||||
beforeEach ->
|
||||
atom.config.set('core.themes', ['installed-dark-ui', 'atom-light-syntax'])
|
||||
|
||||
waitsForPromise ->
|
||||
themeManager.activateThemes()
|
||||
|
||||
it 'uses the default dark UI theme', ->
|
||||
activeThemeNames = themeManager.getActiveNames()
|
||||
expect(activeThemeNames.length).toBe(2)
|
||||
expect(activeThemeNames).toContain('atom-dark-ui')
|
||||
expect(activeThemeNames).toContain('atom-light-syntax')
|
||||
|
||||
describe 'when the enabled syntax theme is not bundled with Atom', ->
|
||||
beforeEach ->
|
||||
atom.config.set('core.themes', ['atom-light-ui', 'installed-dark-syntax'])
|
||||
|
||||
waitsForPromise ->
|
||||
themeManager.activateThemes()
|
||||
|
||||
it 'uses the default dark syntax theme', ->
|
||||
activeThemeNames = themeManager.getActiveNames()
|
||||
expect(activeThemeNames.length).toBe(2)
|
||||
expect(activeThemeNames).toContain('atom-light-ui')
|
||||
expect(activeThemeNames).toContain('atom-dark-syntax')
|
||||
|
||||
@@ -18,7 +18,7 @@ class TimeReporter extends jasmine.Reporter
|
||||
log ?= (line) -> console.log(line)
|
||||
log "Longest running suites:"
|
||||
suites = _.map(window.timedSuites, (key, value) -> [value, key])
|
||||
for suite in _.sortBy(suites, (suite) -> -suite[1])[0...number]
|
||||
for suite in _.sortBy(suites, (suite) => -suite[1])[0...number]
|
||||
time = Math.round(suite[1] / 100) / 10
|
||||
log " #{suite[0]} (#{time}s)"
|
||||
undefined
|
||||
|
||||
+156
-221
@@ -1,5 +1,4 @@
|
||||
TokenizedBuffer = require '../src/tokenized-buffer'
|
||||
TextBuffer = require 'text-buffer'
|
||||
_ = require 'underscore-plus'
|
||||
|
||||
describe "TokenizedBuffer", ->
|
||||
@@ -13,9 +12,6 @@ describe "TokenizedBuffer", ->
|
||||
waitsForPromise ->
|
||||
atom.packages.activatePackage('language-javascript')
|
||||
|
||||
afterEach ->
|
||||
tokenizedBuffer?.destroy()
|
||||
|
||||
startTokenizing = (tokenizedBuffer) ->
|
||||
tokenizedBuffer.setVisible(true)
|
||||
|
||||
@@ -41,7 +37,7 @@ describe "TokenizedBuffer", ->
|
||||
buffer = atom.project.bufferForPathSync('sample.js')
|
||||
tokenizedBuffer = new TokenizedBuffer({buffer})
|
||||
startTokenizing(tokenizedBuffer)
|
||||
tokenizedBuffer.onDidChange changeHandler = jasmine.createSpy('changeHandler')
|
||||
tokenizedBuffer.on "changed", changeHandler = jasmine.createSpy('changeHandler')
|
||||
|
||||
afterEach ->
|
||||
tokenizedBuffer.destroy()
|
||||
@@ -49,38 +45,38 @@ describe "TokenizedBuffer", ->
|
||||
|
||||
describe "on construction", ->
|
||||
it "initially creates un-tokenized screen lines, then tokenizes lines chunk at a time in the background", ->
|
||||
line0 = tokenizedBuffer.tokenizedLineForRow(0)
|
||||
line0 = tokenizedBuffer.lineForScreenRow(0)
|
||||
expect(line0.tokens.length).toBe 1
|
||||
expect(line0.tokens[0]).toEqual(value: line0.text, scopes: ['source.js'])
|
||||
|
||||
line11 = tokenizedBuffer.tokenizedLineForRow(11)
|
||||
line11 = tokenizedBuffer.lineForScreenRow(11)
|
||||
expect(line11.tokens.length).toBe 2
|
||||
expect(line11.tokens[0]).toEqual(value: " ", scopes: ['source.js'], isAtomic: true)
|
||||
expect(line11.tokens[1]).toEqual(value: "return sort(Array.apply(this, arguments));", scopes: ['source.js'])
|
||||
|
||||
# background tokenization has not begun
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(0).ruleStack).toBeUndefined()
|
||||
expect(tokenizedBuffer.lineForScreenRow(0).ruleStack).toBeUndefined()
|
||||
|
||||
# tokenize chunk 1
|
||||
advanceClock()
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(0).ruleStack?).toBeTruthy()
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(4).ruleStack?).toBeTruthy()
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(5).ruleStack?).toBeFalsy()
|
||||
expect(tokenizedBuffer.lineForScreenRow(0).ruleStack?).toBeTruthy()
|
||||
expect(tokenizedBuffer.lineForScreenRow(4).ruleStack?).toBeTruthy()
|
||||
expect(tokenizedBuffer.lineForScreenRow(5).ruleStack?).toBeFalsy()
|
||||
expect(changeHandler).toHaveBeenCalledWith(start: 0, end: 4, delta: 0)
|
||||
changeHandler.reset()
|
||||
|
||||
# tokenize chunk 2
|
||||
advanceClock()
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(5).ruleStack?).toBeTruthy()
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(9).ruleStack?).toBeTruthy()
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(10).ruleStack?).toBeFalsy()
|
||||
expect(tokenizedBuffer.lineForScreenRow(5).ruleStack?).toBeTruthy()
|
||||
expect(tokenizedBuffer.lineForScreenRow(9).ruleStack?).toBeTruthy()
|
||||
expect(tokenizedBuffer.lineForScreenRow(10).ruleStack?).toBeFalsy()
|
||||
expect(changeHandler).toHaveBeenCalledWith(start: 5, end: 9, delta: 0)
|
||||
changeHandler.reset()
|
||||
|
||||
# tokenize last chunk
|
||||
advanceClock()
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(10).ruleStack?).toBeTruthy()
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(12).ruleStack?).toBeTruthy()
|
||||
expect(tokenizedBuffer.lineForScreenRow(10).ruleStack?).toBeTruthy()
|
||||
expect(tokenizedBuffer.lineForScreenRow(12).ruleStack?).toBeTruthy()
|
||||
expect(changeHandler).toHaveBeenCalledWith(start: 10, end: 12, delta: 0)
|
||||
|
||||
describe "when the buffer is partially tokenized", ->
|
||||
@@ -134,8 +130,8 @@ describe "TokenizedBuffer", ->
|
||||
expect(tokenizedBuffer.firstInvalidRow()).toBe 5
|
||||
buffer.setTextInRange([[6, 0], [7, 0]], "\n\n\n")
|
||||
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(6).ruleStack?).toBeFalsy()
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(7).ruleStack?).toBeFalsy()
|
||||
expect(tokenizedBuffer.lineForScreenRow(6).ruleStack?).toBeFalsy()
|
||||
expect(tokenizedBuffer.lineForScreenRow(7).ruleStack?).toBeFalsy()
|
||||
|
||||
changeHandler.reset()
|
||||
expect(tokenizedBuffer.firstInvalidRow()).toBe 5
|
||||
@@ -149,10 +145,10 @@ describe "TokenizedBuffer", ->
|
||||
it "updates tokens to reflect the change", ->
|
||||
buffer.setTextInRange([[0, 0], [2, 0]], "foo()\n7\n")
|
||||
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(0).tokens[1]).toEqual(value: '(', scopes: ['source.js', 'meta.brace.round.js'])
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(1).tokens[0]).toEqual(value: '7', scopes: ['source.js', 'constant.numeric.js'])
|
||||
expect(tokenizedBuffer.lineForScreenRow(0).tokens[1]).toEqual(value: '(', scopes: ['source.js', 'meta.brace.round.js'])
|
||||
expect(tokenizedBuffer.lineForScreenRow(1).tokens[0]).toEqual(value: '7', scopes: ['source.js', 'constant.numeric.js'])
|
||||
# line 2 is unchanged
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(2).tokens[2]).toEqual(value: 'if', scopes: ['source.js', 'keyword.control.js'])
|
||||
expect(tokenizedBuffer.lineForScreenRow(2).tokens[2]).toEqual(value: 'if', scopes: ['source.js', 'keyword.control.js'])
|
||||
|
||||
expect(changeHandler).toHaveBeenCalled()
|
||||
[event] = changeHandler.argsForCall[0]
|
||||
@@ -164,7 +160,7 @@ describe "TokenizedBuffer", ->
|
||||
buffer.insert([5, 30], '/* */')
|
||||
changeHandler.reset()
|
||||
buffer.insert([2, 0], '/*')
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(3).tokens[0].scopes).toEqual ['source.js']
|
||||
expect(tokenizedBuffer.lineForScreenRow(3).tokens[0].scopes).toEqual ['source.js']
|
||||
expect(changeHandler).toHaveBeenCalled()
|
||||
[event] = changeHandler.argsForCall[0]
|
||||
delete event.bufferChange
|
||||
@@ -172,9 +168,9 @@ describe "TokenizedBuffer", ->
|
||||
changeHandler.reset()
|
||||
|
||||
advanceClock()
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(3).tokens[0].scopes).toEqual ['source.js', 'comment.block.js']
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(4).tokens[0].scopes).toEqual ['source.js', 'comment.block.js']
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(5).tokens[0].scopes).toEqual ['source.js', 'comment.block.js']
|
||||
expect(tokenizedBuffer.lineForScreenRow(3).tokens[0].scopes).toEqual ['source.js', 'comment.block.js']
|
||||
expect(tokenizedBuffer.lineForScreenRow(4).tokens[0].scopes).toEqual ['source.js', 'comment.block.js']
|
||||
expect(tokenizedBuffer.lineForScreenRow(5).tokens[0].scopes).toEqual ['source.js', 'comment.block.js']
|
||||
expect(changeHandler).toHaveBeenCalled()
|
||||
[event] = changeHandler.argsForCall[0]
|
||||
delete event.bufferChange
|
||||
@@ -185,23 +181,23 @@ describe "TokenizedBuffer", ->
|
||||
buffer.insert([5, 0], '*/')
|
||||
|
||||
buffer.insert([1, 0], 'var ')
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(1).tokens[0].scopes).toEqual ['source.js', 'comment.block.js']
|
||||
expect(tokenizedBuffer.lineForScreenRow(1).tokens[0].scopes).toEqual ['source.js', 'comment.block.js']
|
||||
|
||||
describe "when lines are both updated and removed", ->
|
||||
it "updates tokens to reflect the change", ->
|
||||
buffer.setTextInRange([[1, 0], [3, 0]], "foo()")
|
||||
|
||||
# previous line 0 remains
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(0).tokens[0]).toEqual(value: 'var', scopes: ['source.js', 'storage.modifier.js'])
|
||||
expect(tokenizedBuffer.lineForScreenRow(0).tokens[0]).toEqual(value: 'var', scopes: ['source.js', 'storage.modifier.js'])
|
||||
|
||||
# previous line 3 should be combined with input to form line 1
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(1).tokens[0]).toEqual(value: 'foo', scopes: ['source.js'])
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(1).tokens[6]).toEqual(value: '=', scopes: ['source.js', 'keyword.operator.js'])
|
||||
expect(tokenizedBuffer.lineForScreenRow(1).tokens[0]).toEqual(value: 'foo', scopes: ['source.js'])
|
||||
expect(tokenizedBuffer.lineForScreenRow(1).tokens[6]).toEqual(value: '=', scopes: ['source.js', 'keyword.operator.js'])
|
||||
|
||||
# lines below deleted regions should be shifted upward
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(2).tokens[2]).toEqual(value: 'while', scopes: ['source.js', 'keyword.control.js'])
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(3).tokens[4]).toEqual(value: '=', scopes: ['source.js', 'keyword.operator.js'])
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(4).tokens[4]).toEqual(value: '<', scopes: ['source.js', 'keyword.operator.js'])
|
||||
expect(tokenizedBuffer.lineForScreenRow(2).tokens[2]).toEqual(value: 'while', scopes: ['source.js', 'keyword.control.js'])
|
||||
expect(tokenizedBuffer.lineForScreenRow(3).tokens[4]).toEqual(value: '=', scopes: ['source.js', 'keyword.operator.js'])
|
||||
expect(tokenizedBuffer.lineForScreenRow(4).tokens[4]).toEqual(value: '<', scopes: ['source.js', 'keyword.operator.js'])
|
||||
|
||||
expect(changeHandler).toHaveBeenCalled()
|
||||
[event] = changeHandler.argsForCall[0]
|
||||
@@ -214,8 +210,8 @@ describe "TokenizedBuffer", ->
|
||||
changeHandler.reset()
|
||||
|
||||
buffer.setTextInRange([[2, 0], [3, 0]], '/*')
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(2).tokens[0].scopes).toEqual ['source.js', 'comment.block.js', 'punctuation.definition.comment.js']
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(3).tokens[0].scopes).toEqual ['source.js']
|
||||
expect(tokenizedBuffer.lineForScreenRow(2).tokens[0].scopes).toEqual ['source.js', 'comment.block.js', 'punctuation.definition.comment.js']
|
||||
expect(tokenizedBuffer.lineForScreenRow(3).tokens[0].scopes).toEqual ['source.js']
|
||||
expect(changeHandler).toHaveBeenCalled()
|
||||
[event] = changeHandler.argsForCall[0]
|
||||
delete event.bufferChange
|
||||
@@ -223,8 +219,8 @@ describe "TokenizedBuffer", ->
|
||||
changeHandler.reset()
|
||||
|
||||
advanceClock()
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(3).tokens[0].scopes).toEqual ['source.js', 'comment.block.js']
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(4).tokens[0].scopes).toEqual ['source.js', 'comment.block.js']
|
||||
expect(tokenizedBuffer.lineForScreenRow(3).tokens[0].scopes).toEqual ['source.js', 'comment.block.js']
|
||||
expect(tokenizedBuffer.lineForScreenRow(4).tokens[0].scopes).toEqual ['source.js', 'comment.block.js']
|
||||
expect(changeHandler).toHaveBeenCalled()
|
||||
[event] = changeHandler.argsForCall[0]
|
||||
delete event.bufferChange
|
||||
@@ -235,19 +231,19 @@ describe "TokenizedBuffer", ->
|
||||
buffer.setTextInRange([[1, 0], [2, 0]], "foo()\nbar()\nbaz()\nquux()")
|
||||
|
||||
# previous line 0 remains
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(0).tokens[0]).toEqual( value: 'var', scopes: ['source.js', 'storage.modifier.js'])
|
||||
expect(tokenizedBuffer.lineForScreenRow(0).tokens[0]).toEqual( value: 'var', scopes: ['source.js', 'storage.modifier.js'])
|
||||
|
||||
# 3 new lines inserted
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(1).tokens[0]).toEqual(value: 'foo', scopes: ['source.js'])
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(2).tokens[0]).toEqual(value: 'bar', scopes: ['source.js'])
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(3).tokens[0]).toEqual(value: 'baz', scopes: ['source.js'])
|
||||
expect(tokenizedBuffer.lineForScreenRow(1).tokens[0]).toEqual(value: 'foo', scopes: ['source.js'])
|
||||
expect(tokenizedBuffer.lineForScreenRow(2).tokens[0]).toEqual(value: 'bar', scopes: ['source.js'])
|
||||
expect(tokenizedBuffer.lineForScreenRow(3).tokens[0]).toEqual(value: 'baz', scopes: ['source.js'])
|
||||
|
||||
# previous line 2 is joined with quux() on line 4
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(4).tokens[0]).toEqual(value: 'quux', scopes: ['source.js'])
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(4).tokens[4]).toEqual(value: 'if', scopes: ['source.js', 'keyword.control.js'])
|
||||
expect(tokenizedBuffer.lineForScreenRow(4).tokens[0]).toEqual(value: 'quux', scopes: ['source.js'])
|
||||
expect(tokenizedBuffer.lineForScreenRow(4).tokens[4]).toEqual(value: 'if', scopes: ['source.js', 'keyword.control.js'])
|
||||
|
||||
# previous line 3 is pushed down to become line 5
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(5).tokens[4]).toEqual(value: '=', scopes: ['source.js', 'keyword.operator.js'])
|
||||
expect(tokenizedBuffer.lineForScreenRow(5).tokens[4]).toEqual(value: '=', scopes: ['source.js', 'keyword.operator.js'])
|
||||
|
||||
expect(changeHandler).toHaveBeenCalled()
|
||||
[event] = changeHandler.argsForCall[0]
|
||||
@@ -264,17 +260,17 @@ describe "TokenizedBuffer", ->
|
||||
[event] = changeHandler.argsForCall[0]
|
||||
delete event.bufferChange
|
||||
expect(event).toEqual(start: 2, end: 2, delta: 2)
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(2).tokens[0].scopes).toEqual ['source.js', 'comment.block.js', 'punctuation.definition.comment.js']
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(3).tokens[0].scopes).toEqual ['source.js', 'comment.block.js']
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(4).tokens[0].scopes).toEqual ['source.js', 'comment.block.js']
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(5).tokens[0].scopes).toEqual ['source.js']
|
||||
expect(tokenizedBuffer.lineForScreenRow(2).tokens[0].scopes).toEqual ['source.js', 'comment.block.js', 'punctuation.definition.comment.js']
|
||||
expect(tokenizedBuffer.lineForScreenRow(3).tokens[0].scopes).toEqual ['source.js', 'comment.block.js']
|
||||
expect(tokenizedBuffer.lineForScreenRow(4).tokens[0].scopes).toEqual ['source.js', 'comment.block.js']
|
||||
expect(tokenizedBuffer.lineForScreenRow(5).tokens[0].scopes).toEqual ['source.js']
|
||||
changeHandler.reset()
|
||||
|
||||
advanceClock() # tokenize invalidated lines in background
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(5).tokens[0].scopes).toEqual ['source.js', 'comment.block.js']
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(6).tokens[0].scopes).toEqual ['source.js', 'comment.block.js']
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(7).tokens[0].scopes).toEqual ['source.js', 'comment.block.js']
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(8).tokens[0].scopes).not.toBe ['source.js', 'comment.block.js']
|
||||
expect(tokenizedBuffer.lineForScreenRow(5).tokens[0].scopes).toEqual ['source.js', 'comment.block.js']
|
||||
expect(tokenizedBuffer.lineForScreenRow(6).tokens[0].scopes).toEqual ['source.js', 'comment.block.js']
|
||||
expect(tokenizedBuffer.lineForScreenRow(7).tokens[0].scopes).toEqual ['source.js', 'comment.block.js']
|
||||
expect(tokenizedBuffer.lineForScreenRow(8).tokens[0].scopes).not.toBe ['source.js', 'comment.block.js']
|
||||
|
||||
expect(changeHandler).toHaveBeenCalled()
|
||||
[event] = changeHandler.argsForCall[0]
|
||||
@@ -285,13 +281,13 @@ describe "TokenizedBuffer", ->
|
||||
it "tokenizes the initial chunk synchronously, then tokenizes the remaining lines in the background", ->
|
||||
commentBlock = _.multiplyString("// a comment\n", tokenizedBuffer.chunkSize + 2)
|
||||
buffer.insert([0,0], commentBlock)
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(0).ruleStack?).toBeTruthy()
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(4).ruleStack?).toBeTruthy()
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(5).ruleStack?).toBeFalsy()
|
||||
expect(tokenizedBuffer.lineForScreenRow(0).ruleStack?).toBeTruthy()
|
||||
expect(tokenizedBuffer.lineForScreenRow(4).ruleStack?).toBeTruthy()
|
||||
expect(tokenizedBuffer.lineForScreenRow(5).ruleStack?).toBeFalsy()
|
||||
|
||||
advanceClock()
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(5).ruleStack?).toBeTruthy()
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(6).ruleStack?).toBeTruthy()
|
||||
expect(tokenizedBuffer.lineForScreenRow(5).ruleStack?).toBeTruthy()
|
||||
expect(tokenizedBuffer.lineForScreenRow(6).ruleStack?).toBeTruthy()
|
||||
|
||||
describe ".findOpeningBracket(closingBufferPosition)", ->
|
||||
it "returns the position of the matching bracket, skipping any nested brackets", ->
|
||||
@@ -302,18 +298,18 @@ describe "TokenizedBuffer", ->
|
||||
expect(tokenizedBuffer.findClosingBracket([1, 29])).toEqual [9, 2]
|
||||
|
||||
it "tokenizes leading whitespace based on the new tab length", ->
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(5).tokens[0].isAtomic).toBeTruthy()
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(5).tokens[0].value).toBe " "
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(5).tokens[1].isAtomic).toBeTruthy()
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(5).tokens[1].value).toBe " "
|
||||
expect(tokenizedBuffer.lineForScreenRow(5).tokens[0].isAtomic).toBeTruthy()
|
||||
expect(tokenizedBuffer.lineForScreenRow(5).tokens[0].value).toBe " "
|
||||
expect(tokenizedBuffer.lineForScreenRow(5).tokens[1].isAtomic).toBeTruthy()
|
||||
expect(tokenizedBuffer.lineForScreenRow(5).tokens[1].value).toBe " "
|
||||
|
||||
tokenizedBuffer.setTabLength(4)
|
||||
fullyTokenize(tokenizedBuffer)
|
||||
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(5).tokens[0].isAtomic).toBeTruthy()
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(5).tokens[0].value).toBe " "
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(5).tokens[1].isAtomic).toBeFalsy()
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(5).tokens[1].value).toBe " current "
|
||||
expect(tokenizedBuffer.lineForScreenRow(5).tokens[0].isAtomic).toBeTruthy()
|
||||
expect(tokenizedBuffer.lineForScreenRow(5).tokens[0].value).toBe " "
|
||||
expect(tokenizedBuffer.lineForScreenRow(5).tokens[1].isAtomic).toBeFalsy()
|
||||
expect(tokenizedBuffer.lineForScreenRow(5).tokens[1].value).toBe " current "
|
||||
|
||||
describe "when the buffer contains hard-tabs", ->
|
||||
beforeEach ->
|
||||
@@ -335,7 +331,7 @@ describe "TokenizedBuffer", ->
|
||||
|
||||
it "renders each tab as its own atomic token with a value of size tabLength", ->
|
||||
tabAsSpaces = _.multiplyString(' ', tokenizedBuffer.getTabLength())
|
||||
screenLine0 = tokenizedBuffer.tokenizedLineForRow(0)
|
||||
screenLine0 = tokenizedBuffer.lineForScreenRow(0)
|
||||
expect(screenLine0.text).toBe "# Econ 101#{tabAsSpaces}"
|
||||
{ tokens } = screenLine0
|
||||
|
||||
@@ -347,7 +343,7 @@ describe "TokenizedBuffer", ->
|
||||
expect(tokens[2].isAtomic).toBeTruthy()
|
||||
expect(tokens[3].value).toBe ""
|
||||
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(2).text).toBe "#{tabAsSpaces} buy()#{tabAsSpaces}while supply > demand"
|
||||
expect(tokenizedBuffer.lineForScreenRow(2).text).toBe "#{tabAsSpaces} buy()#{tabAsSpaces}while supply > demand"
|
||||
|
||||
it "aligns the hard tabs to the correct tab stop column", ->
|
||||
buffer.setText """
|
||||
@@ -359,62 +355,62 @@ describe "TokenizedBuffer", ->
|
||||
tokenizedBuffer.setTabLength(4)
|
||||
fullyTokenize(tokenizedBuffer)
|
||||
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(0).text).toBe "1 2 3 4"
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(0).tokens[1].bufferDelta).toBe 1
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(0).tokens[1].screenDelta).toBe 3
|
||||
expect(tokenizedBuffer.lineForScreenRow(0).text).toBe "1 2 3 4"
|
||||
expect(tokenizedBuffer.lineForScreenRow(0).tokens[1].bufferDelta).toBe 1
|
||||
expect(tokenizedBuffer.lineForScreenRow(0).tokens[1].screenDelta).toBe 3
|
||||
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(1).text).toBe "12 3 4 5"
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(1).tokens[1].bufferDelta).toBe 1
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(1).tokens[1].screenDelta).toBe 2
|
||||
expect(tokenizedBuffer.lineForScreenRow(1).text).toBe "12 3 4 5"
|
||||
expect(tokenizedBuffer.lineForScreenRow(1).tokens[1].bufferDelta).toBe 1
|
||||
expect(tokenizedBuffer.lineForScreenRow(1).tokens[1].screenDelta).toBe 2
|
||||
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(2).text).toBe "123 4 5 6"
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(2).tokens[1].bufferDelta).toBe 1
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(2).tokens[1].screenDelta).toBe 1
|
||||
expect(tokenizedBuffer.lineForScreenRow(2).text).toBe "123 4 5 6"
|
||||
expect(tokenizedBuffer.lineForScreenRow(2).tokens[1].bufferDelta).toBe 1
|
||||
expect(tokenizedBuffer.lineForScreenRow(2).tokens[1].screenDelta).toBe 1
|
||||
|
||||
tokenizedBuffer.setTabLength(3)
|
||||
fullyTokenize(tokenizedBuffer)
|
||||
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(0).text).toBe "1 2 3 4"
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(0).tokens[1].bufferDelta).toBe 1
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(0).tokens[1].screenDelta).toBe 2
|
||||
expect(tokenizedBuffer.lineForScreenRow(0).text).toBe "1 2 3 4"
|
||||
expect(tokenizedBuffer.lineForScreenRow(0).tokens[1].bufferDelta).toBe 1
|
||||
expect(tokenizedBuffer.lineForScreenRow(0).tokens[1].screenDelta).toBe 2
|
||||
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(1).text).toBe "12 3 4 5"
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(1).tokens[1].bufferDelta).toBe 1
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(1).tokens[1].screenDelta).toBe 1
|
||||
expect(tokenizedBuffer.lineForScreenRow(1).text).toBe "12 3 4 5"
|
||||
expect(tokenizedBuffer.lineForScreenRow(1).tokens[1].bufferDelta).toBe 1
|
||||
expect(tokenizedBuffer.lineForScreenRow(1).tokens[1].screenDelta).toBe 1
|
||||
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(2).text).toBe "123 4 5 6"
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(2).tokens[1].bufferDelta).toBe 1
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(2).tokens[1].screenDelta).toBe 3
|
||||
expect(tokenizedBuffer.lineForScreenRow(2).text).toBe "123 4 5 6"
|
||||
expect(tokenizedBuffer.lineForScreenRow(2).tokens[1].bufferDelta).toBe 1
|
||||
expect(tokenizedBuffer.lineForScreenRow(2).tokens[1].screenDelta).toBe 3
|
||||
|
||||
tokenizedBuffer.setTabLength(2)
|
||||
fullyTokenize(tokenizedBuffer)
|
||||
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(0).text).toBe "1 2 3 4"
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(0).tokens[1].bufferDelta).toBe 1
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(0).tokens[1].screenDelta).toBe 1
|
||||
expect(tokenizedBuffer.lineForScreenRow(0).text).toBe "1 2 3 4"
|
||||
expect(tokenizedBuffer.lineForScreenRow(0).tokens[1].bufferDelta).toBe 1
|
||||
expect(tokenizedBuffer.lineForScreenRow(0).tokens[1].screenDelta).toBe 1
|
||||
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(1).text).toBe "12 3 4 5"
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(1).tokens[1].bufferDelta).toBe 1
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(1).tokens[1].screenDelta).toBe 2
|
||||
expect(tokenizedBuffer.lineForScreenRow(1).text).toBe "12 3 4 5"
|
||||
expect(tokenizedBuffer.lineForScreenRow(1).tokens[1].bufferDelta).toBe 1
|
||||
expect(tokenizedBuffer.lineForScreenRow(1).tokens[1].screenDelta).toBe 2
|
||||
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(2).text).toBe "123 4 5 6"
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(2).tokens[1].bufferDelta).toBe 1
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(2).tokens[1].screenDelta).toBe 1
|
||||
expect(tokenizedBuffer.lineForScreenRow(2).text).toBe "123 4 5 6"
|
||||
expect(tokenizedBuffer.lineForScreenRow(2).tokens[1].bufferDelta).toBe 1
|
||||
expect(tokenizedBuffer.lineForScreenRow(2).tokens[1].screenDelta).toBe 1
|
||||
|
||||
tokenizedBuffer.setTabLength(1)
|
||||
fullyTokenize(tokenizedBuffer)
|
||||
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(0).text).toBe "1 2 3 4"
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(0).tokens[1].bufferDelta).toBe 1
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(0).tokens[1].screenDelta).toBe 1
|
||||
expect(tokenizedBuffer.lineForScreenRow(0).text).toBe "1 2 3 4"
|
||||
expect(tokenizedBuffer.lineForScreenRow(0).tokens[1].bufferDelta).toBe 1
|
||||
expect(tokenizedBuffer.lineForScreenRow(0).tokens[1].screenDelta).toBe 1
|
||||
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(1).text).toBe "12 3 4 5"
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(1).tokens[1].bufferDelta).toBe 1
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(1).tokens[1].screenDelta).toBe 1
|
||||
expect(tokenizedBuffer.lineForScreenRow(1).text).toBe "12 3 4 5"
|
||||
expect(tokenizedBuffer.lineForScreenRow(1).tokens[1].bufferDelta).toBe 1
|
||||
expect(tokenizedBuffer.lineForScreenRow(1).tokens[1].screenDelta).toBe 1
|
||||
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(2).text).toBe "123 4 5 6"
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(2).tokens[1].bufferDelta).toBe 1
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(2).tokens[1].screenDelta).toBe 1
|
||||
expect(tokenizedBuffer.lineForScreenRow(2).text).toBe "123 4 5 6"
|
||||
expect(tokenizedBuffer.lineForScreenRow(2).tokens[1].bufferDelta).toBe 1
|
||||
expect(tokenizedBuffer.lineForScreenRow(2).tokens[1].screenDelta).toBe 1
|
||||
|
||||
describe "when the buffer contains UTF-8 surrogate pairs", ->
|
||||
beforeEach ->
|
||||
@@ -435,7 +431,7 @@ describe "TokenizedBuffer", ->
|
||||
buffer.release()
|
||||
|
||||
it "renders each UTF-8 surrogate pair as its own atomic token", ->
|
||||
screenLine0 = tokenizedBuffer.tokenizedLineForRow(0)
|
||||
screenLine0 = tokenizedBuffer.lineForScreenRow(0)
|
||||
expect(screenLine0.text).toBe "'abc\uD835\uDF97def'"
|
||||
{ tokens } = screenLine0
|
||||
|
||||
@@ -447,7 +443,7 @@ describe "TokenizedBuffer", ->
|
||||
expect(tokens[3].value).toBe "def"
|
||||
expect(tokens[4].value).toBe "'"
|
||||
|
||||
screenLine1 = tokenizedBuffer.tokenizedLineForRow(1)
|
||||
screenLine1 = tokenizedBuffer.lineForScreenRow(1)
|
||||
expect(screenLine1.text).toBe "//\uD835\uDF97xyz"
|
||||
{ tokens } = screenLine1
|
||||
|
||||
@@ -468,7 +464,7 @@ describe "TokenizedBuffer", ->
|
||||
|
||||
runs ->
|
||||
tokenizedBuffer = editor.displayBuffer.tokenizedBuffer
|
||||
tokenizedBuffer.onDidTokenize tokenizedHandler
|
||||
tokenizedBuffer.on 'tokenized', tokenizedHandler
|
||||
fullyTokenize(tokenizedBuffer)
|
||||
expect(tokenizedHandler.callCount).toBe(1)
|
||||
|
||||
@@ -483,7 +479,7 @@ describe "TokenizedBuffer", ->
|
||||
tokenizedBuffer = editor.displayBuffer.tokenizedBuffer
|
||||
fullyTokenize(tokenizedBuffer)
|
||||
|
||||
tokenizedBuffer.onDidTokenize tokenizedHandler
|
||||
tokenizedBuffer.on 'tokenized', tokenizedHandler
|
||||
editor.getBuffer().insert([0, 0], "'")
|
||||
fullyTokenize(tokenizedBuffer)
|
||||
expect(tokenizedHandler).not.toHaveBeenCalled()
|
||||
@@ -499,7 +495,7 @@ describe "TokenizedBuffer", ->
|
||||
|
||||
runs ->
|
||||
tokenizedBuffer = editor.displayBuffer.tokenizedBuffer
|
||||
tokenizedBuffer.onDidTokenize tokenizedHandler
|
||||
tokenizedBuffer.on 'tokenized', tokenizedHandler
|
||||
fullyTokenize(tokenizedBuffer)
|
||||
tokenizedHandler.reset()
|
||||
|
||||
@@ -525,7 +521,7 @@ describe "TokenizedBuffer", ->
|
||||
tokenizedBuffer.setGrammar(atom.syntax.selectGrammar('test.erb'))
|
||||
fullyTokenize(tokenizedBuffer)
|
||||
|
||||
{tokens} = tokenizedBuffer.tokenizedLineForRow(0)
|
||||
{tokens} = tokenizedBuffer.lineForScreenRow(0)
|
||||
expect(tokens[0]).toEqual value: "<div class='name'>", scopes: ["text.html.ruby"]
|
||||
|
||||
waitsForPromise ->
|
||||
@@ -533,7 +529,7 @@ describe "TokenizedBuffer", ->
|
||||
|
||||
runs ->
|
||||
fullyTokenize(tokenizedBuffer)
|
||||
{tokens} = tokenizedBuffer.tokenizedLineForRow(0)
|
||||
{tokens} = tokenizedBuffer.lineForScreenRow(0)
|
||||
expect(tokens[0]).toEqual value: '<', scopes: ["text.html.ruby","meta.tag.block.any.html","punctuation.definition.tag.begin.html"]
|
||||
|
||||
describe ".tokenForPosition(position)", ->
|
||||
@@ -588,112 +584,51 @@ describe "TokenizedBuffer", ->
|
||||
atom.config.set('editor.tabLength', 0)
|
||||
expect(tokenizedBuffer.tokenForPosition([0,0]).value).toBe ' '
|
||||
|
||||
describe "when the invisibles value changes", ->
|
||||
beforeEach ->
|
||||
|
||||
it "updates the tokens with the appropriate invisible characters", ->
|
||||
buffer = new TextBuffer(text: " \t a line with tabs\tand \tspaces \t ")
|
||||
tokenizedBuffer = new TokenizedBuffer({buffer})
|
||||
fullyTokenize(tokenizedBuffer)
|
||||
|
||||
tokenizedBuffer.setInvisibles(space: 'S', tab: 'T')
|
||||
fullyTokenize(tokenizedBuffer)
|
||||
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(0).text).toBe "SST Sa line with tabsTand T spacesSTS"
|
||||
# Also needs to work for copies
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(0).copy().text).toBe "SST Sa line with tabsTand T spacesSTS"
|
||||
|
||||
it "assigns endOfLineInvisibles to tokenized lines", ->
|
||||
buffer = new TextBuffer(text: "a line that ends in a carriage-return-line-feed \r\na line that ends in just a line-feed\na line with no ending")
|
||||
tokenizedBuffer = new TokenizedBuffer({buffer})
|
||||
|
||||
atom.config.set('editor.showInvisibles', true)
|
||||
tokenizedBuffer.setInvisibles(cr: 'R', eol: 'N')
|
||||
fullyTokenize(tokenizedBuffer)
|
||||
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(0).endOfLineInvisibles).toEqual ['R', 'N']
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(1).endOfLineInvisibles).toEqual ['N']
|
||||
|
||||
# Lines ending in soft wraps get no invisibles
|
||||
[left, right] = tokenizedBuffer.tokenizedLineForRow(0).softWrapAt(20)
|
||||
expect(left.endOfLineInvisibles).toBe null
|
||||
expect(right.endOfLineInvisibles).toEqual ['R', 'N']
|
||||
|
||||
tokenizedBuffer.setInvisibles(cr: 'R', eol: false)
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(0).endOfLineInvisibles).toEqual ['R']
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(1).endOfLineInvisibles).toEqual []
|
||||
|
||||
describe "leading and trailing whitespace", ->
|
||||
beforeEach ->
|
||||
buffer = atom.project.bufferForPathSync('sample.js')
|
||||
tokenizedBuffer = new TokenizedBuffer({buffer})
|
||||
fullyTokenize(tokenizedBuffer)
|
||||
|
||||
it "assigns ::firstNonWhitespaceIndex on tokens that have leading whitespace", ->
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(0).tokens[0].firstNonWhitespaceIndex).toBe null
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(1).tokens[0].firstNonWhitespaceIndex).toBe 2
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(1).tokens[1].firstNonWhitespaceIndex).toBe null
|
||||
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(2).tokens[0].firstNonWhitespaceIndex).toBe 2
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(2).tokens[1].firstNonWhitespaceIndex).toBe 2
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(2).tokens[2].firstNonWhitespaceIndex).toBe null
|
||||
it "sets ::hasLeadingWhitespace to true on tokens that have leading whitespace", ->
|
||||
expect(tokenizedBuffer.lineForScreenRow(0).tokens[0].hasLeadingWhitespace).toBe false
|
||||
expect(tokenizedBuffer.lineForScreenRow(1).tokens[0].hasLeadingWhitespace).toBe true
|
||||
expect(tokenizedBuffer.lineForScreenRow(1).tokens[1].hasLeadingWhitespace).toBe false
|
||||
expect(tokenizedBuffer.lineForScreenRow(2).tokens[0].hasLeadingWhitespace).toBe true
|
||||
expect(tokenizedBuffer.lineForScreenRow(2).tokens[1].hasLeadingWhitespace).toBe true
|
||||
expect(tokenizedBuffer.lineForScreenRow(2).tokens[2].hasLeadingWhitespace).toBe false
|
||||
|
||||
# The 4th token *has* leading whitespace, but isn't entirely whitespace
|
||||
buffer.insert([5, 0], ' ')
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(5).tokens[3].firstNonWhitespaceIndex).toBe 1
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(5).tokens[4].firstNonWhitespaceIndex).toBe null
|
||||
expect(tokenizedBuffer.lineForScreenRow(5).tokens[3].hasLeadingWhitespace).toBe true
|
||||
expect(tokenizedBuffer.lineForScreenRow(5).tokens[4].hasLeadingWhitespace).toBe false
|
||||
|
||||
# Lines that are *only* whitespace are not considered to have leading whitespace
|
||||
buffer.insert([10, 0], ' ')
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(10).tokens[0].firstNonWhitespaceIndex).toBe null
|
||||
expect(tokenizedBuffer.lineForScreenRow(10).tokens[0].hasLeadingWhitespace).toBe false
|
||||
|
||||
it "assigns ::firstTrailingWhitespaceIndex on tokens that have trailing whitespace", ->
|
||||
it "sets ::hasTrailingWhitespace to true on tokens that have trailing whitespace", ->
|
||||
buffer.insert([0, Infinity], ' ')
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(0).tokens[11].firstTrailingWhitespaceIndex).toBe null
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(0).tokens[12].firstTrailingWhitespaceIndex).toBe 0
|
||||
expect(tokenizedBuffer.lineForScreenRow(0).tokens[11].hasTrailingWhitespace).toBe false
|
||||
expect(tokenizedBuffer.lineForScreenRow(0).tokens[12].hasTrailingWhitespace).toBe true
|
||||
|
||||
# The last token *has* trailing whitespace, but isn't entirely whitespace
|
||||
buffer.setTextInRange([[2, 39], [2, 40]], ' ')
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(2).tokens[14].firstTrailingWhitespaceIndex).toBe null
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(2).tokens[15].firstTrailingWhitespaceIndex).toBe 6
|
||||
expect(tokenizedBuffer.lineForScreenRow(2).tokens[14].hasTrailingWhitespace).toBe false
|
||||
expect(tokenizedBuffer.lineForScreenRow(2).tokens[15].hasTrailingWhitespace).toBe true
|
||||
|
||||
# Lines that are *only* whitespace are considered to have trailing whitespace
|
||||
buffer.insert([10, 0], ' ')
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(10).tokens[0].firstTrailingWhitespaceIndex).toBe 0
|
||||
expect(tokenizedBuffer.lineForScreenRow(10).tokens[0].hasTrailingWhitespace).toBe true
|
||||
|
||||
it "only marks trailing whitespace on the last segment of a soft-wrapped line", ->
|
||||
buffer.insert([0, Infinity], ' ')
|
||||
tokenizedLine = tokenizedBuffer.tokenizedLineForRow(0)
|
||||
tokenizedLine = tokenizedBuffer.lineForScreenRow(0)
|
||||
[segment1, segment2] = tokenizedLine.softWrapAt(16)
|
||||
expect(segment1.tokens[5].value).toBe ' '
|
||||
expect(segment1.tokens[5].firstTrailingWhitespaceIndex).toBe null
|
||||
expect(segment1.tokens[5].hasTrailingWhitespace).toBe false
|
||||
expect(segment2.tokens[6].value).toBe ' '
|
||||
expect(segment2.tokens[6].firstTrailingWhitespaceIndex).toBe 0
|
||||
|
||||
it "sets leading and trailing whitespace correctly on a line with invisible characters that is copied", ->
|
||||
buffer.setText(" \t a line with tabs\tand \tspaces \t ")
|
||||
|
||||
tokenizedBuffer.setInvisibles(space: 'S', tab: 'T')
|
||||
fullyTokenize(tokenizedBuffer)
|
||||
|
||||
line = tokenizedBuffer.tokenizedLineForRow(0).copy()
|
||||
expect(line.tokens[0].firstNonWhitespaceIndex).toBe 2
|
||||
expect(line.tokens[line.tokens.length - 1].firstTrailingWhitespaceIndex).toBe 0
|
||||
|
||||
it "sets the ::firstNonWhitespaceIndex and ::firstTrailingWhitespaceIndex correctly when tokens are split for soft-wrapping", ->
|
||||
tokenizedBuffer.setInvisibles(space: 'S')
|
||||
buffer.setText(" token ")
|
||||
fullyTokenize(tokenizedBuffer)
|
||||
token = tokenizedBuffer.tokenizedLines[0].tokens[0]
|
||||
|
||||
[leftToken, rightToken] = token.splitAt(1)
|
||||
expect(leftToken.hasInvisibleCharacters).toBe true
|
||||
expect(leftToken.firstNonWhitespaceIndex).toBe 1
|
||||
expect(leftToken.firstTrailingWhitespaceIndex).toBe null
|
||||
|
||||
expect(leftToken.hasInvisibleCharacters).toBe true
|
||||
expect(rightToken.firstNonWhitespaceIndex).toBe null
|
||||
expect(rightToken.firstTrailingWhitespaceIndex).toBe 5
|
||||
expect(segment2.tokens[6].hasTrailingWhitespace).toBe true
|
||||
|
||||
describe "indent level", ->
|
||||
beforeEach ->
|
||||
@@ -703,84 +638,84 @@ describe "TokenizedBuffer", ->
|
||||
|
||||
describe "when the line is non-empty", ->
|
||||
it "has an indent level based on the leading whitespace on the line", ->
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(0).indentLevel).toBe 0
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(1).indentLevel).toBe 1
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(2).indentLevel).toBe 2
|
||||
expect(tokenizedBuffer.lineForScreenRow(0).indentLevel).toBe 0
|
||||
expect(tokenizedBuffer.lineForScreenRow(1).indentLevel).toBe 1
|
||||
expect(tokenizedBuffer.lineForScreenRow(2).indentLevel).toBe 2
|
||||
buffer.insert([2, 0], ' ')
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(2).indentLevel).toBe 2.5
|
||||
expect(tokenizedBuffer.lineForScreenRow(2).indentLevel).toBe 2.5
|
||||
|
||||
describe "when the line is empty", ->
|
||||
it "assumes the indentation level of the first non-empty line below or above if one exists", ->
|
||||
buffer.insert([12, 0], ' ')
|
||||
buffer.insert([12, Infinity], '\n\n')
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(13).indentLevel).toBe 2
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(14).indentLevel).toBe 2
|
||||
expect(tokenizedBuffer.lineForScreenRow(13).indentLevel).toBe 2
|
||||
expect(tokenizedBuffer.lineForScreenRow(14).indentLevel).toBe 2
|
||||
|
||||
buffer.insert([1, Infinity], '\n\n')
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(2).indentLevel).toBe 2
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(3).indentLevel).toBe 2
|
||||
expect(tokenizedBuffer.lineForScreenRow(2).indentLevel).toBe 2
|
||||
expect(tokenizedBuffer.lineForScreenRow(3).indentLevel).toBe 2
|
||||
|
||||
buffer.setText('\n\n\n')
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(1).indentLevel).toBe 0
|
||||
expect(tokenizedBuffer.lineForScreenRow(1).indentLevel).toBe 0
|
||||
|
||||
describe "when the changed lines are surrounded by whitespace-only lines", ->
|
||||
it "updates the indentLevel of empty lines that precede the change", ->
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(12).indentLevel).toBe 0
|
||||
expect(tokenizedBuffer.lineForScreenRow(12).indentLevel).toBe 0
|
||||
|
||||
buffer.insert([12, 0], '\n')
|
||||
buffer.insert([13, 0], ' ')
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(12).indentLevel).toBe 1
|
||||
expect(tokenizedBuffer.lineForScreenRow(12).indentLevel).toBe 1
|
||||
|
||||
it "updates empty line indent guides when the empty line is the last line", ->
|
||||
buffer.insert([12, 2], '\n')
|
||||
|
||||
# The newline and he tab need to be in two different operations to surface the bug
|
||||
buffer.insert([12, 0], ' ')
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(13).indentLevel).toBe 1
|
||||
expect(tokenizedBuffer.lineForScreenRow(13).indentLevel).toBe 1
|
||||
|
||||
buffer.insert([12, 0], ' ')
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(13).indentLevel).toBe 2
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(14)).not.toBeDefined()
|
||||
expect(tokenizedBuffer.lineForScreenRow(13).indentLevel).toBe 2
|
||||
expect(tokenizedBuffer.lineForScreenRow(14)).not.toBeDefined()
|
||||
|
||||
it "updates the indentLevel of empty lines surrounding a change that inserts lines", ->
|
||||
# create some new lines
|
||||
buffer.insert([7, 0], '\n\n')
|
||||
buffer.insert([5, 0], '\n\n')
|
||||
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(5).indentLevel).toBe 3
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(6).indentLevel).toBe 3
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(9).indentLevel).toBe 3
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(10).indentLevel).toBe 3
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(11).indentLevel).toBe 2
|
||||
expect(tokenizedBuffer.lineForScreenRow(5).indentLevel).toBe 3
|
||||
expect(tokenizedBuffer.lineForScreenRow(6).indentLevel).toBe 3
|
||||
expect(tokenizedBuffer.lineForScreenRow(9).indentLevel).toBe 3
|
||||
expect(tokenizedBuffer.lineForScreenRow(10).indentLevel).toBe 3
|
||||
expect(tokenizedBuffer.lineForScreenRow(11).indentLevel).toBe 2
|
||||
|
||||
tokenizedBuffer.onDidChange changeHandler = jasmine.createSpy('changeHandler')
|
||||
tokenizedBuffer.on "changed", changeHandler = jasmine.createSpy('changeHandler')
|
||||
|
||||
buffer.setTextInRange([[7, 0], [8, 65]], ' one\n two\n three\n four')
|
||||
|
||||
delete changeHandler.argsForCall[0][0].bufferChange
|
||||
expect(changeHandler).toHaveBeenCalledWith(start: 5, end: 10, delta: 2)
|
||||
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(5).indentLevel).toBe 4
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(6).indentLevel).toBe 4
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(11).indentLevel).toBe 4
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(12).indentLevel).toBe 4
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(13).indentLevel).toBe 2
|
||||
expect(tokenizedBuffer.lineForScreenRow(5).indentLevel).toBe 4
|
||||
expect(tokenizedBuffer.lineForScreenRow(6).indentLevel).toBe 4
|
||||
expect(tokenizedBuffer.lineForScreenRow(11).indentLevel).toBe 4
|
||||
expect(tokenizedBuffer.lineForScreenRow(12).indentLevel).toBe 4
|
||||
expect(tokenizedBuffer.lineForScreenRow(13).indentLevel).toBe 2
|
||||
|
||||
it "updates the indentLevel of empty lines surrounding a change that removes lines", ->
|
||||
# create some new lines
|
||||
buffer.insert([7, 0], '\n\n')
|
||||
buffer.insert([5, 0], '\n\n')
|
||||
|
||||
tokenizedBuffer.onDidChange changeHandler = jasmine.createSpy('changeHandler')
|
||||
tokenizedBuffer.on "changed", changeHandler = jasmine.createSpy('changeHandler')
|
||||
|
||||
buffer.setTextInRange([[7, 0], [8, 65]], ' ok')
|
||||
|
||||
delete changeHandler.argsForCall[0][0].bufferChange
|
||||
expect(changeHandler).toHaveBeenCalledWith(start: 5, end: 10, delta: -1)
|
||||
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(5).indentLevel).toBe 2
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(6).indentLevel).toBe 2
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(7).indentLevel).toBe 2 # new text
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(8).indentLevel).toBe 2
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(9).indentLevel).toBe 2
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(10).indentLevel).toBe 2 # }
|
||||
expect(tokenizedBuffer.lineForScreenRow(5).indentLevel).toBe 2
|
||||
expect(tokenizedBuffer.lineForScreenRow(6).indentLevel).toBe 2
|
||||
expect(tokenizedBuffer.lineForScreenRow(7).indentLevel).toBe 2 # new text
|
||||
expect(tokenizedBuffer.lineForScreenRow(8).indentLevel).toBe 2
|
||||
expect(tokenizedBuffer.lineForScreenRow(9).indentLevel).toBe 2
|
||||
expect(tokenizedBuffer.lineForScreenRow(10).indentLevel).toBe 2 # }
|
||||
|
||||
@@ -10,13 +10,13 @@ describe "TokenizedLine", ->
|
||||
atom.project.open('coffee.coffee').then (o) -> editor = o
|
||||
|
||||
it "returns true when the line is only whitespace", ->
|
||||
expect(editor.tokenizedLineForScreenRow(3).isOnlyWhitespace()).toBe true
|
||||
expect(editor.tokenizedLineForScreenRow(7).isOnlyWhitespace()).toBe true
|
||||
expect(editor.tokenizedLineForScreenRow(23).isOnlyWhitespace()).toBe true
|
||||
expect(editor.lineForScreenRow(3).isOnlyWhitespace()).toBe true
|
||||
expect(editor.lineForScreenRow(7).isOnlyWhitespace()).toBe true
|
||||
expect(editor.lineForScreenRow(23).isOnlyWhitespace()).toBe true
|
||||
|
||||
it "returns false when the line is not only whitespace", ->
|
||||
expect(editor.tokenizedLineForScreenRow(0).isOnlyWhitespace()).toBe false
|
||||
expect(editor.tokenizedLineForScreenRow(2).isOnlyWhitespace()).toBe false
|
||||
expect(editor.lineForScreenRow(0).isOnlyWhitespace()).toBe false
|
||||
expect(editor.lineForScreenRow(2).isOnlyWhitespace()).toBe false
|
||||
|
||||
describe "::getScopeTree()", ->
|
||||
it "returns a tree whose inner nodes are scopes and whose leaf nodes are tokens in those scopes", ->
|
||||
@@ -35,6 +35,6 @@ describe "TokenizedLine", ->
|
||||
|
||||
runs ->
|
||||
tokenIndex = 0
|
||||
tokens = editor.tokenizedLineForScreenRow(1).tokens
|
||||
scopeTree = editor.tokenizedLineForScreenRow(1).getScopeTree()
|
||||
tokens = editor.lineForScreenRow(1).tokens
|
||||
scopeTree = editor.lineForScreenRow(1).getScopeTree()
|
||||
ensureValidScopeTree(scopeTree)
|
||||
|
||||
@@ -113,7 +113,6 @@ describe "Window", ->
|
||||
expect(atom.state.project).toEqual projectState
|
||||
expect(atom.saveSync).toHaveBeenCalled()
|
||||
|
||||
describe ".removeEditorWindow()", ->
|
||||
it "unsubscribes from all buffers", ->
|
||||
waitsForPromise ->
|
||||
atom.workspace.open("sample.js")
|
||||
@@ -124,7 +123,7 @@ describe "Window", ->
|
||||
pane.splitRight(pane.copyActiveItem())
|
||||
expect(atom.workspaceView.find('.editor').length).toBe 2
|
||||
|
||||
atom.removeEditorWindow()
|
||||
atom.unloadEditorWindow()
|
||||
|
||||
expect(buffer.getSubscriptionCount()).toBe 0
|
||||
|
||||
@@ -256,35 +255,3 @@ describe "Window", ->
|
||||
|
||||
elements.trigger "core:focus-previous"
|
||||
expect(elements.find("[tabindex=1]:focus")).toExist()
|
||||
|
||||
describe "the window:open-path event", ->
|
||||
beforeEach ->
|
||||
spyOn(atom.workspace, 'open')
|
||||
|
||||
describe "when the project does not have a path", ->
|
||||
beforeEach ->
|
||||
atom.project.setPath()
|
||||
|
||||
describe "when the opened path exists", ->
|
||||
it "sets the project path to the opened path", ->
|
||||
$(window).trigger('window:open-path', [{pathToOpen: __filename}])
|
||||
|
||||
expect(atom.project.getPath()).toBe __dirname
|
||||
|
||||
describe "when the opened path does not exist but its parent directory does", ->
|
||||
it "sets the project path to the opened path's parent directory", ->
|
||||
$(window).trigger('window:open-path', [{pathToOpen: path.join(__dirname, 'this-path-does-not-exist.txt')}])
|
||||
|
||||
expect(atom.project.getPath()).toBe __dirname
|
||||
|
||||
describe "when the opened path is a file", ->
|
||||
it "opens it in the workspace", ->
|
||||
$(window).trigger('window:open-path', [{pathToOpen: __filename}])
|
||||
|
||||
expect(atom.workspace.open.mostRecentCall.args[0]).toBe __filename
|
||||
|
||||
describe "when the opened path is a directory", ->
|
||||
it "does not open it in the workspace", ->
|
||||
$(window).trigger('window:open-path', [{pathToOpen: __dirname}])
|
||||
|
||||
expect(atom.workspace.open.callCount).toBe 0
|
||||
|
||||
@@ -8,12 +8,8 @@ describe "Workspace", ->
|
||||
atom.workspace = workspace = new Workspace
|
||||
|
||||
describe "::open(uri, options)", ->
|
||||
openEvents = null
|
||||
|
||||
beforeEach ->
|
||||
openEvents = []
|
||||
workspace.onDidOpen (event) -> openEvents.push(event)
|
||||
spyOn(workspace.getActivePane(), 'activate').andCallThrough()
|
||||
spyOn(workspace.activePane, 'activate').andCallThrough()
|
||||
|
||||
describe "when the 'searchAllPanes' option is false (default)", ->
|
||||
describe "when called without a uri", ->
|
||||
@@ -25,21 +21,18 @@ describe "Workspace", ->
|
||||
|
||||
runs ->
|
||||
expect(editor1.getPath()).toBeUndefined()
|
||||
expect(workspace.getActivePane().items).toEqual [editor1]
|
||||
expect(workspace.getActivePaneItem()).toBe editor1
|
||||
expect(workspace.getActivePane().activate).toHaveBeenCalled()
|
||||
expect(openEvents).toEqual [{uri: undefined, pane: workspace.getActivePane(), item: editor1, index: 0}]
|
||||
openEvents = []
|
||||
expect(workspace.activePane.items).toEqual [editor1]
|
||||
expect(workspace.activePaneItem).toBe editor1
|
||||
expect(workspace.activePane.activate).toHaveBeenCalled()
|
||||
|
||||
waitsForPromise ->
|
||||
workspace.open().then (editor) -> editor2 = editor
|
||||
|
||||
runs ->
|
||||
expect(editor2.getPath()).toBeUndefined()
|
||||
expect(workspace.getActivePane().items).toEqual [editor1, editor2]
|
||||
expect(workspace.getActivePaneItem()).toBe editor2
|
||||
expect(workspace.getActivePane().activate).toHaveBeenCalled()
|
||||
expect(openEvents).toEqual [{uri: undefined, pane: workspace.getActivePane(), item: editor2, index: 1}]
|
||||
expect(workspace.activePane.items).toEqual [editor1, editor2]
|
||||
expect(workspace.activePaneItem).toBe editor2
|
||||
expect(workspace.activePane.activate).toHaveBeenCalled()
|
||||
|
||||
describe "when called with a uri", ->
|
||||
describe "when the active pane already has an editor for the given uri", ->
|
||||
@@ -58,29 +51,8 @@ describe "Workspace", ->
|
||||
|
||||
runs ->
|
||||
expect(editor).toBe editor1
|
||||
expect(workspace.getActivePaneItem()).toBe editor
|
||||
expect(workspace.getActivePane().activate).toHaveBeenCalled()
|
||||
|
||||
expect(openEvents).toEqual [
|
||||
{
|
||||
uri: atom.project.resolve('a')
|
||||
item: editor1
|
||||
pane: atom.workspace.getActivePane()
|
||||
index: 0
|
||||
}
|
||||
{
|
||||
uri: atom.project.resolve('b')
|
||||
item: editor2
|
||||
pane: atom.workspace.getActivePane()
|
||||
index: 1
|
||||
}
|
||||
{
|
||||
uri: atom.project.resolve('a')
|
||||
item: editor1
|
||||
pane: atom.workspace.getActivePane()
|
||||
index: 0
|
||||
}
|
||||
]
|
||||
expect(workspace.activePaneItem).toBe editor
|
||||
expect(workspace.activePane.activate).toHaveBeenCalled()
|
||||
|
||||
describe "when the active pane does not have an editor for the given uri", ->
|
||||
it "adds and activates a new editor for the given path on the active pane", ->
|
||||
@@ -90,9 +62,9 @@ describe "Workspace", ->
|
||||
|
||||
runs ->
|
||||
expect(editor.getUri()).toBe atom.project.resolve('a')
|
||||
expect(workspace.getActivePaneItem()).toBe editor
|
||||
expect(workspace.getActivePane().items).toEqual [editor]
|
||||
expect(workspace.getActivePane().activate).toHaveBeenCalled()
|
||||
expect(workspace.activePaneItem).toBe editor
|
||||
expect(workspace.activePane.items).toEqual [editor]
|
||||
expect(workspace.activePane.activate).toHaveBeenCalled()
|
||||
|
||||
describe "when the 'searchAllPanes' option is true", ->
|
||||
describe "when an editor for the given uri is already open on an inactive pane", ->
|
||||
@@ -111,14 +83,14 @@ describe "Workspace", ->
|
||||
workspace.open('b').then (o) -> editor2 = o
|
||||
|
||||
runs ->
|
||||
expect(workspace.getActivePaneItem()).toBe editor2
|
||||
expect(workspace.activePaneItem).toBe editor2
|
||||
|
||||
waitsForPromise ->
|
||||
workspace.open('a', searchAllPanes: true)
|
||||
|
||||
runs ->
|
||||
expect(workspace.getActivePane()).toBe pane1
|
||||
expect(workspace.getActivePaneItem()).toBe editor1
|
||||
expect(workspace.activePane).toBe pane1
|
||||
expect(workspace.activePaneItem).toBe editor1
|
||||
|
||||
describe "when no editor for the given uri is open in any pane", ->
|
||||
it "opens an editor for the given uri in the active pane", ->
|
||||
@@ -127,21 +99,21 @@ describe "Workspace", ->
|
||||
workspace.open('a', searchAllPanes: true).then (o) -> editor = o
|
||||
|
||||
runs ->
|
||||
expect(workspace.getActivePaneItem()).toBe editor
|
||||
expect(workspace.activePaneItem).toBe editor
|
||||
|
||||
describe "when the 'split' option is set", ->
|
||||
describe "when the 'split' option is 'left'", ->
|
||||
it "opens the editor in the leftmost pane of the current pane axis", ->
|
||||
pane1 = workspace.getActivePane()
|
||||
pane1 = workspace.activePane
|
||||
pane2 = pane1.splitRight()
|
||||
expect(workspace.getActivePane()).toBe pane2
|
||||
expect(workspace.activePane).toBe pane2
|
||||
|
||||
editor = null
|
||||
waitsForPromise ->
|
||||
workspace.open('a', split: 'left').then (o) -> editor = o
|
||||
|
||||
runs ->
|
||||
expect(workspace.getActivePane()).toBe pane1
|
||||
expect(workspace.activePane).toBe pane1
|
||||
expect(pane1.items).toEqual [editor]
|
||||
expect(pane2.items).toEqual []
|
||||
|
||||
@@ -151,37 +123,37 @@ describe "Workspace", ->
|
||||
workspace.open('a', split: 'left').then (o) -> editor = o
|
||||
|
||||
runs ->
|
||||
expect(workspace.getActivePane()).toBe pane1
|
||||
expect(workspace.activePane).toBe pane1
|
||||
expect(pane1.items).toEqual [editor]
|
||||
expect(pane2.items).toEqual []
|
||||
|
||||
describe "when a pane axis is the leftmost sibling of the current pane", ->
|
||||
it "opens the new item in the current pane", ->
|
||||
editor = null
|
||||
pane1 = workspace.getActivePane()
|
||||
pane1 = workspace.activePane
|
||||
pane2 = pane1.splitLeft()
|
||||
pane3 = pane2.splitDown()
|
||||
pane1.activate()
|
||||
expect(workspace.getActivePane()).toBe pane1
|
||||
expect(workspace.activePane).toBe pane1
|
||||
|
||||
waitsForPromise ->
|
||||
workspace.open('a', split: 'left').then (o) -> editor = o
|
||||
|
||||
runs ->
|
||||
expect(workspace.getActivePane()).toBe pane1
|
||||
expect(workspace.activePane).toBe pane1
|
||||
expect(pane1.items).toEqual [editor]
|
||||
|
||||
describe "when the 'split' option is 'right'", ->
|
||||
it "opens the editor in the rightmost pane of the current pane axis", ->
|
||||
editor = null
|
||||
pane1 = workspace.getActivePane()
|
||||
pane1 = workspace.activePane
|
||||
pane2 = null
|
||||
waitsForPromise ->
|
||||
workspace.open('a', split: 'right').then (o) -> editor = o
|
||||
|
||||
runs ->
|
||||
pane2 = workspace.getPanes().filter((p) -> p != pane1)[0]
|
||||
expect(workspace.getActivePane()).toBe pane2
|
||||
expect(workspace.activePane).toBe pane2
|
||||
expect(pane1.items).toEqual []
|
||||
expect(pane2.items).toEqual [editor]
|
||||
|
||||
@@ -191,18 +163,18 @@ describe "Workspace", ->
|
||||
workspace.open('a', split: 'right').then (o) -> editor = o
|
||||
|
||||
runs ->
|
||||
expect(workspace.getActivePane()).toBe pane2
|
||||
expect(workspace.activePane).toBe pane2
|
||||
expect(pane1.items).toEqual []
|
||||
expect(pane2.items).toEqual [editor]
|
||||
|
||||
describe "when a pane axis is the rightmost sibling of the current pane", ->
|
||||
it "opens the new item in a new pane split to the right of the current pane", ->
|
||||
editor = null
|
||||
pane1 = workspace.getActivePane()
|
||||
pane1 = workspace.activePane
|
||||
pane2 = pane1.splitRight()
|
||||
pane3 = pane2.splitDown()
|
||||
pane1.activate()
|
||||
expect(workspace.getActivePane()).toBe pane1
|
||||
expect(workspace.activePane).toBe pane1
|
||||
pane4 = null
|
||||
|
||||
waitsForPromise ->
|
||||
@@ -210,7 +182,7 @@ describe "Workspace", ->
|
||||
|
||||
runs ->
|
||||
pane4 = workspace.getPanes().filter((p) -> p != pane1)[0]
|
||||
expect(workspace.getActivePane()).toBe pane4
|
||||
expect(workspace.activePane).toBe pane4
|
||||
expect(pane4.items).toEqual [editor]
|
||||
expect(workspace.paneContainer.root.children[0]).toBe pane1
|
||||
expect(workspace.paneContainer.root.children[1]).toBe pane4
|
||||
@@ -231,21 +203,21 @@ describe "Workspace", ->
|
||||
workspace.open("bar://baz").then (item) ->
|
||||
expect(item).toEqual { bar: "bar://baz" }
|
||||
|
||||
it "notifies ::onDidAddTextEditor observers", ->
|
||||
it "emits an 'editor-created' event", ->
|
||||
absolutePath = require.resolve('./fixtures/dir/a')
|
||||
newEditorHandler = jasmine.createSpy('newEditorHandler')
|
||||
workspace.onDidAddTextEditor newEditorHandler
|
||||
workspace.on 'editor-created', newEditorHandler
|
||||
|
||||
editor = null
|
||||
waitsForPromise ->
|
||||
workspace.open(absolutePath).then (e) -> editor = e
|
||||
|
||||
runs ->
|
||||
expect(newEditorHandler.argsForCall[0][0].textEditor).toBe editor
|
||||
expect(newEditorHandler).toHaveBeenCalledWith editor
|
||||
|
||||
describe "::reopenItem()", ->
|
||||
it "opens the uri associated with the last closed pane that isn't currently open", ->
|
||||
pane = workspace.getActivePane()
|
||||
pane = workspace.activePane
|
||||
waitsForPromise ->
|
||||
workspace.open('a').then ->
|
||||
workspace.open('b').then ->
|
||||
@@ -254,44 +226,44 @@ describe "Workspace", ->
|
||||
|
||||
runs ->
|
||||
# does not reopen items with no uri
|
||||
expect(workspace.getActivePaneItem().getUri()).toBeUndefined()
|
||||
expect(workspace.activePaneItem.getUri()).toBeUndefined()
|
||||
pane.destroyActiveItem()
|
||||
|
||||
waitsForPromise ->
|
||||
workspace.reopenItem()
|
||||
|
||||
runs ->
|
||||
expect(workspace.getActivePaneItem().getUri()).not.toBeUndefined()
|
||||
expect(workspace.activePaneItem.getUri()).not.toBeUndefined()
|
||||
|
||||
# destroy all items
|
||||
expect(workspace.getActivePaneItem().getUri()).toBe atom.project.resolve('file1')
|
||||
expect(workspace.activePaneItem.getUri()).toBe atom.project.resolve('file1')
|
||||
pane.destroyActiveItem()
|
||||
expect(workspace.getActivePaneItem().getUri()).toBe atom.project.resolve('b')
|
||||
expect(workspace.activePaneItem.getUri()).toBe atom.project.resolve('b')
|
||||
pane.destroyActiveItem()
|
||||
expect(workspace.getActivePaneItem().getUri()).toBe atom.project.resolve('a')
|
||||
expect(workspace.activePaneItem.getUri()).toBe atom.project.resolve('a')
|
||||
pane.destroyActiveItem()
|
||||
|
||||
# reopens items with uris
|
||||
expect(workspace.getActivePaneItem()).toBeUndefined()
|
||||
expect(workspace.activePaneItem).toBeUndefined()
|
||||
|
||||
waitsForPromise ->
|
||||
workspace.reopenItem()
|
||||
|
||||
runs ->
|
||||
expect(workspace.getActivePaneItem().getUri()).toBe atom.project.resolve('a')
|
||||
expect(workspace.activePaneItem.getUri()).toBe atom.project.resolve('a')
|
||||
|
||||
# does not reopen items that are already open
|
||||
waitsForPromise ->
|
||||
workspace.open('b')
|
||||
|
||||
runs ->
|
||||
expect(workspace.getActivePaneItem().getUri()).toBe atom.project.resolve('b')
|
||||
expect(workspace.activePaneItem.getUri()).toBe atom.project.resolve('b')
|
||||
|
||||
waitsForPromise ->
|
||||
workspace.reopenItem()
|
||||
|
||||
runs ->
|
||||
expect(workspace.getActivePaneItem().getUri()).toBe atom.project.resolve('file1')
|
||||
expect(workspace.activePaneItem.getUri()).toBe atom.project.resolve('file1')
|
||||
|
||||
describe "::increase/decreaseFontSize()", ->
|
||||
it "increases/decreases the font size without going below 1", ->
|
||||
@@ -310,22 +282,7 @@ describe "Workspace", ->
|
||||
describe "::openLicense()", ->
|
||||
it "opens the license as plain-text in a buffer", ->
|
||||
waitsForPromise -> workspace.openLicense()
|
||||
runs -> expect(workspace.getActivePaneItem().getText()).toMatch /Copyright/
|
||||
|
||||
describe "::observeTextEditors()", ->
|
||||
it "invokes the observer with current and future text editors", ->
|
||||
observed = []
|
||||
|
||||
waitsForPromise -> workspace.open()
|
||||
waitsForPromise -> workspace.open()
|
||||
waitsForPromise -> workspace.openLicense()
|
||||
|
||||
runs ->
|
||||
workspace.observeTextEditors (editor) -> observed.push(editor)
|
||||
|
||||
waitsForPromise -> workspace.open()
|
||||
|
||||
expect(observed).toEqual workspace.getTextEditors()
|
||||
runs -> expect(workspace.activePaneItem.getText()).toMatch /Copyright/
|
||||
|
||||
describe "when an editor is destroyed", ->
|
||||
it "removes the editor", ->
|
||||
@@ -335,9 +292,23 @@ describe "Workspace", ->
|
||||
workspace.open("a").then (e) -> editor = e
|
||||
|
||||
runs ->
|
||||
expect(workspace.getTextEditors()).toHaveLength 1
|
||||
expect(workspace.getEditors()).toHaveLength 1
|
||||
editor.destroy()
|
||||
expect(workspace.getTextEditors()).toHaveLength 0
|
||||
expect(workspace.getEditors()).toHaveLength 0
|
||||
|
||||
describe "when an editor is copied", ->
|
||||
it "emits an 'editor-created' event", ->
|
||||
editor = null
|
||||
handler = jasmine.createSpy('editorCreatedHandler')
|
||||
workspace.on 'editor-created', handler
|
||||
|
||||
waitsForPromise ->
|
||||
workspace.open("a").then (o) -> editor = o
|
||||
|
||||
runs ->
|
||||
expect(handler.callCount).toBe 1
|
||||
editorCopy = editor.copy()
|
||||
expect(handler.callCount).toBe 2
|
||||
|
||||
it "stores the active grammars used by all the open editors", ->
|
||||
waitsForPromise ->
|
||||
@@ -346,19 +317,14 @@ describe "Workspace", ->
|
||||
waitsForPromise ->
|
||||
atom.packages.activatePackage('language-coffee-script')
|
||||
|
||||
waitsForPromise ->
|
||||
atom.packages.activatePackage('language-todo')
|
||||
|
||||
waitsForPromise ->
|
||||
atom.workspace.open('sample.coffee')
|
||||
|
||||
runs ->
|
||||
atom.workspace.getActiveEditor().setText """
|
||||
i = /test/; #FIXME
|
||||
"""
|
||||
atom.workspace.getActiveEditor().setText('i = /test/;')
|
||||
|
||||
state = atom.workspace.serialize()
|
||||
expect(state.packagesWithActiveGrammars).toEqual ['language-coffee-script', 'language-javascript', 'language-todo']
|
||||
expect(state.packagesWithActiveGrammars).toEqual ['language-coffee-script', 'language-javascript']
|
||||
|
||||
jsPackage = atom.packages.getLoadedPackage('language-javascript')
|
||||
coffeePackage = atom.packages.getLoadedPackage('language-coffee-script')
|
||||
|
||||
@@ -42,7 +42,7 @@ describe "WorkspaceView", ->
|
||||
runs ->
|
||||
editorView1 = atom.workspaceView.getActiveView()
|
||||
buffer = editorView1.getEditor().getBuffer()
|
||||
editorView1.getPaneView().getModel().splitRight(copyActiveItem: true)
|
||||
editorView1.splitRight()
|
||||
expect(atom.workspaceView.getActivePaneView()).toBe atom.workspaceView.getPaneViews()[1]
|
||||
|
||||
simulateReload()
|
||||
@@ -195,29 +195,27 @@ describe "WorkspaceView", ->
|
||||
atom.workspaceView.height(200)
|
||||
atom.workspaceView.attachToDom()
|
||||
rightEditorView = atom.workspaceView.getActiveView()
|
||||
rightEditorView.getEditor().setText("\t \n")
|
||||
rightEditorView.getPaneView().getModel().splitLeft(copyActiveItem: true)
|
||||
leftEditorView = atom.workspaceView.getActiveView()
|
||||
rightEditorView.getEditor().setText("\t ")
|
||||
leftEditorView = rightEditorView.splitLeft()
|
||||
expect(rightEditorView.find(".line:first").text()).toBe " "
|
||||
expect(leftEditorView.find(".line:first").text()).toBe " "
|
||||
|
||||
{space, tab, eol} = atom.config.get('editor.invisibles')
|
||||
{invisibles} = rightEditorView.component.state
|
||||
{space, tab, eol} = invisibles
|
||||
withInvisiblesShowing = "#{tab} #{space}#{space}#{eol}"
|
||||
|
||||
atom.workspaceView.trigger "window:toggle-invisibles"
|
||||
expect(rightEditorView.find(".line:first").text()).toBe withInvisiblesShowing
|
||||
expect(leftEditorView.find(".line:first").text()).toBe withInvisiblesShowing
|
||||
|
||||
leftEditorView.getPaneView().getModel().splitDown(copyActiveItem: true)
|
||||
lowerLeftEditorView = atom.workspaceView.getActiveView()
|
||||
lowerLeftEditorView = leftEditorView.splitDown()
|
||||
expect(lowerLeftEditorView.find(".line:first").text()).toBe withInvisiblesShowing
|
||||
|
||||
atom.workspaceView.trigger "window:toggle-invisibles"
|
||||
expect(rightEditorView.find(".line:first").text()).toBe " "
|
||||
expect(leftEditorView.find(".line:first").text()).toBe " "
|
||||
|
||||
rightEditorView.getPaneView().getModel().splitDown(copyActiveItem: true)
|
||||
lowerRightEditorView = atom.workspaceView.getActiveView()
|
||||
lowerRightEditorView = rightEditorView.splitDown()
|
||||
expect(lowerRightEditorView.find(".line:first").text()).toBe " "
|
||||
|
||||
describe ".eachEditorView(callback)", ->
|
||||
@@ -244,7 +242,7 @@ describe "WorkspaceView", ->
|
||||
atom.workspaceView.eachEditorView(callback)
|
||||
count = 0
|
||||
callbackEditor = null
|
||||
atom.workspaceView.getActiveView().getPaneView().getModel().splitRight(copyActiveItem: true)
|
||||
atom.workspaceView.getActiveView().splitRight()
|
||||
expect(count).toBe 1
|
||||
expect(callbackEditor).toBe atom.workspaceView.getActiveView()
|
||||
|
||||
@@ -262,10 +260,10 @@ describe "WorkspaceView", ->
|
||||
|
||||
subscription = atom.workspaceView.eachEditorView(callback)
|
||||
expect(count).toBe 1
|
||||
atom.workspaceView.getActiveView().getPaneView().getModel().splitRight(copyActiveItem: true)
|
||||
atom.workspaceView.getActiveView().splitRight()
|
||||
expect(count).toBe 2
|
||||
subscription.off()
|
||||
atom.workspaceView.getActiveView().getPaneView().getModel().splitRight(copyActiveItem: true)
|
||||
atom.workspaceView.getActiveView().splitRight()
|
||||
expect(count).toBe 2
|
||||
|
||||
describe "core:close", ->
|
||||
@@ -274,7 +272,7 @@ describe "WorkspaceView", ->
|
||||
|
||||
paneView1 = atom.workspaceView.getActivePaneView()
|
||||
editorView = atom.workspaceView.getActiveView()
|
||||
editorView.getPaneView().getModel().splitRight(copyActiveItem: true)
|
||||
editorView.splitRight()
|
||||
paneView2 = atom.workspaceView.getActivePaneView()
|
||||
|
||||
expect(paneView1).not.toBe paneView2
|
||||
|
||||
+262
-386
@@ -7,25 +7,39 @@ screen = require 'screen'
|
||||
shell = require 'shell'
|
||||
|
||||
_ = require 'underscore-plus'
|
||||
{deprecate} = require 'grim'
|
||||
{Emitter} = require 'event-kit'
|
||||
{deprecated} = require 'grim'
|
||||
{Model} = require 'theorist'
|
||||
fs = require 'fs-plus'
|
||||
|
||||
{$} = require './space-pen-extensions'
|
||||
WindowEventHandler = require './window-event-handler'
|
||||
|
||||
# Essential: Atom global for dealing with packages, themes, menus, and the window.
|
||||
# Public: Atom global for dealing with packages, themes, menus, and the window.
|
||||
#
|
||||
# An instance of this class is always available as the `atom` global.
|
||||
#
|
||||
# ## Useful properties available:
|
||||
#
|
||||
# * `atom.clipboard` - A {Clipboard} instance
|
||||
# * `atom.config` - A {Config} instance
|
||||
# * `atom.contextMenu` - A {ContextMenuManager} instance
|
||||
# * `atom.deserializers` - A {DeserializerManager} instance
|
||||
# * `atom.keymaps` - A {KeymapManager} instance
|
||||
# * `atom.menu` - A {MenuManager} instance
|
||||
# * `atom.packages` - A {PackageManager} instance
|
||||
# * `atom.project` - A {Project} instance
|
||||
# * `atom.syntax` - A {Syntax} instance
|
||||
# * `atom.themes` - A {ThemeManager} instance
|
||||
# * `atom.workspace` - A {Workspace} instance
|
||||
# * `atom.workspaceView` - A {WorkspaceView} instance
|
||||
module.exports =
|
||||
class Atom extends Model
|
||||
@version: 1 # Increment this when the serialization format changes
|
||||
|
||||
# Load or create the Atom environment in the given mode.
|
||||
# Public: Load or create the Atom environment in the given mode.
|
||||
#
|
||||
# * `mode` A {String} mode that is either 'editor' or 'spec' depending on the
|
||||
# kind of environment you want to build.
|
||||
# mode - Pass 'editor' or 'spec' depending on the kind of environment you
|
||||
# want to build.
|
||||
#
|
||||
# Returns an Atom instance, fully initialized
|
||||
@loadOrCreate: (mode) ->
|
||||
@@ -97,71 +111,20 @@ class Atom extends Model
|
||||
remote.getCurrentWindow()
|
||||
|
||||
workspaceViewParentSelector: 'body'
|
||||
lastUncaughtError: null
|
||||
|
||||
###
|
||||
Section: Properties
|
||||
###
|
||||
|
||||
# Public: A {CommandRegistry} instance
|
||||
commands: null
|
||||
|
||||
# Public: A {Config} instance
|
||||
config: null
|
||||
|
||||
# Public: A {Clipboard} instance
|
||||
clipboard: null
|
||||
|
||||
# Public: A {ContextMenuManager} instance
|
||||
contextMenu: null
|
||||
|
||||
# Public: A {MenuManager} instance
|
||||
menu: null
|
||||
|
||||
# Public: A {KeymapManager} instance
|
||||
keymaps: null
|
||||
|
||||
# Public: A {Project} instance
|
||||
project: null
|
||||
|
||||
# Public: A {Syntax} instance
|
||||
syntax: null
|
||||
|
||||
# Public: A {PackageManager} instance
|
||||
packages: null
|
||||
|
||||
# Public: A {ThemeManager} instance
|
||||
themes: null
|
||||
|
||||
# Public: A {DeserializerManager} instance
|
||||
deserializers: null
|
||||
|
||||
# Public: A {Workspace} instance
|
||||
workspace: null
|
||||
|
||||
# Public: A {WorkspaceView} instance
|
||||
workspaceView: null
|
||||
|
||||
###
|
||||
Section: Construction and Destruction
|
||||
###
|
||||
|
||||
# Call .loadOrCreate instead
|
||||
constructor: (@state) ->
|
||||
@emitter = new Emitter
|
||||
{@mode} = @state
|
||||
DeserializerManager = require './deserializer-manager'
|
||||
@deserializers = new DeserializerManager()
|
||||
|
||||
# Sets up the basic services that should be available in all modes
|
||||
# (both spec and application).
|
||||
#
|
||||
# Call after this instance has been assigned to the `atom` global.
|
||||
# Public: Sets up the basic services that should be available in all modes
|
||||
# (both spec and application). Call after this instance has been assigned to
|
||||
# the `atom` global.
|
||||
initialize: ->
|
||||
window.onerror = =>
|
||||
@openDevTools()
|
||||
@executeJavaScriptInDevTools('InspectorFrontendAPI.showConsole()')
|
||||
@lastUncaughtError = Array::slice.call(arguments)
|
||||
@emit 'uncaught-error', arguments...
|
||||
|
||||
@unsubscribe()
|
||||
@@ -171,7 +134,6 @@ class Atom extends Model
|
||||
|
||||
Config = require './config'
|
||||
KeymapManager = require './keymap-extensions'
|
||||
CommandRegistry = require './command-registry'
|
||||
PackageManager = require './package-manager'
|
||||
Clipboard = require './clipboard'
|
||||
Syntax = require './syntax'
|
||||
@@ -193,16 +155,15 @@ class Atom extends Model
|
||||
@config = new Config({configDirPath, resourcePath})
|
||||
@keymaps = new KeymapManager({configDirPath, resourcePath})
|
||||
@keymap = @keymaps # Deprecated
|
||||
@commands = new CommandRegistry
|
||||
@packages = new PackageManager({devMode, configDirPath, resourcePath, safeMode})
|
||||
@themes = new ThemeManager({packageManager: @packages, configDirPath, resourcePath, safeMode})
|
||||
@contextMenu = new ContextMenuManager({resourcePath, devMode})
|
||||
@themes = new ThemeManager({packageManager: @packages, configDirPath, resourcePath})
|
||||
@contextMenu = new ContextMenuManager(devMode)
|
||||
@menu = new MenuManager({resourcePath})
|
||||
@clipboard = new Clipboard()
|
||||
|
||||
@syntax = @deserializers.deserialize(@state.syntax) ? new Syntax()
|
||||
|
||||
@subscribe @packages.onDidActivateAll => @watchThemes()
|
||||
@subscribe @packages, 'activated', => @watchThemes()
|
||||
|
||||
Project = require './project'
|
||||
TextBuffer = require 'text-buffer'
|
||||
@@ -213,196 +174,41 @@ class Atom extends Model
|
||||
|
||||
@windowEventHandler = new WindowEventHandler
|
||||
|
||||
###
|
||||
Section: Event Subscription
|
||||
###
|
||||
# Deprecated: Callers should be converted to use atom.deserializers
|
||||
registerRepresentationClass: ->
|
||||
deprecated("Callers should be converted to use atom.deserializers")
|
||||
|
||||
# Extended: Invoke the given callback whenever {::beep} is called.
|
||||
#
|
||||
# * `callback` {Function} to be called whenever {::beep} is called.
|
||||
#
|
||||
# Returns a {Disposable} on which `.dispose()` can be called to unsubscribe.
|
||||
onDidBeep: (callback) ->
|
||||
@emitter.on 'did-beep', callback
|
||||
# Deprecated: Callers should be converted to use atom.deserializers
|
||||
registerRepresentationClasses: ->
|
||||
deprecated("Callers should be converted to use atom.deserializers")
|
||||
|
||||
###
|
||||
Section: Atom Details
|
||||
###
|
||||
setBodyPlatformClass: ->
|
||||
document.body.classList.add("platform-#{process.platform}")
|
||||
|
||||
# Public: Is the current window in development mode?
|
||||
inDevMode: ->
|
||||
@getLoadSettings().devMode
|
||||
|
||||
# Public: Is the current window running specs?
|
||||
inSpecMode: ->
|
||||
@getLoadSettings().isSpec
|
||||
|
||||
# Public: Get the version of the Atom application.
|
||||
#
|
||||
# Returns the version text {String}.
|
||||
getVersion: ->
|
||||
@appVersion ?= @getLoadSettings().appVersion
|
||||
|
||||
# Public: Determine whether the current version is an official release.
|
||||
isReleasedVersion: ->
|
||||
not /\w{7}/.test(@getVersion()) # Check if the release is a 7-character SHA prefix
|
||||
|
||||
# Public: Get the directory path to Atom's configuration area.
|
||||
#
|
||||
# Returns the absolute path to `~/.atom`.
|
||||
getConfigDirPath: ->
|
||||
@constructor.getConfigDirPath()
|
||||
|
||||
# Public: Get the time taken to completely load the current window.
|
||||
#
|
||||
# This time include things like loading and activating packages, creating
|
||||
# DOM elements for the editor, and reading the config.
|
||||
#
|
||||
# Returns the {Number} of milliseconds taken to load the window or null
|
||||
# if the window hasn't finished loading yet.
|
||||
getWindowLoadTime: ->
|
||||
@loadTime
|
||||
|
||||
# Public: Get the load settings for the current window.
|
||||
#
|
||||
# Returns an {Object} containing all the load setting key/value pairs.
|
||||
getLoadSettings: ->
|
||||
@constructor.getLoadSettings()
|
||||
|
||||
###
|
||||
Section: Managing The Atom Window
|
||||
###
|
||||
|
||||
# Essential: Open a new Atom window using the given options.
|
||||
#
|
||||
# Calling this method without an options parameter will open a prompt to pick
|
||||
# a file/folder to open in the new window.
|
||||
#
|
||||
# * `options` An {Object} with the following keys:
|
||||
# * `pathsToOpen` An {Array} of {String} paths to open.
|
||||
# * `newWindow` A {Boolean}, true to always open a new window instead of
|
||||
# reusing existing windows depending on the paths to open.
|
||||
# * `devMode` A {Boolean}, true to open the window in development mode.
|
||||
# Development mode loads the Atom source from the locally cloned
|
||||
# repository and also loads all the packages in ~/.atom/dev/packages
|
||||
# * `safeMode` A {Boolean}, true to open the window in safe mode. Safe
|
||||
# mode prevents all packages installed to ~/.atom/packages from loading.
|
||||
open: (options) ->
|
||||
ipc.send('open', options)
|
||||
|
||||
# Essential: Close the current window.
|
||||
close: ->
|
||||
@getCurrentWindow().close()
|
||||
|
||||
# Essential: Get the size of current window.
|
||||
#
|
||||
# Returns an {Object} in the format `{width: 1000, height: 700}`
|
||||
getSize: ->
|
||||
[width, height] = @getCurrentWindow().getSize()
|
||||
{width, height}
|
||||
|
||||
# Essential: Set the size of current window.
|
||||
#
|
||||
# * `width` The {Number} of pixels.
|
||||
# * `height` The {Number} of pixels.
|
||||
setSize: (width, height) ->
|
||||
@getCurrentWindow().setSize(width, height)
|
||||
|
||||
# Essential: Get the position of current window.
|
||||
#
|
||||
# Returns an {Object} in the format `{x: 10, y: 20}`
|
||||
getPosition: ->
|
||||
[x, y] = @getCurrentWindow().getPosition()
|
||||
{x, y}
|
||||
|
||||
# Essential: Set the position of current window.
|
||||
#
|
||||
# * `x` The {Number} of pixels.
|
||||
# * `y` The {Number} of pixels.
|
||||
setPosition: (x, y) ->
|
||||
ipc.send('call-window-method', 'setPosition', x, y)
|
||||
|
||||
# Extended: Get the current window
|
||||
# Public: Get the current window
|
||||
getCurrentWindow: ->
|
||||
@constructor.getCurrentWindow()
|
||||
|
||||
# Extended: Move current window to the center of the screen.
|
||||
center: ->
|
||||
ipc.send('call-window-method', 'center')
|
||||
|
||||
# Extended: Focus the current window.
|
||||
focus: ->
|
||||
ipc.send('call-window-method', 'focus')
|
||||
$(window).focus()
|
||||
|
||||
# Extended: Show the current window.
|
||||
show: ->
|
||||
ipc.send('call-window-method', 'show')
|
||||
|
||||
# Extended: Hide the current window.
|
||||
hide: ->
|
||||
ipc.send('call-window-method', 'hide')
|
||||
|
||||
# Extended: Reload the current window.
|
||||
reload: ->
|
||||
ipc.send('call-window-method', 'restart')
|
||||
|
||||
# Extended: Returns a {Boolean} true when the current window is maximized.
|
||||
isMaximixed: ->
|
||||
@getCurrentWindow().isMaximized()
|
||||
|
||||
maximize: ->
|
||||
ipc.send('call-window-method', 'maximize')
|
||||
|
||||
# Extended: Is the current window in full screen mode?
|
||||
isFullScreen: ->
|
||||
@getCurrentWindow().isFullScreen()
|
||||
|
||||
# Extended: Set the full screen state of the current window.
|
||||
setFullScreen: (fullScreen=false) ->
|
||||
ipc.send('call-window-method', 'setFullScreen', fullScreen)
|
||||
if fullScreen then document.body.classList.add("fullscreen") else document.body.classList.remove("fullscreen")
|
||||
|
||||
# Extended: Toggle the full screen state of the current window.
|
||||
toggleFullScreen: ->
|
||||
@setFullScreen(!@isFullScreen())
|
||||
|
||||
# Schedule the window to be shown and focused on the next tick.
|
||||
# Public: Get the dimensions of this window.
|
||||
#
|
||||
# This is done in a next tick to prevent a white flicker from occurring
|
||||
# if called synchronously.
|
||||
displayWindow: ({maximize}={}) ->
|
||||
setImmediate =>
|
||||
@show()
|
||||
@focus()
|
||||
@setFullScreen(true) if @workspace.fullScreen
|
||||
@maximize() if maximize
|
||||
|
||||
# Get the dimensions of this window.
|
||||
#
|
||||
# Returns an {Object} with the following keys:
|
||||
# * `x` The window's x-position {Number}.
|
||||
# * `y` The window's y-position {Number}.
|
||||
# * `width` The window's width {Number}.
|
||||
# * `height` The window's height {Number}.
|
||||
# Returns an object with x, y, width, and height keys.
|
||||
getWindowDimensions: ->
|
||||
browserWindow = @getCurrentWindow()
|
||||
[x, y] = browserWindow.getPosition()
|
||||
[width, height] = browserWindow.getSize()
|
||||
maximized = browserWindow.isMaximized()
|
||||
{x, y, width, height, maximized}
|
||||
{x, y, width, height}
|
||||
|
||||
# Set the dimensions of the window.
|
||||
# Public: Set the dimensions of the window.
|
||||
#
|
||||
# The window will be centered if either the x or y coordinate is not set
|
||||
# in the dimensions parameter. If x or y are omitted the window will be
|
||||
# centered. If height or width are omitted only the position will be changed.
|
||||
#
|
||||
# * `dimensions` An {Object} with the following keys:
|
||||
# * `x` The new x coordinate.
|
||||
# * `y` The new y coordinate.
|
||||
# * `width` The new width.
|
||||
# * `height` The new height.
|
||||
# dimensions - An {Object} with the following keys:
|
||||
# :x - The new x coordinate.
|
||||
# :y - The new y coordinate.
|
||||
# :width - The new width.
|
||||
# :height - The new height.
|
||||
setWindowDimensions: ({x, y, width, height}) ->
|
||||
if width? and height?
|
||||
@setSize(width, height)
|
||||
@@ -443,131 +249,16 @@ class Atom extends Model
|
||||
unless @isValidDimensions(dimensions)
|
||||
dimensions = @getDefaultWindowDimensions()
|
||||
@setWindowDimensions(dimensions)
|
||||
dimensions
|
||||
|
||||
storeWindowDimensions: ->
|
||||
dimensions = @getWindowDimensions()
|
||||
@state.windowDimensions = dimensions if @isValidDimensions(dimensions)
|
||||
|
||||
# Call this method when establishing a real application window.
|
||||
startEditorWindow: ->
|
||||
{resourcePath, safeMode} = @getLoadSettings()
|
||||
|
||||
CommandInstaller = require './command-installer'
|
||||
CommandInstaller.installAtomCommand resourcePath, false, (error) ->
|
||||
console.warn error.message if error?
|
||||
CommandInstaller.installApmCommand resourcePath, false, (error) ->
|
||||
console.warn error.message if error?
|
||||
|
||||
dimensions = @restoreWindowDimensions()
|
||||
@config.load()
|
||||
@config.setDefaults('core', require('./workspace-view').configDefaults)
|
||||
@config.setDefaults('editor', require('./editor-view').configDefaults)
|
||||
@keymaps.loadBundledKeymaps()
|
||||
@themes.loadBaseStylesheets()
|
||||
@packages.loadPackages()
|
||||
@deserializeEditorWindow()
|
||||
|
||||
@watchProjectPath()
|
||||
|
||||
@packages.activate()
|
||||
@keymaps.loadUserKeymap()
|
||||
@requireUserInitScript() unless safeMode
|
||||
@menu.update()
|
||||
|
||||
maximize = dimensions?.maximized and process.platform isnt 'darwin'
|
||||
@displayWindow({maximize})
|
||||
|
||||
unloadEditorWindow: ->
|
||||
return if not @project and not @workspaceView
|
||||
|
||||
@state.syntax = @syntax.serialize()
|
||||
@state.project = @project.serialize()
|
||||
@state.workspace = @workspace.serialize()
|
||||
@packages.deactivatePackages()
|
||||
@state.packageStates = @packages.packageStates
|
||||
@saveSync()
|
||||
@windowState = null
|
||||
|
||||
removeEditorWindow: ->
|
||||
return if not @project and not @workspaceView
|
||||
|
||||
@workspaceView?.remove()
|
||||
@workspaceView = null
|
||||
@project?.destroy()
|
||||
@project = null
|
||||
|
||||
@windowEventHandler?.unsubscribe()
|
||||
|
||||
###
|
||||
Section: Messaging the User
|
||||
###
|
||||
|
||||
# Essential: Visually and audibly trigger a beep.
|
||||
beep: ->
|
||||
shell.beep() if @config.get('core.audioBeep')
|
||||
@workspaceView.trigger 'beep'
|
||||
@emitter.emit 'did-beep'
|
||||
|
||||
# Essential: A flexible way to open a dialog akin to an alert dialog.
|
||||
# Public: Get the load settings for the current window.
|
||||
#
|
||||
# ## Examples
|
||||
#
|
||||
# ```coffee
|
||||
# atom.confirm
|
||||
# message: 'How you feeling?'
|
||||
# detailedMessage: 'Be honest.'
|
||||
# buttons:
|
||||
# Good: -> window.alert('good to hear')
|
||||
# Bad: -> window.alert('bummer')
|
||||
# ```
|
||||
#
|
||||
# * `options` An {Object} with the following keys:
|
||||
# * `message` The {String} message to display.
|
||||
# * `detailedMessage` (optional) The {String} detailed message to display.
|
||||
# * `buttons` (optional) Either an array of strings or an object where keys are
|
||||
# button names and the values are callbacks to invoke when clicked.
|
||||
#
|
||||
# Returns the chosen button index {Number} if the buttons option was an array.
|
||||
confirm: ({message, detailedMessage, buttons}={}) ->
|
||||
buttons ?= {}
|
||||
if _.isArray(buttons)
|
||||
buttonLabels = buttons
|
||||
else
|
||||
buttonLabels = Object.keys(buttons)
|
||||
|
||||
dialog = remote.require('dialog')
|
||||
chosen = dialog.showMessageBox @getCurrentWindow(),
|
||||
type: 'info'
|
||||
message: message
|
||||
detail: detailedMessage
|
||||
buttons: buttonLabels
|
||||
|
||||
if _.isArray(buttons)
|
||||
chosen
|
||||
else
|
||||
callback = buttons[buttonLabels[chosen]]
|
||||
callback?()
|
||||
|
||||
###
|
||||
Section: Managing the Dev Tools
|
||||
###
|
||||
|
||||
# Extended: Open the dev tools for the current window.
|
||||
openDevTools: ->
|
||||
ipc.send('call-window-method', 'openDevTools')
|
||||
|
||||
# Extended: Toggle the visibility of the dev tools for the current window.
|
||||
toggleDevTools: ->
|
||||
ipc.send('call-window-method', 'toggleDevTools')
|
||||
|
||||
# Extended: Execute code in dev tools.
|
||||
executeJavaScriptInDevTools: (code) ->
|
||||
ipc.send('call-window-method', 'executeJavaScriptInDevTools', code)
|
||||
|
||||
###
|
||||
Section: Private
|
||||
###
|
||||
# Returns an object containing all the load setting key/value pairs.
|
||||
getLoadSettings: ->
|
||||
@constructor.getLoadSettings()
|
||||
|
||||
deserializeProject: ->
|
||||
Project = require './project'
|
||||
@@ -598,33 +289,111 @@ class Atom extends Model
|
||||
@deserializeProject()
|
||||
@deserializeWorkspaceView()
|
||||
|
||||
# Call this method when establishing a real application window.
|
||||
startEditorWindow: ->
|
||||
CommandInstaller = require './command-installer'
|
||||
resourcePath = atom.getLoadSettings().resourcePath
|
||||
CommandInstaller.installAtomCommand resourcePath, false, (error) ->
|
||||
console.warn error.message if error?
|
||||
CommandInstaller.installApmCommand resourcePath, false, (error) ->
|
||||
console.warn error.message if error?
|
||||
|
||||
@restoreWindowDimensions()
|
||||
@config.load()
|
||||
@config.setDefaults('core', require('./workspace-view').configDefaults)
|
||||
@config.setDefaults('editor', require('./editor-view').configDefaults)
|
||||
@keymaps.loadBundledKeymaps()
|
||||
@themes.loadBaseStylesheets()
|
||||
@packages.loadPackages()
|
||||
@deserializeEditorWindow()
|
||||
@packages.activate()
|
||||
@keymaps.loadUserKeymap()
|
||||
@requireUserInitScript()
|
||||
@menu.update()
|
||||
|
||||
$(window).on 'unload', =>
|
||||
$(document.body).css('visibility', 'hidden')
|
||||
@unloadEditorWindow()
|
||||
false
|
||||
|
||||
@displayWindow()
|
||||
|
||||
unloadEditorWindow: ->
|
||||
return if not @project and not @workspaceView
|
||||
|
||||
@state.syntax = @syntax.serialize()
|
||||
@state.project = @project.serialize()
|
||||
@state.workspace = @workspace.serialize()
|
||||
@packages.deactivatePackages()
|
||||
@state.packageStates = @packages.packageStates
|
||||
@saveSync()
|
||||
@workspaceView.remove()
|
||||
@workspaceView = null
|
||||
@project.destroy()
|
||||
@windowEventHandler?.unsubscribe()
|
||||
@keymaps.destroy()
|
||||
@windowState = null
|
||||
|
||||
loadThemes: ->
|
||||
@themes.load()
|
||||
|
||||
watchThemes: ->
|
||||
@themes.onDidReloadAll =>
|
||||
@themes.on 'reloaded', =>
|
||||
# Only reload stylesheets from non-theme packages
|
||||
for pack in @packages.getActivePackages() when pack.getType() isnt 'theme'
|
||||
pack.reloadStylesheets?()
|
||||
null
|
||||
|
||||
# Notify the browser project of the window's current project path
|
||||
watchProjectPath: ->
|
||||
onProjectPathChanged = =>
|
||||
ipc.send('window-command', 'project-path-changed', @project.getPath())
|
||||
@subscribe @project, 'path-changed', onProjectPathChanged
|
||||
onProjectPathChanged()
|
||||
# Public: Open a new Atom window using the given options.
|
||||
#
|
||||
# Calling this method without an options parameter will open a prompt to pick
|
||||
# a file/folder to open in the new window.
|
||||
#
|
||||
# options - An {Object} with the following keys:
|
||||
# :pathsToOpen - An {Array} of {String} paths to open.
|
||||
open: (options) ->
|
||||
ipc.send('open', options)
|
||||
|
||||
exit: (status) ->
|
||||
app = remote.require('app')
|
||||
app.emit('will-exit')
|
||||
remote.process.exit(status)
|
||||
# Public: Open a confirm dialog.
|
||||
#
|
||||
# ## Example
|
||||
#
|
||||
# ```coffee
|
||||
# atom.confirm
|
||||
# message: 'How you feeling?'
|
||||
# detailedMessage: 'Be honest.'
|
||||
# buttons:
|
||||
# Good: -> window.alert('good to hear')
|
||||
# Bad: -> window.alert('bummer')
|
||||
# ```
|
||||
#
|
||||
# options - An {Object} with the following keys:
|
||||
# :message - The {String} message to display.
|
||||
# :detailedMessage - The {String} detailed message to display.
|
||||
# :buttons - Either an array of strings or an object where keys are
|
||||
# button names and the values are callbacks to invoke when
|
||||
# clicked.
|
||||
#
|
||||
# Returns the chosen button index {Number} if the buttons option was an array.
|
||||
confirm: ({message, detailedMessage, buttons}={}) ->
|
||||
buttons ?= {}
|
||||
if _.isArray(buttons)
|
||||
buttonLabels = buttons
|
||||
else
|
||||
buttonLabels = Object.keys(buttons)
|
||||
|
||||
setDocumentEdited: (edited) ->
|
||||
ipc.send('call-window-method', 'setDocumentEdited', edited)
|
||||
dialog = remote.require('dialog')
|
||||
chosen = dialog.showMessageBox @getCurrentWindow(),
|
||||
type: 'info'
|
||||
message: message
|
||||
detail: detailedMessage
|
||||
buttons: buttonLabels
|
||||
|
||||
setRepresentedFilename: (filename) ->
|
||||
ipc.send('call-window-method', 'setRepresentedFilename', filename)
|
||||
if _.isArray(buttons)
|
||||
chosen
|
||||
else
|
||||
callback = buttons[buttonLabels[chosen]]
|
||||
callback?()
|
||||
|
||||
showSaveDialog: (callback) ->
|
||||
callback(showSaveDialogSync())
|
||||
@@ -635,6 +404,109 @@ class Atom extends Model
|
||||
dialog = remote.require('dialog')
|
||||
dialog.showSaveDialog currentWindow, {title: 'Save File', defaultPath}
|
||||
|
||||
# Public: Open the dev tools for the current window.
|
||||
openDevTools: ->
|
||||
ipc.send('call-window-method', 'openDevTools')
|
||||
|
||||
# Public: Toggle the visibility of the dev tools for the current window.
|
||||
toggleDevTools: ->
|
||||
ipc.send('call-window-method', 'toggleDevTools')
|
||||
|
||||
# Public: Execute code in dev tools.
|
||||
executeJavaScriptInDevTools: (code) ->
|
||||
ipc.send('call-window-method', 'executeJavaScriptInDevTools', code)
|
||||
|
||||
# Public: Reload the current window.
|
||||
reload: ->
|
||||
ipc.send('call-window-method', 'restart')
|
||||
|
||||
# Public: Focus the current window.
|
||||
focus: ->
|
||||
ipc.send('call-window-method', 'focus')
|
||||
$(window).focus()
|
||||
|
||||
# Public: Show the current window.
|
||||
show: ->
|
||||
ipc.send('call-window-method', 'show')
|
||||
|
||||
# Public: Hide the current window.
|
||||
hide: ->
|
||||
ipc.send('call-window-method', 'hide')
|
||||
|
||||
# Public: Set the size of current window.
|
||||
#
|
||||
# width - The {Number} of pixels.
|
||||
# height - The {Number} of pixels.
|
||||
setSize: (width, height) ->
|
||||
@getCurrentWindow().setSize(width, height)
|
||||
|
||||
# Public: Set the position of current window.
|
||||
#
|
||||
# x - The {Number} of pixels.
|
||||
# y - The {Number} of pixels.
|
||||
setPosition: (x, y) ->
|
||||
ipc.send('call-window-method', 'setPosition', x, y)
|
||||
|
||||
# Public: Move current window to the center of the screen.
|
||||
center: ->
|
||||
ipc.send('call-window-method', 'center')
|
||||
|
||||
# Schedule the window to be shown and focused on the next tick.
|
||||
#
|
||||
# This is done in a next tick to prevent a white flicker from occurring
|
||||
# if called synchronously.
|
||||
displayWindow: ->
|
||||
setImmediate =>
|
||||
@show()
|
||||
@focus()
|
||||
@setFullScreen(true) if @workspaceView.fullScreen
|
||||
|
||||
# Public: Close the current window.
|
||||
close: ->
|
||||
@getCurrentWindow().close()
|
||||
|
||||
exit: (status) ->
|
||||
app = remote.require('app')
|
||||
app.emit('will-exit')
|
||||
remote.process.exit(status)
|
||||
|
||||
# Public: Is the current window in development mode?
|
||||
inDevMode: ->
|
||||
@getLoadSettings().devMode
|
||||
|
||||
# Public: Is the current window running specs?
|
||||
inSpecMode: ->
|
||||
@getLoadSettings().isSpec
|
||||
|
||||
# Public: Toggle the full screen state of the current window.
|
||||
toggleFullScreen: ->
|
||||
@setFullScreen(!@isFullScreen())
|
||||
|
||||
# Public: Set the full screen state of the current window.
|
||||
setFullScreen: (fullScreen=false) ->
|
||||
ipc.send('call-window-method', 'setFullScreen', fullScreen)
|
||||
if fullScreen then document.body.classList.add("fullscreen") else document.body.classList.remove("fullscreen")
|
||||
|
||||
# Public: Is the current window in full screen mode?
|
||||
isFullScreen: ->
|
||||
@getCurrentWindow().isFullScreen()
|
||||
|
||||
# Public: Get the version of the Atom application.
|
||||
#
|
||||
# Returns the version text {String}.
|
||||
getVersion: ->
|
||||
@appVersion ?= @getLoadSettings().appVersion
|
||||
|
||||
# Public: Determine whether the current version is an official release.
|
||||
isReleasedVersion: ->
|
||||
not /\w{7}/.test(@getVersion()) # Check if the release is a 7-character SHA prefix
|
||||
|
||||
# Public: Get the directory path to Atom's configuration area.
|
||||
#
|
||||
# Returns the absolute path to `~/.atom`.
|
||||
getConfigDirPath: ->
|
||||
@constructor.getConfigDirPath()
|
||||
|
||||
saveSync: ->
|
||||
stateString = JSON.stringify(@state)
|
||||
if statePath = @constructor.getStatePath(@mode)
|
||||
@@ -642,12 +514,27 @@ class Atom extends Model
|
||||
else
|
||||
@getCurrentWindow().loadSettings.windowState = stateString
|
||||
|
||||
# Public: Get the time taken to completely load the current window.
|
||||
#
|
||||
# This time include things like loading and activating packages, creating
|
||||
# DOM elements for the editor, and reading the config.
|
||||
#
|
||||
# Returns the number of milliseconds taken to load the window or null
|
||||
# if the window hasn't finished loading yet.
|
||||
getWindowLoadTime: ->
|
||||
@loadTime
|
||||
|
||||
crashMainProcess: ->
|
||||
remote.process.crash()
|
||||
|
||||
crashRenderProcess: ->
|
||||
process.crash()
|
||||
|
||||
# Public: Visually and audibly trigger a beep.
|
||||
beep: ->
|
||||
shell.beep() if @config.get('core.audioBeep')
|
||||
@workspaceView.trigger 'beep'
|
||||
|
||||
getUserInitScriptPath: ->
|
||||
initScriptPath = fs.resolve(@getConfigDirPath(), 'init', ['js', 'coffee'])
|
||||
initScriptPath ? path.join(@getConfigDirPath(), 'init.coffee')
|
||||
@@ -659,13 +546,13 @@ class Atom extends Model
|
||||
catch error
|
||||
console.error "Failed to load `#{userInitScriptPath}`", error.stack, error
|
||||
|
||||
# Require the module with the given globals.
|
||||
# Public: Require the module with the given globals.
|
||||
#
|
||||
# The globals will be set on the `window` object and removed after the
|
||||
# require completes.
|
||||
#
|
||||
# * `id` The {String} module name or path.
|
||||
# * `globals` An optinal {Object} to set as globals during require.
|
||||
# id - The {String} module name or path.
|
||||
# globals - An {Object} to set as globals during require (default: {})
|
||||
requireWithGlobals: (id, globals={}) ->
|
||||
existingGlobals = {}
|
||||
for key, value of globals
|
||||
@@ -679,14 +566,3 @@ class Atom extends Model
|
||||
delete window[key]
|
||||
else
|
||||
window[key] = value
|
||||
|
||||
# Deprecated: Callers should be converted to use atom.deserializers
|
||||
registerRepresentationClass: ->
|
||||
deprecate("Callers should be converted to use atom.deserializers")
|
||||
|
||||
# Deprecated: Callers should be converted to use atom.deserializers
|
||||
registerRepresentationClasses: ->
|
||||
deprecate("Callers should be converted to use atom.deserializers")
|
||||
|
||||
setBodyPlatformClass: ->
|
||||
document.body.classList.add("platform-#{process.platform}")
|
||||
|
||||
@@ -116,7 +116,7 @@ class ApplicationMenu
|
||||
item.metadata ?= {}
|
||||
if item.command
|
||||
item.accelerator = @acceleratorForCommand(item.command, keystrokesByCommand)
|
||||
item.click = -> global.atomApplication.sendCommand(item.command)
|
||||
item.click = => global.atomApplication.sendCommand(item.command)
|
||||
item.metadata['windowSpecific'] = true unless /^application:/.test(item.command)
|
||||
@translateTemplate(item.submenu, keystrokesByCommand) if item.submenu
|
||||
template
|
||||
|
||||
@@ -5,11 +5,13 @@ AutoUpdateManager = require './auto-update-manager'
|
||||
BrowserWindow = require 'browser-window'
|
||||
Menu = require 'menu'
|
||||
app = require 'app'
|
||||
dialog = require 'dialog'
|
||||
fs = require 'fs'
|
||||
ipc = require 'ipc'
|
||||
path = require 'path'
|
||||
os = require 'os'
|
||||
net = require 'net'
|
||||
shell = require 'shell'
|
||||
url = require 'url'
|
||||
{EventEmitter} = require 'events'
|
||||
_ = require 'underscore-plus'
|
||||
@@ -101,13 +103,6 @@ class AtomApplication
|
||||
window.once 'window:loaded', =>
|
||||
@autoUpdateManager.emitUpdateAvailableEvent(window)
|
||||
|
||||
unless window.isSpec
|
||||
focusHandler = => @lastFocusedWindow = window
|
||||
window.browserWindow.on 'focus', focusHandler
|
||||
window.browserWindow.once 'closed', =>
|
||||
@lastFocusedWindow = null if window is @lastFocusedWindow
|
||||
window.browserWindow.removeListener 'focus', focusHandler
|
||||
|
||||
# Creates server to listen for additional atom application launches.
|
||||
#
|
||||
# You can run the atom command multiple times, but after the first launch
|
||||
@@ -140,7 +135,7 @@ class AtomApplication
|
||||
|
||||
# Registers basic application commands, non-idempotent.
|
||||
handleEvents: ->
|
||||
@on 'application:run-all-specs', -> @runSpecs(exitWhenDone: false, resourcePath: global.devResourcePath, safeMode: @focusedWindow()?.safeMode)
|
||||
@on 'application:run-all-specs', -> @runSpecs(exitWhenDone: false, resourcePath: global.devResourcePath)
|
||||
@on 'application:run-benchmarks', -> @runBenchmarks()
|
||||
@on 'application:quit', -> app.quit()
|
||||
@on 'application:new-window', -> @openPath(windowDimensions: @focusedWindow()?.getDimensions())
|
||||
@@ -154,8 +149,8 @@ class AtomApplication
|
||||
atomWindow ?= @focusedWindow()
|
||||
atomWindow?.browserWindow.inspectElement(x, y)
|
||||
|
||||
@on 'application:open-documentation', -> require('shell').openExternal('https://atom.io/docs/latest/?app')
|
||||
@on 'application:open-terms-of-use', -> require('shell').openExternal('https://atom.io/terms')
|
||||
@on 'application:open-documentation', -> shell.openExternal('https://atom.io/docs/latest/?app')
|
||||
@on 'application:open-terms-of-use', -> shell.openExternal('https://atom.io/terms')
|
||||
@on 'application:install-update', -> @autoUpdateManager.install()
|
||||
@on 'application:check-for-update', => @autoUpdateManager.check()
|
||||
|
||||
@@ -204,15 +199,13 @@ class AtomApplication
|
||||
|
||||
# A request from the associated render process to open a new render process.
|
||||
ipc.on 'open', (event, options) =>
|
||||
window = @windowForEvent(event)
|
||||
if options?
|
||||
if options.pathsToOpen?.length > 0
|
||||
options.window = window
|
||||
@openPaths(options)
|
||||
else
|
||||
new AtomWindow(options)
|
||||
else
|
||||
@promptForPath({window})
|
||||
@promptForPath()
|
||||
|
||||
ipc.on 'update-application-menu', (event, template, keystrokesByCommand) =>
|
||||
@applicationMenu.update(template, keystrokesByCommand)
|
||||
@@ -288,12 +281,8 @@ class AtomApplication
|
||||
|
||||
# Returns the {AtomWindow} for the given path.
|
||||
windowForPath: (pathToOpen) ->
|
||||
_.find @windows, (atomWindow) -> atomWindow.containsPath(pathToOpen)
|
||||
|
||||
# Returns the {AtomWindow} for the given ipc event.
|
||||
windowForEvent: ({sender}) ->
|
||||
window = BrowserWindow.fromWebContents(sender)
|
||||
_.find @windows, ({browserWindow}) -> window is browserWindow
|
||||
for atomWindow in @windows
|
||||
return atomWindow if atomWindow.containsPath(pathToOpen)
|
||||
|
||||
# Public: Returns the currently focused {AtomWindow} or undefined if none.
|
||||
focusedWindow: ->
|
||||
@@ -307,10 +296,9 @@ class AtomApplication
|
||||
# :newWindow - Boolean of whether this should be opened in a new window.
|
||||
# :devMode - Boolean to control the opened window's dev mode.
|
||||
# :safeMode - Boolean to control the opened window's safe mode.
|
||||
# :window - {AtomWindow} to open file paths in.
|
||||
openPaths: ({pathsToOpen, pidToKillWhenClosed, newWindow, devMode, safeMode, window}) ->
|
||||
openPaths: ({pathsToOpen, pidToKillWhenClosed, newWindow, devMode, safeMode}) ->
|
||||
for pathToOpen in pathsToOpen ? []
|
||||
@openPath({pathToOpen, pidToKillWhenClosed, newWindow, devMode, safeMode, window})
|
||||
@openPath({pathToOpen, pidToKillWhenClosed, newWindow, devMode, safeMode})
|
||||
|
||||
# Public: Opens a single path, in an existing window if possible.
|
||||
#
|
||||
@@ -321,33 +309,15 @@ class AtomApplication
|
||||
# :devMode - Boolean to control the opened window's dev mode.
|
||||
# :safeMode - Boolean to control the opened window's safe mode.
|
||||
# :windowDimensions - Object with height and width keys.
|
||||
# :window - {AtomWindow} to open file paths in.
|
||||
openPath: ({pathToOpen, pidToKillWhenClosed, newWindow, devMode, safeMode, windowDimensions, window}={}) ->
|
||||
openPath: ({pathToOpen, pidToKillWhenClosed, newWindow, devMode, safeMode, windowDimensions}={}) ->
|
||||
{pathToOpen, initialLine, initialColumn} = @locationForPathToOpen(pathToOpen)
|
||||
|
||||
unless pidToKillWhenClosed or newWindow
|
||||
pathToOpenStat = fs.statSyncNoException(pathToOpen)
|
||||
|
||||
# Default to using the specified window or the last focused window
|
||||
currentWindow = window ? @lastFocusedWindow
|
||||
|
||||
if pathToOpenStat.isFile?()
|
||||
# Open the file in the current window
|
||||
existingWindow = currentWindow
|
||||
else if pathToOpenStat.isDirectory?()
|
||||
# Open the folder in the current window if it doesn't have a path
|
||||
existingWindow = currentWindow unless currentWindow?.hasProjectPath()
|
||||
|
||||
# Don't reuse windows in dev mode
|
||||
existingWindow ?= @windowForPath(pathToOpen) unless devMode
|
||||
|
||||
if existingWindow?
|
||||
unless devMode
|
||||
existingWindow = @windowForPath(pathToOpen) unless pidToKillWhenClosed or newWindow
|
||||
if existingWindow
|
||||
openedWindow = existingWindow
|
||||
openedWindow.openPath(pathToOpen, initialLine)
|
||||
if openedWindow.isMinimized()
|
||||
openedWindow.restore()
|
||||
else
|
||||
openedWindow.focus()
|
||||
openedWindow.restore()
|
||||
else
|
||||
if devMode
|
||||
try
|
||||
@@ -361,7 +331,7 @@ class AtomApplication
|
||||
if pidToKillWhenClosed?
|
||||
@pidsToOpenWindows[pidToKillWhenClosed] = openedWindow
|
||||
|
||||
openedWindow.browserWindow.once 'closed', =>
|
||||
openedWindow.browserWindow.on 'closed', =>
|
||||
@killProcessForWindow(openedWindow)
|
||||
|
||||
# Kill all processes associated with opened windows.
|
||||
@@ -417,13 +387,11 @@ class AtomApplication
|
||||
# Opens up a new {AtomWindow} to run specs within.
|
||||
#
|
||||
# options -
|
||||
# :exitWhenDone - A Boolean that, if true, will close the window upon
|
||||
# :exitWhenDone - A Boolean that if true, will close the window upon
|
||||
# completion.
|
||||
# :resourcePath - The path to include specs from.
|
||||
# :specPath - The directory to load specs from.
|
||||
# :safeMode - A Boolean that, if true, won't run specs from ~/.atom/packages
|
||||
# and ~/.atom/dev/packages, defaults to false.
|
||||
runSpecs: ({exitWhenDone, resourcePath, specDirectory, logFile, safeMode}) ->
|
||||
runSpecs: ({exitWhenDone, resourcePath, specDirectory, logFile}) ->
|
||||
if resourcePath isnt @resourcePath and not fs.existsSync(resourcePath)
|
||||
resourcePath = @resourcePath
|
||||
|
||||
@@ -434,20 +402,16 @@ class AtomApplication
|
||||
|
||||
isSpec = true
|
||||
devMode = true
|
||||
safeMode ?= false
|
||||
new AtomWindow({bootstrapScript, resourcePath, exitWhenDone, isSpec, devMode, specDirectory, logFile, safeMode})
|
||||
new AtomWindow({bootstrapScript, resourcePath, exitWhenDone, isSpec, devMode, specDirectory, logFile})
|
||||
|
||||
runBenchmarks: ({exitWhenDone, specDirectory}={}) ->
|
||||
runBenchmarks: ->
|
||||
try
|
||||
bootstrapScript = require.resolve(path.resolve(global.devResourcePath, 'benchmark', 'benchmark-bootstrap'))
|
||||
catch error
|
||||
bootstrapScript = require.resolve(path.resolve(__dirname, '..', '..', 'benchmark', 'benchmark-bootstrap'))
|
||||
|
||||
specDirectory ?= path.dirname(bootstrapScript)
|
||||
|
||||
isSpec = true
|
||||
devMode = true
|
||||
new AtomWindow({bootstrapScript, @resourcePath, exitWhenDone, isSpec, specDirectory, devMode})
|
||||
new AtomWindow({bootstrapScript, @resourcePath, isSpec})
|
||||
|
||||
locationForPathToOpen: (pathToOpen) ->
|
||||
return {pathToOpen} unless pathToOpen
|
||||
@@ -474,8 +438,7 @@ class AtomApplication
|
||||
# should be in dev mode or not.
|
||||
# :safeMode - A Boolean which controls whether any newly opened windows
|
||||
# should be in safe mode or not.
|
||||
# :window - An {AtomWindow} to use for opening a selected file path.
|
||||
promptForPath: ({type, devMode, safeMode, window}={}) ->
|
||||
promptForPath: ({type, devMode, safeMode}={}) ->
|
||||
type ?= 'all'
|
||||
properties =
|
||||
switch type
|
||||
@@ -483,15 +446,5 @@ class AtomApplication
|
||||
when 'folder' then ['openDirectory']
|
||||
when 'all' then ['openFile', 'openDirectory']
|
||||
else throw new Error("#{type} is an invalid type for promptForPath")
|
||||
|
||||
# Show the open dialog as child window on Windows and Linux, and as
|
||||
# independent dialog on OS X. This matches most native apps.
|
||||
parentWindow =
|
||||
if process.platform is 'darwin'
|
||||
null
|
||||
else
|
||||
BrowserWindow.getFocusedWindow()
|
||||
|
||||
dialog = require 'dialog'
|
||||
dialog.showOpenDialog parentWindow, title: 'Open', properties: properties.concat(['multiSelections', 'createDirectory']), (pathsToOpen) =>
|
||||
@openPaths({pathsToOpen, devMode, safeMode, window})
|
||||
dialog.showOpenDialog title: 'Open', properties: properties.concat(['multiSelections', 'createDirectory']), (pathsToOpen) =>
|
||||
@openPaths({pathsToOpen, devMode, safeMode})
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
app = require 'app'
|
||||
fs = require 'fs'
|
||||
fs = require 'fs-plus'
|
||||
path = require 'path'
|
||||
protocol = require 'protocol'
|
||||
|
||||
@@ -24,5 +24,5 @@ class AtomProtocolHandler
|
||||
relativePath = path.normalize(request.url.substr(7))
|
||||
for loadPath in @loadPaths
|
||||
filePath = path.join(loadPath, relativePath)
|
||||
break if fs.statSyncNoException(filePath).isFile?()
|
||||
break if fs.isFileSync(filePath)
|
||||
return new protocol.RequestFileJob(filePath)
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
BrowserWindow = require 'browser-window'
|
||||
ContextMenu = require './context-menu'
|
||||
app = require 'app'
|
||||
dialog = require 'dialog'
|
||||
path = require 'path'
|
||||
fs = require 'fs'
|
||||
url = require 'url'
|
||||
@@ -18,19 +20,14 @@ class AtomWindow
|
||||
isSpec: null
|
||||
|
||||
constructor: (settings={}) ->
|
||||
{@resourcePath, pathToOpen, initialLine, initialColumn, @isSpec, @exitWhenDone, @safeMode} = settings
|
||||
{@resourcePath, pathToOpen, initialLine, initialColumn, @isSpec, @exitWhenDone} = settings
|
||||
|
||||
# Normalize to make sure drive letter case is consistent on Windows
|
||||
@resourcePath = path.normalize(@resourcePath) if @resourcePath
|
||||
|
||||
@browserWindow = new BrowserWindow
|
||||
show: false
|
||||
title: 'Atom'
|
||||
icon: @constructor.iconPath
|
||||
'web-preferences':
|
||||
'subpixel-font-scaling': false
|
||||
global.atomApplication.addWindow(this)
|
||||
|
||||
@browserWindow = new BrowserWindow show: false, title: 'Atom', icon: @constructor.iconPath
|
||||
@handleEvents()
|
||||
|
||||
loadSettings = _.extend({}, settings)
|
||||
@@ -52,8 +49,6 @@ class AtomWindow
|
||||
@emit 'window:loaded'
|
||||
@loaded = true
|
||||
|
||||
@browserWindow.on 'project-path-changed', (@projectPath) =>
|
||||
|
||||
@browserWindow.loadUrl @getUrl(loadSettings)
|
||||
@browserWindow.focusOnWebView() if @isSpec
|
||||
|
||||
@@ -71,18 +66,9 @@ class AtomWindow
|
||||
slashes: true
|
||||
query: {loadSettings: JSON.stringify(loadSettings)}
|
||||
|
||||
hasProjectPath: -> @projectPath?.length > 0
|
||||
|
||||
getInitialPath: ->
|
||||
@browserWindow.loadSettings.initialPath
|
||||
|
||||
setupContextMenu: ->
|
||||
ContextMenu = null
|
||||
|
||||
@browserWindow.on 'context-menu', (menuTemplate) =>
|
||||
ContextMenu ?= require './context-menu'
|
||||
new ContextMenu(menuTemplate, this)
|
||||
|
||||
containsPath: (pathToCheck) ->
|
||||
initialPath = @getInitialPath()
|
||||
if not initialPath
|
||||
@@ -105,7 +91,6 @@ class AtomWindow
|
||||
@browserWindow.on 'unresponsive', =>
|
||||
return if @isSpec
|
||||
|
||||
dialog = require 'dialog'
|
||||
chosen = dialog.showMessageBox @browserWindow,
|
||||
type: 'warning'
|
||||
buttons: ['Close', 'Keep Waiting']
|
||||
@@ -116,7 +101,6 @@ class AtomWindow
|
||||
@browserWindow.webContents.on 'crashed', =>
|
||||
global.atomApplication.exit(100) if @exitWhenDone
|
||||
|
||||
dialog = require 'dialog'
|
||||
chosen = dialog.showMessageBox @browserWindow,
|
||||
type: 'warning'
|
||||
buttons: ['Close Window', 'Reload', 'Keep It Open']
|
||||
@@ -126,7 +110,8 @@ class AtomWindow
|
||||
when 0 then @browserWindow.destroy()
|
||||
when 1 then @browserWindow.restart()
|
||||
|
||||
@setupContextMenu()
|
||||
@browserWindow.on 'context-menu', (menuTemplate) =>
|
||||
new ContextMenu(menuTemplate, this)
|
||||
|
||||
if @isSpec
|
||||
# Workaround for https://github.com/atom/atom-shell/issues/380
|
||||
@@ -184,8 +169,6 @@ class AtomWindow
|
||||
|
||||
isFocused: -> @browserWindow.isFocused()
|
||||
|
||||
isMinimized: -> @browserWindow.isMinimized()
|
||||
|
||||
isWebViewFocused: -> @browserWindow.isWebViewFocused()
|
||||
|
||||
isSpecWindow: -> @isSpec
|
||||
|
||||
@@ -1,47 +1,44 @@
|
||||
autoUpdater = null
|
||||
https = require 'https'
|
||||
autoUpdater = require 'auto-updater'
|
||||
dialog = require 'dialog'
|
||||
_ = require 'underscore-plus'
|
||||
{EventEmitter} = require 'events'
|
||||
|
||||
IdleState = 'idle'
|
||||
CheckingState = 'checking'
|
||||
DownladingState = 'downloading'
|
||||
UpdateAvailableState = 'update-available'
|
||||
NoUpdateAvailableState = 'no-update-available'
|
||||
ErrorState = 'error'
|
||||
IDLE_STATE='idle'
|
||||
CHECKING_STATE='checking'
|
||||
DOWNLOADING_STATE='downloading'
|
||||
UPDATE_AVAILABLE_STATE='update-available'
|
||||
NO_UPDATE_AVAILABLE_STATE='no-update-available'
|
||||
ERROR_STATE='error'
|
||||
|
||||
module.exports =
|
||||
class AutoUpdateManager
|
||||
_.extend @prototype, EventEmitter.prototype
|
||||
|
||||
constructor: (@version) ->
|
||||
@state = IdleState
|
||||
@state = IDLE_STATE
|
||||
@feedUrl = "https://atom.io/api/updates?version=#{@version}"
|
||||
|
||||
process.nextTick => @setupAutoUpdater()
|
||||
|
||||
setupAutoUpdater: ->
|
||||
autoUpdater = require 'auto-updater'
|
||||
|
||||
if process.platform is 'win32'
|
||||
autoUpdater.checkForUpdates = => @checkForUpdatesShim()
|
||||
|
||||
autoUpdater.setFeedUrl @feedUrl
|
||||
|
||||
autoUpdater.on 'checking-for-update', =>
|
||||
@setState(CheckingState)
|
||||
@setState(CHECKING_STATE)
|
||||
|
||||
autoUpdater.on 'update-not-available', =>
|
||||
@setState(NoUpdateAvailableState)
|
||||
@setState(NO_UPDATE_AVAILABLE_STATE)
|
||||
|
||||
autoUpdater.on 'update-available', =>
|
||||
@setState(DownladingState)
|
||||
@setState(DOWNLOADING_STATE)
|
||||
|
||||
autoUpdater.on 'error', (event, message) =>
|
||||
@setState(ErrorState)
|
||||
@setState(ERROR_STATE)
|
||||
console.error "Error Downloading Update: #{message}"
|
||||
|
||||
autoUpdater.on 'update-downloaded', (event, @releaseNotes, @releaseVersion) =>
|
||||
@setState(UpdateAvailableState)
|
||||
@setState(UPDATE_AVAILABLE_STATE)
|
||||
@emitUpdateAvailableEvent(@getWindows()...)
|
||||
|
||||
# Only released versions should check for updates.
|
||||
@@ -51,8 +48,6 @@ class AutoUpdateManager
|
||||
# Windows doesn't have an auto-updater, so use this method to shim the events.
|
||||
checkForUpdatesShim: ->
|
||||
autoUpdater.emit 'checking-for-update'
|
||||
|
||||
https = require 'https'
|
||||
request = https.get @feedUrl, (response) ->
|
||||
if response.statusCode == 200
|
||||
body = ""
|
||||
@@ -72,14 +67,14 @@ class AutoUpdateManager
|
||||
atomWindow.sendCommand('window:update-available', [@releaseVersion, @releaseNotes])
|
||||
|
||||
setState: (state) ->
|
||||
return if @state is state
|
||||
return unless @state != state
|
||||
@state = state
|
||||
@emit 'state-changed', @state
|
||||
|
||||
getState: ->
|
||||
@state
|
||||
|
||||
check: ({hidePopups}={}) ->
|
||||
check: ({hidePopups}={})->
|
||||
unless hidePopups
|
||||
autoUpdater.once 'update-not-available', @onUpdateNotAvailable
|
||||
autoUpdater.once 'error', @onUpdateError
|
||||
@@ -91,12 +86,10 @@ class AutoUpdateManager
|
||||
|
||||
onUpdateNotAvailable: =>
|
||||
autoUpdater.removeListener 'error', @onUpdateError
|
||||
dialog = require 'dialog'
|
||||
dialog.showMessageBox type: 'info', buttons: ['OK'], message: 'No update available.', detail: "Version #{@version} is the latest version."
|
||||
|
||||
onUpdateError: (event, message) =>
|
||||
autoUpdater.removeListener 'update-not-available', @onUpdateNotAvailable
|
||||
dialog = require 'dialog'
|
||||
dialog.showMessageBox type: 'warning', buttons: ['OK'], message: 'There was an error checking for updates.', detail: message
|
||||
|
||||
getWindows: ->
|
||||
|
||||
@@ -3,9 +3,11 @@ global.shellStartTime = Date.now()
|
||||
crashReporter = require 'crash-reporter'
|
||||
app = require 'app'
|
||||
fs = require 'fs'
|
||||
module = require 'module'
|
||||
path = require 'path'
|
||||
optimist = require 'optimist'
|
||||
nslog = require 'nslog'
|
||||
dialog = require 'dialog'
|
||||
|
||||
console.log = nslog
|
||||
|
||||
@@ -31,14 +33,14 @@ start = ->
|
||||
app.on 'will-finish-launching', ->
|
||||
setupCrashReporter()
|
||||
|
||||
app.on 'ready', ->
|
||||
app.on 'finish-launching', ->
|
||||
app.removeListener 'open-file', addPathToOpen
|
||||
app.removeListener 'open-url', addUrlToOpen
|
||||
|
||||
args.pathsToOpen = args.pathsToOpen.map (pathToOpen) ->
|
||||
path.resolve(args.executedFrom ? process.cwd(), pathToOpen.toString())
|
||||
|
||||
setupCoffeeScript()
|
||||
require('coffee-script').register()
|
||||
if args.devMode
|
||||
require(path.join(args.resourcePath, 'src', 'coffee-cache')).register()
|
||||
AtomApplication = require path.join(args.resourcePath, 'src', 'browser', 'atom-application')
|
||||
@@ -55,29 +57,13 @@ global.devResourcePath = path.normalize(global.devResourcePath) if global.devRes
|
||||
setupCrashReporter = ->
|
||||
crashReporter.start(productName: 'Atom', companyName: 'GitHub')
|
||||
|
||||
setupCoffeeScript = ->
|
||||
CoffeeScript = null
|
||||
|
||||
require.extensions['.coffee'] = (module, filePath) ->
|
||||
CoffeeScript ?= require('coffee-script')
|
||||
coffee = fs.readFileSync(filePath, 'utf8')
|
||||
js = CoffeeScript.compile(coffee, filename: filePath)
|
||||
module._compile(js, filePath)
|
||||
|
||||
parseCommandLine = ->
|
||||
version = app.getVersion()
|
||||
options = optimist(process.argv[1..])
|
||||
options.usage """
|
||||
Atom Editor v#{version}
|
||||
|
||||
Usage: atom [options] [path ...]
|
||||
|
||||
One or more paths to files or folders to open may be specified.
|
||||
|
||||
File paths will open in the current window.
|
||||
|
||||
Folder paths will open in an existing window if that folder has already been
|
||||
opened or a new window if it hasn't.
|
||||
Usage: atom [options] [file ...]
|
||||
"""
|
||||
options.alias('d', 'dev').boolean('d').describe('d', 'Run in development mode.')
|
||||
options.alias('f', 'foreground').boolean('f').describe('f', 'Keep the browser process in the foreground.')
|
||||
@@ -116,7 +102,9 @@ parseCommandLine = ->
|
||||
else if devMode
|
||||
resourcePath = global.devResourcePath
|
||||
|
||||
unless fs.statSyncNoException(resourcePath)
|
||||
try
|
||||
fs.statSync resourcePath
|
||||
catch
|
||||
resourcePath = path.dirname(path.dirname(__dirname))
|
||||
|
||||
{resourcePath, pathsToOpen, executedFrom, test, version, pidToKillWhenClosed, devMode, safeMode, newWindow, specDirectory, logFile}
|
||||
|
||||
@@ -1,40 +1,39 @@
|
||||
BufferedProcess = require './buffered-process'
|
||||
path = require 'path'
|
||||
|
||||
# Extended: Like {BufferedProcess}, but accepts a Node script as the command
|
||||
# Public: Like {BufferedProcess}, but accepts a Node script as the command
|
||||
# to run.
|
||||
#
|
||||
# This is necessary on Windows since it doesn't support shebang `#!` lines.
|
||||
#
|
||||
# ## Examples
|
||||
# ## Requiring in packages
|
||||
#
|
||||
# ```coffee
|
||||
# {BufferedNodeProcess} = require 'atom'
|
||||
# {BufferedNodeProcess} = require 'atom'
|
||||
# ```
|
||||
module.exports =
|
||||
class BufferedNodeProcess extends BufferedProcess
|
||||
|
||||
# Public: Runs the given Node script by spawning a new child process.
|
||||
#
|
||||
# * `options` An {Object} with the following keys:
|
||||
# * `command` The {String} path to the JavaScript script to execute.
|
||||
# * `args` The {Array} of arguments to pass to the script (optional).
|
||||
# * `options` The options {Object} to pass to Node's `ChildProcess.spawn`
|
||||
# method (optional).
|
||||
# * `stdout` The callback {Function} that receives a single argument which
|
||||
# contains the standard output from the command. The callback is
|
||||
# called as data is received but it's buffered to ensure only
|
||||
# complete lines are passed until the source stream closes. After
|
||||
# the source stream has closed all remaining data is sent in a
|
||||
# final call (optional).
|
||||
# * `stderr` The callback {Function} that receives a single argument which
|
||||
# contains the standard error output from the command. The
|
||||
# callback is called as data is received but it's buffered to
|
||||
# ensure only complete lines are passed until the source stream
|
||||
# closes. After the source stream has closed all remaining data
|
||||
# is sent in a final call (optional).
|
||||
# * `exit` The callback {Function} which receives a single argument
|
||||
# containing the exit status (optional).
|
||||
# options - An {Object} with the following keys:
|
||||
# :command - The {String} path to the JavaScript script to execute.
|
||||
# :args - The {Array} of arguments to pass to the script (optional).
|
||||
# :options - The options {Object} to pass to Node's `ChildProcess.spawn`
|
||||
# method (optional).
|
||||
# :stdout - The callback {Function} that receives a single argument which
|
||||
# contains the standard output from the command. The callback is
|
||||
# called as data is received but it's buffered to ensure only
|
||||
# complete lines are passed until the source stream closes. After
|
||||
# the source stream has closed all remaining data is sent in a
|
||||
# final call (optional).
|
||||
# :stderr - The callback {Function} that receives a single argument which
|
||||
# contains the standard error output from the command. The
|
||||
# callback is called as data is received but it's buffered to
|
||||
# ensure only complete lines are passed until the source stream
|
||||
# closes. After the source stream has closed all remaining data
|
||||
# is sent in a final call (optional).
|
||||
# :exit - The callback {Function} which receives a single argument
|
||||
# containing the exit status (optional).
|
||||
constructor: ({command, args, options, stdout, stderr, exit}) ->
|
||||
node =
|
||||
if process.platform is 'darwin'
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
_ = require 'underscore-plus'
|
||||
ChildProcess = require 'child_process'
|
||||
|
||||
# Extended: A wrapper which provides standard error/output line buffering for
|
||||
# Public: A wrapper which provides standard error/output line buffering for
|
||||
# Node's ChildProcess.
|
||||
#
|
||||
# ## Examples
|
||||
# ## Requiring in packages
|
||||
#
|
||||
# ```coffee
|
||||
# {BufferedProcess} = require 'atom'
|
||||
@@ -19,45 +19,43 @@ module.exports =
|
||||
class BufferedProcess
|
||||
# Public: Runs the given command by spawning a new child process.
|
||||
#
|
||||
# * `options` An {Object} with the following keys:
|
||||
# * `command` The {String} command to execute.
|
||||
# * `args` The {Array} of arguments to pass to the command (optional).
|
||||
# * `options` The options {Object} to pass to Node's `ChildProcess.spawn`
|
||||
# method (optional).
|
||||
# * `stdout` The callback {Function} that receives a single argument which
|
||||
# contains the standard output from the command. The callback is
|
||||
# called as data is received but it's buffered to ensure only
|
||||
# complete lines are passed until the source stream closes. After
|
||||
# the source stream has closed all remaining data is sent in a
|
||||
# final call (optional).
|
||||
# * `stderr` The callback {Function} that receives a single argument which
|
||||
# contains the standard error output from the command. The
|
||||
# callback is called as data is received but it's buffered to
|
||||
# ensure only complete lines are passed until the source stream
|
||||
# closes. After the source stream has closed all remaining data
|
||||
# is sent in a final call (optional).
|
||||
# * `exit` The callback {Function} which receives a single argument
|
||||
# containing the exit status (optional).
|
||||
# options - An {Object} with the following keys:
|
||||
# :command - The {String} command to execute.
|
||||
# :args - The {Array} of arguments to pass to the command (optional).
|
||||
# :options - The options {Object} to pass to Node's `ChildProcess.spawn`
|
||||
# method (optional).
|
||||
# :stdout - The callback {Function} that receives a single argument which
|
||||
# contains the standard output from the command. The callback is
|
||||
# called as data is received but it's buffered to ensure only
|
||||
# complete lines are passed until the source stream closes. After
|
||||
# the source stream has closed all remaining data is sent in a
|
||||
# final call (optional).
|
||||
# :stderr - The callback {Function} that receives a single argument which
|
||||
# contains the standard error output from the command. The
|
||||
# callback is called as data is received but it's buffered to
|
||||
# ensure only complete lines are passed until the source stream
|
||||
# closes. After the source stream has closed all remaining data
|
||||
# is sent in a final call (optional).
|
||||
# :exit - The callback {Function} which receives a single argument
|
||||
# containing the exit status (optional).
|
||||
constructor: ({command, args, options, stdout, stderr, exit}={}) ->
|
||||
options ?= {}
|
||||
# Quick hack. Killing @process will only kill cmd.exe, and not the child
|
||||
# process and will just orphan it. Does not escape ^ (cmd's escape symbol).
|
||||
# Related to joyent/node#2318
|
||||
if process.platform is "win32"
|
||||
# Quote all arguments and escapes inner quotes
|
||||
if args?
|
||||
cmdArgs = args.filter (arg) -> arg?
|
||||
cmdArgs = cmdArgs.map (arg) ->
|
||||
cmdArgs = args.map (arg) ->
|
||||
if command in ['explorer.exe', 'explorer'] and /^\/[a-zA-Z]+,.*$/.test(arg)
|
||||
# Don't wrap /root,C:\folder style arguments to explorer calls in
|
||||
# quotes since they will not be interpreted correctly if they are
|
||||
arg
|
||||
else
|
||||
"\"#{arg.toString().replace(/"/g, '\\"')}\""
|
||||
"\"#{arg.replace(/"/g, '\\"')}\""
|
||||
else
|
||||
cmdArgs = []
|
||||
if /\s/.test(command)
|
||||
cmdArgs.unshift("\"#{command}\"")
|
||||
else
|
||||
cmdArgs.unshift(command)
|
||||
cmdArgs.unshift("\"#{command}\"")
|
||||
cmdArgs = ['/s', '/c', "\"#{cmdArgs.join(' ')}\""]
|
||||
cmdOptions = _.clone(options)
|
||||
cmdOptions.windowsVerbatimArguments = true
|
||||
@@ -96,9 +94,9 @@ class BufferedProcess
|
||||
|
||||
# Helper method to pass data line by line.
|
||||
#
|
||||
# * `stream` The Stream to read from.
|
||||
# * `onLines` The callback to call with each line of data.
|
||||
# * `onDone` The callback to call when the stream has closed.
|
||||
# stream - The Stream to read from.
|
||||
# onLines - The callback to call with each line of data.
|
||||
# onDone - The callback to call when the stream has closed.
|
||||
bufferStream: (stream, onLines, onDone) ->
|
||||
stream.setEncoding('utf8')
|
||||
buffered = ''
|
||||
@@ -116,48 +114,8 @@ class BufferedProcess
|
||||
onLines(buffered) if buffered.length > 0
|
||||
onDone()
|
||||
|
||||
# Kill all child processes of the spawned cmd.exe process on Windows.
|
||||
#
|
||||
# This is required since killing the cmd.exe does not terminate child
|
||||
# processes.
|
||||
killOnWindows: ->
|
||||
parentPid = @process.pid
|
||||
cmd = 'wmic'
|
||||
args = [
|
||||
'process'
|
||||
'where'
|
||||
"(ParentProcessId=#{parentPid})"
|
||||
'get'
|
||||
'processid'
|
||||
]
|
||||
|
||||
wmicProcess = ChildProcess.spawn(cmd, args)
|
||||
wmicProcess.on 'error', -> # ignore errors
|
||||
output = ''
|
||||
wmicProcess.stdout.on 'data', (data) -> output += data
|
||||
wmicProcess.stdout.on 'close', =>
|
||||
pidsToKill = output.split(/\s+/)
|
||||
.filter (pid) -> /^\d+$/.test(pid)
|
||||
.map (pid) -> parseInt(pid)
|
||||
.filter (pid) -> pid isnt parentPid and 0 < pid < Infinity
|
||||
|
||||
for pid in pidsToKill
|
||||
try
|
||||
process.kill(pid)
|
||||
@killProcess()
|
||||
|
||||
killProcess: ->
|
||||
@process?.kill()
|
||||
@process = null
|
||||
|
||||
# Public: Terminate the process.
|
||||
kill: ->
|
||||
return if @killed
|
||||
|
||||
@killed = true
|
||||
if process.platform is 'win32'
|
||||
@killOnWindows()
|
||||
else
|
||||
@killProcess()
|
||||
|
||||
undefined
|
||||
@process.kill()
|
||||
@process = null
|
||||
|
||||
+6
-14
@@ -1,17 +1,9 @@
|
||||
clipboard = require 'clipboard'
|
||||
crypto = require 'crypto'
|
||||
|
||||
# Extended: Represents the clipboard used for copying and pasting in Atom.
|
||||
# Public: Represents the clipboard used for copying and pasting in Atom.
|
||||
#
|
||||
# An instance of this class is always available as the `atom.clipboard` global.
|
||||
#
|
||||
# ## Examples
|
||||
#
|
||||
# ```coffee
|
||||
# atom.clipboard.write('hello')
|
||||
#
|
||||
# console.log(atom.clipboard.read()) # 'hello'
|
||||
# ```
|
||||
module.exports =
|
||||
class Clipboard
|
||||
metadata: null
|
||||
@@ -19,7 +11,7 @@ class Clipboard
|
||||
|
||||
# Creates an `md5` hash of some text.
|
||||
#
|
||||
# * `text` A {String} to hash.
|
||||
# text - A {String} to hash.
|
||||
#
|
||||
# Returns a hashed {String}.
|
||||
md5: (text) ->
|
||||
@@ -30,8 +22,8 @@ class Clipboard
|
||||
# The metadata associated with the text is available by calling
|
||||
# {::readWithMetadata}.
|
||||
#
|
||||
# * `text` The {String} to store.
|
||||
# * `metadata` The additional info to associate with the text.
|
||||
# text - The {String} to store.
|
||||
# metadata - The additional info to associate with the text.
|
||||
write: (text, metadata) ->
|
||||
@signatureForMetadata = @md5(text)
|
||||
@metadata = metadata
|
||||
@@ -47,8 +39,8 @@ class Clipboard
|
||||
# associated metadata.
|
||||
#
|
||||
# Returns an {Object} with the following keys:
|
||||
# * `text` The {String} clipboard text.
|
||||
# * `metadata` The metadata stored by an earlier call to {::write}.
|
||||
# :text - The {String} clipboard text.
|
||||
# :metadata - The metadata stored by an earlier call to {::write}.
|
||||
readWithMetadata: ->
|
||||
text = @read()
|
||||
if @signatureForMetadata is @md5(text)
|
||||
|
||||
@@ -41,7 +41,7 @@ module.exports =
|
||||
callback()
|
||||
return
|
||||
|
||||
symlinkCommand commandPath, destinationPath, (error) ->
|
||||
symlinkCommand commandPath, destinationPath, (error) =>
|
||||
if askForPrivilege and error?.code is 'EACCES'
|
||||
try
|
||||
error = null
|
||||
|
||||
@@ -1,176 +0,0 @@
|
||||
{Disposable, CompositeDisposable} = require 'event-kit'
|
||||
{specificity} = require 'clear-cut'
|
||||
_ = require 'underscore-plus'
|
||||
{$} = require './space-pen-extensions'
|
||||
|
||||
SequenceCount = 0
|
||||
SpecificityCache = {}
|
||||
|
||||
module.exports =
|
||||
|
||||
# Experimental: Associates listener functions with commands in a
|
||||
# context-sensitive way using CSS selectors. You can access a global instance of
|
||||
# this class via `atom.commands`, and commands registered there will be
|
||||
# presented in the command palette.
|
||||
#
|
||||
# The global command registry facilitates a style of event handling known as
|
||||
# *event delegation* that was popularized by jQuery. Atom commands are expressed
|
||||
# as custom DOM events that can be invoked on the currently focused element via
|
||||
# a key binding or manually via the command palette. Rather than binding
|
||||
# listeners for command events directly to DOM nodes, you instead register
|
||||
# command event listeners globally on `atom.commands` and constrain them to
|
||||
# specific kinds of elements with CSS selectors.
|
||||
#
|
||||
# As the event bubbles upward through the DOM, all registered event listeners
|
||||
# with matching selectors are invoked in order of specificity. In the event of a
|
||||
# specificity tie, the most recently registered listener is invoked first. This
|
||||
# mirrors the "cascade" semantics of CSS. Event listeners are invoked in the
|
||||
# context of the current DOM node, meaning `this` always points at
|
||||
# `event.currentTarget`. As is normally the case with DOM events,
|
||||
# `stopPropagation` and `stopImmediatePropagation` can be used to terminate the
|
||||
# bubbling process and prevent invocation of additional listeners.
|
||||
#
|
||||
# ## Example
|
||||
#
|
||||
# Here is a command that inserts the current date in an editor:
|
||||
#
|
||||
# ```coffee
|
||||
# atom.commands.add '.editor',
|
||||
# 'user:insert-date': (event) ->
|
||||
# editor = $(this).view().getModel()
|
||||
# # soon the above above line will be:
|
||||
# # editor = @getModel()
|
||||
# editor.insertText(new Date().toLocaleString())
|
||||
# ```
|
||||
class CommandRegistry
|
||||
constructor: (@rootNode) ->
|
||||
@listenersByCommandName = {}
|
||||
|
||||
setRootNode: (newRootNode) ->
|
||||
oldRootNode = @rootNode
|
||||
@rootNode = newRootNode
|
||||
|
||||
for commandName of @listenersByCommandName
|
||||
oldRootNode?.removeEventListener(commandName, @dispatchCommand, true)
|
||||
newRootNode?.addEventListener(commandName, @dispatchCommand, true)
|
||||
|
||||
# Public: Add one or more command listeners associated with a selector.
|
||||
#
|
||||
# ## Arguments: Registering One Command
|
||||
#
|
||||
# * `selector` A {String} containing a CSS selector matching elements on which
|
||||
# you want to handle the commands. The `,` combinator is not currently
|
||||
# supported.
|
||||
# * `commandName` A {String} containing the name of a command you want to
|
||||
# handle such as `user:insert-date`.
|
||||
# * `callback` A {Function} to call when the given command is invoked on an
|
||||
# element matching the selector. It will be called with `this` referencing
|
||||
# the matching DOM node.
|
||||
# * `event` A standard DOM event instance. Call `stopPropagation` or
|
||||
# `stopImmediatePropagation` to terminate bubbling early.
|
||||
#
|
||||
# ## Arguments: Registering Multiple Commands
|
||||
#
|
||||
# * `selector` A {String} containing a CSS selector matching elements on which
|
||||
# you want to handle the commands. The `,` combinator is not currently
|
||||
# supported.
|
||||
# * `commands` An {Object} mapping command names like `user:insert-date` to
|
||||
# listener {Function}s.
|
||||
#
|
||||
# Returns a {Disposable} on which `.dispose()` can be called to remove the
|
||||
# added command handler(s).
|
||||
add: (selector, commandName, callback) ->
|
||||
if typeof commandName is 'object'
|
||||
commands = commandName
|
||||
disposable = new CompositeDisposable
|
||||
for commandName, callback of commands
|
||||
disposable.add @add(selector, commandName, callback)
|
||||
return disposable
|
||||
|
||||
unless @listenersByCommandName[commandName]?
|
||||
@rootNode?.addEventListener(commandName, @dispatchCommand, true)
|
||||
@listenersByCommandName[commandName] = []
|
||||
|
||||
listener = new CommandListener(selector, callback)
|
||||
listenersForCommand = @listenersByCommandName[commandName]
|
||||
listenersForCommand.push(listener)
|
||||
|
||||
new Disposable =>
|
||||
listenersForCommand.splice(listenersForCommand.indexOf(listener), 1)
|
||||
if listenersForCommand.length is 0
|
||||
delete @listenersByCommandName[commandName]
|
||||
@rootNode.removeEventListener(commandName, @dispatchCommand, true)
|
||||
|
||||
dispatchCommand: (event) =>
|
||||
propagationStopped = false
|
||||
immediatePropagationStopped = false
|
||||
currentTarget = event.target
|
||||
|
||||
syntheticEvent = Object.create event,
|
||||
eventPhase: value: Event.BUBBLING_PHASE
|
||||
currentTarget: get: -> currentTarget
|
||||
stopPropagation: value: ->
|
||||
propagationStopped = true
|
||||
stopImmediatePropagation: value: ->
|
||||
propagationStopped = true
|
||||
immediatePropagationStopped = true
|
||||
|
||||
loop
|
||||
matchingListeners =
|
||||
@listenersByCommandName[event.type]
|
||||
.filter (listener) -> currentTarget.webkitMatchesSelector(listener.selector)
|
||||
.sort (a, b) -> a.compare(b)
|
||||
|
||||
for listener in matchingListeners
|
||||
break if immediatePropagationStopped
|
||||
listener.callback.call(currentTarget, syntheticEvent)
|
||||
|
||||
break if propagationStopped
|
||||
break if currentTarget is @rootNode
|
||||
currentTarget = currentTarget.parentNode
|
||||
|
||||
# Public: Find all registered commands matching a query.
|
||||
#
|
||||
# * `params` An {Object} containing one or more of the following keys:
|
||||
# * `target` A DOM node that is the hypothetical target of a given command.
|
||||
#
|
||||
# Returns an {Array} of {Object}s containing the following keys:
|
||||
# * `name` The name of the command. For example, `user:insert-date`.
|
||||
# * `displayName` The display name of the command. For example,
|
||||
# `User: Insert Date`.
|
||||
# * `jQuery` Present if the command was registered with the legacy
|
||||
# `$::command` method.
|
||||
findCommands: ({target}) ->
|
||||
commands = []
|
||||
target = @rootNode unless @rootNode.contains(target)
|
||||
currentTarget = target
|
||||
loop
|
||||
for commandName, listeners of @listenersByCommandName
|
||||
for listener in listeners
|
||||
if currentTarget.webkitMatchesSelector(listener.selector)
|
||||
commands.push
|
||||
name: commandName
|
||||
displayName: _.humanizeEventName(commandName)
|
||||
|
||||
break if currentTarget is @rootNode
|
||||
currentTarget = currentTarget.parentNode
|
||||
|
||||
for name, displayName of $(target).events() when displayName
|
||||
commands.push({name, displayName, jQuery: true})
|
||||
|
||||
for name, displayName of $(window).events() when displayName
|
||||
commands.push({name, displayName, jQuery: true})
|
||||
|
||||
commands
|
||||
|
||||
clear: ->
|
||||
@listenersByCommandName = {}
|
||||
|
||||
class CommandListener
|
||||
constructor: (@selector, @callback) ->
|
||||
@specificity = (SpecificityCache[@selector] ?= specificity(@selector))
|
||||
@sequenceNumber = SequenceCount++
|
||||
|
||||
compare: (other) ->
|
||||
other.specificity - @specificity or
|
||||
other.sequenceNumber - @sequenceNumber
|
||||
+47
-55
@@ -6,7 +6,7 @@ path = require 'path'
|
||||
async = require 'async'
|
||||
pathWatcher = require 'pathwatcher'
|
||||
|
||||
# Essential: Used to access all of Atom's configuration details.
|
||||
# Public: Used to access all of Atom's configuration details.
|
||||
#
|
||||
# An instance of this class is always available as the `atom.config` global.
|
||||
#
|
||||
@@ -15,9 +15,9 @@ pathWatcher = require 'pathwatcher'
|
||||
# * Create your own root keypath using your package's name.
|
||||
# * Don't depend on (or write to) configuration keys outside of your keypath.
|
||||
#
|
||||
# ## Examples
|
||||
# ## Example
|
||||
#
|
||||
# ```coffee
|
||||
# ```coffeescript
|
||||
# atom.config.set('my-package.key', 'value')
|
||||
# atom.config.observe 'my-package.key', ->
|
||||
# console.log 'My configuration changed:', atom.config.get('my-package.key')
|
||||
@@ -39,7 +39,7 @@ class Config
|
||||
|
||||
fs.makeTreeSync(@configDirPath)
|
||||
|
||||
queue = async.queue ({sourcePath, destinationPath}, callback) ->
|
||||
queue = async.queue ({sourcePath, destinationPath}, callback) =>
|
||||
fs.copy(sourcePath, destinationPath, callback)
|
||||
queue.drain = done
|
||||
|
||||
@@ -88,48 +88,37 @@ class Config
|
||||
_.extend hash, defaults
|
||||
@emit 'updated'
|
||||
|
||||
# Extended: Get the {String} path to the config file being used.
|
||||
# Public: Get the {String} path to the config file being used.
|
||||
getUserConfigPath: ->
|
||||
@configFilePath
|
||||
|
||||
# Extended: Returns a new {Object} containing all of settings and defaults.
|
||||
# Public: Returns a new {Object} containing all of settings and defaults.
|
||||
getSettings: ->
|
||||
_.deepExtend(@settings, @defaultSettings)
|
||||
|
||||
# Essential: Retrieves the setting for the given key.
|
||||
# Public: Retrieves the setting for the given key.
|
||||
#
|
||||
# * `keyPath` The {String} name of the key to retrieve.
|
||||
# keyPath - The {String} name of the key to retrieve.
|
||||
#
|
||||
# Returns the value from Atom's default settings, the user's configuration
|
||||
# file, or `null` if the key doesn't exist in either.
|
||||
get: (keyPath) ->
|
||||
value = _.valueForKeyPath(@settings, keyPath)
|
||||
defaultValue = _.valueForKeyPath(@defaultSettings, keyPath)
|
||||
value = _.valueForKeyPath(@settings, keyPath) ? _.valueForKeyPath(@defaultSettings, keyPath)
|
||||
_.deepClone(value)
|
||||
|
||||
if value?
|
||||
value = _.deepClone(value)
|
||||
valueIsObject = _.isObject(value) and not _.isArray(value)
|
||||
defaultValueIsObject = _.isObject(defaultValue) and not _.isArray(defaultValue)
|
||||
if valueIsObject and defaultValueIsObject
|
||||
_.defaults(value, defaultValue)
|
||||
else
|
||||
value = _.deepClone(defaultValue)
|
||||
|
||||
value
|
||||
|
||||
# Extended: Retrieves the setting for the given key as an integer.
|
||||
# Public: Retrieves the setting for the given key as an integer.
|
||||
#
|
||||
# * `keyPath` The {String} name of the key to retrieve
|
||||
# keyPath - The {String} name of the key to retrieve
|
||||
#
|
||||
# Returns the value from Atom's default settings, the user's configuration
|
||||
# file, or `NaN` if the key doesn't exist in either.
|
||||
getInt: (keyPath) ->
|
||||
parseInt(@get(keyPath))
|
||||
|
||||
# Extended: Retrieves the setting for the given key as a positive integer.
|
||||
# Public: Retrieves the setting for the given key as a positive integer.
|
||||
#
|
||||
# * `keyPath` The {String} name of the key to retrieve
|
||||
# * `defaultValue` The integer {Number} to fall back to if the value isn't
|
||||
# keyPath - The {String} name of the key to retrieve
|
||||
# defaultValue - The integer {Number} to fall back to if the value isn't
|
||||
# positive, defaults to 0.
|
||||
#
|
||||
# Returns the value from Atom's default settings, the user's configuration
|
||||
@@ -137,12 +126,12 @@ class Config
|
||||
getPositiveInt: (keyPath, defaultValue=0) ->
|
||||
Math.max(@getInt(keyPath), 0) or defaultValue
|
||||
|
||||
# Essential: Sets the value for a configuration setting.
|
||||
# Public: Sets the value for a configuration setting.
|
||||
#
|
||||
# This value is stored in Atom's internal configuration file.
|
||||
#
|
||||
# * `keyPath` The {String} name of the key.
|
||||
# * `value` The value of the setting.
|
||||
# keyPath - The {String} name of the key.
|
||||
# value - The value of the setting.
|
||||
#
|
||||
# Returns the `value`.
|
||||
set: (keyPath, value) ->
|
||||
@@ -153,47 +142,47 @@ class Config
|
||||
@update()
|
||||
value
|
||||
|
||||
# Extended: Toggle the value at the key path.
|
||||
# Public: Toggle the value at the key path.
|
||||
#
|
||||
# The new value will be `true` if the value is currently falsy and will be
|
||||
# `false` if the value is currently truthy.
|
||||
#
|
||||
# * `keyPath` The {String} name of the key.
|
||||
# keyPath - The {String} name of the key.
|
||||
#
|
||||
# Returns the new value.
|
||||
toggle: (keyPath) ->
|
||||
@set(keyPath, !@get(keyPath))
|
||||
|
||||
# Extended: Restore the key path to its default value.
|
||||
# Public: Restore the key path to its default value.
|
||||
#
|
||||
# * `keyPath` The {String} name of the key.
|
||||
# keyPath - The {String} name of the key.
|
||||
#
|
||||
# Returns the new value.
|
||||
restoreDefault: (keyPath) ->
|
||||
@set(keyPath, _.valueForKeyPath(@defaultSettings, keyPath))
|
||||
|
||||
# Extended: Get the default value of the key path.
|
||||
# Public: Get the default value of the key path.
|
||||
#
|
||||
# * `keyPath` The {String} name of the key.
|
||||
# keyPath - The {String} name of the key.
|
||||
#
|
||||
# Returns the default value.
|
||||
getDefault: (keyPath) ->
|
||||
defaultValue = _.valueForKeyPath(@defaultSettings, keyPath)
|
||||
_.deepClone(defaultValue)
|
||||
|
||||
# Extended: Is the key path value its default value?
|
||||
# Public: Is the key path value its default value?
|
||||
#
|
||||
# * `keyPath` The {String} name of the key.
|
||||
# keyPath - The {String} name of the key.
|
||||
#
|
||||
# Returns a {Boolean}, `true` if the current value is the default, `false`
|
||||
# otherwise.
|
||||
isDefault: (keyPath) ->
|
||||
not _.valueForKeyPath(@settings, keyPath)?
|
||||
|
||||
# Extended: Push the value to the array at the key path.
|
||||
# Public: Push the value to the array at the key path.
|
||||
#
|
||||
# * `keyPath` The {String} key path.
|
||||
# * `value` The value to push to the array.
|
||||
# keyPath - The {String} key path.
|
||||
# value - The value to push to the array.
|
||||
#
|
||||
# Returns the new array length {Number} of the setting.
|
||||
pushAtKeyPath: (keyPath, value) ->
|
||||
@@ -202,10 +191,10 @@ class Config
|
||||
@set(keyPath, arrayValue)
|
||||
result
|
||||
|
||||
# Extended: Add the value to the beginning of the array at the key path.
|
||||
# Public: Add the value to the beginning of the array at the key path.
|
||||
#
|
||||
# * `keyPath` The {String} key path.
|
||||
# * `value` The value to shift onto the array.
|
||||
# keyPath - The {String} key path.
|
||||
# value - The value to shift onto the array.
|
||||
#
|
||||
# Returns the new array length {Number} of the setting.
|
||||
unshiftAtKeyPath: (keyPath, value) ->
|
||||
@@ -216,8 +205,8 @@ class Config
|
||||
|
||||
# Public: Remove the value from the array at the key path.
|
||||
#
|
||||
# * `keyPath` The {String} key path.
|
||||
# * `value` The value to remove from the array.
|
||||
# keyPath - The {String} key path.
|
||||
# value - The value to remove from the array.
|
||||
#
|
||||
# Returns the new array value of the setting.
|
||||
removeAtKeyPath: (keyPath, value) ->
|
||||
@@ -226,17 +215,20 @@ class Config
|
||||
@set(keyPath, arrayValue)
|
||||
result
|
||||
|
||||
# Essential: Add a listener for changes to a given key path.
|
||||
# Public: Establishes an event listener for a given key.
|
||||
#
|
||||
# * `keyPath` The {String} name of the key to observe
|
||||
# * `options` An optional {Object} containing the `callNow` key.
|
||||
# * `callback` The {Function} to call when the value of the key changes.
|
||||
# The first argument will be the new value of the key and the
|
||||
# second argument will be an {Object} with a `previous` property
|
||||
# that is the prior value of the key.
|
||||
# `callback` is fired whenever the value of the key is changed and will
|
||||
# be fired immediately unless the `callNow` option is `false`.
|
||||
#
|
||||
# keyPath - The {String} name of the key to observe
|
||||
# options - An optional {Object} containing the `callNow` key.
|
||||
# callback - The {Function} to call when the value of the key changes.
|
||||
# The first argument will be the new value of the key and the
|
||||
# second argument will be an {Object} with a `previous` property
|
||||
# that is the prior value of the key.
|
||||
#
|
||||
# Returns an {Object} with the following keys:
|
||||
# * `off` A {Function} that unobserves the `keyPath` when called.
|
||||
# :off - A {Function} that unobserves the `keyPath` when called.
|
||||
observe: (keyPath, options={}, callback) ->
|
||||
if _.isFunction(options)
|
||||
callback = options
|
||||
@@ -256,9 +248,9 @@ class Config
|
||||
callback(value) if options.callNow ? true
|
||||
subscription
|
||||
|
||||
# Unobserve all callbacks on a given key.
|
||||
# Public: Unobserve all callbacks on a given key.
|
||||
#
|
||||
# * `keyPath` The {String} name of the key to unobserve.
|
||||
# keyPath - The {String} name of the key to unobserve.
|
||||
unobserve: (keyPath) ->
|
||||
@off("updated.#{keyPath.replace(/\./, '-')}")
|
||||
|
||||
|
||||
@@ -1,18 +1,15 @@
|
||||
{$} = require './space-pen-extensions'
|
||||
_ = require 'underscore-plus'
|
||||
remote = require 'remote'
|
||||
path = require 'path'
|
||||
CSON = require 'season'
|
||||
fs = require 'fs-plus'
|
||||
|
||||
# Extended: Provides a registry for commands that you'd like to appear in the
|
||||
# Public: Provides a registry for commands that you'd like to appear in the
|
||||
# context menu.
|
||||
#
|
||||
# An instance of this class is always available as the `atom.contextMenu`
|
||||
# global.
|
||||
module.exports =
|
||||
class ContextMenuManager
|
||||
constructor: ({@resourcePath, @devMode}) ->
|
||||
constructor: (@devMode=false) ->
|
||||
@definitions = {}
|
||||
@devModeDefinitions = {}
|
||||
@activeElement = null
|
||||
@@ -24,22 +21,16 @@ class ContextMenuManager
|
||||
@commandOptions = x: e.pageX, y: e.pageY
|
||||
]
|
||||
|
||||
atom.keymaps.onDidLoadBundledKeymaps => @loadPlatformItems()
|
||||
|
||||
loadPlatformItems: ->
|
||||
menusDirPath = path.join(@resourcePath, 'menus')
|
||||
platformMenuPath = fs.resolve(menusDirPath, process.platform, ['cson', 'json'])
|
||||
map = CSON.readFileSync(platformMenuPath)
|
||||
atom.contextMenu.add(platformMenuPath, map['context-menu'])
|
||||
|
||||
# Public: Creates menu definitions from the object specified by the menu
|
||||
# JSON API.
|
||||
# cson API.
|
||||
#
|
||||
# * `name` The path of the file that contains the menu definitions.
|
||||
# * `object` The 'context-menu' object specified in the menu JSON API.
|
||||
# * `options` An optional {Object} with the following keys:
|
||||
# * `devMode` Determines whether the entries should only be shown when
|
||||
# the window is in dev mode.
|
||||
# name - The path of the file that contains the menu definitions.
|
||||
# object - The 'context-menu' object specified in the menu cson API.
|
||||
# options - An {Object} with the following keys:
|
||||
# :devMode - Determines whether the entries should only be shown when
|
||||
# the window is in dev mode.
|
||||
#
|
||||
# Returns nothing.
|
||||
add: (name, object, {devMode}={}) ->
|
||||
for selector, items of object
|
||||
for label, commandOrSubmenu of items
|
||||
@@ -52,8 +43,6 @@ class ContextMenuManager
|
||||
menuItem = @buildMenuItem(label, commandOrSubmenu)
|
||||
@addBySelector(selector, menuItem, {devMode})
|
||||
|
||||
undefined
|
||||
|
||||
buildMenuItem: (label, command) ->
|
||||
if command is '-'
|
||||
{type: 'separator'}
|
||||
@@ -63,12 +52,12 @@ class ContextMenuManager
|
||||
# Registers a command to be displayed when the relevant item is right
|
||||
# clicked.
|
||||
#
|
||||
# * `selector` The css selector for the active element which should include
|
||||
# the given command in its context menu.
|
||||
# * `definition` The object containing keys which match the menu template API.
|
||||
# * `options` An optional {Object} with the following keys:
|
||||
# * `devMode` Indicates whether this command should only appear while the
|
||||
# editor is in dev mode.
|
||||
# selector - The css selector for the active element which should include
|
||||
# the given command in its context menu.
|
||||
# definition - The object containing keys which match the menu template API.
|
||||
# options - An {Object} with the following keys:
|
||||
# :devMode - Indicates whether this command should only appear while the
|
||||
# editor is in dev mode.
|
||||
addBySelector: (selector, definition, {devMode}={}) ->
|
||||
definitions = if devMode then @devModeDefinitions else @definitions
|
||||
if not _.findWhere(definitions[selector], definition) or _.isEqual(definition, {type: 'separator'})
|
||||
@@ -90,7 +79,7 @@ class ContextMenuManager
|
||||
# active element are listed first. The further down the list you go, the higher
|
||||
# up the ancestor hierarchy they match.
|
||||
#
|
||||
# * `element` The DOM element to generate the menu template for.
|
||||
# element - The DOM element to generate the menu template for.
|
||||
menuTemplateForMostSpecificElement: (element, {devMode}={}) ->
|
||||
menuTemplate = @definitionsForElement(element, {devMode})
|
||||
if element.parentElement
|
||||
@@ -120,12 +109,9 @@ class ContextMenuManager
|
||||
delete template.executeAtBuild
|
||||
|
||||
# Public: Request a context menu to be displayed.
|
||||
#
|
||||
# * `event` A DOM event.
|
||||
showForEvent: (event) ->
|
||||
@activeElement = event.target
|
||||
menuTemplate = @combinedMenuTemplateForElement(event.target)
|
||||
return unless menuTemplate?.length > 0
|
||||
@executeBuildHandlers(event, menuTemplate)
|
||||
remote.getCurrentWindow().emit('context-menu', menuTemplate)
|
||||
undefined
|
||||
|
||||
@@ -0,0 +1,113 @@
|
||||
{View} = require './space-pen-extensions'
|
||||
_ = require 'underscore-plus'
|
||||
|
||||
module.exports =
|
||||
class CursorView extends View
|
||||
@content: ->
|
||||
@div class: 'cursor idle', => @raw ' '
|
||||
|
||||
@blinkPeriod: 800
|
||||
|
||||
@blinkCursors: ->
|
||||
element.classList.toggle('blink-off') for [element] in @cursorViews
|
||||
|
||||
@startBlinking: (cursorView) ->
|
||||
@cursorViews ?= []
|
||||
@cursorViews.push(cursorView)
|
||||
if @cursorViews.length is 1
|
||||
@blinkInterval = setInterval(@blinkCursors.bind(this), @blinkPeriod / 2)
|
||||
|
||||
@stopBlinking: (cursorView) ->
|
||||
cursorView[0].classList.remove('blink-off')
|
||||
_.remove(@cursorViews, cursorView)
|
||||
clearInterval(@blinkInterval) if @cursorViews.length is 0
|
||||
|
||||
blinking: false
|
||||
visible: true
|
||||
needsUpdate: true
|
||||
needsRemoval: false
|
||||
shouldPauseBlinking: false
|
||||
|
||||
initialize: (@cursor, @editorView) ->
|
||||
@subscribe @cursor, 'moved', =>
|
||||
@needsUpdate = true
|
||||
@shouldPauseBlinking = true
|
||||
|
||||
@subscribe @cursor, 'visibility-changed', =>
|
||||
@needsUpdate = true
|
||||
|
||||
@subscribe @cursor, 'autoscrolled', =>
|
||||
@editorView.requestDisplayUpdate()
|
||||
|
||||
@subscribe @cursor, 'destroyed', =>
|
||||
@needsRemoval = true
|
||||
|
||||
beforeRemove: ->
|
||||
@editorView.removeCursorView(this)
|
||||
@stopBlinking()
|
||||
|
||||
updateDisplay: ->
|
||||
screenPosition = @getScreenPosition()
|
||||
pixelPosition = @getPixelPosition()
|
||||
|
||||
unless _.isEqual(@lastPixelPosition, pixelPosition)
|
||||
@lastPixelPosition = pixelPosition
|
||||
@css(pixelPosition)
|
||||
@trigger 'cursor:moved'
|
||||
|
||||
if @shouldPauseBlinking
|
||||
@resetBlinking()
|
||||
else if !@startBlinkingTimeout
|
||||
@startBlinking()
|
||||
|
||||
@setVisible(@cursor.isVisible() and not @editorView.getEditor().isFoldedAtScreenRow(screenPosition.row))
|
||||
|
||||
# Override for speed. The base function checks the computedStyle
|
||||
isHidden: ->
|
||||
this[0].style.display is 'none' or not @isOnDom()
|
||||
|
||||
needsAutoscroll: ->
|
||||
@cursor.needsAutoscroll
|
||||
|
||||
clearAutoscroll: ->
|
||||
@cursor.clearAutoscroll()
|
||||
|
||||
getPixelPosition: ->
|
||||
@editorView.pixelPositionForScreenPosition(@getScreenPosition())
|
||||
|
||||
setVisible: (visible) ->
|
||||
unless @visible is visible
|
||||
@visible = visible
|
||||
hiddenCursor = 'hidden-cursor'
|
||||
if visible
|
||||
@removeClass hiddenCursor
|
||||
else
|
||||
@addClass hiddenCursor
|
||||
|
||||
stopBlinking: ->
|
||||
@constructor.stopBlinking(this) if @blinking
|
||||
@blinking = false
|
||||
|
||||
startBlinking: ->
|
||||
@constructor.startBlinking(this) unless @blinking
|
||||
@blinking = true
|
||||
|
||||
resetBlinking: ->
|
||||
@stopBlinking()
|
||||
@startBlinking()
|
||||
|
||||
getBufferPosition: ->
|
||||
@cursor.getBufferPosition()
|
||||
|
||||
getScreenPosition: ->
|
||||
@cursor.getScreenPosition()
|
||||
|
||||
removeIdleClassTemporarily: ->
|
||||
@removeClass 'idle'
|
||||
window.clearTimeout(@idleTimeout) if @idleTimeout
|
||||
@idleTimeout = window.setTimeout (=> @addClass 'idle'), 200
|
||||
|
||||
resetCursorAnimation: ->
|
||||
window.clearTimeout(@idleTimeout) if @idleTimeout
|
||||
@removeClass 'idle'
|
||||
_.defer => @addClass 'idle'
|
||||
+203
-336
@@ -1,10 +1,8 @@
|
||||
{Point, Range} = require 'text-buffer'
|
||||
{Model} = require 'theorist'
|
||||
{Emitter} = require 'event-kit'
|
||||
_ = require 'underscore-plus'
|
||||
Grim = require 'grim'
|
||||
|
||||
# Extended: The `Cursor` class represents the little blinking line identifying
|
||||
# Public: The `Cursor` class represents the little blinking line identifying
|
||||
# where text can be inserted.
|
||||
#
|
||||
# Cursors belong to {Editor}s and have some metadata attached in the form
|
||||
@@ -19,11 +17,9 @@ class Cursor extends Model
|
||||
|
||||
# Instantiated by an {Editor}
|
||||
constructor: ({@editor, @marker, id}) ->
|
||||
@emitter = new Emitter
|
||||
|
||||
@assignId(id)
|
||||
@updateVisibility()
|
||||
@marker.onDidChange (e) =>
|
||||
@marker.on 'changed', (e) =>
|
||||
@updateVisibility()
|
||||
{oldHeadScreenPosition, newHeadScreenPosition} = e
|
||||
{oldHeadBufferPosition, newHeadBufferPosition} = e
|
||||
@@ -42,80 +38,36 @@ class Cursor extends Model
|
||||
newBufferPosition: newHeadBufferPosition
|
||||
newScreenPosition: newHeadScreenPosition
|
||||
textChanged: textChanged
|
||||
cursor: this
|
||||
|
||||
@emit 'moved', movedEvent
|
||||
@emitter.emit 'did-change-position'
|
||||
@editor.cursorMoved(movedEvent)
|
||||
@marker.onDidDestroy =>
|
||||
@marker.on 'destroyed', =>
|
||||
@destroyed = true
|
||||
@editor.removeCursor(this)
|
||||
@emit 'destroyed'
|
||||
@emitter.emit 'did-destroy'
|
||||
@emitter.dispose()
|
||||
@needsAutoscroll = true
|
||||
|
||||
destroy: ->
|
||||
@marker.destroy()
|
||||
|
||||
###
|
||||
Section: Event Subscription
|
||||
###
|
||||
changePosition: (options, fn) ->
|
||||
@clearSelection()
|
||||
@needsAutoscroll = options.autoscroll ? @isLastCursor()
|
||||
fn()
|
||||
if @needsAutoscroll
|
||||
@emit 'autoscrolled' # Support legacy editor
|
||||
@autoscroll() if @needsAutoscroll and @editor.manageScrollPosition # Support react editor view
|
||||
|
||||
# Public: Calls your `callback` when the cursor has been moved.
|
||||
#
|
||||
# * `callback` {Function}
|
||||
# * `event` {Object}
|
||||
# * `oldBufferPosition` {Point}
|
||||
# * `oldScreenPosition` {Point}
|
||||
# * `newBufferPosition` {Point}
|
||||
# * `newScreenPosition` {Point}
|
||||
# * `textChanged` {Boolean}
|
||||
# * `Cursor` {Cursor} that triggered the event
|
||||
#
|
||||
# Returns a {Disposable} on which `.dispose()` can be called to unsubscribe.
|
||||
onDidChangePosition: (callback) ->
|
||||
@emitter.on 'did-change-position', callback
|
||||
|
||||
# Public: Calls your `callback` when the cursor is destroyed
|
||||
#
|
||||
# * `callback` {Function}
|
||||
#
|
||||
# Returns a {Disposable} on which `.dispose()` can be called to unsubscribe.
|
||||
onDidDestroy: (callback) ->
|
||||
@emitter.on 'did-destroy', callback
|
||||
|
||||
# Public: Calls your `callback` when the cursor's visibility has changed
|
||||
#
|
||||
# * `callback` {Function}
|
||||
# * `visibility` {Boolean}
|
||||
#
|
||||
# Returns a {Disposable} on which `.dispose()` can be called to unsubscribe.
|
||||
onDidChangeVisibility: (callback) ->
|
||||
@emitter.on 'did-change-visibility', callback
|
||||
|
||||
on: (eventName) ->
|
||||
switch eventName
|
||||
when 'moved'
|
||||
Grim.deprecate("Use Cursor::onDidChangePosition instead")
|
||||
when 'destroyed'
|
||||
Grim.deprecate("Use Cursor::onDidDestroy 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
|
||||
###
|
||||
getPixelRect: ->
|
||||
@editor.pixelRectForScreenRange(@getScreenRange())
|
||||
|
||||
# Public: Moves a cursor to a given screen position.
|
||||
#
|
||||
# * `screenPosition` {Array} of two numbers: the screen row, and the screen column.
|
||||
# * `options` (optional) {Object} with the following keys:
|
||||
# * `autoscroll` A Boolean which, if `true`, scrolls the {Editor} to wherever
|
||||
# the cursor moves to.
|
||||
# screenPosition - An {Array} of two numbers: the screen row, and the screen
|
||||
# column.
|
||||
# options - An {Object} with the following keys:
|
||||
# :autoscroll - A Boolean which, if `true`, scrolls the {Editor} to wherever
|
||||
# the cursor moves to.
|
||||
setScreenPosition: (screenPosition, options={}) ->
|
||||
@changePosition options, =>
|
||||
@marker.setHeadScreenPosition(screenPosition, options)
|
||||
@@ -124,12 +76,17 @@ class Cursor extends Model
|
||||
getScreenPosition: ->
|
||||
@marker.getHeadScreenPosition()
|
||||
|
||||
getScreenRange: ->
|
||||
{row, column} = @getScreenPosition()
|
||||
new Range(new Point(row, column), new Point(row, column + 1))
|
||||
|
||||
# Public: Moves a cursor to a given buffer position.
|
||||
#
|
||||
# * `bufferPosition` {Array} of two numbers: the buffer row, and the buffer column.
|
||||
# * `options` (optional) {Object} with the following keys:
|
||||
# * `autoscroll` A Boolean which, if `true`, scrolls the {Editor} to wherever
|
||||
# the cursor moves to.
|
||||
# bufferPosition - An {Array} of two numbers: the buffer row, and the buffer
|
||||
# column.
|
||||
# options - An {Object} with the following keys:
|
||||
# :autoscroll - A Boolean which, if `true`, scrolls the {Editor} to wherever
|
||||
# the cursor moves to.
|
||||
setBufferPosition: (bufferPosition, options={}) ->
|
||||
@changePosition options, =>
|
||||
@marker.setHeadBufferPosition(bufferPosition, options)
|
||||
@@ -138,38 +95,47 @@ class Cursor extends Model
|
||||
getBufferPosition: ->
|
||||
@marker.getHeadBufferPosition()
|
||||
|
||||
# Public: Returns the cursor's current screen row.
|
||||
getScreenRow: ->
|
||||
@getScreenPosition().row
|
||||
autoscroll: ->
|
||||
@editor.scrollToScreenRange(@getScreenRange())
|
||||
|
||||
# Public: Returns the cursor's current screen column.
|
||||
getScreenColumn: ->
|
||||
@getScreenPosition().column
|
||||
# Public: If the marker range is empty, the cursor is marked as being visible.
|
||||
updateVisibility: ->
|
||||
@setVisible(@marker.getBufferRange().isEmpty())
|
||||
|
||||
# Public: Retrieves the cursor's current buffer row.
|
||||
getBufferRow: ->
|
||||
@getBufferPosition().row
|
||||
# Public: Sets whether the cursor is visible.
|
||||
setVisible: (visible) ->
|
||||
if @visible != visible
|
||||
@visible = visible
|
||||
@needsAutoscroll ?= true if @visible and @isLastCursor()
|
||||
@emit 'visibility-changed', @visible
|
||||
|
||||
# Public: Returns the cursor's current buffer column.
|
||||
getBufferColumn: ->
|
||||
@getBufferPosition().column
|
||||
# Public: Returns the visibility of the cursor.
|
||||
isVisible: -> @visible
|
||||
|
||||
# Public: Returns the cursor's current buffer row of text excluding its line
|
||||
# ending.
|
||||
getCurrentBufferLine: ->
|
||||
@editor.lineTextForBufferRow(@getBufferRow())
|
||||
# Public: Get the RegExp used by the cursor to determine what a "word" is.
|
||||
#
|
||||
# options: An optional {Object} with the following keys:
|
||||
# :includeNonWordCharacters - A {Boolean} indicating whether to include
|
||||
# non-word characters in the regex.
|
||||
# (default: true)
|
||||
#
|
||||
# Returns a {RegExp}.
|
||||
wordRegExp: ({includeNonWordCharacters}={})->
|
||||
includeNonWordCharacters ?= true
|
||||
nonWordCharacters = atom.config.get('editor.nonWordCharacters')
|
||||
segments = ["^[\t ]*$"]
|
||||
segments.push("[^\\s#{_.escapeRegExp(nonWordCharacters)}]+")
|
||||
if includeNonWordCharacters
|
||||
segments.push("[#{_.escapeRegExp(nonWordCharacters)}]+")
|
||||
new RegExp(segments.join("|"), "g")
|
||||
|
||||
# Public: Returns whether the cursor is at the start of a line.
|
||||
isAtBeginningOfLine: ->
|
||||
@getBufferPosition().column == 0
|
||||
|
||||
# Public: Returns whether the cursor is on the line return character.
|
||||
isAtEndOfLine: ->
|
||||
@getBufferPosition().isEqual(@getCurrentLineBufferRange().end)
|
||||
|
||||
###
|
||||
Section: Cursor Position Details
|
||||
###
|
||||
# Public: Identifies if this cursor is the last in the {Editor}.
|
||||
#
|
||||
# "Last" is defined as the most recently added cursor.
|
||||
#
|
||||
# Returns a {Boolean}.
|
||||
isLastCursor: ->
|
||||
this == @editor.getCursor()
|
||||
|
||||
# Public: Identifies if the cursor is surrounded by whitespace.
|
||||
#
|
||||
@@ -207,50 +173,37 @@ class Cursor extends Model
|
||||
range = [[row, column], [row, Infinity]]
|
||||
@editor.getTextInBufferRange(range).search(@wordRegExp()) == 0
|
||||
|
||||
# Public: Returns the indentation level of the current line.
|
||||
getIndentLevel: ->
|
||||
if @editor.getSoftTabs()
|
||||
@getBufferColumn() / @editor.getTabLength()
|
||||
else
|
||||
@getBufferColumn()
|
||||
# Public: Prevents this cursor from causing scrolling.
|
||||
clearAutoscroll: ->
|
||||
@needsAutoscroll = null
|
||||
|
||||
# Public: Retrieves the grammar's token scopes for the line.
|
||||
#
|
||||
# Returns an {Array} of {String}s.
|
||||
getScopes: ->
|
||||
@editor.scopesForBufferPosition(@getBufferPosition())
|
||||
# Public: Deselects the current selection.
|
||||
clearSelection: ->
|
||||
@selection?.clear()
|
||||
|
||||
# Public: Returns true if this cursor has no non-whitespace characters before
|
||||
# its current position.
|
||||
hasPrecedingCharactersOnLine: ->
|
||||
bufferPosition = @getBufferPosition()
|
||||
line = @editor.lineTextForBufferRow(bufferPosition.row)
|
||||
firstCharacterColumn = line.search(/\S/)
|
||||
# Public: Returns the cursor's current screen row.
|
||||
getScreenRow: ->
|
||||
@getScreenPosition().row
|
||||
|
||||
if firstCharacterColumn is -1
|
||||
false
|
||||
else
|
||||
bufferPosition.column > firstCharacterColumn
|
||||
# Public: Returns the cursor's current screen column.
|
||||
getScreenColumn: ->
|
||||
@getScreenPosition().column
|
||||
|
||||
# Public: Identifies if this cursor is the last in the {Editor}.
|
||||
#
|
||||
# "Last" is defined as the most recently added cursor.
|
||||
#
|
||||
# Returns a {Boolean}.
|
||||
isLastCursor: ->
|
||||
this == @editor.getLastCursor()
|
||||
# Public: Retrieves the cursor's current buffer row.
|
||||
getBufferRow: ->
|
||||
@getBufferPosition().row
|
||||
|
||||
###
|
||||
Section: Moving the Cursor
|
||||
###
|
||||
# Public: Returns the cursor's current buffer column.
|
||||
getBufferColumn: ->
|
||||
@getBufferPosition().column
|
||||
|
||||
# Public: Returns the cursor's current buffer row of text excluding its line
|
||||
# ending.
|
||||
getCurrentBufferLine: ->
|
||||
@editor.lineForBufferRow(@getBufferRow())
|
||||
|
||||
# Public: Moves the cursor up one screen row.
|
||||
#
|
||||
# * `rowCount` (optional) {Number} number of rows to move (default: 1)
|
||||
# * `options` (optional) {Object} with the following keys:
|
||||
# * `moveToEndOfSelection` if true, move to the left of the selection if a
|
||||
# selection exists.
|
||||
moveUp: (rowCount=1, {moveToEndOfSelection}={}) ->
|
||||
moveUp: (rowCount = 1, {moveToEndOfSelection}={}) ->
|
||||
range = @marker.getScreenRange()
|
||||
if moveToEndOfSelection and not range.isEmpty()
|
||||
{ row, column } = range.start
|
||||
@@ -262,12 +215,7 @@ class Cursor extends Model
|
||||
@goalColumn = column
|
||||
|
||||
# Public: Moves the cursor down one screen row.
|
||||
#
|
||||
# * `rowCount` (optional) {Number} number of rows to move (default: 1)
|
||||
# * `options` (optional) {Object} with the following keys:
|
||||
# * `moveToEndOfSelection` if true, move to the left of the selection if a
|
||||
# selection exists.
|
||||
moveDown: (rowCount=1, {moveToEndOfSelection}={}) ->
|
||||
moveDown: (rowCount = 1, {moveToEndOfSelection}={}) ->
|
||||
range = @marker.getScreenRange()
|
||||
if moveToEndOfSelection and not range.isEmpty()
|
||||
{ row, column } = range.end
|
||||
@@ -280,51 +228,30 @@ class Cursor extends Model
|
||||
|
||||
# Public: Moves the cursor left one screen column.
|
||||
#
|
||||
# * `columnCount` (optional) {Number} number of columns to move (default: 1)
|
||||
# * `options` (optional) {Object} with the following keys:
|
||||
# * `moveToEndOfSelection` if true, move to the left of the selection if a
|
||||
# selection exists.
|
||||
moveLeft: (columnCount=1, {moveToEndOfSelection}={}) ->
|
||||
# options - An {Object} with the following keys:
|
||||
# :moveToEndOfSelection - if true, move to the left of the selection if a
|
||||
# selection exists.
|
||||
moveLeft: ({moveToEndOfSelection}={}) ->
|
||||
range = @marker.getScreenRange()
|
||||
if moveToEndOfSelection and not range.isEmpty()
|
||||
@setScreenPosition(range.start)
|
||||
else
|
||||
{row, column} = @getScreenPosition()
|
||||
|
||||
while columnCount > column and row > 0
|
||||
columnCount -= column
|
||||
column = @editor.lineTextForScreenRow(--row).length
|
||||
columnCount-- # subtract 1 for the row move
|
||||
|
||||
column = column - columnCount
|
||||
[row, column] = if column > 0 then [row, column - 1] else [row - 1, Infinity]
|
||||
@setScreenPosition({row, column})
|
||||
|
||||
# Public: Moves the cursor right one screen column.
|
||||
#
|
||||
# * `columnCount` (optional) {Number} number of columns to move (default: 1)
|
||||
# * `options` (optional) {Object} with the following keys:
|
||||
# * `moveToEndOfSelection` if true, move to the right of the selection if a
|
||||
# selection exists.
|
||||
moveRight: (columnCount=1, {moveToEndOfSelection}={}) ->
|
||||
# options - An {Object} with the following keys:
|
||||
# :moveToEndOfSelection - if true, move to the right of the selection if a
|
||||
# selection exists.
|
||||
moveRight: ({moveToEndOfSelection}={}) ->
|
||||
range = @marker.getScreenRange()
|
||||
if moveToEndOfSelection and not range.isEmpty()
|
||||
@setScreenPosition(range.end)
|
||||
else
|
||||
{ row, column } = @getScreenPosition()
|
||||
maxLines = @editor.getScreenLineCount()
|
||||
rowLength = @editor.lineTextForScreenRow(row).length
|
||||
columnsRemainingInLine = rowLength - column
|
||||
|
||||
while columnCount > columnsRemainingInLine and row < maxLines - 1
|
||||
columnCount -= columnsRemainingInLine
|
||||
columnCount-- # subtract 1 for the row move
|
||||
|
||||
column = 0
|
||||
rowLength = @editor.lineTextForScreenRow(++row).length
|
||||
columnsRemainingInLine = rowLength
|
||||
|
||||
column = column + columnCount
|
||||
@setScreenPosition({row, column}, skipAtomicTokens: true, wrapBeyondNewlines: true, wrapAtSoftNewlines: true)
|
||||
@setScreenPosition([row, column + 1], skipAtomicTokens: true, wrapBeyondNewlines: true, wrapAtSoftNewlines: true)
|
||||
|
||||
# Public: Moves the cursor to the top of the buffer.
|
||||
moveToTop: ->
|
||||
@@ -345,20 +272,23 @@ class Cursor extends Model
|
||||
# Public: Moves the cursor to the beginning of the first character in the
|
||||
# line.
|
||||
moveToFirstCharacterOfLine: ->
|
||||
screenRow = @getScreenRow()
|
||||
lineBufferRange = @editor.bufferRangeForScreenRange([[screenRow, 0], [screenRow, Infinity]])
|
||||
{row, column} = @getScreenPosition()
|
||||
screenline = @editor.lineForScreenRow(row)
|
||||
|
||||
firstCharacterColumn = null
|
||||
@editor.scanInBufferRange /\S/, lineBufferRange, ({range, stop}) ->
|
||||
firstCharacterColumn = range.start.column
|
||||
stop()
|
||||
goalColumn = screenline.text.search(/\S/)
|
||||
goalColumn = 0 if goalColumn == column or goalColumn == -1
|
||||
@setScreenPosition([row, goalColumn])
|
||||
|
||||
if firstCharacterColumn? and firstCharacterColumn isnt @getBufferColumn()
|
||||
targetBufferColumn = firstCharacterColumn
|
||||
else
|
||||
targetBufferColumn = lineBufferRange.start.column
|
||||
# Public: Moves the cursor to the beginning of the buffer line, skipping all
|
||||
# whitespace.
|
||||
skipLeadingWhitespace: ->
|
||||
position = @getBufferPosition()
|
||||
scanRange = @getCurrentLineBufferRange()
|
||||
endOfLeadingWhitespace = null
|
||||
@editor.scanInBufferRange /^[ \t]*/, scanRange, ({range}) =>
|
||||
endOfLeadingWhitespace = range.end
|
||||
|
||||
@setBufferPosition([lineBufferRange.start.row, targetBufferColumn])
|
||||
@setBufferPosition(endOfLeadingWhitespace) if endOfLeadingWhitespace.isGreaterThan(position)
|
||||
|
||||
# Public: Moves the cursor to the end of the line.
|
||||
moveToEndOfScreenLine: ->
|
||||
@@ -392,41 +322,16 @@ class Cursor extends Model
|
||||
if position = @getMoveNextWordBoundaryBufferPosition()
|
||||
@setBufferPosition(position)
|
||||
|
||||
# Public: Moves the cursor to the beginning of the buffer line, skipping all
|
||||
# whitespace.
|
||||
skipLeadingWhitespace: ->
|
||||
position = @getBufferPosition()
|
||||
scanRange = @getCurrentLineBufferRange()
|
||||
endOfLeadingWhitespace = null
|
||||
@editor.scanInBufferRange /^[ \t]*/, scanRange, ({range}) ->
|
||||
endOfLeadingWhitespace = range.end
|
||||
|
||||
@setBufferPosition(endOfLeadingWhitespace) if endOfLeadingWhitespace.isGreaterThan(position)
|
||||
|
||||
# Public: Moves the cursor to the beginning of the next paragraph
|
||||
moveToBeginningOfNextParagraph: ->
|
||||
if position = @getBeginningOfNextParagraphBufferPosition()
|
||||
@setBufferPosition(position)
|
||||
|
||||
# Public: Moves the cursor to the beginning of the previous paragraph
|
||||
moveToBeginningOfPreviousParagraph: ->
|
||||
if position = @getBeginningOfPreviousParagraphBufferPosition()
|
||||
@setBufferPosition(position)
|
||||
|
||||
###
|
||||
Section: Local Positions and Ranges
|
||||
###
|
||||
|
||||
# Public: Retrieves the buffer position of where the current word starts.
|
||||
#
|
||||
# * `options` (optional) An {Object} with the following keys:
|
||||
# * `wordRegex` A {RegExp} indicating what constitutes a "word"
|
||||
# (default: {::wordRegExp}).
|
||||
# * `includeNonWordCharacters` A {Boolean} indicating whether to include
|
||||
# non-word characters in the default word regex.
|
||||
# Has no effect if wordRegex is set.
|
||||
# * `allowPrevious` A {Boolean} indicating whether the beginning of the
|
||||
# previous word can be returned.
|
||||
# options - An {Object} with the following keys:
|
||||
# :wordRegex - A {RegExp} indicating what constitutes a "word"
|
||||
# (default: {::wordRegExp}).
|
||||
# :includeNonWordCharacters - A {Boolean} indicating whether to include
|
||||
# non-word characters in the default word regex.
|
||||
# Has no effect if wordRegex is set.
|
||||
# :allowPrevious - A {Boolean} indicating whether the beginning of the
|
||||
# previous word can be returned.
|
||||
#
|
||||
# Returns a {Range}.
|
||||
getBeginningOfCurrentWordBufferPosition: (options = {}) ->
|
||||
@@ -436,7 +341,7 @@ class Cursor extends Model
|
||||
scanRange = [[previousNonBlankRow, 0], currentBufferPosition]
|
||||
|
||||
beginningOfWordPosition = null
|
||||
@editor.backwardsScanInBufferRange (options.wordRegex ? @wordRegExp(options)), scanRange, ({range, stop}) ->
|
||||
@editor.backwardsScanInBufferRange (options.wordRegex ? @wordRegExp(options)), scanRange, ({range, stop}) =>
|
||||
if range.end.isGreaterThanOrEqual(currentBufferPosition) or allowPrevious
|
||||
beginningOfWordPosition = range.start
|
||||
if not beginningOfWordPosition?.isEqual(currentBufferPosition)
|
||||
@@ -457,7 +362,7 @@ class Cursor extends Model
|
||||
scanRange = [[previousNonBlankRow, 0], currentBufferPosition]
|
||||
|
||||
beginningOfWordPosition = null
|
||||
@editor.backwardsScanInBufferRange (options.wordRegex ? @wordRegExp()), scanRange, ({range, stop}) ->
|
||||
@editor.backwardsScanInBufferRange (options.wordRegex ? @wordRegExp()), scanRange, ({range, stop}) =>
|
||||
if range.start.row < currentBufferPosition.row and currentBufferPosition.column > 0
|
||||
# force it to stop at the beginning of each line
|
||||
beginningOfWordPosition = new Point(currentBufferPosition.row, 0)
|
||||
@@ -478,7 +383,7 @@ class Cursor extends Model
|
||||
scanRange = [currentBufferPosition, @editor.getEofBufferPosition()]
|
||||
|
||||
endOfWordPosition = null
|
||||
@editor.scanInBufferRange (options.wordRegex ? @wordRegExp()), scanRange, ({range, stop}) ->
|
||||
@editor.scanInBufferRange (options.wordRegex ? @wordRegExp()), scanRange, ({range, stop}) =>
|
||||
if range.start.row > currentBufferPosition.row
|
||||
# force it to stop at the beginning of each line
|
||||
endOfWordPosition = new Point(range.start.row, 0)
|
||||
@@ -494,12 +399,12 @@ class Cursor extends Model
|
||||
|
||||
# Public: Retrieves the buffer position of where the current word ends.
|
||||
#
|
||||
# * `options` (optional) {Object} with the following keys:
|
||||
# * `wordRegex` A {RegExp} indicating what constitutes a "word"
|
||||
# (default: {::wordRegExp})
|
||||
# * `includeNonWordCharacters` A Boolean indicating whether to include
|
||||
# non-word characters in the default word regex. Has no effect if
|
||||
# wordRegex is set.
|
||||
# options - An {Object} with the following keys:
|
||||
# :wordRegex - A {RegExp} indicating what constitutes a "word"
|
||||
# (default: {::wordRegExp})
|
||||
# :includeNonWordCharacters - A Boolean indicating whether to include
|
||||
# non-word characters in the default word regex.
|
||||
# Has no effect if wordRegex is set.
|
||||
#
|
||||
# Returns a {Range}.
|
||||
getEndOfCurrentWordBufferPosition: (options = {}) ->
|
||||
@@ -508,7 +413,7 @@ class Cursor extends Model
|
||||
scanRange = [currentBufferPosition, @editor.getEofBufferPosition()]
|
||||
|
||||
endOfWordPosition = null
|
||||
@editor.scanInBufferRange (options.wordRegex ? @wordRegExp(options)), scanRange, ({range, stop}) ->
|
||||
@editor.scanInBufferRange (options.wordRegex ? @wordRegExp(options)), scanRange, ({range, stop}) =>
|
||||
if range.start.isLessThanOrEqual(currentBufferPosition) or allowNext
|
||||
endOfWordPosition = range.end
|
||||
if not endOfWordPosition?.isEqual(currentBufferPosition)
|
||||
@@ -518,18 +423,18 @@ class Cursor extends Model
|
||||
|
||||
# Public: Retrieves the buffer position of where the next word starts.
|
||||
#
|
||||
# * `options` (optional) {Object}
|
||||
# * `wordRegex` A {RegExp} indicating what constitutes a "word"
|
||||
# (default: {::wordRegExp}).
|
||||
# options -
|
||||
# :wordRegex - A {RegExp} indicating what constitutes a "word"
|
||||
# (default: {::wordRegExp}).
|
||||
#
|
||||
# Returns a {Range}
|
||||
# Returns a {Range}.
|
||||
getBeginningOfNextWordBufferPosition: (options = {}) ->
|
||||
currentBufferPosition = @getBufferPosition()
|
||||
start = if @isInsideWord() then @getEndOfCurrentWordBufferPosition() else currentBufferPosition
|
||||
scanRange = [start, @editor.getEofBufferPosition()]
|
||||
|
||||
beginningOfNextWordPosition = null
|
||||
@editor.scanInBufferRange (options.wordRegex ? @wordRegExp()), scanRange, ({range, stop}) ->
|
||||
@editor.scanInBufferRange (options.wordRegex ? @wordRegExp()), scanRange, ({range, stop}) =>
|
||||
beginningOfNextWordPosition = range.start
|
||||
stop()
|
||||
|
||||
@@ -537,9 +442,9 @@ class Cursor extends Model
|
||||
|
||||
# Public: Returns the buffer Range occupied by the word located under the cursor.
|
||||
#
|
||||
# * `options` (optional) {Object}
|
||||
# * `wordRegex` A {RegExp} indicating what constitutes a "word"
|
||||
# (default: {::wordRegExp}).
|
||||
# options -
|
||||
# :wordRegex - A {RegExp} indicating what constitutes a "word"
|
||||
# (default: {::wordRegExp}).
|
||||
getCurrentWordBufferRange: (options={}) ->
|
||||
startOptions = _.extend(_.clone(options), allowPrevious: false)
|
||||
endOptions = _.extend(_.clone(options), allowNext: false)
|
||||
@@ -547,12 +452,49 @@ class Cursor extends Model
|
||||
|
||||
# Public: Returns the buffer Range for the current line.
|
||||
#
|
||||
# * `options` (optional) {Object}
|
||||
# * `includeNewline` A {Boolean} which controls whether the Range should
|
||||
# include the newline.
|
||||
# options -
|
||||
# :includeNewline: - A {Boolean} which controls whether the Range should
|
||||
# include the newline.
|
||||
getCurrentLineBufferRange: (options) ->
|
||||
@editor.bufferRangeForBufferRow(@getBufferRow(), options)
|
||||
|
||||
# Public: Moves the cursor to the beginning of the next paragraph
|
||||
moveToBeginningOfNextParagraph: ->
|
||||
if position = @getBeginningOfNextParagraphBufferPosition()
|
||||
@setBufferPosition(position)
|
||||
|
||||
# Public: Moves the cursor to the beginning of the previous paragraph
|
||||
moveToBeginningOfPreviousParagraph: ->
|
||||
if position = @getBeginningOfPreviousParagraphBufferPosition()
|
||||
@setBufferPosition(position)
|
||||
|
||||
getBeginningOfNextParagraphBufferPosition: (editor) ->
|
||||
start = @getBufferPosition()
|
||||
eof = @editor.getEofBufferPosition()
|
||||
scanRange = [start, eof]
|
||||
|
||||
{row, column} = eof
|
||||
position = new Point(row, column - 1)
|
||||
|
||||
@editor.scanInBufferRange /^\n*$/g, scanRange, ({range, stop}) =>
|
||||
if !range.start.isEqual(start)
|
||||
position = range.start
|
||||
stop()
|
||||
@editor.screenPositionForBufferPosition(position)
|
||||
|
||||
getBeginningOfPreviousParagraphBufferPosition: (editor) ->
|
||||
start = @editor.getCursorBufferPosition()
|
||||
|
||||
{row, column} = start
|
||||
scanRange = [[row-1, column], [0,0]]
|
||||
position = new Point(0, 0)
|
||||
zero = new Point(0,0)
|
||||
@editor.backwardsScanInBufferRange /^\n*$/g, scanRange, ({range, stop}) =>
|
||||
if !range.start.isEqual(zero)
|
||||
position = range.start
|
||||
stop()
|
||||
@editor.screenPositionForBufferPosition(position)
|
||||
|
||||
# Public: Retrieves the range for the current paragraph.
|
||||
#
|
||||
# A paragraph is defined as a block of text surrounded by empty lines.
|
||||
@@ -565,110 +507,35 @@ class Cursor extends Model
|
||||
getCurrentWordPrefix: ->
|
||||
@editor.getTextInBufferRange([@getBeginningOfCurrentWordBufferPosition(), @getBufferPosition()])
|
||||
|
||||
###
|
||||
Section: Visibility
|
||||
###
|
||||
# Public: Returns whether the cursor is at the start of a line.
|
||||
isAtBeginningOfLine: ->
|
||||
@getBufferPosition().column == 0
|
||||
|
||||
# Public: If the marker range is empty, the cursor is marked as being visible.
|
||||
updateVisibility: ->
|
||||
@setVisible(@marker.getBufferRange().isEmpty())
|
||||
# Public: Returns the indentation level of the current line.
|
||||
getIndentLevel: ->
|
||||
if @editor.getSoftTabs()
|
||||
@getBufferColumn() / @editor.getTabLength()
|
||||
else
|
||||
@getBufferColumn()
|
||||
|
||||
# Public: Sets whether the cursor is visible.
|
||||
setVisible: (visible) ->
|
||||
if @visible != visible
|
||||
@visible = visible
|
||||
@needsAutoscroll ?= true if @visible and @isLastCursor()
|
||||
@emit 'visibility-changed', @visible
|
||||
@emitter.emit 'did-change-visibility', @visible
|
||||
# Public: Returns whether the cursor is on the line return character.
|
||||
isAtEndOfLine: ->
|
||||
@getBufferPosition().isEqual(@getCurrentLineBufferRange().end)
|
||||
|
||||
# Public: Returns the visibility of the cursor.
|
||||
isVisible: -> @visible
|
||||
|
||||
###
|
||||
Section: Comparing to another cursor
|
||||
###
|
||||
|
||||
# Public: Compare this cursor's buffer position to another cursor's buffer position.
|
||||
# Public: Retrieves the grammar's token scopes for the line.
|
||||
#
|
||||
# See {Point::compare} for more details.
|
||||
#
|
||||
# * `otherCursor`{Cursor} to compare against
|
||||
compare: (otherCursor) ->
|
||||
@getBufferPosition().compare(otherCursor.getBufferPosition())
|
||||
# Returns an {Array} of {String}s.
|
||||
getScopes: ->
|
||||
@editor.scopesForBufferPosition(@getBufferPosition())
|
||||
|
||||
###
|
||||
Section: Utilities
|
||||
###
|
||||
# Public: Returns true if this cursor has no non-whitespace characters before
|
||||
# its current position.
|
||||
hasPrecedingCharactersOnLine: ->
|
||||
bufferPosition = @getBufferPosition()
|
||||
line = @editor.lineForBufferRow(bufferPosition.row)
|
||||
firstCharacterColumn = line.search(/\S/)
|
||||
|
||||
# Public: Prevents this cursor from causing scrolling.
|
||||
clearAutoscroll: ->
|
||||
@needsAutoscroll = null
|
||||
|
||||
# Public: Deselects the current selection.
|
||||
clearSelection: ->
|
||||
@selection?.clear()
|
||||
|
||||
# Public: Get the RegExp used by the cursor to determine what a "word" is.
|
||||
#
|
||||
# * `options` (optional) {Object} with the following keys:
|
||||
# * `includeNonWordCharacters` A {Boolean} indicating whether to include
|
||||
# non-word characters in the regex. (default: true)
|
||||
#
|
||||
# Returns a {RegExp}.
|
||||
wordRegExp: ({includeNonWordCharacters}={}) ->
|
||||
includeNonWordCharacters ?= true
|
||||
nonWordCharacters = atom.config.get('editor.nonWordCharacters')
|
||||
segments = ["^[\t ]*$"]
|
||||
segments.push("[^\\s#{_.escapeRegExp(nonWordCharacters)}]+")
|
||||
if includeNonWordCharacters
|
||||
segments.push("[#{_.escapeRegExp(nonWordCharacters)}]+")
|
||||
new RegExp(segments.join("|"), "g")
|
||||
|
||||
###
|
||||
Section: Private
|
||||
###
|
||||
|
||||
changePosition: (options, fn) ->
|
||||
@clearSelection()
|
||||
@needsAutoscroll = options.autoscroll ? @isLastCursor()
|
||||
fn()
|
||||
if @needsAutoscroll
|
||||
@emit 'autoscrolled' # Support legacy editor
|
||||
@autoscroll() if @needsAutoscroll and @editor.manageScrollPosition # Support react editor view
|
||||
|
||||
getPixelRect: ->
|
||||
@editor.pixelRectForScreenRange(@getScreenRange())
|
||||
|
||||
getScreenRange: ->
|
||||
{row, column} = @getScreenPosition()
|
||||
new Range(new Point(row, column), new Point(row, column + 1))
|
||||
|
||||
autoscroll: (options) ->
|
||||
@editor.scrollToScreenRange(@getScreenRange(), options)
|
||||
|
||||
getBeginningOfNextParagraphBufferPosition: (editor) ->
|
||||
start = @getBufferPosition()
|
||||
eof = @editor.getEofBufferPosition()
|
||||
scanRange = [start, eof]
|
||||
|
||||
{row, column} = eof
|
||||
position = new Point(row, column - 1)
|
||||
|
||||
@editor.scanInBufferRange /^\n*$/g, scanRange, ({range, stop}) ->
|
||||
if !range.start.isEqual(start)
|
||||
position = range.start
|
||||
stop()
|
||||
@editor.screenPositionForBufferPosition(position)
|
||||
|
||||
getBeginningOfPreviousParagraphBufferPosition: (editor) ->
|
||||
start = @editor.getCursorBufferPosition()
|
||||
|
||||
{row, column} = start
|
||||
scanRange = [[row-1, column], [0,0]]
|
||||
position = new Point(0, 0)
|
||||
zero = new Point(0,0)
|
||||
@editor.backwardsScanInBufferRange /^\n*$/g, scanRange, ({range, stop}) ->
|
||||
if !range.start.isEqual(zero)
|
||||
position = range.start
|
||||
stop()
|
||||
@editor.screenPositionForBufferPosition(position)
|
||||
if firstCharacterColumn is -1
|
||||
false
|
||||
else
|
||||
bufferPosition.column > firstCharacterColumn
|
||||
|
||||
+45
-122
@@ -1,12 +1,10 @@
|
||||
_ = require 'underscore-plus'
|
||||
EmitterMixin = require('emissary').Emitter
|
||||
{Emitter} = require 'event-kit'
|
||||
Grim = require 'grim'
|
||||
{Subscriber, Emitter} = require 'emissary'
|
||||
|
||||
idCounter = 0
|
||||
nextId = -> idCounter++
|
||||
|
||||
# Essential: Represents a decoration that follows a {Marker}. A decoration is
|
||||
# Public: Represents a decoration that follows a {Marker}. A decoration is
|
||||
# basically a visual representation of a marker. It allows you to add CSS
|
||||
# classes to line numbers in the gutter, lines, and add selection-line regions
|
||||
# around marked ranges of text.
|
||||
@@ -22,163 +20,88 @@ nextId = -> idCounter++
|
||||
#
|
||||
# Best practice for destorying the decoration is by destroying the {Marker}.
|
||||
#
|
||||
# ```coffee
|
||||
# ```
|
||||
# marker.destroy()
|
||||
# ```
|
||||
#
|
||||
# You should only use {Decoration::destroy} when you still need or do not own
|
||||
# the marker.
|
||||
#
|
||||
# ### IDs
|
||||
# Each {Decoration} has a unique ID available via `decoration.id`.
|
||||
#
|
||||
# ### Events
|
||||
# A couple of events are emitted:
|
||||
#
|
||||
# * `destroyed`: When the {Decoration} is destroyed
|
||||
# * `updated`: When the {Decoration} is updated via {Decoration::update}.
|
||||
# Event object has properties `oldParams` and `newParams`
|
||||
#
|
||||
module.exports =
|
||||
class Decoration
|
||||
EmitterMixin.includeInto(this)
|
||||
Emitter.includeInto(this)
|
||||
|
||||
# Private: Check if the `decorationProperties.type` matches `type`
|
||||
#
|
||||
# * `decorationProperties` {Object} eg. `{type: 'gutter', class: 'my-new-class'}`
|
||||
# * `type` {String} type like `'gutter'`, `'line'`, etc. `type` can also
|
||||
# be an {Array} of {String}s, where it will return true if the decoration's
|
||||
# type matches any in the array.
|
||||
#
|
||||
# Returns {Boolean}
|
||||
@isType: (decorationProperties, type) ->
|
||||
if _.isArray(decorationProperties.type)
|
||||
type in decorationProperties.type
|
||||
@isType: (decorationParams, type) ->
|
||||
if _.isArray(decorationParams.type)
|
||||
type in decorationParams.type
|
||||
else
|
||||
type is decorationProperties.type
|
||||
type is decorationParams.type
|
||||
|
||||
###
|
||||
Section: Construction and Destruction
|
||||
###
|
||||
|
||||
constructor: (@marker, @displayBuffer, @properties) ->
|
||||
@emitter = new Emitter
|
||||
constructor: (@marker, @displayBuffer, @params) ->
|
||||
@id = nextId()
|
||||
@properties.id = @id
|
||||
@params.id = @id
|
||||
@flashQueue = null
|
||||
@destroyed = false
|
||||
@isDestroyed = false
|
||||
|
||||
@markerDestroyDisposable = @marker.onDidDestroy => @destroy()
|
||||
|
||||
# Essential: Destroy this marker.
|
||||
# Public: Destroy this marker.
|
||||
#
|
||||
# If you own the marker, you should use {Marker::destroy} which will destroy
|
||||
# this decoration.
|
||||
destroy: ->
|
||||
return if @destroyed
|
||||
@markerDestroyDisposable.dispose()
|
||||
@markerDestroyDisposable = null
|
||||
@destroyed = true
|
||||
return if @isDestroyed
|
||||
@isDestroyed = true
|
||||
@displayBuffer.removeDecoration(this)
|
||||
@emit 'destroyed'
|
||||
@emitter.emit 'did-destroy'
|
||||
@emitter.dispose()
|
||||
|
||||
###
|
||||
Section: Event Subscription
|
||||
###
|
||||
|
||||
# Essential: When the {Decoration} is updated via {Decoration::update}.
|
||||
# Public: Update the marker with new params. Allows you to change the decoration's class.
|
||||
#
|
||||
# * `callback` {Function}
|
||||
# * `event` {Object}
|
||||
# * `oldProperties` {Object} the old parameters the decoration used to have
|
||||
# * `newProperties` {Object} the new parameters the decoration now has
|
||||
#
|
||||
# Returns a {Disposable} on which `.dispose()` can be called to unsubscribe.
|
||||
onDidChangeProperties: (callback) ->
|
||||
@emitter.on 'did-change-properties', callback
|
||||
# ```
|
||||
# decoration.update({type: 'gutter', class: 'my-new-class'})
|
||||
# ```
|
||||
update: (newParams) ->
|
||||
return if @isDestroyed
|
||||
oldParams = @params
|
||||
@params = newParams
|
||||
@params.id = @id
|
||||
@displayBuffer.decorationUpdated(this)
|
||||
@emit 'updated', {oldParams, newParams}
|
||||
|
||||
# Essential: Invoke the given callback when the {Decoration} is destroyed
|
||||
#
|
||||
# * `callback` {Function}
|
||||
#
|
||||
# Returns a {Disposable} on which `.dispose()` can be called to unsubscribe.
|
||||
onDidDestroy: (callback) ->
|
||||
@emitter.on 'did-destroy', callback
|
||||
|
||||
###
|
||||
Section: Decoration Details
|
||||
###
|
||||
|
||||
# Essential: An id unique across all {Decoration} objects
|
||||
getId: -> @id
|
||||
|
||||
# Essential: Returns the marker associated with this {Decoration}
|
||||
# Public: Returns the marker associated with this {Decoration}
|
||||
getMarker: -> @marker
|
||||
|
||||
# Public: Returns the {Decoration}'s params.
|
||||
getParams: -> @params
|
||||
|
||||
# Public: Check if this decoration is of type `type`
|
||||
#
|
||||
# * `type` {String} type like `'gutter'`, `'line'`, etc. `type` can also
|
||||
# be an {Array} of {String}s, where it will return true if the decoration's
|
||||
# type matches any in the array.
|
||||
# type - A {String} type like `'gutter'`
|
||||
#
|
||||
# Returns {Boolean}
|
||||
# Returns a {Boolean}
|
||||
isType: (type) ->
|
||||
Decoration.isType(@properties, type)
|
||||
|
||||
###
|
||||
Section: Properties
|
||||
###
|
||||
|
||||
# Essential: Returns the {Decoration}'s properties.
|
||||
getProperties: ->
|
||||
@properties
|
||||
getParams: ->
|
||||
Grim.deprecate 'Use Decoration::getProperties instead'
|
||||
@getProperties()
|
||||
|
||||
# Essential: Update the marker with new Properties. Allows you to change the decoration's class.
|
||||
#
|
||||
# ## Examples
|
||||
#
|
||||
# ```coffee
|
||||
# decoration.update({type: 'gutter', class: 'my-new-class'})
|
||||
# ```
|
||||
#
|
||||
# * `newProperties` {Object} eg. `{type: 'gutter', class: 'my-new-class'}`
|
||||
setProperties: (newProperties) ->
|
||||
return if @destroyed
|
||||
oldProperties = @properties
|
||||
@properties = newProperties
|
||||
@properties.id = @id
|
||||
@emit 'updated', {oldParams: oldProperties, newParams: newProperties}
|
||||
@emitter.emit 'did-change-properties', {oldProperties, newProperties}
|
||||
update: (newProperties) ->
|
||||
Grim.deprecate 'Use Decoration::setProperties instead'
|
||||
@setProperties(newProperties)
|
||||
|
||||
###
|
||||
Section: Private methods
|
||||
###
|
||||
Decoration.isType(@params, type)
|
||||
|
||||
matchesPattern: (decorationPattern) ->
|
||||
return false unless decorationPattern?
|
||||
for key, value of decorationPattern
|
||||
return false if @properties[key] != value
|
||||
return false if @params[key] != value
|
||||
true
|
||||
|
||||
onDidFlash: (callback) ->
|
||||
@emitter.on 'did-flash', callback
|
||||
|
||||
flash: (klass, duration=500) ->
|
||||
flashObject = {class: klass, duration}
|
||||
@flashQueue ?= []
|
||||
@flashQueue.push(flashObject)
|
||||
@emit 'flash'
|
||||
@emitter.emit 'did-flash'
|
||||
|
||||
consumeNextFlash: ->
|
||||
return @flashQueue.shift() if @flashQueue?.length > 0
|
||||
null
|
||||
|
||||
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)
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
# Extended: Manages the deserializers used for serialized state
|
||||
# Public: Manages the deserializers used for serialized state
|
||||
#
|
||||
# An instance of this class is always available as the `atom.deserializers`
|
||||
# global.
|
||||
#
|
||||
# ## Examples
|
||||
# ### Registering a deserializer
|
||||
#
|
||||
# ```coffee
|
||||
# class MyPackageView extends View
|
||||
@@ -24,21 +24,21 @@ class DeserializerManager
|
||||
|
||||
# Public: Register the given class(es) as deserializers.
|
||||
#
|
||||
# * `classes` One or more classes to register.
|
||||
# classes - One or more classes to register.
|
||||
add: (classes...) ->
|
||||
@deserializers[klass.name] = klass for klass in classes
|
||||
|
||||
# Public: Remove the given class(es) as deserializers.
|
||||
#
|
||||
# * `classes` One or more classes to remove.
|
||||
# classes - One or more classes to remove.
|
||||
remove: (classes...) ->
|
||||
delete @deserializers[name] for {name} in classes
|
||||
|
||||
# Public: Deserialize the state and params.
|
||||
#
|
||||
# * `state` The state {Object} to deserialize.
|
||||
# * `params` The params {Object} to pass as the second arguments to the
|
||||
# deserialize method of the deserializer.
|
||||
# state - The state {Object} to deserialize.
|
||||
# params - The params {Object} to pass as the second arguments to the
|
||||
# deserialize method of the deserializer.
|
||||
deserialize: (state, params) ->
|
||||
return unless state?
|
||||
|
||||
@@ -51,7 +51,7 @@ class DeserializerManager
|
||||
|
||||
# Get the deserializer for the state.
|
||||
#
|
||||
# * `state` The state {Object} being deserialized.
|
||||
# state - The state {Object} being deserialized.
|
||||
get: (state) ->
|
||||
return unless state?
|
||||
|
||||
|
||||
@@ -0,0 +1,231 @@
|
||||
{Range} = require 'text-buffer'
|
||||
_ = require 'underscore-plus'
|
||||
{Emitter, Subscriber} = require 'emissary'
|
||||
|
||||
module.exports =
|
||||
class DisplayBufferMarker
|
||||
Emitter.includeInto(this)
|
||||
Subscriber.includeInto(this)
|
||||
|
||||
bufferMarkerSubscription: null
|
||||
oldHeadBufferPosition: null
|
||||
oldHeadScreenPosition: null
|
||||
oldTailBufferPosition: null
|
||||
oldTailScreenPosition: null
|
||||
wasValid: true
|
||||
|
||||
constructor: ({@bufferMarker, @displayBuffer}) ->
|
||||
@id = @bufferMarker.id
|
||||
@oldHeadBufferPosition = @getHeadBufferPosition()
|
||||
@oldHeadScreenPosition = @getHeadScreenPosition()
|
||||
@oldTailBufferPosition = @getTailBufferPosition()
|
||||
@oldTailScreenPosition = @getTailScreenPosition()
|
||||
@wasValid = @isValid()
|
||||
|
||||
@subscribe @bufferMarker, 'destroyed', => @destroyed()
|
||||
@subscribe @bufferMarker, 'changed', (event) => @notifyObservers(event)
|
||||
|
||||
copy: (attributes) ->
|
||||
@displayBuffer.getMarker(@bufferMarker.copy(attributes).id)
|
||||
|
||||
# Gets the screen range of the display marker.
|
||||
#
|
||||
# Returns a {Range}.
|
||||
getScreenRange: ->
|
||||
@displayBuffer.screenRangeForBufferRange(@getBufferRange(), wrapAtSoftNewlines: true)
|
||||
|
||||
# Modifies the screen range of the display marker.
|
||||
#
|
||||
# screenRange - The new {Range} to use
|
||||
# options - A hash of options matching those found in {Marker::setRange}
|
||||
setScreenRange: (screenRange, options) ->
|
||||
@setBufferRange(@displayBuffer.bufferRangeForScreenRange(screenRange), options)
|
||||
|
||||
# Gets the buffer range of the display marker.
|
||||
#
|
||||
# Returns a {Range}.
|
||||
getBufferRange: ->
|
||||
@bufferMarker.getRange()
|
||||
|
||||
# Modifies the buffer range of the display marker.
|
||||
#
|
||||
# screenRange - The new {Range} to use
|
||||
# options - A hash of options matching those found in {Marker::setRange}
|
||||
setBufferRange: (bufferRange, options) ->
|
||||
@bufferMarker.setRange(bufferRange, options)
|
||||
|
||||
getPixelRange: ->
|
||||
@displayBuffer.pixelRangeForScreenRange(@getScreenRange(), false)
|
||||
|
||||
# Retrieves the screen position of the marker's head.
|
||||
#
|
||||
# Returns a {Point}.
|
||||
getHeadScreenPosition: ->
|
||||
@displayBuffer.screenPositionForBufferPosition(@getHeadBufferPosition(), wrapAtSoftNewlines: true)
|
||||
|
||||
# Sets the screen position of the marker's head.
|
||||
#
|
||||
# screenRange - The new {Point} to use
|
||||
# options - A hash of options matching those found in {DisplayBuffer::bufferPositionForScreenPosition}
|
||||
setHeadScreenPosition: (screenPosition, options) ->
|
||||
screenPosition = @displayBuffer.clipScreenPosition(screenPosition, options)
|
||||
@setHeadBufferPosition(@displayBuffer.bufferPositionForScreenPosition(screenPosition, options))
|
||||
|
||||
# Retrieves the buffer position of the marker's head.
|
||||
#
|
||||
# Returns a {Point}.
|
||||
getHeadBufferPosition: ->
|
||||
@bufferMarker.getHeadPosition()
|
||||
|
||||
# Sets the buffer position of the marker's head.
|
||||
#
|
||||
# screenRange - The new {Point} to use
|
||||
# options - A hash of options matching those found in {DisplayBuffer::bufferPositionForScreenPosition}
|
||||
setHeadBufferPosition: (bufferPosition) ->
|
||||
@bufferMarker.setHeadPosition(bufferPosition)
|
||||
|
||||
# Retrieves the screen position of the marker's tail.
|
||||
#
|
||||
# Returns a {Point}.
|
||||
getTailScreenPosition: ->
|
||||
@displayBuffer.screenPositionForBufferPosition(@getTailBufferPosition(), wrapAtSoftNewlines: true)
|
||||
|
||||
# Sets the screen position of the marker's tail.
|
||||
#
|
||||
# screenRange - The new {Point} to use
|
||||
# options - A hash of options matching those found in {DisplayBuffer::bufferPositionForScreenPosition}
|
||||
setTailScreenPosition: (screenPosition, options) ->
|
||||
screenPosition = @displayBuffer.clipScreenPosition(screenPosition, options)
|
||||
@setTailBufferPosition(@displayBuffer.bufferPositionForScreenPosition(screenPosition, options))
|
||||
|
||||
# Retrieves the buffer position of the marker's tail.
|
||||
#
|
||||
# Returns a {Point}.
|
||||
getTailBufferPosition: ->
|
||||
@bufferMarker.getTailPosition()
|
||||
|
||||
# Sets the buffer position of the marker's tail.
|
||||
#
|
||||
# screenRange - The new {Point} to use
|
||||
# options - A hash of options matching those found in {DisplayBuffer::bufferPositionForScreenPosition}
|
||||
setTailBufferPosition: (bufferPosition) ->
|
||||
@bufferMarker.setTailPosition(bufferPosition)
|
||||
|
||||
# Retrieves the screen position of the marker's start. This will always be
|
||||
# less than or equal to the result of {DisplayBufferMarker::getEndScreenPosition}.
|
||||
#
|
||||
# Returns a {Point}.
|
||||
getStartScreenPosition: ->
|
||||
@displayBuffer.screenPositionForBufferPosition(@getStartBufferPosition(), wrapAtSoftNewlines: true)
|
||||
|
||||
# Retrieves the buffer position of the marker's start. This will always be
|
||||
# less than or equal to the result of {DisplayBufferMarker::getEndBufferPosition}.
|
||||
#
|
||||
# Returns a {Point}.
|
||||
getStartBufferPosition: ->
|
||||
@bufferMarker.getStartPosition()
|
||||
|
||||
# Retrieves the screen position of the marker's end. This will always be
|
||||
# greater than or equal to the result of {DisplayBufferMarker::getStartScreenPosition}.
|
||||
#
|
||||
# Returns a {Point}.
|
||||
getEndScreenPosition: ->
|
||||
@displayBuffer.screenPositionForBufferPosition(@getEndBufferPosition(), wrapAtSoftNewlines: true)
|
||||
|
||||
# Retrieves the buffer position of the marker's end. This will always be
|
||||
# greater than or equal to the result of {DisplayBufferMarker::getStartBufferPosition}.
|
||||
#
|
||||
# Returns a {Point}.
|
||||
getEndBufferPosition: ->
|
||||
@bufferMarker.getEndPosition()
|
||||
|
||||
# Sets the marker's tail to the same position as the marker's head.
|
||||
#
|
||||
# This only works if there isn't already a tail position.
|
||||
#
|
||||
# Returns a {Point} representing the new tail position.
|
||||
plantTail: ->
|
||||
@bufferMarker.plantTail()
|
||||
|
||||
# Removes the tail from the marker.
|
||||
clearTail: ->
|
||||
@bufferMarker.clearTail()
|
||||
|
||||
hasTail: ->
|
||||
@bufferMarker.hasTail()
|
||||
|
||||
# Returns whether the head precedes the tail in the buffer
|
||||
isReversed: ->
|
||||
@bufferMarker.isReversed()
|
||||
|
||||
# Returns a {Boolean} indicating whether the marker is valid. Markers can be
|
||||
# invalidated when a region surrounding them in the buffer is changed.
|
||||
isValid: ->
|
||||
@bufferMarker.isValid()
|
||||
|
||||
# Returns a {Boolean} indicating whether the marker has been destroyed. A marker
|
||||
# can be invalid without being destroyed, in which case undoing the invalidating
|
||||
# operation would restore the marker. Once a marker is destroyed by calling
|
||||
# {Marker::destroy}, no undo/redo operation can ever bring it back.
|
||||
isDestroyed: ->
|
||||
@bufferMarker.isDestroyed()
|
||||
|
||||
getAttributes: ->
|
||||
@bufferMarker.getProperties()
|
||||
|
||||
setAttributes: (attributes) ->
|
||||
@bufferMarker.setProperties(attributes)
|
||||
|
||||
matchesAttributes: (attributes) ->
|
||||
attributes = @displayBuffer.translateToBufferMarkerParams(attributes)
|
||||
@bufferMarker.matchesAttributes(attributes)
|
||||
|
||||
# Destroys the marker
|
||||
destroy: ->
|
||||
@bufferMarker.destroy()
|
||||
@unsubscribe()
|
||||
|
||||
isEqual: (other) ->
|
||||
return false unless other instanceof @constructor
|
||||
@bufferMarker.isEqual(other.bufferMarker)
|
||||
|
||||
compare: (other) ->
|
||||
@bufferMarker.compare(other.bufferMarker)
|
||||
|
||||
# Returns a {String} representation of the marker
|
||||
inspect: ->
|
||||
"DisplayBufferMarker(id: #{@id}, bufferRange: #{@getBufferRange()})"
|
||||
|
||||
destroyed: ->
|
||||
delete @displayBuffer.markers[@id]
|
||||
@emit 'destroyed'
|
||||
|
||||
notifyObservers: ({textChanged}) ->
|
||||
textChanged ?= false
|
||||
|
||||
newHeadBufferPosition = @getHeadBufferPosition()
|
||||
newHeadScreenPosition = @getHeadScreenPosition()
|
||||
newTailBufferPosition = @getTailBufferPosition()
|
||||
newTailScreenPosition = @getTailScreenPosition()
|
||||
isValid = @isValid()
|
||||
|
||||
return if _.isEqual(isValid, @wasValid) and
|
||||
_.isEqual(newHeadBufferPosition, @oldHeadBufferPosition) and
|
||||
_.isEqual(newHeadScreenPosition, @oldHeadScreenPosition) and
|
||||
_.isEqual(newTailBufferPosition, @oldTailBufferPosition) and
|
||||
_.isEqual(newTailScreenPosition, @oldTailScreenPosition)
|
||||
|
||||
@emit 'changed', {
|
||||
@oldHeadScreenPosition, newHeadScreenPosition,
|
||||
@oldTailScreenPosition, newTailScreenPosition,
|
||||
@oldHeadBufferPosition, newHeadBufferPosition,
|
||||
@oldTailBufferPosition, newTailBufferPosition,
|
||||
textChanged,
|
||||
isValid
|
||||
}
|
||||
|
||||
@oldHeadBufferPosition = newHeadBufferPosition
|
||||
@oldHeadScreenPosition = newHeadScreenPosition
|
||||
@oldTailBufferPosition = newTailBufferPosition
|
||||
@oldTailScreenPosition = newTailScreenPosition
|
||||
@wasValid = isValid
|
||||
+99
-150
@@ -1,17 +1,15 @@
|
||||
_ = require 'underscore-plus'
|
||||
EmitterMixin = require('emissary').Emitter
|
||||
{Emitter} = require 'emissary'
|
||||
guid = require 'guid'
|
||||
Serializable = require 'serializable'
|
||||
{Model} = require 'theorist'
|
||||
{Emitter} = require 'event-kit'
|
||||
{Point, Range} = require 'text-buffer'
|
||||
TokenizedBuffer = require './tokenized-buffer'
|
||||
RowMap = require './row-map'
|
||||
Fold = require './fold'
|
||||
Token = require './token'
|
||||
Decoration = require './decoration'
|
||||
Marker = require './marker'
|
||||
Grim = require 'grim'
|
||||
DisplayBufferMarker = require './display-buffer-marker'
|
||||
|
||||
class BufferToScreenConversionError extends Error
|
||||
constructor: (@message, @metadata) ->
|
||||
@@ -24,7 +22,7 @@ class DisplayBuffer extends Model
|
||||
|
||||
@properties
|
||||
manageScrollPosition: false
|
||||
softWrapped: null
|
||||
softWrap: null
|
||||
editorWidthInChars: null
|
||||
lineHeightInPixels: null
|
||||
defaultCharWidth: null
|
||||
@@ -33,55 +31,57 @@ class DisplayBuffer extends Model
|
||||
scrollTop: 0
|
||||
scrollLeft: 0
|
||||
scrollWidth: 0
|
||||
verticalScrollbarWidth: 15
|
||||
horizontalScrollbarHeight: 15
|
||||
|
||||
verticalScrollMargin: 2
|
||||
horizontalScrollMargin: 6
|
||||
horizontalScrollbarHeight: 15
|
||||
verticalScrollbarWidth: 15
|
||||
scopedCharacterWidthsChangeCount: 0
|
||||
|
||||
constructor: ({tabLength, @editorWidthInChars, @tokenizedBuffer, buffer, @invisibles}={}) ->
|
||||
constructor: ({tabLength, @editorWidthInChars, @tokenizedBuffer, buffer}={}) ->
|
||||
super
|
||||
|
||||
@emitter = new Emitter
|
||||
|
||||
@softWrapped ?= atom.config.get('editor.softWrap') ? false
|
||||
@tokenizedBuffer ?= new TokenizedBuffer({tabLength, buffer, @invisibles})
|
||||
@softWrap ?= atom.config.get('editor.softWrap') ? false
|
||||
@tokenizedBuffer ?= new TokenizedBuffer({tabLength, buffer})
|
||||
@buffer = @tokenizedBuffer.buffer
|
||||
@charWidthsByScope = {}
|
||||
@markers = {}
|
||||
@foldsByMarkerId = {}
|
||||
@decorationsById = {}
|
||||
@decorationsByMarkerId = {}
|
||||
@decorationMarkerChangedSubscriptions = {}
|
||||
@decorationMarkerDestroyedSubscriptions = {}
|
||||
@updateAllScreenLines()
|
||||
@createFoldForMarker(marker) for marker in @buffer.findMarkers(@getFoldMarkerAttributes())
|
||||
@subscribe @tokenizedBuffer.onDidChange @handleTokenizedBufferChange
|
||||
@subscribe @buffer.onDidUpdateMarkers @handleBufferMarkersUpdated
|
||||
@subscribe @buffer.onDidCreateMarker @handleBufferMarkerCreated
|
||||
@subscribe @tokenizedBuffer, 'grammar-changed', (grammar) => @emit 'grammar-changed', grammar
|
||||
@subscribe @tokenizedBuffer, 'tokenized', => @emit 'tokenized'
|
||||
@subscribe @tokenizedBuffer, 'changed', @handleTokenizedBufferChange
|
||||
@subscribe @buffer, 'markers-updated', @handleBufferMarkersUpdated
|
||||
@subscribe @buffer, 'marker-created', @handleBufferMarkerCreated
|
||||
|
||||
@subscribe @$softWrap, (softWrap) =>
|
||||
@emit 'soft-wrap-changed', softWrap
|
||||
@updateWrappedScreenLines()
|
||||
|
||||
@subscribe atom.config.observe 'editor.preferredLineLength', callNow: false, =>
|
||||
@updateWrappedScreenLines() if @isSoftWrapped() and atom.config.get('editor.softWrapAtPreferredLineLength')
|
||||
@updateWrappedScreenLines() if @softWrap and atom.config.get('editor.softWrapAtPreferredLineLength')
|
||||
|
||||
@subscribe atom.config.observe 'editor.softWrapAtPreferredLineLength', callNow: false, =>
|
||||
@updateWrappedScreenLines() if @isSoftWrapped()
|
||||
|
||||
@updateAllScreenLines()
|
||||
@updateWrappedScreenLines() if @softWrap
|
||||
|
||||
serializeParams: ->
|
||||
id: @id
|
||||
softWrapped: @isSoftWrapped()
|
||||
softWrap: @softWrap
|
||||
editorWidthInChars: @editorWidthInChars
|
||||
scrollTop: @scrollTop
|
||||
scrollLeft: @scrollLeft
|
||||
tokenizedBuffer: @tokenizedBuffer.serialize()
|
||||
invisibles: _.clone(@invisibles)
|
||||
|
||||
deserializeParams: (params) ->
|
||||
params.tokenizedBuffer = TokenizedBuffer.deserialize(params.tokenizedBuffer)
|
||||
params
|
||||
|
||||
copy: ->
|
||||
newDisplayBuffer = new DisplayBuffer({@buffer, tabLength: @getTabLength(), @invisibles})
|
||||
newDisplayBuffer = new DisplayBuffer({@buffer, tabLength: @getTabLength()})
|
||||
newDisplayBuffer.setScrollTop(@getScrollTop())
|
||||
newDisplayBuffer.setScrollLeft(@getScrollLeft())
|
||||
|
||||
@@ -95,71 +95,12 @@ class DisplayBuffer extends Model
|
||||
@rowMap = new RowMap
|
||||
@updateScreenLines(0, @buffer.getLineCount(), null, suppressChangeEvent: true)
|
||||
|
||||
onDidChangeSoftWrapped: (callback) ->
|
||||
@emitter.on 'did-change-soft-wrapped', callback
|
||||
|
||||
onDidChangeGrammar: (callback) ->
|
||||
@tokenizedBuffer.onDidChangeGrammar(callback)
|
||||
|
||||
onDidTokenize: (callback) ->
|
||||
@tokenizedBuffer.onDidTokenize(callback)
|
||||
|
||||
onDidChange: (callback) ->
|
||||
@emitter.on 'did-change', callback
|
||||
|
||||
onDidChangeCharacterWidths: (callback) ->
|
||||
@emitter.on 'did-change-character-widths', callback
|
||||
|
||||
observeDecorations: (callback) ->
|
||||
callback(decoration) for decoration in @getDecorations()
|
||||
@onDidAddDecoration(callback)
|
||||
|
||||
onDidAddDecoration: (callback) ->
|
||||
@emitter.on 'did-add-decoration', callback
|
||||
|
||||
onDidRemoveDecoration: (callback) ->
|
||||
@emitter.on 'did-remove-decoration', callback
|
||||
|
||||
onDidCreateMarker: (callback) ->
|
||||
@emitter.on 'did-create-marker', callback
|
||||
|
||||
onDidUpdateMarkers: (callback) ->
|
||||
@emitter.on 'did-update-markers', callback
|
||||
|
||||
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)
|
||||
|
||||
emitDidChange: (eventProperties, refreshMarkers=true) ->
|
||||
emitChanged: (eventProperties, refreshMarkers=true) ->
|
||||
if refreshMarkers
|
||||
@pauseMarkerChangeEvents()
|
||||
@pauseMarkerObservers()
|
||||
@refreshMarkerScreenPositions()
|
||||
@emit 'changed', eventProperties
|
||||
@emitter.emit 'did-change', eventProperties
|
||||
@resumeMarkerChangeEvents()
|
||||
@resumeMarkerObservers()
|
||||
|
||||
updateWrappedScreenLines: ->
|
||||
start = 0
|
||||
@@ -167,7 +108,7 @@ class DisplayBuffer extends Model
|
||||
@updateAllScreenLines()
|
||||
screenDelta = @getLastRow() - end
|
||||
bufferDelta = 0
|
||||
@emitDidChange({ start, end, screenDelta, bufferDelta })
|
||||
@emitChanged({ start, end, screenDelta, bufferDelta })
|
||||
|
||||
# Sets the visibility of the tokenized buffer.
|
||||
#
|
||||
@@ -211,7 +152,7 @@ class DisplayBuffer extends Model
|
||||
|
||||
horizontallyScrollable: (reentrant) ->
|
||||
return false unless @width?
|
||||
return false if @isSoftWrapped()
|
||||
return false if @getSoftWrap()
|
||||
if reentrant
|
||||
@getScrollWidth() > @getWidth()
|
||||
else
|
||||
@@ -236,7 +177,7 @@ class DisplayBuffer extends Model
|
||||
setWidth: (newWidth) ->
|
||||
oldWidth = @width
|
||||
@width = newWidth
|
||||
@updateWrappedScreenLines() if newWidth isnt oldWidth and @isSoftWrapped()
|
||||
@updateWrappedScreenLines() if newWidth isnt oldWidth and @softWrap
|
||||
@setScrollTop(@getScrollTop()) # Ensure scrollTop is still valid in case horizontal scrollbar disappeared
|
||||
@width
|
||||
|
||||
@@ -309,7 +250,6 @@ class DisplayBuffer extends Model
|
||||
characterWidthsChanged: ->
|
||||
@computeScrollWidth()
|
||||
@emit 'character-widths-changed', @scopedCharacterWidthsChangeCount
|
||||
@emitter.emit 'did-change-character-widths', @scopedCharacterWidthsChangeCount
|
||||
|
||||
clearScopedCharWidths: ->
|
||||
@charWidthsByScope = {}
|
||||
@@ -400,18 +340,11 @@ class DisplayBuffer extends Model
|
||||
setTabLength: (tabLength) ->
|
||||
@tokenizedBuffer.setTabLength(tabLength)
|
||||
|
||||
setInvisibles: (@invisibles) ->
|
||||
@tokenizedBuffer.setInvisibles(@invisibles)
|
||||
# Deprecated: Use the softWrap property directly
|
||||
setSoftWrap: (@softWrap) -> @softWrap
|
||||
|
||||
setSoftWrapped: (softWrapped) ->
|
||||
if softWrapped isnt @softWrapped
|
||||
@softWrapped = softWrapped
|
||||
@updateWrappedScreenLines()
|
||||
@emit 'soft-wrap-changed', @softWrapped
|
||||
@emitter.emit 'did-change-soft-wrapped', @softWrapped
|
||||
@softWrapped
|
||||
|
||||
isSoftWrapped: -> @softWrapped
|
||||
# Deprecated: Use the softWrap property directly
|
||||
getSoftWrap: -> @softWrap
|
||||
|
||||
# Set the number of characters that fit horizontally in the editor.
|
||||
#
|
||||
@@ -420,7 +353,7 @@ class DisplayBuffer extends Model
|
||||
if editorWidthInChars > 0
|
||||
previousWidthInChars = @editorWidthInChars
|
||||
@editorWidthInChars = editorWidthInChars
|
||||
if editorWidthInChars isnt previousWidthInChars and @isSoftWrapped()
|
||||
if editorWidthInChars isnt previousWidthInChars and @softWrap
|
||||
@updateWrappedScreenLines()
|
||||
|
||||
# Returns the editor width in characters for soft wrap.
|
||||
@@ -440,25 +373,25 @@ class DisplayBuffer extends Model
|
||||
|
||||
# Gets the screen line for the given screen row.
|
||||
#
|
||||
# * `screenRow` - A {Number} indicating the screen row.
|
||||
# screenRow - A {Number} indicating the screen row.
|
||||
#
|
||||
# Returns {TokenizedLine}
|
||||
tokenizedLineForScreenRow: (screenRow) ->
|
||||
@screenLines[screenRow]
|
||||
# Returns a {ScreenLine}.
|
||||
lineForRow: (row) ->
|
||||
@screenLines[row]
|
||||
|
||||
# Gets the screen lines for the given screen row range.
|
||||
#
|
||||
# startRow - A {Number} indicating the beginning screen row.
|
||||
# endRow - A {Number} indicating the ending screen row.
|
||||
#
|
||||
# Returns an {Array} of {TokenizedLine}s.
|
||||
tokenizedLinesForScreenRows: (startRow, endRow) ->
|
||||
# Returns an {Array} of {ScreenLine}s.
|
||||
linesForRows: (startRow, endRow) ->
|
||||
@screenLines[startRow..endRow]
|
||||
|
||||
# Gets all the screen lines.
|
||||
#
|
||||
# Returns an {Array} of {TokenizedLine}s.
|
||||
getTokenizedLines: ->
|
||||
# Returns an {Array} of {ScreenLines}s.
|
||||
getLines: ->
|
||||
new Array(@screenLines...)
|
||||
|
||||
indentLevelForLine: (line) ->
|
||||
@@ -618,7 +551,7 @@ class DisplayBuffer extends Model
|
||||
top = targetRow * @lineHeightInPixels
|
||||
left = 0
|
||||
column = 0
|
||||
for token in @tokenizedLineForScreenRow(targetRow).tokens
|
||||
for token in @lineForRow(targetRow).tokens
|
||||
charWidths = @getScopedCharWidths(token.scopes)
|
||||
for char in token.value
|
||||
return {top, left} if column is targetColumn
|
||||
@@ -636,7 +569,7 @@ class DisplayBuffer extends Model
|
||||
|
||||
left = 0
|
||||
column = 0
|
||||
for token in @tokenizedLineForScreenRow(row).tokens
|
||||
for token in @lineForRow(row).tokens
|
||||
charWidths = @getScopedCharWidths(token.scopes)
|
||||
for char in token.value
|
||||
charWidth = charWidths[char] ? defaultCharWidth
|
||||
@@ -690,7 +623,7 @@ class DisplayBuffer extends Model
|
||||
|
||||
unless screenLine?
|
||||
throw new BufferToScreenConversionError "No screen line exists when converting buffer row to screen row",
|
||||
softWrapEnabled: @isSoftWrapped()
|
||||
softWrapEnabled: @getSoftWrap()
|
||||
foldCount: @findFoldMarkers().length
|
||||
lastBufferRow: @buffer.getLastRow()
|
||||
lastScreenRow: @getLastRow()
|
||||
@@ -806,7 +739,7 @@ class DisplayBuffer extends Model
|
||||
# Returns a {Number} representing the `line` position where the wrap would take place.
|
||||
# Returns `null` if a wrap wouldn't occur.
|
||||
findWrapColumn: (line, softWrapColumn=@getSoftWrapColumn()) ->
|
||||
return unless @isSoftWrapped()
|
||||
return unless @softWrap
|
||||
return unless line.length > softWrapColumn
|
||||
|
||||
if /\s/.test(line[softWrapColumn])
|
||||
@@ -829,12 +762,6 @@ class DisplayBuffer extends Model
|
||||
decorationForId: (id) ->
|
||||
@decorationsById[id]
|
||||
|
||||
getDecorations: ->
|
||||
allDecorations = []
|
||||
for markerId, decorations of @decorationsByMarkerId
|
||||
allDecorations = allDecorations.concat(decorations) if decorations?
|
||||
allDecorations
|
||||
|
||||
decorationsForScreenRowRange: (startScreenRow, endScreenRow) ->
|
||||
decorationsByMarkerId = {}
|
||||
for marker in @findMarkers(intersectsScreenRowRange: [startScreenRow, endScreenRow])
|
||||
@@ -844,13 +771,24 @@ class DisplayBuffer extends Model
|
||||
|
||||
decorateMarker: (marker, decorationParams) ->
|
||||
marker = @getMarker(marker.id)
|
||||
|
||||
@decorationMarkerDestroyedSubscriptions[marker.id] ?= @subscribe marker, 'destroyed', =>
|
||||
@removeAllDecorationsForMarker(marker)
|
||||
|
||||
@decorationMarkerChangedSubscriptions[marker.id] ?= @subscribe marker, 'changed', (event) =>
|
||||
decorations = @decorationsByMarkerId[marker.id]
|
||||
|
||||
# Why check existence? Markers may get destroyed or decorations removed
|
||||
# in the change handler. Bookmarks does this.
|
||||
if decorations?
|
||||
for decoration in decorations
|
||||
@emit 'decoration-changed', marker, decoration, event
|
||||
|
||||
decoration = new Decoration(marker, this, decorationParams)
|
||||
@subscribe decoration.onDidDestroy => @removeDecoration(decoration)
|
||||
@decorationsByMarkerId[marker.id] ?= []
|
||||
@decorationsByMarkerId[marker.id].push(decoration)
|
||||
@decorationsById[decoration.id] = decoration
|
||||
@emit 'decoration-added', decoration
|
||||
@emitter.emit 'did-add-decoration', decoration
|
||||
@emit 'decoration-added', marker, decoration
|
||||
decoration
|
||||
|
||||
removeDecoration: (decoration) ->
|
||||
@@ -861,25 +799,41 @@ class DisplayBuffer extends Model
|
||||
if index > -1
|
||||
decorations.splice(index, 1)
|
||||
delete @decorationsById[decoration.id]
|
||||
@emit 'decoration-removed', decoration
|
||||
@emitter.emit 'did-remove-decoration', decoration
|
||||
delete @decorationsByMarkerId[marker.id] if decorations.length is 0
|
||||
@emit 'decoration-removed', marker, decoration
|
||||
@removedAllMarkerDecorations(marker) if decorations.length is 0
|
||||
|
||||
# Retrieves a {Marker} based on its id.
|
||||
removeAllDecorationsForMarker: (marker) ->
|
||||
decorations = @decorationsByMarkerId[marker.id].slice()
|
||||
for decoration in decorations
|
||||
@emit 'decoration-removed', marker, decoration
|
||||
@removedAllMarkerDecorations(marker)
|
||||
|
||||
removedAllMarkerDecorations: (marker) ->
|
||||
@decorationMarkerChangedSubscriptions[marker.id].off()
|
||||
@decorationMarkerDestroyedSubscriptions[marker.id].off()
|
||||
|
||||
delete @decorationsByMarkerId[marker.id]
|
||||
delete @decorationMarkerChangedSubscriptions[marker.id]
|
||||
delete @decorationMarkerDestroyedSubscriptions[marker.id]
|
||||
|
||||
decorationUpdated: (decoration) ->
|
||||
@emit 'decoration-updated', decoration
|
||||
|
||||
# Retrieves a {DisplayBufferMarker} based on its id.
|
||||
#
|
||||
# id - A {Number} representing a marker id
|
||||
#
|
||||
# Returns the {Marker} (if it exists).
|
||||
# Returns the {DisplayBufferMarker} (if it exists).
|
||||
getMarker: (id) ->
|
||||
unless marker = @markers[id]
|
||||
if bufferMarker = @buffer.getMarker(id)
|
||||
marker = new Marker({bufferMarker, displayBuffer: this})
|
||||
marker = new DisplayBufferMarker({bufferMarker, displayBuffer: this})
|
||||
@markers[id] = marker
|
||||
marker
|
||||
|
||||
# Retrieves the active markers in the buffer.
|
||||
#
|
||||
# Returns an {Array} of existing {Marker}s.
|
||||
# Returns an {Array} of existing {DisplayBufferMarker}s.
|
||||
getMarkers: ->
|
||||
@buffer.getMarkers().map ({id}) => @getMarker(id)
|
||||
|
||||
@@ -934,7 +888,7 @@ class DisplayBuffer extends Model
|
||||
#
|
||||
# Refer to {DisplayBuffer::findMarkers} for details.
|
||||
#
|
||||
# Returns a {Marker} or null
|
||||
# Returns a {DisplayBufferMarker} or null
|
||||
findMarker: (params) ->
|
||||
@findMarkers(params)[0]
|
||||
|
||||
@@ -955,7 +909,7 @@ class DisplayBuffer extends Model
|
||||
# :containedInBufferRange - A {Range} or range-compatible {Array}. Only
|
||||
# returns markers contained within this range.
|
||||
#
|
||||
# Returns an {Array} of {Marker}s
|
||||
# Returns an {Array} of {DisplayBufferMarker}s
|
||||
findMarkers: (params) ->
|
||||
params = @translateToBufferMarkerParams(params)
|
||||
@buffer.findMarkers(params).map (stringMarker) => @getMarker(stringMarker.id)
|
||||
@@ -1007,13 +961,12 @@ class DisplayBuffer extends Model
|
||||
getFoldMarkerAttributes: (attributes={}) ->
|
||||
_.extend(attributes, class: 'fold', displayBufferId: @id)
|
||||
|
||||
pauseMarkerChangeEvents: ->
|
||||
marker.pauseChangeEvents() for marker in @getMarkers()
|
||||
pauseMarkerObservers: ->
|
||||
marker.pauseEvents() for marker in @getMarkers()
|
||||
|
||||
resumeMarkerChangeEvents: ->
|
||||
marker.resumeChangeEvents() for marker in @getMarkers()
|
||||
resumeMarkerObservers: ->
|
||||
marker.resumeEvents() for marker in @getMarkers()
|
||||
@emit 'markers-updated'
|
||||
@emitter.emit 'did-update-markers'
|
||||
|
||||
refreshMarkerScreenPositions: ->
|
||||
for marker in @getMarkers()
|
||||
@@ -1024,9 +977,9 @@ class DisplayBuffer extends Model
|
||||
@tokenizedBuffer.destroy()
|
||||
@unsubscribe()
|
||||
|
||||
logLines: (start=0, end=@getLastRow()) ->
|
||||
logLines: (start=0, end=@getLastRow())->
|
||||
for row in [start..end]
|
||||
line = @tokenizedLineForScreenRow(row).text
|
||||
line = @lineForRow(row).text
|
||||
console.log row, @bufferRowForScreenRow(row), line, line.length
|
||||
|
||||
handleTokenizedBufferChange: (tokenizedBufferChange) =>
|
||||
@@ -1055,10 +1008,10 @@ class DisplayBuffer extends Model
|
||||
bufferDelta: bufferDelta
|
||||
|
||||
if options.delayChangeEvent
|
||||
@pauseMarkerChangeEvents()
|
||||
@pauseMarkerObservers()
|
||||
@pendingChangeEvent = changeEvent
|
||||
else
|
||||
@emitDidChange(changeEvent, options.refreshMarkers)
|
||||
@emitChanged(changeEvent, options.refreshMarkers)
|
||||
|
||||
buildScreenLines: (startBufferRow, endBufferRow) ->
|
||||
screenLines = []
|
||||
@@ -1067,7 +1020,7 @@ class DisplayBuffer extends Model
|
||||
|
||||
bufferRow = startBufferRow
|
||||
while bufferRow < endBufferRow
|
||||
tokenizedLine = @tokenizedBuffer.tokenizedLineForRow(bufferRow)
|
||||
tokenizedLine = @tokenizedBuffer.lineForScreenRow(bufferRow)
|
||||
|
||||
if fold = @largestFoldStartingAtBufferRow(bufferRow)
|
||||
foldLine = tokenizedLine.copy()
|
||||
@@ -1130,21 +1083,17 @@ class DisplayBuffer extends Model
|
||||
|
||||
computeScrollWidth: ->
|
||||
@scrollWidth = @pixelPositionForScreenPosition([@longestScreenRow, @maxLineLength]).left
|
||||
@scrollWidth += 1 unless @isSoftWrapped()
|
||||
@scrollWidth += 1 unless @getSoftWrap()
|
||||
@setScrollLeft(Math.min(@getScrollLeft(), @getMaxScrollLeft()))
|
||||
|
||||
handleBufferMarkersUpdated: =>
|
||||
if event = @pendingChangeEvent
|
||||
@pendingChangeEvent = null
|
||||
@emitDidChange(event, false)
|
||||
@emitChanged(event, false)
|
||||
|
||||
handleBufferMarkerCreated: (textBufferMarker) =>
|
||||
@createFoldForMarker(textBufferMarker) if textBufferMarker.matchesParams(@getFoldMarkerAttributes())
|
||||
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
|
||||
@emitter.emit 'did-create-marker', marker
|
||||
handleBufferMarkerCreated: (marker) =>
|
||||
@createFoldForMarker(marker) if marker.matchesAttributes(@getFoldMarkerAttributes())
|
||||
@emit 'marker-created', @getMarker(marker.id)
|
||||
|
||||
createFoldForMarker: (marker) ->
|
||||
@decorateMarker(marker, type: 'gutter', class: 'folded')
|
||||
|
||||
+219
-298
@@ -1,10 +1,8 @@
|
||||
_ = require 'underscore-plus'
|
||||
React = require 'react-atom-fork'
|
||||
{div, span} = require 'reactionary-atom-fork'
|
||||
{debounce, defaults, isEqualForProperties} = require 'underscore-plus'
|
||||
scrollbarStyle = require 'scrollbar-style'
|
||||
{Range, Point} = require 'text-buffer'
|
||||
grim = require 'grim'
|
||||
|
||||
GutterComponent = require './gutter-component'
|
||||
InputComponent = require './input-component'
|
||||
@@ -31,27 +29,30 @@ EditorComponent = React.createClass
|
||||
updateRequested: false
|
||||
updatesPaused: false
|
||||
updateRequestedWhilePaused: false
|
||||
cursorMoved: false
|
||||
cursorsMoved: false
|
||||
selectionChanged: false
|
||||
selectionAdded: false
|
||||
scrollingVertically: false
|
||||
refreshingScrollbars: false
|
||||
measuringScrollbars: true
|
||||
mouseWheelScreenRow: null
|
||||
mouseWheelScreenRowClearDelay: 150
|
||||
scrollSensitivity: 0.4
|
||||
heightAndWidthMeasurementRequested: false
|
||||
measureLineHeightAndDefaultCharWidthWhenShown: false
|
||||
remeasureCharacterWidthsWhenShown: false
|
||||
inputEnabled: true
|
||||
scopedCharacterWidthsChangeCount: null
|
||||
domPollingInterval: 100
|
||||
domPollingIntervalId: null
|
||||
domPollingPaused: false
|
||||
measureScrollbarsWhenShown: true
|
||||
measureLineHeightAndDefaultCharWidthWhenShown: true
|
||||
remeasureCharacterWidthsWhenShown: false
|
||||
|
||||
render: ->
|
||||
{focused, showIndentGuide, showLineNumbers, visible} = @state
|
||||
{focused, showIndentGuide, showInvisibles, showLineNumbers, visible} = @state
|
||||
{editor, mini, cursorBlinkPeriod, cursorBlinkResumeDelay} = @props
|
||||
maxLineNumberDigits = editor.getLineCount().toString().length
|
||||
hasSelection = editor.getLastSelection()? and !editor.getLastSelection().isEmpty()
|
||||
invisibles = if showInvisibles and not mini then @state.invisibles else {}
|
||||
hasSelection = editor.getSelection()? and !editor.getSelection().isEmpty()
|
||||
style = {}
|
||||
|
||||
if @performedInitialMeasurement
|
||||
@@ -59,13 +60,10 @@ EditorComponent = React.createClass
|
||||
[renderedStartRow, renderedEndRow] = renderedRowRange
|
||||
cursorPixelRects = @getCursorPixelRects(renderedRowRange)
|
||||
|
||||
tokenizedLines = editor.tokenizedLinesForScreenRows(renderedStartRow, renderedEndRow - 1)
|
||||
|
||||
decorations = editor.decorationsForScreenRowRange(renderedStartRow, renderedEndRow)
|
||||
highlightDecorations = @getHighlightDecorations(decorations)
|
||||
lineDecorations = @getLineDecorations(decorations)
|
||||
placeholderText = @props.placeholderText if @props.placeholderText? and editor.isEmpty()
|
||||
visible = @isVisible()
|
||||
|
||||
scrollHeight = editor.getScrollHeight()
|
||||
scrollWidth = editor.getScrollWidth()
|
||||
@@ -109,12 +107,12 @@ EditorComponent = React.createClass
|
||||
|
||||
LinesComponent {
|
||||
ref: 'lines',
|
||||
editor, lineHeightInPixels, defaultCharWidth, tokenizedLines, lineDecorations, highlightDecorations,
|
||||
editor, lineHeightInPixels, defaultCharWidth, lineDecorations, highlightDecorations,
|
||||
showIndentGuide, renderedRowRange, @pendingChanges, scrollTop, scrollLeft,
|
||||
@scrollingVertically, scrollHeight, scrollWidth, mouseWheelScreenRow,
|
||||
visible, scrollViewHeight, @scopedCharacterWidthsChangeCount, lineWidth, @useHardwareAcceleration,
|
||||
@scrollingVertically, scrollHeight, scrollWidth, mouseWheelScreenRow, invisibles,
|
||||
@visible, scrollViewHeight, @scopedCharacterWidthsChangeCount, lineWidth, @useHardwareAcceleration,
|
||||
placeholderText, @performedInitialMeasurement, @backgroundColor, cursorPixelRects,
|
||||
cursorBlinkPeriod, cursorBlinkResumeDelay, mini
|
||||
cursorBlinkPeriod, cursorBlinkResumeDelay
|
||||
}
|
||||
|
||||
ScrollbarComponent
|
||||
@@ -124,11 +122,10 @@ EditorComponent = React.createClass
|
||||
onScroll: @onHorizontalScroll
|
||||
scrollLeft: scrollLeft
|
||||
scrollWidth: scrollWidth
|
||||
visible: horizontallyScrollable
|
||||
visible: horizontallyScrollable and not @refreshingScrollbars and not @measuringScrollbars
|
||||
scrollableInOppositeDirection: verticallyScrollable
|
||||
verticalScrollbarWidth: verticalScrollbarWidth
|
||||
horizontalScrollbarHeight: horizontalScrollbarHeight
|
||||
useHardwareAcceleration: @useHardwareAcceleration
|
||||
|
||||
ScrollbarComponent
|
||||
ref: 'verticalScrollbar'
|
||||
@@ -137,16 +134,15 @@ EditorComponent = React.createClass
|
||||
onScroll: @onVerticalScroll
|
||||
scrollTop: scrollTop
|
||||
scrollHeight: scrollHeight
|
||||
visible: verticallyScrollable
|
||||
visible: verticallyScrollable and not @refreshingScrollbars and not @measuringScrollbars
|
||||
scrollableInOppositeDirection: horizontallyScrollable
|
||||
verticalScrollbarWidth: verticalScrollbarWidth
|
||||
horizontalScrollbarHeight: horizontalScrollbarHeight
|
||||
useHardwareAcceleration: @useHardwareAcceleration
|
||||
|
||||
# Also used to measure the height/width of scrollbars after the initial render
|
||||
ScrollbarCornerComponent
|
||||
ref: 'scrollbarCorner'
|
||||
visible: horizontallyScrollable and verticallyScrollable
|
||||
visible: not @refreshingScrollbars and (@measuringScrollbars or horizontallyScrollable and verticallyScrollable)
|
||||
measuringScrollbars: @measuringScrollbars
|
||||
height: horizontalScrollbarHeight
|
||||
width: verticalScrollbarWidth
|
||||
@@ -174,60 +170,59 @@ EditorComponent = React.createClass
|
||||
componentDidMount: ->
|
||||
{editor} = @props
|
||||
|
||||
@domPollingIntervalId = setInterval(@pollDOM, @domPollingInterval)
|
||||
|
||||
@observeEditor()
|
||||
@listenForDOMEvents()
|
||||
@listenForCommands()
|
||||
|
||||
@subscribe atom.themes.onDidAddStylesheet @onStylesheetsChanged
|
||||
@subscribe atom.themes.onDidUpdateStylesheet @onStylesheetsChanged
|
||||
@subscribe atom.themes.onDidRemoveStylesheet @onStylesheetsChanged
|
||||
unless atom.themes.isInitialLoadComplete()
|
||||
@subscribe atom.themes.onDidReloadAll @onStylesheetsChanged
|
||||
@subscribe atom.themes, 'stylesheet-added stylesheet-removed stylesheet-updated', @onStylesheetsChanged
|
||||
@subscribe scrollbarStyle.changes, @refreshScrollbars
|
||||
|
||||
@domPollingIntervalId = setInterval(@pollDOM, @domPollingInterval)
|
||||
@updateParentViewFocusedClassIfNeeded({})
|
||||
@updateParentViewMiniClassIfNeeded({})
|
||||
@checkForVisibilityChange()
|
||||
if @visible = @isVisible()
|
||||
@performInitialMeasurement()
|
||||
@forceUpdate()
|
||||
|
||||
componentWillUnmount: ->
|
||||
{editor, parentView} = @props
|
||||
|
||||
parentView.trigger 'editor:will-be-removed', [parentView]
|
||||
@props.parentView.trigger 'editor:will-be-removed', [@props.parentView]
|
||||
@unsubscribe()
|
||||
window.removeEventListener 'resize', @requestHeightAndWidthMeasurement
|
||||
clearInterval(@domPollingIntervalId)
|
||||
@domPollingIntervalId = null
|
||||
|
||||
componentWillReceiveProps: (newProps) ->
|
||||
@props.editor.setMini(newProps.mini)
|
||||
componentWillUpdate: ->
|
||||
wasVisible = @visible
|
||||
@visible = @isVisible()
|
||||
@performInitialMeasurement() if @visible and not wasVisible
|
||||
|
||||
componentDidUpdate: (prevProps, prevState) ->
|
||||
cursorMoved = @cursorMoved
|
||||
cursorsMoved = @cursorsMoved
|
||||
selectionChanged = @selectionChanged
|
||||
@pendingChanges.length = 0
|
||||
@cursorMoved = false
|
||||
@cursorsMoved = false
|
||||
@selectionChanged = false
|
||||
@refreshingScrollbars = false
|
||||
|
||||
if @props.editor.isAlive()
|
||||
@updateParentViewFocusedClassIfNeeded(prevState)
|
||||
@updateParentViewMiniClassIfNeeded(prevState)
|
||||
@props.parentView.trigger 'cursor:moved' if cursorMoved
|
||||
@props.parentView.trigger 'cursor:moved' if cursorsMoved
|
||||
@props.parentView.trigger 'selection:changed' if selectionChanged
|
||||
@props.parentView.trigger 'editor:display-updated'
|
||||
|
||||
becameVisible: ->
|
||||
if @performedInitialMeasurement
|
||||
@measureScrollbars() if @measuringScrollbars
|
||||
|
||||
performInitialMeasurement: ->
|
||||
@updatesPaused = true
|
||||
@measureHeightAndWidth()
|
||||
@sampleFontStyling()
|
||||
@sampleBackgroundColors()
|
||||
@measureHeightAndWidth()
|
||||
@measureScrollbars() if @measureScrollbarsWhenShown
|
||||
@measureScrollbars()
|
||||
@measureLineHeightAndDefaultCharWidth() if @measureLineHeightAndDefaultCharWidthWhenShown
|
||||
@remeasureCharacterWidths() if @remeasureCharacterWidthsWhenShown
|
||||
@props.editor.setVisible(true)
|
||||
@performedInitialMeasurement = true
|
||||
@updatesPaused = false
|
||||
@forceUpdate() if @updateRequestedWhilePaused
|
||||
@performedInitialMeasurement = true
|
||||
|
||||
requestUpdate: ->
|
||||
return unless @isMounted()
|
||||
@@ -267,9 +262,9 @@ EditorComponent = React.createClass
|
||||
getHiddenInputPosition: ->
|
||||
{editor} = @props
|
||||
{focused} = @state
|
||||
return {top: 0, left: 0} unless @isMounted() and focused and editor.getLastCursor()?
|
||||
return {top: 0, left: 0} unless @isMounted() and focused and editor.getCursor()?
|
||||
|
||||
{top, left, height, width} = editor.getLastCursor().getPixelRect()
|
||||
{top, left, height, width} = editor.getCursor().getPixelRect()
|
||||
width = 2 if width is 0 # Prevent autoscroll at the end of longest line
|
||||
top -= editor.getScrollTop()
|
||||
left -= editor.getScrollLeft()
|
||||
@@ -313,7 +308,7 @@ EditorComponent = React.createClass
|
||||
if marker.isValid()
|
||||
for decoration in decorations
|
||||
if decoration.isType('gutter') or decoration.isType('line')
|
||||
decorationParams = decoration.getProperties()
|
||||
decorationParams = decoration.getParams()
|
||||
screenRange ?= marker.getScreenRange()
|
||||
headScreenRow ?= marker.getHeadScreenPosition().row
|
||||
startRow = screenRange.start.row
|
||||
@@ -340,7 +335,7 @@ EditorComponent = React.createClass
|
||||
if marker.isValid() and not screenRange.isEmpty()
|
||||
for decoration in decorations
|
||||
if decoration.isType('highlight')
|
||||
decorationParams = decoration.getProperties()
|
||||
decorationParams = decoration.getParams()
|
||||
filteredDecorations[markerId] ?=
|
||||
id: markerId
|
||||
startPixelPosition: editor.pixelPositionForScreenPosition(screenRange.start)
|
||||
@@ -352,16 +347,17 @@ EditorComponent = React.createClass
|
||||
|
||||
observeEditor: ->
|
||||
{editor} = @props
|
||||
@subscribe editor.onDidChange(@onScreenLinesChanged)
|
||||
@subscribe editor.observeCursors(@onCursorAdded)
|
||||
@subscribe editor.observeSelections(@onSelectionAdded)
|
||||
@subscribe editor.observeDecorations(@onDecorationAdded)
|
||||
@subscribe editor.onDidRemoveDecoration(@onDecorationRemoved)
|
||||
@subscribe editor.onDidChangeCharacterWidths(@onCharacterWidthsChanged)
|
||||
@subscribe editor, 'screen-lines-changed', @onScreenLinesChanged
|
||||
@subscribe editor, 'cursors-moved', @onCursorsMoved
|
||||
@subscribe editor, 'selection-removed selection-screen-range-changed', @onSelectionChanged
|
||||
@subscribe editor, 'selection-added', @onSelectionAdded
|
||||
@subscribe editor, 'decoration-added', @onDecorationChanged
|
||||
@subscribe editor, 'decoration-removed', @onDecorationChanged
|
||||
@subscribe editor, 'decoration-changed', @onDecorationChanged
|
||||
@subscribe editor, 'decoration-updated', @onDecorationChanged
|
||||
@subscribe editor, 'character-widths-changed', @onCharacterWidthsChanged
|
||||
@subscribe editor.$scrollTop.changes, @onScrollTopChanged
|
||||
@subscribe editor.$scrollLeft.changes, @requestUpdate
|
||||
@subscribe editor.$verticalScrollbarWidth.changes, @requestUpdate
|
||||
@subscribe editor.$horizontalScrollbarHeight.changes, @requestUpdate
|
||||
@subscribe editor.$height.changes, @requestUpdate
|
||||
@subscribe editor.$width.changes, @requestUpdate
|
||||
@subscribe editor.$defaultCharWidth.changes, @requestUpdate
|
||||
@@ -409,135 +405,125 @@ EditorComponent = React.createClass
|
||||
{parentView, editor, mini} = @props
|
||||
|
||||
@addCommandListeners
|
||||
'core:move-left': -> editor.moveLeft()
|
||||
'core:move-right': -> editor.moveRight()
|
||||
'core:select-left': -> editor.selectLeft()
|
||||
'core:select-right': -> editor.selectRight()
|
||||
'core:select-all': -> editor.selectAll()
|
||||
'core:backspace': -> editor.backspace()
|
||||
'core:delete': -> editor.delete()
|
||||
'core:undo': -> editor.undo()
|
||||
'core:redo': -> editor.redo()
|
||||
'core:cut': -> editor.cutSelectedText()
|
||||
'core:copy': -> editor.copySelectedText()
|
||||
'core:paste': -> editor.pasteText()
|
||||
'editor:move-to-previous-word': -> editor.moveToPreviousWord()
|
||||
'editor:select-word': -> editor.selectWordsContainingCursors()
|
||||
'core:move-left': => editor.moveCursorLeft()
|
||||
'core:move-right': => editor.moveCursorRight()
|
||||
'core:select-left': => editor.selectLeft()
|
||||
'core:select-right': => editor.selectRight()
|
||||
'core:select-all': => editor.selectAll()
|
||||
'core:backspace': => editor.backspace()
|
||||
'core:delete': => editor.delete()
|
||||
'core:undo': => editor.undo()
|
||||
'core:redo': => editor.redo()
|
||||
'core:cut': => editor.cutSelectedText()
|
||||
'core:copy': => editor.copySelectedText()
|
||||
'core:paste': => editor.pasteText()
|
||||
'editor:move-to-previous-word': => editor.moveCursorToPreviousWord()
|
||||
'editor:select-word': => editor.selectWord()
|
||||
'editor:consolidate-selections': @consolidateSelections
|
||||
'editor:delete-to-beginning-of-word': -> editor.deleteToBeginningOfWord()
|
||||
'editor:delete-to-beginning-of-line': -> editor.deleteToBeginningOfLine()
|
||||
'editor:delete-to-end-of-line': -> editor.deleteToEndOfLine()
|
||||
'editor:delete-to-end-of-word': -> editor.deleteToEndOfWord()
|
||||
'editor:delete-line': -> editor.deleteLine()
|
||||
'editor:cut-to-end-of-line': -> editor.cutToEndOfLine()
|
||||
'editor:move-to-beginning-of-next-paragraph': -> editor.moveToBeginningOfNextParagraph()
|
||||
'editor:move-to-beginning-of-previous-paragraph': -> editor.moveToBeginningOfPreviousParagraph()
|
||||
'editor:move-to-beginning-of-screen-line': -> editor.moveToBeginningOfScreenLine()
|
||||
'editor:move-to-beginning-of-line': -> editor.moveToBeginningOfLine()
|
||||
'editor:move-to-end-of-screen-line': -> editor.moveToEndOfScreenLine()
|
||||
'editor:move-to-end-of-line': -> editor.moveToEndOfLine()
|
||||
'editor:move-to-first-character-of-line': -> editor.moveToFirstCharacterOfLine()
|
||||
'editor:move-to-beginning-of-word': -> editor.moveToBeginningOfWord()
|
||||
'editor:move-to-end-of-word': -> editor.moveToEndOfWord()
|
||||
'editor:move-to-beginning-of-next-word': -> editor.moveToBeginningOfNextWord()
|
||||
'editor:move-to-previous-word-boundary': -> editor.moveToPreviousWordBoundary()
|
||||
'editor:move-to-next-word-boundary': -> editor.moveToNextWordBoundary()
|
||||
'editor:select-to-beginning-of-next-paragraph': -> editor.selectToBeginningOfNextParagraph()
|
||||
'editor:select-to-beginning-of-previous-paragraph': -> editor.selectToBeginningOfPreviousParagraph()
|
||||
'editor:select-to-end-of-line': -> editor.selectToEndOfLine()
|
||||
'editor:select-to-beginning-of-line': -> editor.selectToBeginningOfLine()
|
||||
'editor:select-to-end-of-word': -> editor.selectToEndOfWord()
|
||||
'editor:select-to-beginning-of-word': -> editor.selectToBeginningOfWord()
|
||||
'editor:select-to-beginning-of-next-word': -> editor.selectToBeginningOfNextWord()
|
||||
'editor:select-to-next-word-boundary': -> editor.selectToNextWordBoundary()
|
||||
'editor:select-to-previous-word-boundary': -> editor.selectToPreviousWordBoundary()
|
||||
'editor:select-to-first-character-of-line': -> editor.selectToFirstCharacterOfLine()
|
||||
'editor:select-line': -> editor.selectLinesContainingCursors()
|
||||
'editor:transpose': -> editor.transpose()
|
||||
'editor:upper-case': -> editor.upperCase()
|
||||
'editor:lower-case': -> editor.lowerCase()
|
||||
'editor:delete-to-beginning-of-word': => editor.deleteToBeginningOfWord()
|
||||
'editor:delete-to-beginning-of-line': => editor.deleteToBeginningOfLine()
|
||||
'editor:delete-to-end-of-line': => editor.deleteToEndOfLine()
|
||||
'editor:delete-to-end-of-word': => editor.deleteToEndOfWord()
|
||||
'editor:delete-line': => editor.deleteLine()
|
||||
'editor:cut-to-end-of-line': => editor.cutToEndOfLine()
|
||||
'editor:move-to-beginning-of-next-paragraph': => editor.moveCursorToBeginningOfNextParagraph()
|
||||
'editor:move-to-beginning-of-previous-paragraph': => editor.moveCursorToBeginningOfPreviousParagraph()
|
||||
'editor:move-to-beginning-of-screen-line': => editor.moveCursorToBeginningOfScreenLine()
|
||||
'editor:move-to-beginning-of-line': => editor.moveCursorToBeginningOfLine()
|
||||
'editor:move-to-end-of-screen-line': => editor.moveCursorToEndOfScreenLine()
|
||||
'editor:move-to-end-of-line': => editor.moveCursorToEndOfLine()
|
||||
'editor:move-to-first-character-of-line': => editor.moveCursorToFirstCharacterOfLine()
|
||||
'editor:move-to-beginning-of-word': => editor.moveCursorToBeginningOfWord()
|
||||
'editor:move-to-end-of-word': => editor.moveCursorToEndOfWord()
|
||||
'editor:move-to-beginning-of-next-word': => editor.moveCursorToBeginningOfNextWord()
|
||||
'editor:move-to-previous-word-boundary': => editor.moveCursorToPreviousWordBoundary()
|
||||
'editor:move-to-next-word-boundary': => editor.moveCursorToNextWordBoundary()
|
||||
'editor:select-to-beginning-of-next-paragraph': => editor.selectToBeginningOfNextParagraph()
|
||||
'editor:select-to-beginning-of-previous-paragraph': => editor.selectToBeginningOfPreviousParagraph()
|
||||
'editor:select-to-end-of-line': => editor.selectToEndOfLine()
|
||||
'editor:select-to-beginning-of-line': => editor.selectToBeginningOfLine()
|
||||
'editor:select-to-end-of-word': => editor.selectToEndOfWord()
|
||||
'editor:select-to-beginning-of-word': => editor.selectToBeginningOfWord()
|
||||
'editor:select-to-beginning-of-next-word': => editor.selectToBeginningOfNextWord()
|
||||
'editor:select-to-next-word-boundary': => editor.selectToNextWordBoundary()
|
||||
'editor:select-to-previous-word-boundary': => editor.selectToPreviousWordBoundary()
|
||||
'editor:select-to-first-character-of-line': => editor.selectToFirstCharacterOfLine()
|
||||
'editor:select-line': => editor.selectLine()
|
||||
'editor:transpose': => editor.transpose()
|
||||
'editor:upper-case': => editor.upperCase()
|
||||
'editor:lower-case': => editor.lowerCase()
|
||||
|
||||
unless mini
|
||||
@addCommandListeners
|
||||
'core:move-up': -> editor.moveUp()
|
||||
'core:move-down': -> editor.moveDown()
|
||||
'core:move-to-top': -> editor.moveToTop()
|
||||
'core:move-to-bottom': -> editor.moveToBottom()
|
||||
'core:page-up': -> editor.pageUp()
|
||||
'core:page-down': -> editor.pageDown()
|
||||
'core:select-up': -> editor.selectUp()
|
||||
'core:select-down': -> editor.selectDown()
|
||||
'core:select-to-top': -> editor.selectToTop()
|
||||
'core:select-to-bottom': -> editor.selectToBottom()
|
||||
'core:select-page-up': -> editor.selectPageUp()
|
||||
'core:select-page-down': -> editor.selectPageDown()
|
||||
'editor:indent': -> editor.indent()
|
||||
'editor:auto-indent': -> editor.autoIndentSelectedRows()
|
||||
'editor:indent-selected-rows': -> editor.indentSelectedRows()
|
||||
'editor:outdent-selected-rows': -> editor.outdentSelectedRows()
|
||||
'editor:newline': -> editor.insertNewline()
|
||||
'editor:newline-below': -> editor.insertNewlineBelow()
|
||||
'editor:newline-above': -> editor.insertNewlineAbove()
|
||||
'editor:add-selection-below': -> editor.addSelectionBelow()
|
||||
'editor:add-selection-above': -> editor.addSelectionAbove()
|
||||
'editor:split-selections-into-lines': -> editor.splitSelectionsIntoLines()
|
||||
'editor:toggle-soft-tabs': -> editor.toggleSoftTabs()
|
||||
'editor:toggle-soft-wrap': -> editor.toggleSoftWrapped()
|
||||
'editor:fold-all': -> editor.foldAll()
|
||||
'editor:unfold-all': -> editor.unfoldAll()
|
||||
'editor:fold-current-row': -> editor.foldCurrentRow()
|
||||
'editor:unfold-current-row': -> editor.unfoldCurrentRow()
|
||||
'editor:fold-selection': -> editor.foldSelectedLines()
|
||||
'editor:fold-at-indent-level-1': -> editor.foldAllAtIndentLevel(0)
|
||||
'editor:fold-at-indent-level-2': -> editor.foldAllAtIndentLevel(1)
|
||||
'editor:fold-at-indent-level-3': -> editor.foldAllAtIndentLevel(2)
|
||||
'editor:fold-at-indent-level-4': -> editor.foldAllAtIndentLevel(3)
|
||||
'editor:fold-at-indent-level-5': -> editor.foldAllAtIndentLevel(4)
|
||||
'editor:fold-at-indent-level-6': -> editor.foldAllAtIndentLevel(5)
|
||||
'editor:fold-at-indent-level-7': -> editor.foldAllAtIndentLevel(6)
|
||||
'editor:fold-at-indent-level-8': -> editor.foldAllAtIndentLevel(7)
|
||||
'editor:fold-at-indent-level-9': -> editor.foldAllAtIndentLevel(8)
|
||||
'editor:toggle-line-comments': -> editor.toggleLineCommentsInSelection()
|
||||
'editor:log-cursor-scope': -> editor.logCursorScope()
|
||||
'editor:checkout-head-revision': -> atom.project.getRepo()?.checkoutHeadForEditor(editor)
|
||||
'editor:copy-path': -> editor.copyPathToClipboard()
|
||||
'editor:move-line-up': -> editor.moveLineUp()
|
||||
'editor:move-line-down': -> editor.moveLineDown()
|
||||
'editor:duplicate-lines': -> editor.duplicateLines()
|
||||
'editor:join-lines': -> editor.joinLines()
|
||||
'editor:toggle-indent-guide': -> atom.config.toggle('editor.showIndentGuide')
|
||||
'editor:toggle-line-numbers': -> atom.config.toggle('editor.showLineNumbers')
|
||||
'editor:scroll-to-cursor': -> editor.scrollToCursorPosition()
|
||||
'core:move-up': => editor.moveCursorUp()
|
||||
'core:move-down': => editor.moveCursorDown()
|
||||
'core:move-to-top': => editor.moveCursorToTop()
|
||||
'core:move-to-bottom': => editor.moveCursorToBottom()
|
||||
'core:page-up': => editor.pageUp()
|
||||
'core:page-down': => editor.pageDown()
|
||||
'core:select-up': => editor.selectUp()
|
||||
'core:select-down': => editor.selectDown()
|
||||
'core:select-to-top': => editor.selectToTop()
|
||||
'core:select-to-bottom': => editor.selectToBottom()
|
||||
'core:select-page-up': => editor.selectPageUp()
|
||||
'core:select-page-down': => editor.selectPageDown()
|
||||
'editor:indent': => editor.indent()
|
||||
'editor:auto-indent': => editor.autoIndentSelectedRows()
|
||||
'editor:indent-selected-rows': => editor.indentSelectedRows()
|
||||
'editor:outdent-selected-rows': => editor.outdentSelectedRows()
|
||||
'editor:newline': => editor.insertNewline()
|
||||
'editor:newline-below': => editor.insertNewlineBelow()
|
||||
'editor:newline-above': => editor.insertNewlineAbove()
|
||||
'editor:add-selection-below': => editor.addSelectionBelow()
|
||||
'editor:add-selection-above': => editor.addSelectionAbove()
|
||||
'editor:split-selections-into-lines': => editor.splitSelectionsIntoLines()
|
||||
'editor:toggle-soft-tabs': => editor.toggleSoftTabs()
|
||||
'editor:toggle-soft-wrap': => editor.toggleSoftWrap()
|
||||
'editor:fold-all': => editor.foldAll()
|
||||
'editor:unfold-all': => editor.unfoldAll()
|
||||
'editor:fold-current-row': => editor.foldCurrentRow()
|
||||
'editor:unfold-current-row': => editor.unfoldCurrentRow()
|
||||
'editor:fold-selection': => editor.foldSelectedLines()
|
||||
'editor:fold-at-indent-level-1': => editor.foldAllAtIndentLevel(0)
|
||||
'editor:fold-at-indent-level-2': => editor.foldAllAtIndentLevel(1)
|
||||
'editor:fold-at-indent-level-3': => editor.foldAllAtIndentLevel(2)
|
||||
'editor:fold-at-indent-level-4': => editor.foldAllAtIndentLevel(3)
|
||||
'editor:fold-at-indent-level-5': => editor.foldAllAtIndentLevel(4)
|
||||
'editor:fold-at-indent-level-6': => editor.foldAllAtIndentLevel(5)
|
||||
'editor:fold-at-indent-level-7': => editor.foldAllAtIndentLevel(6)
|
||||
'editor:fold-at-indent-level-8': => editor.foldAllAtIndentLevel(7)
|
||||
'editor:fold-at-indent-level-9': => editor.foldAllAtIndentLevel(8)
|
||||
'editor:toggle-line-comments': => editor.toggleLineCommentsInSelection()
|
||||
'editor:log-cursor-scope': => editor.logCursorScope()
|
||||
'editor:checkout-head-revision': => editor.checkoutHead()
|
||||
'editor:copy-path': => editor.copyPathToClipboard()
|
||||
'editor:move-line-up': => editor.moveLineUp()
|
||||
'editor:move-line-down': => editor.moveLineDown()
|
||||
'editor:duplicate-lines': => editor.duplicateLines()
|
||||
'editor:join-lines': => editor.joinLines()
|
||||
'editor:toggle-indent-guide': => atom.config.toggle('editor.showIndentGuide')
|
||||
'editor:toggle-line-numbers': => atom.config.toggle('editor.showLineNumbers')
|
||||
'editor:scroll-to-cursor': => editor.scrollToCursorPosition()
|
||||
'benchmark:scroll': @runScrollBenchmark
|
||||
|
||||
addCommandListeners: (listenersByCommandName) ->
|
||||
{parentView} = @props
|
||||
|
||||
addListener = (command, listener) ->
|
||||
parentView.command command, (event) ->
|
||||
event.stopPropagation()
|
||||
listener(event)
|
||||
|
||||
addListener(command, listener) for command, listener of listenersByCommandName
|
||||
|
||||
return
|
||||
for command, listener of listenersByCommandName
|
||||
parentView.command command, listener
|
||||
|
||||
observeConfig: ->
|
||||
@subscribe atom.config.observe 'editor.showIndentGuide', @setShowIndentGuide
|
||||
@subscribe atom.config.observe 'editor.invisibles', @setInvisibles
|
||||
@subscribe atom.config.observe 'editor.showInvisibles', @setShowInvisibles
|
||||
@subscribe atom.config.observe 'editor.showLineNumbers', @setShowLineNumbers
|
||||
@subscribe atom.config.observe 'editor.scrollSensitivity', @setScrollSensitivity
|
||||
@subscribe atom.config.observe 'editor.useHardwareAcceleration', @setUseHardwareAcceleration
|
||||
|
||||
onFocus: ->
|
||||
@refs.input.focus() if @isMounted()
|
||||
@refs.input.focus()
|
||||
|
||||
onTextInput: (event) ->
|
||||
event.stopPropagation()
|
||||
|
||||
# If we prevent the insertion of a space character, then the browser
|
||||
# interprets the spacebar keypress as a page-down command.
|
||||
event.preventDefault() unless event.data is ' '
|
||||
|
||||
return unless @isInputEnabled()
|
||||
|
||||
{editor} = @props
|
||||
@@ -550,8 +536,12 @@ EditorComponent = React.createClass
|
||||
selectedLength = inputNode.selectionEnd - inputNode.selectionStart
|
||||
editor.selectLeft() if selectedLength is 1
|
||||
|
||||
inputNode.value = event.data if editor.insertText(event.data)
|
||||
editor.insertText(event.data)
|
||||
inputNode.value = event.data
|
||||
|
||||
# If we prevent the insertion of a space character, then the browser
|
||||
# interprets the spacebar keypress as a page-down command.
|
||||
event.preventDefault() unless event.data is ' '
|
||||
|
||||
onInputFocused: ->
|
||||
@setState(focused: true)
|
||||
@@ -612,17 +602,9 @@ EditorComponent = React.createClass
|
||||
|
||||
onMouseDown: (event) ->
|
||||
return unless event.button is 0 # only handle the left mouse button
|
||||
return if event.target?.classList.contains('horizontal-scrollbar')
|
||||
|
||||
{editor} = @props
|
||||
{detail, shiftKey, metaKey, ctrlKey} = event
|
||||
|
||||
# CTRL+click brings up the context menu on OSX, so don't handle those either
|
||||
return if ctrlKey and process.platform is 'darwin'
|
||||
|
||||
# Prevent focusout event on hidden input if editor is already focused
|
||||
event.preventDefault() if @state.focused
|
||||
|
||||
screenPosition = @screenPositionForMouseEvent(event)
|
||||
|
||||
if event.target?.classList.contains('fold-marker')
|
||||
@@ -649,12 +631,8 @@ EditorComponent = React.createClass
|
||||
onGutterMouseDown: (event) ->
|
||||
return unless event.button is 0 # only handle the left mouse button
|
||||
|
||||
{shiftKey, metaKey, ctrlKey} = event
|
||||
|
||||
if shiftKey
|
||||
if event.shiftKey
|
||||
@onGutterShiftClick(event)
|
||||
else if metaKey or (ctrlKey and process.platform isnt 'darwin')
|
||||
@onGutterMetaClick(event)
|
||||
else
|
||||
@onGutterClick(event)
|
||||
|
||||
@@ -662,43 +640,19 @@ EditorComponent = React.createClass
|
||||
{editor} = @props
|
||||
clickedRow = @screenPositionForMouseEvent(event).row
|
||||
|
||||
editor.setSelectedScreenRange([[clickedRow, 0], [clickedRow + 1, 0]], preserveFolds: true)
|
||||
editor.setCursorScreenPosition([clickedRow, 0])
|
||||
|
||||
@handleDragUntilMouseUp event, (screenPosition) ->
|
||||
dragRow = screenPosition.row
|
||||
if dragRow < clickedRow # dragging up
|
||||
editor.setSelectedScreenRange([[dragRow, 0], [clickedRow + 1, 0]], preserveFolds: true)
|
||||
editor.setSelectedScreenRange([[dragRow, 0], [clickedRow + 1, 0]])
|
||||
else
|
||||
editor.setSelectedScreenRange([[clickedRow, 0], [dragRow + 1, 0]], preserveFolds: true)
|
||||
|
||||
onGutterMetaClick: (event) ->
|
||||
{editor} = @props
|
||||
clickedRow = @screenPositionForMouseEvent(event).row
|
||||
|
||||
bufferRange = editor.bufferRangeForScreenRange([[clickedRow, 0], [clickedRow + 1, 0]])
|
||||
rowSelection = editor.addSelectionForBufferRange(bufferRange, preserveFolds: true)
|
||||
|
||||
@handleDragUntilMouseUp event, (screenPosition) ->
|
||||
dragRow = screenPosition.row
|
||||
|
||||
if dragRow < clickedRow # dragging up
|
||||
rowSelection.setScreenRange([[dragRow, 0], [clickedRow + 1, 0]], preserveFolds: true)
|
||||
else
|
||||
rowSelection.setScreenRange([[clickedRow, 0], [dragRow + 1, 0]], preserveFolds: true)
|
||||
|
||||
# After updating the selected screen range, merge overlapping selections
|
||||
editor.mergeIntersectingSelections(preserveFolds: true)
|
||||
|
||||
# The merge process will possibly destroy the current selection because
|
||||
# it will be merged into another one. Therefore, we need to obtain a
|
||||
# reference to the new selection that contains the originally selected row
|
||||
rowSelection = _.find editor.getSelections(), (selection) ->
|
||||
selection.intersectsBufferRange(bufferRange)
|
||||
editor.setSelectedScreenRange([[clickedRow, 0], [dragRow + 1, 0]])
|
||||
|
||||
onGutterShiftClick: (event) ->
|
||||
{editor} = @props
|
||||
clickedRow = @screenPositionForMouseEvent(event).row
|
||||
tailPosition = editor.getLastSelection().getTailScreenPosition()
|
||||
tailPosition = editor.getSelection().getTailScreenPosition()
|
||||
|
||||
if clickedRow < tailPosition.row
|
||||
editor.selectToScreenPosition([clickedRow, 0])
|
||||
@@ -708,15 +662,14 @@ EditorComponent = React.createClass
|
||||
@handleDragUntilMouseUp event, (screenPosition) ->
|
||||
dragRow = screenPosition.row
|
||||
if dragRow < tailPosition.row # dragging up
|
||||
editor.setSelectedScreenRange([[dragRow, 0], tailPosition], preserveFolds: true)
|
||||
editor.setSelectedScreenRange([[dragRow, 0], tailPosition])
|
||||
else
|
||||
editor.setSelectedScreenRange([tailPosition, [dragRow + 1, 0]], preserveFolds: true)
|
||||
editor.setSelectedScreenRange([tailPosition, [dragRow + 1, 0]])
|
||||
|
||||
onStylesheetsChanged: (stylesheet) ->
|
||||
return unless @performedInitialMeasurement
|
||||
return unless atom.themes.isInitialLoadComplete()
|
||||
|
||||
@refreshScrollbars() if not stylesheet? or @containsScrollbarSelector(stylesheet)
|
||||
@refreshScrollbars() if @containsScrollbarSelector(stylesheet)
|
||||
@sampleFontStyling()
|
||||
@sampleBackgroundColors()
|
||||
@remeasureCharacterWidths()
|
||||
@@ -726,24 +679,19 @@ EditorComponent = React.createClass
|
||||
@pendingChanges.push(change)
|
||||
@requestUpdate() if editor.intersectsVisibleRowRange(change.start, change.end + 1) # TODO: Use closed-open intervals for change events
|
||||
|
||||
onSelectionAdded: (selection) ->
|
||||
{editor} = @props
|
||||
|
||||
@subscribe selection.onDidChangeRange => @onSelectionChanged(selection)
|
||||
@subscribe selection.onDidDestroy =>
|
||||
@onSelectionChanged(selection)
|
||||
@unsubscribe(selection)
|
||||
|
||||
if editor.selectionIntersectsVisibleRowRange(selection)
|
||||
@selectionChanged = true
|
||||
@requestUpdate()
|
||||
|
||||
onSelectionChanged: (selection) ->
|
||||
{editor} = @props
|
||||
if editor.selectionIntersectsVisibleRowRange(selection)
|
||||
@selectionChanged = true
|
||||
@requestUpdate()
|
||||
|
||||
onSelectionAdded: (selection) ->
|
||||
{editor} = @props
|
||||
if editor.selectionIntersectsVisibleRowRange(selection)
|
||||
@selectionChanged = true
|
||||
@selectionAdded = true
|
||||
@requestUpdate()
|
||||
|
||||
onScrollTopChanged: ->
|
||||
@scrollingVertically = true
|
||||
@requestUpdate()
|
||||
@@ -759,24 +707,13 @@ EditorComponent = React.createClass
|
||||
|
||||
onStoppedScrollingAfterDelay: null # created lazily
|
||||
|
||||
onCursorAdded: (cursor) ->
|
||||
@subscribe cursor.onDidChangePosition @onCursorMoved
|
||||
|
||||
onCursorMoved: ->
|
||||
@cursorMoved = true
|
||||
@requestUpdate()
|
||||
|
||||
onDecorationAdded: (decoration) ->
|
||||
@subscribe decoration.onDidChangeProperties(@onDecorationChanged)
|
||||
@subscribe decoration.getMarker().onDidChange(@onDecorationChanged)
|
||||
onCursorsMoved: ->
|
||||
@cursorsMoved = true
|
||||
@requestUpdate()
|
||||
|
||||
onDecorationChanged: ->
|
||||
@requestUpdate()
|
||||
|
||||
onDecorationRemoved: ->
|
||||
@requestUpdate()
|
||||
|
||||
onCharacterWidthsChanged: (@scopedCharacterWidthsChangeCount) ->
|
||||
@requestUpdate()
|
||||
|
||||
@@ -829,20 +766,15 @@ EditorComponent = React.createClass
|
||||
pollDOM: ->
|
||||
return if @domPollingPaused or @updateRequested or not @isMounted()
|
||||
|
||||
unless @checkForVisibilityChange()
|
||||
@sampleBackgroundColors()
|
||||
@measureHeightAndWidth()
|
||||
@sampleFontStyling()
|
||||
|
||||
checkForVisibilityChange: ->
|
||||
if @isVisible()
|
||||
if @wasVisible
|
||||
false
|
||||
wasVisible = @visible
|
||||
if @visible = @isVisible()
|
||||
if wasVisible
|
||||
@measureHeightAndWidth()
|
||||
@sampleFontStyling()
|
||||
@sampleBackgroundColors()
|
||||
else
|
||||
@becameVisible()
|
||||
@wasVisible = true
|
||||
else
|
||||
@wasVisible = false
|
||||
@performInitialMeasurement()
|
||||
@forceUpdate()
|
||||
|
||||
requestHeightAndWidthMeasurement: ->
|
||||
return if @heightAndWidthMeasurementRequested
|
||||
@@ -868,7 +800,7 @@ EditorComponent = React.createClass
|
||||
if position is 'absolute' or height
|
||||
if @autoHeight
|
||||
@autoHeight = false
|
||||
@forceUpdate() unless @updatesPaused
|
||||
@forceUpdate()
|
||||
|
||||
clientHeight = scrollViewNode.clientHeight
|
||||
editor.setHeight(clientHeight) if clientHeight > 0
|
||||
@@ -910,36 +842,30 @@ EditorComponent = React.createClass
|
||||
@requestUpdate() unless suppressUpdate
|
||||
|
||||
measureLineHeightAndDefaultCharWidth: ->
|
||||
if @isVisible()
|
||||
if @visible
|
||||
@measureLineHeightAndDefaultCharWidthWhenShown = false
|
||||
@refs.lines.measureLineHeightAndDefaultCharWidth()
|
||||
else
|
||||
@measureLineHeightAndDefaultCharWidthWhenShown = true
|
||||
|
||||
remeasureCharacterWidths: ->
|
||||
if @isVisible()
|
||||
if @visible
|
||||
@remeasureCharacterWidthsWhenShown = false
|
||||
@refs.lines.remeasureCharacterWidths()
|
||||
else
|
||||
@remeasureCharacterWidthsWhenShown = true
|
||||
|
||||
measureScrollbars: ->
|
||||
@measureScrollbarsWhenShown = false
|
||||
return unless @visible
|
||||
@measuringScrollbars = false
|
||||
|
||||
{editor} = @props
|
||||
cornerNode = @refs.scrollbarCorner.getDOMNode()
|
||||
originalDisplayValue = cornerNode.style.display
|
||||
|
||||
cornerNode.style.display = 'block'
|
||||
|
||||
width = (cornerNode.offsetWidth - cornerNode.clientWidth) or 15
|
||||
height = (cornerNode.offsetHeight - cornerNode.clientHeight) or 15
|
||||
|
||||
scrollbarCornerNode = @refs.scrollbarCorner.getDOMNode()
|
||||
width = (scrollbarCornerNode.offsetWidth - scrollbarCornerNode.clientWidth) or 15
|
||||
height = (scrollbarCornerNode.offsetHeight - scrollbarCornerNode.clientHeight) or 15
|
||||
editor.setVerticalScrollbarWidth(width)
|
||||
editor.setHorizontalScrollbarHeight(height)
|
||||
|
||||
cornerNode.style.display = originalDisplayValue
|
||||
|
||||
containsScrollbarSelector: (stylesheet) ->
|
||||
for rule in stylesheet.cssRules
|
||||
if rule.selectorText?.indexOf('scrollbar') > -1
|
||||
@@ -947,39 +873,24 @@ EditorComponent = React.createClass
|
||||
false
|
||||
|
||||
refreshScrollbars: ->
|
||||
if @isVisible()
|
||||
@measureScrollbarsWhenShown = false
|
||||
else
|
||||
@measureScrollbarsWhenShown = true
|
||||
return
|
||||
# Believe it or not, proper handling of changes to scrollbar styles requires
|
||||
# three DOM updates.
|
||||
|
||||
{verticalScrollbar, horizontalScrollbar, scrollbarCorner} = @refs
|
||||
# Scrollbar style changes won't apply to scrollbars that are already
|
||||
# visible, so first we need to hide scrollbars so we can redisplay them and
|
||||
# force Chromium to apply updates.
|
||||
@refreshingScrollbars = true
|
||||
@forceUpdate()
|
||||
|
||||
verticalNode = verticalScrollbar.getDOMNode()
|
||||
horizontalNode = horizontalScrollbar.getDOMNode()
|
||||
cornerNode = scrollbarCorner.getDOMNode()
|
||||
# Next, we display only the scrollbar corner so we can measure the new
|
||||
# scrollbar dimensions. The ::measuringScrollbars property will be set back
|
||||
# to false after the scrollbars are measured.
|
||||
@measuringScrollbars = true
|
||||
@forceUpdate()
|
||||
|
||||
originalVerticalDisplayValue = verticalNode.style.display
|
||||
originalHorizontalDisplayValue = horizontalNode.style.display
|
||||
originalCornerDisplayValue = cornerNode.style.display
|
||||
|
||||
# First, hide all scrollbars in case they are visible so they take on new
|
||||
# styles when they are shown again.
|
||||
verticalNode.style.display = 'none'
|
||||
horizontalNode.style.display = 'none'
|
||||
cornerNode.style.display = 'none'
|
||||
|
||||
# Force a reflow
|
||||
cornerNode.offsetWidth
|
||||
|
||||
# Now measure the new scrollbar dimensions
|
||||
@measureScrollbars()
|
||||
|
||||
# Now restore the display value for all scrollbars, since they were
|
||||
# previously hidden
|
||||
verticalNode.style.display = originalVerticalDisplayValue
|
||||
horizontalNode.style.display = originalHorizontalDisplayValue
|
||||
cornerNode.style.display = originalCornerDisplayValue
|
||||
# Finally, we restore the scrollbars based on the newly-measured dimensions
|
||||
# if the editor's content and dimensions require them to be visible.
|
||||
@forceUpdate()
|
||||
|
||||
clearMouseWheelScreenRow: ->
|
||||
if @mouseWheelScreenRow?
|
||||
@@ -1023,14 +934,24 @@ EditorComponent = React.createClass
|
||||
setShowIndentGuide: (showIndentGuide) ->
|
||||
@setState({showIndentGuide})
|
||||
|
||||
# Deprecated
|
||||
# Public: Defines which characters are invisible.
|
||||
#
|
||||
# invisibles - An {Object} defining the invisible characters:
|
||||
# :eol - The end of line invisible {String} (default: `\u00ac`).
|
||||
# :space - The space invisible {String} (default: `\u00b7`).
|
||||
# :tab - The tab invisible {String} (default: `\u00bb`).
|
||||
# :cr - The carriage return invisible {String} (default: `\u00a4`).
|
||||
setInvisibles: (invisibles={}) ->
|
||||
grim.deprecate "Use config.set('editor.invisibles', invisibles) instead"
|
||||
atom.config.set('editor.invisibles', invisibles)
|
||||
defaults invisibles,
|
||||
eol: '\u00ac'
|
||||
space: '\u00b7'
|
||||
tab: '\u00bb'
|
||||
cr: '\u00a4'
|
||||
|
||||
@setState({invisibles})
|
||||
|
||||
# Deprecated
|
||||
setShowInvisibles: (showInvisibles) ->
|
||||
atom.config.set('editor.showInvisibles', showInvisibles)
|
||||
@setState({showInvisibles})
|
||||
|
||||
setShowLineNumbers: (showLineNumbers) ->
|
||||
@setState({showLineNumbers})
|
||||
|
||||
+1527
-304
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
+1486
-2203
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
+2
-2
@@ -14,8 +14,8 @@ class Fold
|
||||
@id = @marker.id
|
||||
@displayBuffer.foldsByMarkerId[@marker.id] = this
|
||||
@updateDisplayBuffer()
|
||||
@marker.onDidDestroy => @destroyed()
|
||||
@marker.onDidChange ({isValid}) => @destroy() unless isValid
|
||||
@marker.on 'destroyed', => @destroyed()
|
||||
@marker.on 'changed', ({isValid}) => @destroy() unless isValid
|
||||
|
||||
# Returns whether this fold is contained within another fold
|
||||
isInsideLargerFold: ->
|
||||
|
||||
@@ -1,16 +1,13 @@
|
||||
{basename, join} = require 'path'
|
||||
{join} = require 'path'
|
||||
|
||||
_ = require 'underscore-plus'
|
||||
{Subscriber} = require 'emissary'
|
||||
EmitterMixin = require('emissary').Emitter
|
||||
{Emitter} = require 'event-kit'
|
||||
{Emitter, Subscriber} = require 'emissary'
|
||||
fs = require 'fs-plus'
|
||||
GitUtils = require 'git-utils'
|
||||
{deprecate} = require 'grim'
|
||||
|
||||
Task = require './task'
|
||||
|
||||
# Extended: Represents the underlying git operations performed by Atom.
|
||||
# Public: Represents the underlying git operations performed by Atom.
|
||||
#
|
||||
# This class shouldn't be instantiated directly but instead by accessing the
|
||||
# `atom.project` global and calling `getRepo()`. Note that this will only be
|
||||
@@ -28,25 +25,38 @@ Task = require './task'
|
||||
# repo.getShortHead('vendor/path/to/a/submodule') # 'dead1234'
|
||||
# ```
|
||||
#
|
||||
# ## Examples
|
||||
# ## Example
|
||||
#
|
||||
# ### Logging the URL of the origin remote
|
||||
#
|
||||
# ```coffee
|
||||
# ```coffeescript
|
||||
# git = atom.project.getRepo()
|
||||
# console.log git.getOriginUrl()
|
||||
# ```
|
||||
#
|
||||
# ### Requiring in packages
|
||||
# ## Requiring in packages
|
||||
#
|
||||
# ```coffee
|
||||
# {GitRepository} = require 'atom'
|
||||
# {Git} = require 'atom'
|
||||
# ```
|
||||
module.exports =
|
||||
class GitRepository
|
||||
EmitterMixin.includeInto(this)
|
||||
class Git
|
||||
Emitter.includeInto(this)
|
||||
Subscriber.includeInto(this)
|
||||
|
||||
# Public: Creates a new Git instance.
|
||||
#
|
||||
# path - The path to the Git repository to open.
|
||||
# options - An object with the following keys (default: {}):
|
||||
# :refreshOnWindowFocus - `true` to refresh the index and statuses when the
|
||||
# window is focused.
|
||||
#
|
||||
# Returns a Git instance or null if the repository could not be opened.
|
||||
@open: (path, options) ->
|
||||
return null unless path
|
||||
try
|
||||
new Git(path, options)
|
||||
catch
|
||||
null
|
||||
|
||||
@exists: (path) ->
|
||||
if git = @open(path)
|
||||
git.destroy()
|
||||
@@ -54,27 +64,7 @@ class GitRepository
|
||||
else
|
||||
false
|
||||
|
||||
###
|
||||
Section: Construction and Destruction
|
||||
###
|
||||
|
||||
# Public: Creates a new GitRepository instance.
|
||||
#
|
||||
# * `path` The {String} path to the Git repository to open.
|
||||
# * `options` An optinal {Object} with the following keys:
|
||||
# * `refreshOnWindowFocus` A {Boolean}, `true` to refresh the index and
|
||||
# statuses when the window is focused.
|
||||
#
|
||||
# Returns a {GitRepository} instance or `null` if the repository could not be opened.
|
||||
@open: (path, options) ->
|
||||
return null unless path
|
||||
try
|
||||
new GitRepository(path, options)
|
||||
catch
|
||||
null
|
||||
|
||||
constructor: (path, options={}) ->
|
||||
@emitter = new Emitter
|
||||
@repo = GitUtils.open(path)
|
||||
unless @repo?
|
||||
throw new Error("No Git repository found searching path: #{path}")
|
||||
@@ -96,10 +86,15 @@ class GitRepository
|
||||
if @project?
|
||||
@subscribe @project.eachBuffer (buffer) => @subscribeToBuffer(buffer)
|
||||
|
||||
# Public: Destroy this {GitRepository} object.
|
||||
#
|
||||
# This destroys any tasks and subscriptions and releases the underlying
|
||||
# libgit2 repository handle.
|
||||
# Subscribes to buffer events.
|
||||
subscribeToBuffer: (buffer) ->
|
||||
@subscribe buffer, 'saved reloaded path-changed', =>
|
||||
if path = buffer.getPath()
|
||||
@getPathStatus(path)
|
||||
@subscribe buffer, 'destroyed', => @unsubscribe(buffer)
|
||||
|
||||
# Public: Destroy this `Git` object. This destroys any tasks and subscriptions
|
||||
# and releases the underlying libgit2 repository handle.
|
||||
destroy: ->
|
||||
if @statusTask?
|
||||
@statusTask.terminate()
|
||||
@@ -111,48 +106,16 @@ class GitRepository
|
||||
|
||||
@unsubscribe()
|
||||
|
||||
###
|
||||
Section: Event Subscription
|
||||
###
|
||||
# Returns the corresponding {Repository}
|
||||
getRepo: (path) ->
|
||||
if @repo?
|
||||
@repo.submoduleForPath(path) ? @repo
|
||||
else
|
||||
throw new Error("Repository has been destroyed")
|
||||
|
||||
# Public: Invoke the given callback when a specific file's status has
|
||||
# changed. When a file is updated, reloaded, etc, and the status changes, this
|
||||
# will be fired.
|
||||
#
|
||||
# * `callback` {Function}
|
||||
# * `event` {Object}
|
||||
# * `path` {String} the old parameters the decoration used to have
|
||||
# * `pathStatus` {Number} representing the status. This value can be passed to
|
||||
# {::isStatusModified} or {::isStatusNew} to get more information.
|
||||
#
|
||||
# Returns a {Disposable} on which `.dispose()` can be called to unsubscribe.
|
||||
onDidChangeStatus: (callback) ->
|
||||
@emitter.on 'did-change-status', callback
|
||||
|
||||
# Public: Invoke the given callback when a multiple files' statuses have
|
||||
# changed. For example, on window focus, the status of all the paths in the
|
||||
# repo is checked. If any of them have changed, this will be fired. Call
|
||||
# {::getPathStatus(path)} to get the status for your path of choice.
|
||||
#
|
||||
# * `callback` {Function}
|
||||
#
|
||||
# Returns a {Disposable} on which `.dispose()` can be called to unsubscribe.
|
||||
onDidChangeStatuses: (callback) ->
|
||||
@emitter.on 'did-change-statuses', callback
|
||||
|
||||
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)
|
||||
|
||||
###
|
||||
Section: Repository Details
|
||||
###
|
||||
# Reread the index to update any values that have changed since the
|
||||
# last time the index was read.
|
||||
refreshIndex: -> @getRepo().refreshIndex()
|
||||
|
||||
# Public: Returns the {String} path of the repository.
|
||||
getPath: ->
|
||||
@@ -161,136 +124,9 @@ class GitRepository
|
||||
# Public: Returns the {String} working directory path of the repository.
|
||||
getWorkingDirectory: -> @getRepo().getWorkingDirectory()
|
||||
|
||||
# Public: Returns true if at the root, false if in a subfolder of the
|
||||
# repository.
|
||||
isProjectAtRoot: ->
|
||||
@projectAtRoot ?= @project?.relativize(@getWorkingDirectory()) is ''
|
||||
|
||||
# Public: Makes a path relative to the repository's working directory.
|
||||
relativize: (path) -> @getRepo().relativize(path)
|
||||
|
||||
# Public: Returns true if the given branch exists.
|
||||
hasBranch: (branch) -> @getReferenceTarget("refs/heads/#{branch}")?
|
||||
|
||||
# Public: Retrieves a shortened version of the HEAD reference value.
|
||||
#
|
||||
# This removes the leading segments of `refs/heads`, `refs/tags`, or
|
||||
# `refs/remotes`. It also shortens the SHA-1 of a detached `HEAD` to 7
|
||||
# characters.
|
||||
#
|
||||
# * `path` An optional {String} path in the repository to get this information
|
||||
# for, only needed if the repository contains submodules.
|
||||
#
|
||||
# Returns a {String}.
|
||||
getShortHead: (path) -> @getRepo(path).getShortHead()
|
||||
|
||||
# Public: Is the given path a submodule in the repository?
|
||||
#
|
||||
# * `path` The {String} path to check.
|
||||
#
|
||||
# Returns a {Boolean}.
|
||||
isSubmodule: (path) ->
|
||||
return false unless path
|
||||
|
||||
repo = @getRepo(path)
|
||||
if repo.isSubmodule(repo.relativize(path))
|
||||
true
|
||||
else
|
||||
# Check if the path is a working directory in a repo that isn't the root.
|
||||
repo isnt @getRepo() and repo.relativize(join(path, 'dir')) is 'dir'
|
||||
|
||||
# Public: Returns the number of commits behind the current branch is from the
|
||||
# its upstream remote branch.
|
||||
#
|
||||
# * `reference` The {String} branch reference name.
|
||||
# * `path` The {String} path in the repository to get this information for,
|
||||
# only needed if the repository contains submodules.
|
||||
getAheadBehindCount: (reference, path) ->
|
||||
@getRepo(path).getAheadBehindCount(reference)
|
||||
|
||||
# Public: Get the cached ahead/behind commit counts for the current branch's
|
||||
# upstream branch.
|
||||
#
|
||||
# * `path` An optional {String} path in the repository to get this information
|
||||
# for, only needed if the repository has submodules.
|
||||
#
|
||||
# Returns an {Object} with the following keys:
|
||||
# * `ahead` The {Number} of commits ahead.
|
||||
# * `behind` The {Number} of commits behind.
|
||||
getCachedUpstreamAheadBehindCount: (path) ->
|
||||
@getRepo(path).upstream ? @upstream
|
||||
|
||||
# Public: Returns the git configuration value specified by the key.
|
||||
#
|
||||
# * `path` An optional {String} path in the repository to get this information
|
||||
# for, only needed if the repository has submodules.
|
||||
getConfigValue: (key, path) -> @getRepo(path).getConfigValue(key)
|
||||
|
||||
# Public: Returns the origin url of the repository.
|
||||
#
|
||||
# * `path` (optional) {String} path in the repository to get this information
|
||||
# for, only needed if the repository has submodules.
|
||||
getOriginUrl: (path) -> @getConfigValue('remote.origin.url', path)
|
||||
|
||||
# Public: Returns the upstream branch for the current HEAD, or null if there
|
||||
# is no upstream branch for the current HEAD.
|
||||
#
|
||||
# * `path` An optional {String} path in the repo to get this information for,
|
||||
# only needed if the repository contains submodules.
|
||||
#
|
||||
# Returns a {String} branch name such as `refs/remotes/origin/master`.
|
||||
getUpstreamBranch: (path) -> @getRepo(path).getUpstreamBranch()
|
||||
|
||||
# Public: Gets all the local and remote references.
|
||||
#
|
||||
# * `path` An optional {String} path in the repository to get this information
|
||||
# for, only needed if the repository has submodules.
|
||||
#
|
||||
# Returns an {Object} with the following keys:
|
||||
# * `heads` An {Array} of head reference names.
|
||||
# * `remotes` An {Array} of remote reference names.
|
||||
# * `tags` An {Array} of tag reference names.
|
||||
getReferences: (path) -> @getRepo(path).getReferences()
|
||||
|
||||
# Public: Returns the current {String} SHA for the given reference.
|
||||
#
|
||||
# * `reference` The {String} reference to get the target of.
|
||||
# * `path` An optional {String} path in the repo to get the reference target
|
||||
# for. Only needed if the repository contains submodules.
|
||||
getReferenceTarget: (reference, path) ->
|
||||
@getRepo(path).getReferenceTarget(reference)
|
||||
|
||||
###
|
||||
Section: Reading Status
|
||||
###
|
||||
|
||||
# Public: Returns true if the given path is modified.
|
||||
isPathModified: (path) -> @isStatusModified(@getPathStatus(path))
|
||||
|
||||
# Public: Returns true if the given path is new.
|
||||
isPathNew: (path) -> @isStatusNew(@getPathStatus(path))
|
||||
|
||||
# Public: Is the given path ignored?
|
||||
#
|
||||
# Returns a {Boolean}.
|
||||
isPathIgnored: (path) -> @getRepo().isIgnored(@relativize(path))
|
||||
|
||||
# Public: Get the status of a directory in the repository's working directory.
|
||||
#
|
||||
# * `path` The {String} path to check.
|
||||
#
|
||||
# Returns a {Number} representing the status. This value can be passed to
|
||||
# {::isStatusModified} or {::isStatusNew} to get more information.
|
||||
getDirectoryStatus: (directoryPath) ->
|
||||
directoryPath = "#{@relativize(directoryPath)}/"
|
||||
directoryStatus = 0
|
||||
for path, status of @statuses
|
||||
directoryStatus |= status if path.indexOf(directoryPath) is 0
|
||||
directoryStatus
|
||||
|
||||
# Public: Get the status of a single path in the repository.
|
||||
#
|
||||
# `path` A {String} repository-relative path.
|
||||
# path - A {String} repository-relative path.
|
||||
#
|
||||
# Returns a {Number} representing the status. This value can be passed to
|
||||
# {::isStatusModified} or {::isStatusNew} to get more information.
|
||||
@@ -306,75 +142,56 @@ class GitRepository
|
||||
delete @statuses[relativePath]
|
||||
if currentPathStatus isnt pathStatus
|
||||
@emit 'status-changed', path, pathStatus
|
||||
@emitter.emit 'did-change-status', {path, pathStatus}
|
||||
|
||||
pathStatus
|
||||
|
||||
# Public: Get the cached status for the given path.
|
||||
# Public: Is the given path ignored?
|
||||
#
|
||||
# * `path` A {String} path in the repository, relative or absolute.
|
||||
#
|
||||
# Returns a status {Number} or null if the path is not in the cache.
|
||||
getCachedPathStatus: (path) ->
|
||||
@statuses[@relativize(path)]
|
||||
# Returns a {Boolean}.
|
||||
isPathIgnored: (path) -> @getRepo().isIgnored(@relativize(path))
|
||||
|
||||
# Public: Returns true if the given status indicates modification.
|
||||
isStatusModified: (status) -> @getRepo().isStatusModified(status)
|
||||
|
||||
# Public: Returns true if the given path is modified.
|
||||
isPathModified: (path) -> @isStatusModified(@getPathStatus(path))
|
||||
|
||||
# Public: Returns true if the given status indicates a new path.
|
||||
isStatusNew: (status) -> @getRepo().isStatusNew(status)
|
||||
|
||||
###
|
||||
Section: Retrieving Diffs
|
||||
###
|
||||
# Public: Returns true if the given path is new.
|
||||
isPathNew: (path) -> @isStatusNew(@getPathStatus(path))
|
||||
|
||||
# Public: Retrieves the number of lines added and removed to a path.
|
||||
#
|
||||
# This compares the working directory contents of the path to the `HEAD`
|
||||
# version.
|
||||
#
|
||||
# * `path` The {String} path to check.
|
||||
#
|
||||
# Returns an {Object} with the following keys:
|
||||
# * `added` The {Number} of added lines.
|
||||
# * `deleted` The {Number} of deleted lines.
|
||||
getDiffStats: (path) ->
|
||||
repo = @getRepo(path)
|
||||
repo.getDiffStats(repo.relativize(path))
|
||||
# Public: Returns true if at the root, false if in a subfolder of the
|
||||
# repository.
|
||||
isProjectAtRoot: ->
|
||||
@projectAtRoot ?= @project?.relativize(@getWorkingDirectory()) is ''
|
||||
|
||||
# Public: Retrieves the line diffs comparing the `HEAD` version of the given
|
||||
# path and the given text.
|
||||
#
|
||||
# * `path` The {String} path relative to the repository.
|
||||
# * `text` The {String} to compare against the `HEAD` contents
|
||||
#
|
||||
# Returns an {Array} of hunk {Object}s with the following keys:
|
||||
# * `oldStart` The line {Number} of the old hunk.
|
||||
# * `newStart` The line {Number} of the new hunk.
|
||||
# * `oldLines` The {Number} of lines in the old hunk.
|
||||
# * `newLines` The {Number} of lines in the new hunk
|
||||
getLineDiffs: (path, text) ->
|
||||
# Ignore eol of line differences on windows so that files checked in as
|
||||
# LF don't report every line modified when the text contains CRLF endings.
|
||||
options = ignoreEolWhitespace: process.platform is 'win32'
|
||||
repo = @getRepo(path)
|
||||
repo.getLineDiffs(repo.relativize(path), text, options)
|
||||
# Public: Makes a path relative to the repository's working directory.
|
||||
relativize: (path) -> @getRepo().relativize(path)
|
||||
|
||||
###
|
||||
Section: Checking Out
|
||||
###
|
||||
# Public: Retrieves a shortened version of the HEAD reference value.
|
||||
#
|
||||
# This removes the leading segments of `refs/heads`, `refs/tags`, or
|
||||
# `refs/remotes`. It also shortens the SHA-1 of a detached `HEAD` to 7
|
||||
# characters.
|
||||
#
|
||||
# path - An optional {String} path in the repository to get this information
|
||||
# for, only needed if the repository contains submodules.
|
||||
#
|
||||
# Returns a {String}.
|
||||
getShortHead: (path) -> @getRepo(path).getShortHead()
|
||||
|
||||
# Public: Restore the contents of a path in the working directory and index
|
||||
# to the version at `HEAD`.
|
||||
#
|
||||
# This is essentially the same as running:
|
||||
#
|
||||
# ```sh
|
||||
# git reset HEAD -- <path>
|
||||
# git checkout HEAD -- <path>
|
||||
# ```
|
||||
# git reset HEAD -- <path>
|
||||
# git checkout HEAD -- <path>
|
||||
# ```
|
||||
#
|
||||
# * `path` The {String} path to checkout.
|
||||
# path - The {String} path to checkout.
|
||||
#
|
||||
# Returns a {Boolean} that's true if the method was successful.
|
||||
checkoutHead: (path) ->
|
||||
@@ -385,60 +202,145 @@ class GitRepository
|
||||
|
||||
# Public: Checks out a branch in your repository.
|
||||
#
|
||||
# * `reference` The {String} reference to checkout.
|
||||
# * `create` A {Boolean} value which, if true creates the new reference if
|
||||
# it doesn't exist.
|
||||
# reference - The String reference to checkout
|
||||
# create - A Boolean value which, if true creates the new reference if it
|
||||
# doesn't exist.
|
||||
#
|
||||
# Returns a Boolean that's true if the method was successful.
|
||||
checkoutReference: (reference, create) ->
|
||||
@getRepo().checkoutReference(reference, create)
|
||||
|
||||
###
|
||||
Section: Private
|
||||
###
|
||||
# Public: Retrieves the number of lines added and removed to a path.
|
||||
#
|
||||
# This compares the working directory contents of the path to the `HEAD`
|
||||
# version.
|
||||
#
|
||||
# path - The {String} path to check.
|
||||
#
|
||||
# Returns an {Object} with the following keys:
|
||||
# :added - The {Number} of added lines.
|
||||
# :deleted - The {Number} of deleted lines.
|
||||
getDiffStats: (path) ->
|
||||
repo = @getRepo(path)
|
||||
repo.getDiffStats(repo.relativize(path))
|
||||
|
||||
# Subscribes to buffer events.
|
||||
subscribeToBuffer: (buffer) ->
|
||||
getBufferPathStatus = =>
|
||||
if path = buffer.getPath()
|
||||
@getPathStatus(path)
|
||||
# Public: Is the given path a submodule in the repository?
|
||||
#
|
||||
# path - The {String} path to check.
|
||||
#
|
||||
# Returns a {Boolean}.
|
||||
isSubmodule: (path) ->
|
||||
return false unless path
|
||||
|
||||
@subscribe buffer.onDidSave(getBufferPathStatus)
|
||||
@subscribe buffer.onDidReload(getBufferPathStatus)
|
||||
@subscribe buffer.onDidChangePath(getBufferPathStatus)
|
||||
@subscribe buffer.onDidDestroy => @unsubscribe(buffer)
|
||||
|
||||
# Subscribes to editor view event.
|
||||
checkoutHeadForEditor: (editor) ->
|
||||
filePath = editor.getPath()
|
||||
return unless filePath
|
||||
|
||||
fileName = basename(filePath)
|
||||
|
||||
checkoutHead = =>
|
||||
editor.buffer.reload() if editor.buffer.isModified()
|
||||
@checkoutHead(filePath)
|
||||
|
||||
if atom.config.get('editor.confirmCheckoutHeadRevision')
|
||||
atom.confirm
|
||||
message: 'Confirm Checkout HEAD Revision'
|
||||
detailedMessage: "Are you sure you want to discard all changes to \"#{fileName}\" since the last Git commit?"
|
||||
buttons:
|
||||
OK: checkoutHead
|
||||
Cancel: null
|
||||
repo = @getRepo(path)
|
||||
if repo.isSubmodule(repo.relativize(path))
|
||||
true
|
||||
else
|
||||
checkoutHead()
|
||||
# Check if the path is a working directory in a repo that isn't the root.
|
||||
repo isnt @getRepo() and repo.relativize(join(path, 'dir')) is 'dir'
|
||||
|
||||
# Returns the corresponding {Repository}
|
||||
getRepo: (path) ->
|
||||
if @repo?
|
||||
@repo.submoduleForPath(path) ? @repo
|
||||
else
|
||||
throw new Error("Repository has been destroyed")
|
||||
# Public: Get the status of a directory in the repository's working directory.
|
||||
#
|
||||
# path - The {String} path to check.
|
||||
#
|
||||
# Returns a {Number} representing the status. This value can be passed to
|
||||
# {::isStatusModified} or {::isStatusNew} to get more information.
|
||||
getDirectoryStatus: (directoryPath) ->
|
||||
directoryPath = "#{@relativize(directoryPath)}/"
|
||||
directoryStatus = 0
|
||||
for path, status of @statuses
|
||||
directoryStatus |= status if path.indexOf(directoryPath) is 0
|
||||
directoryStatus
|
||||
|
||||
# Reread the index to update any values that have changed since the
|
||||
# last time the index was read.
|
||||
refreshIndex: -> @getRepo().refreshIndex()
|
||||
# Public: Retrieves the line diffs comparing the `HEAD` version of the given
|
||||
# path and the given text.
|
||||
#
|
||||
# path - The {String} path relative to the repository.
|
||||
# text - The {String} to compare against the `HEAD` contents
|
||||
#
|
||||
# Returns an {Array} of hunk {Object}s with the following keys:
|
||||
# :oldStart - The line {Number} of the old hunk.
|
||||
# :newStart - The line {Number} of the new hunk.
|
||||
# :oldLines - The {Number} of lines in the old hunk.
|
||||
# :newLines - The {Number} of lines in the new hunk
|
||||
getLineDiffs: (path, text) ->
|
||||
# Ignore eol of line differences on windows so that files checked in as
|
||||
# LF don't report every line modified when the text contains CRLF endings.
|
||||
options = ignoreEolWhitespace: process.platform is 'win32'
|
||||
repo = @getRepo(path)
|
||||
repo.getLineDiffs(repo.relativize(path), text, options)
|
||||
|
||||
# Public: Returns the git configuration value specified by the key.
|
||||
#
|
||||
# path - An optional {String} path in the repository to get this information
|
||||
# for, only needed if the repository has submodules.
|
||||
getConfigValue: (key, path) -> @getRepo(path).getConfigValue(key)
|
||||
|
||||
# Public: Returns the origin url of the repository.
|
||||
#
|
||||
# path - An optional {String} path in the repository to get this information
|
||||
# for, only needed if the repository has submodules.
|
||||
getOriginUrl: (path) -> @getConfigValue('remote.origin.url', path)
|
||||
|
||||
# Public: Returns the upstream branch for the current HEAD, or null if there
|
||||
# is no upstream branch for the current HEAD.
|
||||
#
|
||||
# path - An optional {String} path in the repo to get this information for,
|
||||
# only needed if the repository contains submodules.
|
||||
#
|
||||
# Returns a {String} branch name such as `refs/remotes/origin/master`.
|
||||
getUpstreamBranch: (path) -> @getRepo(path).getUpstreamBranch()
|
||||
|
||||
# Public: Returns the current SHA for the given reference.
|
||||
#
|
||||
# reference - The {String} reference to get the target of.
|
||||
# path - An optional {String} path in the repo to get the reference target
|
||||
# for. Only needed if the repository contains submodules.
|
||||
getReferenceTarget: (reference, path) ->
|
||||
@getRepo(path).getReferenceTarget(reference)
|
||||
|
||||
# Public: Gets all the local and remote references.
|
||||
#
|
||||
# path - An optional {String} path in the repository to get this information
|
||||
# for, only needed if the repository has submodules.
|
||||
#
|
||||
# Returns an {Object} with the following keys:
|
||||
# :heads - An {Array} of head reference names.
|
||||
# :remotes - An {Array} of remote reference names.
|
||||
# :tags - An {Array} of tag reference names.
|
||||
getReferences: (path) -> @getRepo(path).getReferences()
|
||||
|
||||
# Public: Returns the number of commits behind the current branch is from the
|
||||
# its upstream remote branch.
|
||||
#
|
||||
# reference - The {String} branch reference name.
|
||||
# path - The {String} path in the repository to get this information for,
|
||||
# only needed if the repository contains submodules.
|
||||
getAheadBehindCount: (reference, path) ->
|
||||
@getRepo(path).getAheadBehindCount(reference)
|
||||
|
||||
# Public: Get the cached ahead/behind commit counts for the current branch's
|
||||
# upstream branch.
|
||||
#
|
||||
# path - An optional {String} path in the repository to get this information
|
||||
# for, only needed if the repository has submodules.
|
||||
#
|
||||
# Returns an {Object} with the following keys:
|
||||
# :ahead - The {Number} of commits ahead.
|
||||
# :behind - The {Number} of commits behind.
|
||||
getCachedUpstreamAheadBehindCount: (path) ->
|
||||
@getRepo(path).upstream ? @upstream
|
||||
|
||||
# Public: Get the cached status for the given path.
|
||||
#
|
||||
# path - A {String} path in the repository, relative or absolute.
|
||||
#
|
||||
# Returns a status {Number} or null if the path is not in the cache.
|
||||
getCachedPathStatus: (path) ->
|
||||
@statuses[@relativize(path)]
|
||||
|
||||
# Public: Returns true if the given branch exists.
|
||||
hasBranch: (branch) -> @getReferenceTarget("refs/heads/#{branch}")?
|
||||
|
||||
# Refreshes the current git status in an outside process and asynchronously
|
||||
# updates the relevant properties.
|
||||
@@ -460,6 +362,4 @@ class GitRepository
|
||||
for submodulePath, submoduleRepo of @getRepo().submodules
|
||||
submoduleRepo.upstream = submodules[submodulePath]?.upstream ? {ahead: 0, behind: 0}
|
||||
|
||||
unless statusesUnchanged
|
||||
@emit 'statuses-changed'
|
||||
@emitter.emit 'did-change-statuses'
|
||||
@emit 'statuses-changed' unless statusesUnchanged
|
||||
@@ -21,7 +21,7 @@ GutterComponent = React.createClass
|
||||
if gutterBackgroundColor isnt 'rbga(0, 0, 0, 0)'
|
||||
backgroundColor = gutterBackgroundColor
|
||||
|
||||
div className: 'gutter', onClick: @onClick, onMouseDown: @onMouseDown,
|
||||
div className: 'gutter', onClick: @onClick, onMouseDown: onMouseDown,
|
||||
div className: 'line-numbers', ref: 'lineNumbers', style:
|
||||
height: Math.max(scrollHeight, scrollViewHeight)
|
||||
WebkitTransform: @getTransform()
|
||||
@@ -215,14 +215,6 @@ GutterComponent = React.createClass
|
||||
lineNumberNodeForScreenRow: (screenRow) ->
|
||||
@lineNumberNodesById[@lineNumberIdsByScreenRow[screenRow]]
|
||||
|
||||
onMouseDown: (event) ->
|
||||
{editor} = @props
|
||||
{target} = event
|
||||
lineNumber = target.parentNode
|
||||
|
||||
unless target.classList.contains('icon-right') and lineNumber.classList.contains('foldable')
|
||||
@props.onMouseDown(event)
|
||||
|
||||
onClick: (event) ->
|
||||
{editor} = @props
|
||||
{target} = event
|
||||
|
||||
@@ -0,0 +1,275 @@
|
||||
{View, $, $$, $$$} = require './space-pen-extensions'
|
||||
{Range} = require 'text-buffer'
|
||||
_ = require 'underscore-plus'
|
||||
|
||||
# Represents the portion of the {EditorView} containing row numbers.
|
||||
#
|
||||
# The gutter also indicates if rows are folded.
|
||||
module.exports =
|
||||
class GutterView extends View
|
||||
@content: ->
|
||||
@div class: 'gutter', =>
|
||||
@div outlet: 'lineNumbers', class: 'line-numbers'
|
||||
|
||||
firstScreenRow: null
|
||||
lastScreenRow: null
|
||||
|
||||
initialize: ->
|
||||
@elementBuilder = document.createElement('div')
|
||||
|
||||
afterAttach: (onDom) ->
|
||||
return if @attached or not onDom
|
||||
@attached = true
|
||||
|
||||
highlightLines = => @highlightLines()
|
||||
@getEditorView().on 'cursor:moved', highlightLines
|
||||
@getEditorView().on 'selection:changed', highlightLines
|
||||
@on 'mousedown', (e) => @handleMouseEvents(e)
|
||||
|
||||
beforeRemove: ->
|
||||
$(document).off(".gutter-#{@getEditorView().id}")
|
||||
|
||||
handleMouseEvents: (e) ->
|
||||
editorView = @getEditorView()
|
||||
editor = @getEditor()
|
||||
startRow = editorView.screenPositionFromMouseEvent(e).row
|
||||
if e.shiftKey
|
||||
editor.selectToScreenPosition([startRow + 1, 0])
|
||||
return
|
||||
else
|
||||
editor.getSelection().setScreenRange([[startRow, 0], [startRow, 0]])
|
||||
|
||||
moveHandler = (e) =>
|
||||
start = startRow
|
||||
end = editorView.screenPositionFromMouseEvent(e).row
|
||||
if end > start then end++ else start++
|
||||
editor.getSelection().setScreenRange([[start, 0], [end, 0]])
|
||||
|
||||
$(document).on "mousemove.gutter-#{editorView.id}", moveHandler
|
||||
$(document).one "mouseup.gutter-#{editorView.id}", => $(document).off 'mousemove', moveHandler
|
||||
|
||||
# Retrieves the containing {EditorView}.
|
||||
#
|
||||
# Returns an {EditorView}.
|
||||
getEditorView: ->
|
||||
@parentView
|
||||
|
||||
getEditor: ->
|
||||
@getEditorView().getEditor()
|
||||
|
||||
# Defines whether to show the gutter or not.
|
||||
#
|
||||
# showLineNumbers - A {Boolean} which, if `false`, hides the gutter
|
||||
setShowLineNumbers: (showLineNumbers) ->
|
||||
if showLineNumbers then @lineNumbers.show() else @lineNumbers.hide()
|
||||
|
||||
# Get all the line-number divs.
|
||||
#
|
||||
# Returns a list of {HTMLElement}s.
|
||||
getLineNumberElements: ->
|
||||
@lineNumbers[0].children
|
||||
|
||||
# Get all the line-number divs.
|
||||
#
|
||||
# Returns a list of {HTMLElement}s.
|
||||
getLineNumberElementsForClass: (klass) ->
|
||||
@lineNumbers[0].getElementsByClassName(klass)
|
||||
|
||||
# Get a single line-number div.
|
||||
#
|
||||
# * bufferRow: 0 based line number
|
||||
#
|
||||
# Returns a list of {HTMLElement}s that correspond to the bufferRow. More than
|
||||
# one in the list indicates a wrapped line.
|
||||
getLineNumberElement: (bufferRow) ->
|
||||
@getLineNumberElementsForClass("line-number-#{bufferRow}")
|
||||
|
||||
# Add a class to all line-number divs.
|
||||
#
|
||||
# * klass: string class name
|
||||
#
|
||||
# Returns true if the class was added to any lines
|
||||
addClassToAllLines: (klass)->
|
||||
elements = @getLineNumberElements()
|
||||
el.classList.add(klass) for el in elements
|
||||
!!elements.length
|
||||
|
||||
# Remove a class from all line-number divs.
|
||||
#
|
||||
# * klass: string class name. Can only be one class name. i.e. 'my-class'
|
||||
#
|
||||
# Returns true if the class was removed from any lines
|
||||
removeClassFromAllLines: (klass)->
|
||||
# This is faster than calling $.removeClass on all lines, and faster than
|
||||
# making a new array and iterating through it.
|
||||
elements = @getLineNumberElementsForClass(klass)
|
||||
willRemoveClasses = !!elements.length
|
||||
elements[0].classList.remove(klass) while elements.length > 0
|
||||
willRemoveClasses
|
||||
|
||||
# Add a class to a single line-number div
|
||||
#
|
||||
# * bufferRow: 0 based line number
|
||||
# * klass: string class name
|
||||
#
|
||||
# Returns true if there were lines the class was added to
|
||||
addClassToLine: (bufferRow, klass)->
|
||||
elements = @getLineNumberElement(bufferRow)
|
||||
el.classList.add(klass) for el in elements
|
||||
!!elements.length
|
||||
|
||||
# Remove a class from a single line-number div
|
||||
#
|
||||
# * bufferRow: 0 based line number
|
||||
# * klass: string class name
|
||||
#
|
||||
# Returns true if there were lines the class was removed from
|
||||
removeClassFromLine: (bufferRow, klass)->
|
||||
classesRemoved = false
|
||||
elements = @getLineNumberElement(bufferRow)
|
||||
for el in elements
|
||||
hasClass = el.classList.contains(klass)
|
||||
classesRemoved |= hasClass
|
||||
el.classList.remove(klass) if hasClass
|
||||
classesRemoved
|
||||
|
||||
updateLineNumbers: (changes, startScreenRow, endScreenRow) ->
|
||||
# Check if we have something already rendered that overlaps the requested range
|
||||
updateAllLines = not (startScreenRow? and endScreenRow?)
|
||||
updateAllLines |= endScreenRow <= @firstScreenRow or startScreenRow >= @lastScreenRow
|
||||
|
||||
unless updateAllLines
|
||||
for change in changes
|
||||
if change.screenDelta or change.bufferDelta
|
||||
updateAllLines = true
|
||||
break
|
||||
|
||||
# Rebuild the entire gutter if a change added or removed lines
|
||||
if updateAllLines
|
||||
@lineNumbers[0].innerHTML = @buildLineElementsHtml(startScreenRow, endScreenRow)
|
||||
|
||||
# Handle changes that didn't add/remove lines more optimally
|
||||
else
|
||||
if startScreenRow < @firstScreenRow
|
||||
@prependLineElements(@buildLineElements(startScreenRow, @firstScreenRow-1))
|
||||
else if startScreenRow != @firstScreenRow
|
||||
@removeLineElements(startScreenRow - @firstScreenRow)
|
||||
|
||||
if endScreenRow > @lastScreenRow
|
||||
@appendLineElements(@buildLineElements(@lastScreenRow+1, endScreenRow))
|
||||
else if endScreenRow != @lastScreenRow
|
||||
@removeLineElements(endScreenRow - @lastScreenRow)
|
||||
|
||||
@updateFoldableClasses(changes)
|
||||
|
||||
@firstScreenRow = startScreenRow
|
||||
@lastScreenRow = endScreenRow
|
||||
@highlightedRows = null
|
||||
@highlightLines()
|
||||
|
||||
prependLineElements: (lineElements) ->
|
||||
anchor = @lineNumbers[0].children[0]
|
||||
return appendLineElements(lineElements) unless anchor?
|
||||
@lineNumbers[0].insertBefore(lineElements[0], anchor) while lineElements.length > 0
|
||||
null # defeat coffeescript array return
|
||||
|
||||
appendLineElements: (lineElements) ->
|
||||
@lineNumbers[0].appendChild(lineElements[0]) while lineElements.length > 0
|
||||
null # defeat coffeescript array return
|
||||
|
||||
removeLineElements: (numberOfElements) ->
|
||||
children = @getLineNumberElements()
|
||||
|
||||
# children is a live NodeList, so remove from the desired end {numberOfElements} times
|
||||
if numberOfElements < 0
|
||||
@lineNumbers[0].removeChild(children[children.length-1]) while numberOfElements++
|
||||
else if numberOfElements > 0
|
||||
@lineNumbers[0].removeChild(children[0]) while numberOfElements--
|
||||
|
||||
null # defeat coffeescript array return
|
||||
|
||||
buildLineElements: (startScreenRow, endScreenRow) ->
|
||||
@elementBuilder.innerHTML = @buildLineElementsHtml(startScreenRow, endScreenRow)
|
||||
@elementBuilder.children
|
||||
|
||||
buildLineElementsHtml: (startScreenRow, endScreenRow) =>
|
||||
editor = @getEditor()
|
||||
maxDigits = editor.getLineCount().toString().length
|
||||
rows = editor.bufferRowsForScreenRows(startScreenRow, endScreenRow)
|
||||
|
||||
html = ''
|
||||
for row in rows
|
||||
if row is lastRow
|
||||
rowValue = '•'
|
||||
else
|
||||
rowValue = (row + 1).toString()
|
||||
|
||||
classes = "line-number line-number-#{row}"
|
||||
classes += ' foldable' if row isnt lastRow and editor.isFoldableAtBufferRow(row)
|
||||
classes += ' folded' if editor.isFoldedAtBufferRow(row)
|
||||
|
||||
rowValuePadding = _.multiplyString(' ', maxDigits - rowValue.length)
|
||||
|
||||
html += """<div class="#{classes}" data-buffer-row=#{row}>#{rowValuePadding}#{rowValue}<div class="icon-right"></div></div>"""
|
||||
|
||||
lastRow = row
|
||||
|
||||
html
|
||||
|
||||
# Called to update the 'foldable' class of line numbers when there's
|
||||
# a change to the display buffer that doesn't regenerate all the line numbers
|
||||
# anyway.
|
||||
updateFoldableClasses: (changes) ->
|
||||
editor = @getEditor()
|
||||
for {start, end} in changes when start <= @lastScreenRow and end >= @firstScreenRow
|
||||
startScreenRow = Math.max(start - 1, @firstScreenRow)
|
||||
endScreenRow = Math.min(end + 1, @lastScreenRow)
|
||||
lastBufferRow = null
|
||||
for bufferRow in editor.bufferRowsForScreenRows(startScreenRow, endScreenRow) when bufferRow isnt lastBufferRow
|
||||
lastBufferRow = bufferRow
|
||||
if lineNumberElement = @getLineNumberElement(bufferRow)[0]
|
||||
if editor.isFoldableAtBufferRow(bufferRow)
|
||||
lineNumberElement.classList.add('foldable')
|
||||
else
|
||||
lineNumberElement.classList.remove('foldable')
|
||||
|
||||
removeLineHighlights: ->
|
||||
return unless @highlightedLineNumbers
|
||||
for line in @highlightedLineNumbers
|
||||
line.classList.remove('cursor-line')
|
||||
line.classList.remove('cursor-line-no-selection')
|
||||
@highlightedLineNumbers = null
|
||||
|
||||
addLineHighlight: (row, emptySelection) ->
|
||||
return if row < @firstScreenRow or row > @lastScreenRow
|
||||
@highlightedLineNumbers ?= []
|
||||
if highlightedLineNumber = @lineNumbers[0].children[row - @firstScreenRow]
|
||||
highlightedLineNumber.classList.add('cursor-line')
|
||||
highlightedLineNumber.classList.add('cursor-line-no-selection') if emptySelection
|
||||
@highlightedLineNumbers.push(highlightedLineNumber)
|
||||
|
||||
highlightLines: ->
|
||||
editor = @getEditor()
|
||||
return unless editor?.isAlive()
|
||||
|
||||
if editor.getSelection().isEmpty()
|
||||
row = editor.getCursorScreenPosition().row
|
||||
rowRange = new Range([row, 0], [row, 0])
|
||||
return if @selectionEmpty and @highlightedRows?.isEqual(rowRange)
|
||||
|
||||
@removeLineHighlights()
|
||||
@addLineHighlight(row, true)
|
||||
@highlightedRows = rowRange
|
||||
@selectionEmpty = true
|
||||
else
|
||||
selectedRows = editor.getSelection().getScreenRange()
|
||||
endRow = selectedRows.end.row
|
||||
endRow-- if selectedRows.end.column is 0
|
||||
selectedRows = new Range([selectedRows.start.row, 0], [endRow, 0])
|
||||
return if not @selectionEmpty and @highlightedRows?.isEqual(selectedRows)
|
||||
|
||||
@removeLineHighlights()
|
||||
for row in [selectedRows.start.row..selectedRows.end.row]
|
||||
@addLineHighlight(row, false)
|
||||
@highlightedRows = selectedRows
|
||||
@selectionEmpty = false
|
||||
@@ -22,12 +22,11 @@ HighlightComponent = React.createClass
|
||||
{editor, decoration} = @props
|
||||
if decoration.id?
|
||||
@decoration = editor.decorationForId(decoration.id)
|
||||
@decorationDisposable = @decoration.onDidFlash @startFlashAnimation
|
||||
@decoration.on 'flash', @startFlashAnimation
|
||||
@startFlashAnimation()
|
||||
|
||||
componentWillUnmount: ->
|
||||
@decorationDisposable?.dispose()
|
||||
@decorationDisposable = null
|
||||
@decoration?.off 'flash', @startFlashAnimation
|
||||
|
||||
startFlashAnimation: ->
|
||||
return unless flash = @decoration.consumeNextFlash()
|
||||
|
||||
@@ -4,13 +4,9 @@ KeymapManager = require 'atom-keymap'
|
||||
CSON = require 'season'
|
||||
{jQuery} = require 'space-pen'
|
||||
|
||||
KeymapManager::onDidLoadBundledKeymaps = (callback) ->
|
||||
@emitter.on 'did-load-bundled-keymaps', callback
|
||||
|
||||
KeymapManager::loadBundledKeymaps = ->
|
||||
@loadKeymap(path.join(@resourcePath, 'keymaps'))
|
||||
@emit 'bundled-keymaps-loaded'
|
||||
@emitter.emit 'did-load-bundled-keymaps'
|
||||
@emit('bundled-keymaps-loaded')
|
||||
|
||||
KeymapManager::getUserKeymapPath = ->
|
||||
if userKeymapPath = CSON.resolve(path.join(@configDirPath, 'keymap'))
|
||||
|
||||
Alguns arquivos não foram exibidos porque demasiados arquivos foram alterados neste diff Mostrar Mais
Referência em uma Nova Issue
Bloquear um usuário