mirror of
https://github.com/atom/atom.git
synced 2026-01-24 14:28:14 -05:00
Merge branch 'master' into dh-async-repo
This commit is contained in:
@@ -1 +0,0 @@
|
||||
v0.12.0
|
||||
@@ -98,7 +98,7 @@ Explain the problem and include additional details to help maintainers reproduce
|
||||
* **Describe the behavior you observed after following the steps** and point out what exactly is the problem with that behavior.
|
||||
* **Explain which behavior you expected to see instead and why.**
|
||||
* **Include screenshots and animated GIFs** which show you following the described steps and clearly demonstrate the problem. If you use the keyboard while following the steps, **record the GIF with the [Keybinding Resolver](https://github.com/atom/keybinding-resolver) shown**. You can use [this tool](http://www.cockos.com/licecap/) to record GIFs on OSX and Windows, and [this tool](https://github.com/colinkeenan/silentcast) or [this tool](https://github.com/GNOME/byzanz) on Linux.
|
||||
* **If you're reporting that Atom crashed**, include a crash report with a stack trace from the operating system. On OSX, the crash report will be available in `Console.app` under "Diagnostic and usage information" > "User diagnostic reports". Include the crash report in the issue in a [code block](https://help.github.com/articles/markdown-basics/#multiple-lines) or put it in a [gist](https://gist.github.com/) and provide link to that gist.
|
||||
* **If you're reporting that Atom crashed**, include a crash report with a stack trace from the operating system. On OSX, the crash report will be available in `Console.app` under "Diagnostic and usage information" > "User diagnostic reports". Include the crash report in the issue in a [code block](https://help.github.com/articles/markdown-basics/#multiple-lines), a [file attachment](https://help.github.com/articles/file-attachments-on-issues-and-pull-requests/), or put it in a [gist](https://gist.github.com/) and provide link to that gist.
|
||||
* **If the problem is related to performance**, include a [CPU profile capture and a screenshot](https://atom.io/docs/latest/hacking-atom-debugging#diagnose-performance-problems-with-the-dev-tools-cpu-profiler) with your report.
|
||||
* **If the Chrome's developer tools pane is shown without you triggering it**, that normally means that an exception was thrown. The Console tab will include an entry for the exception. Expand the exception so that the stack trace is visible, and provide the full exception and stack trace in a [code blocks](https://help.github.com/articles/markdown-basics/#multiple-lines) and as a screenshot.
|
||||
* **If the problem wasn't triggered by a specific action**, describe what you were doing before the problem happened and share more information using the guidelines below.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||

|
||||
|
||||
[](https://travis-ci.org/atom/atom)
|
||||
[](https://travis-ci.org/atom/atom) [](https://ci.appveyor.com/project/Atom/atom)
|
||||
[](https://david-dm.org/atom/atom)
|
||||
[](http://atom-slack.herokuapp.com/)
|
||||
|
||||
|
||||
1
apm/.npmrc
Normal file
1
apm/.npmrc
Normal file
@@ -0,0 +1 @@
|
||||
cache = ~/.atom/.npm
|
||||
35
appveyor.yml
Normal file
35
appveyor.yml
Normal file
@@ -0,0 +1,35 @@
|
||||
version: "{build}"
|
||||
|
||||
skip_tags: true
|
||||
clone_folder: c:\projects\atom
|
||||
clone_depth: 10
|
||||
|
||||
platform:
|
||||
- x86
|
||||
|
||||
environment:
|
||||
global:
|
||||
ATOM_DEV_RESOURCE_PATH: c:\projects\atom
|
||||
ATOM_ACCESS_TOKEN:
|
||||
secure: Q7vxmSq0bVCLTTRPzXw5ZhPTe7XYhWxX0tQV6neEkddTH6pZkOYNmSCG6VnMX2f+
|
||||
|
||||
matrix:
|
||||
- NODE_VERSION: 0.10.35
|
||||
|
||||
install:
|
||||
- SET PATH=C:\Program Files\Atom\resources\cli;%PATH%
|
||||
- ps: Install-Product node $env:NODE_VERSION $env:PLATFORM
|
||||
|
||||
build_script:
|
||||
- cd %APPVEYOR_BUILD_FOLDER%
|
||||
- C:\projects\atom\script\cibuild.cmd
|
||||
|
||||
test: off
|
||||
deploy: off
|
||||
artifacts:
|
||||
- path: atom-build
|
||||
|
||||
cache:
|
||||
- '%USERPROFILE%\.atom\.apm'
|
||||
- '%USERPROFILE%\.atom\.node-gyp\.atom'
|
||||
- '%USERPROFILE%\.atom\.npm'
|
||||
@@ -39,7 +39,16 @@ module.exports = (grunt) ->
|
||||
disableAutoUpdate = grunt.option('no-auto-update') ? false
|
||||
|
||||
channel = grunt.option('channel')
|
||||
channel ?= process.env.JANKY_BRANCH if process.env.JANKY_BRANCH in ['stable', 'beta']
|
||||
releasableBranches = ['stable', 'beta']
|
||||
if process.env.APPVEYOR and not process.env.APPVEYOR_PULL_REQUEST_NUMBER
|
||||
channel ?= process.env.APPVEYOR_REPO_BRANCH if process.env.APPVEYOR_REPO_BRANCH in releasableBranches
|
||||
|
||||
if process.env.TRAVIS and not process.env.TRAVIS_PULL_REQUEST
|
||||
channel ?= process.env.TRAVIS_BRANCH if process.env.TRAVIS_BRANCH in releasableBranches
|
||||
|
||||
if process.env.JANKY_BRANCH
|
||||
channel ?= process.env.JANKY_BRANCH if process.env.JANKY_BRANCH in releasableBranches
|
||||
|
||||
channel ?= 'dev'
|
||||
|
||||
metadata = packageJson
|
||||
@@ -270,16 +279,20 @@ module.exports = (grunt) ->
|
||||
grunt.registerTask('lint', ['standard', 'coffeelint', 'csslint', 'lesslint'])
|
||||
grunt.registerTask('test', ['shell:kill-atom', 'run-specs'])
|
||||
|
||||
ciTasks = ['output-disk-space', 'download-electron', 'download-electron-chromedriver', 'build']
|
||||
ciTasks = []
|
||||
ciTasks.push('output-disk-space') unless process.env.CI
|
||||
ciTasks.push('download-electron')
|
||||
ciTasks.push('download-electron-chromedriver')
|
||||
ciTasks.push('build')
|
||||
ciTasks.push('dump-symbols') if process.platform isnt 'win32'
|
||||
ciTasks.push('set-version', 'check-licenses', 'lint', 'generate-asar')
|
||||
ciTasks.push('mkdeb') if process.platform is 'linux'
|
||||
ciTasks.push('codesign:exe') if process.platform is 'win32' and not process.env.TRAVIS
|
||||
ciTasks.push('codesign:exe') if process.platform is 'win32' and not process.env.CI
|
||||
ciTasks.push('create-windows-installer:installer') if process.platform is 'win32'
|
||||
ciTasks.push('test') if process.platform is 'darwin'
|
||||
ciTasks.push('codesign:installer') if process.platform is 'win32' and not process.env.TRAVIS
|
||||
ciTasks.push('codesign:app') if process.platform is 'darwin' and not process.env.TRAVIS
|
||||
ciTasks.push('publish-build') unless process.env.TRAVIS
|
||||
ciTasks.push('codesign:installer') if process.platform is 'win32' and not process.env.CI
|
||||
ciTasks.push('codesign:app') if process.platform is 'darwin' and not process.env.CI
|
||||
ciTasks.push('publish-build') unless process.env.CI
|
||||
grunt.registerTask('ci', ciTasks)
|
||||
|
||||
defaultTasks = ['download-electron', 'download-electron-chromedriver', 'build', 'set-version', 'generate-asar']
|
||||
|
||||
@@ -9,7 +9,6 @@ request = require 'request'
|
||||
|
||||
grunt = null
|
||||
|
||||
commitSha = process.env.JANKY_SHA1
|
||||
token = process.env.ATOM_ACCESS_TOKEN
|
||||
defaultHeaders =
|
||||
Authorization: "token #{token}"
|
||||
@@ -31,8 +30,8 @@ module.exports = (gruntObject) ->
|
||||
cp path.join(docsOutputDir, 'api.json'), path.join(buildDir, 'atom-api.json')
|
||||
|
||||
grunt.registerTask 'upload-assets', 'Upload the assets to a GitHub release', ->
|
||||
branchName = process.env.JANKY_BRANCH
|
||||
switch branchName
|
||||
channel = grunt.config.get('atom.channel')
|
||||
switch channel
|
||||
when 'stable'
|
||||
isPrerelease = false
|
||||
when 'beta'
|
||||
@@ -55,7 +54,7 @@ module.exports = (gruntObject) ->
|
||||
|
||||
zipAssets buildDir, assets, (error) ->
|
||||
return done(error) if error?
|
||||
getAtomDraftRelease isPrerelease, branchName, (error, release) ->
|
||||
getAtomDraftRelease isPrerelease, channel, (error, release) ->
|
||||
return done(error) if error?
|
||||
assetNames = (asset.assetName for asset in assets)
|
||||
deleteExistingAssets release, assetNames, (error) ->
|
||||
|
||||
@@ -5,18 +5,20 @@ module.exports = (grunt) ->
|
||||
{spawn} = require('./task-helpers')(grunt)
|
||||
|
||||
getVersion = (callback) ->
|
||||
onBuildMachine = process.env.JANKY_SHA1 and process.env.JANKY_BRANCH in ['stable', 'beta']
|
||||
releasableBranches = ['stable', 'beta']
|
||||
channel = grunt.config.get('atom.channel')
|
||||
shouldUseCommitHash = if channel in releasableBranches then false else true
|
||||
inRepository = fs.existsSync(path.resolve(__dirname, '..', '..', '.git'))
|
||||
{version} = require(path.join(grunt.config.get('atom.appDir'), 'package.json'))
|
||||
if onBuildMachine or not inRepository
|
||||
callback(null, version)
|
||||
else
|
||||
if shouldUseCommitHash and inRepository
|
||||
cmd = 'git'
|
||||
args = ['rev-parse', '--short', 'HEAD']
|
||||
spawn {cmd, args}, (error, {stdout}={}, code) ->
|
||||
commitHash = stdout?.trim?()
|
||||
combinedVersion = "#{version}-#{commitHash}"
|
||||
callback(error, combinedVersion)
|
||||
else
|
||||
callback(null, version)
|
||||
|
||||
grunt.registerTask 'set-version', 'Set the version in the plist and package.json', ->
|
||||
done = @async()
|
||||
|
||||
@@ -6,8 +6,7 @@ async = require 'async'
|
||||
|
||||
# TODO: This should really be parallel on every platform, however:
|
||||
# - On Windows, our fixtures step on each others toes.
|
||||
# - On Travis, Mac workers haven't enough horsepower.
|
||||
if process.env.TRAVIS or process.platform is 'win32'
|
||||
if process.platform is 'win32'
|
||||
concurrency = 1
|
||||
else
|
||||
concurrency = 2
|
||||
|
||||
16
package.json
16
package.json
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "atom",
|
||||
"productName": "Atom",
|
||||
"version": "1.3.0-dev",
|
||||
"version": "1.4.0-dev",
|
||||
"description": "A hackable text editor for the 21st Century.",
|
||||
"main": "./src/browser/main.js",
|
||||
"repository": {
|
||||
@@ -15,7 +15,7 @@
|
||||
"electronVersion": "0.34.3",
|
||||
"dependencies": {
|
||||
"async": "0.2.6",
|
||||
"atom-keymap": "^6.1.0",
|
||||
"atom-keymap": "^6.1.1",
|
||||
"babel-core": "^5.8.21",
|
||||
"bootstrap": "^3.3.4",
|
||||
"cached-run-in-this-context": "0.4.0",
|
||||
@@ -53,7 +53,7 @@
|
||||
"service-hub": "^0.7.0",
|
||||
"source-map-support": "^0.3.2",
|
||||
"temp": "0.8.1",
|
||||
"text-buffer": "^8.0.3",
|
||||
"text-buffer": "^8.0.4",
|
||||
"typescript-simple": "1.0.0",
|
||||
"underscore-plus": "^1.6.6",
|
||||
"yargs": "^3.23.0"
|
||||
@@ -98,7 +98,7 @@
|
||||
"keybinding-resolver": "0.33.0",
|
||||
"line-ending-selector": "0.3.0",
|
||||
"link": "0.31.0",
|
||||
"markdown-preview": "0.156.1",
|
||||
"markdown-preview": "0.156.2",
|
||||
"metrics": "0.53.0",
|
||||
"notifications": "0.61.0",
|
||||
"open-on-github": "0.40.0",
|
||||
@@ -109,12 +109,12 @@
|
||||
"spell-check": "0.63.0",
|
||||
"status-bar": "0.80.0",
|
||||
"styleguide": "0.45.0",
|
||||
"symbols-view": "0.110.0",
|
||||
"symbols-view": "0.110.1",
|
||||
"tabs": "0.88.0",
|
||||
"timecop": "0.33.0",
|
||||
"tree-view": "0.198.0",
|
||||
"update-package-dependencies": "0.10.0",
|
||||
"welcome": "0.32.0",
|
||||
"welcome": "0.33.0",
|
||||
"whitespace": "0.32.0",
|
||||
"wrap-guide": "0.38.1",
|
||||
"language-c": "0.49.0",
|
||||
@@ -134,11 +134,11 @@
|
||||
"language-make": "0.20.0",
|
||||
"language-mustache": "0.13.0",
|
||||
"language-objective-c": "0.15.0",
|
||||
"language-perl": "0.30.0",
|
||||
"language-perl": "0.31.0",
|
||||
"language-php": "0.34.0",
|
||||
"language-property-list": "0.8.0",
|
||||
"language-python": "0.41.0",
|
||||
"language-ruby": "0.61.0",
|
||||
"language-ruby": "0.62.0",
|
||||
"language-ruby-on-rails": "0.24.0",
|
||||
"language-sass": "0.43.0",
|
||||
"language-shellscript": "0.20.0",
|
||||
|
||||
@@ -214,6 +214,20 @@
|
||||
<key>LSHandlerRank</key>
|
||||
<string>Alternate</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CFBundleTypeExtensions</key>
|
||||
<array>
|
||||
<string>COMMIT_EDITMSG</string>
|
||||
</array>
|
||||
<key>CFBundleTypeIconFile</key>
|
||||
<string>file.icns</string>
|
||||
<key>CFBundleTypeName</key>
|
||||
<string>Commit message</string>
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>Editor</string>
|
||||
<key>LSHandlerRank</key>
|
||||
<string>Alternate</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CFBundleTypeExtensions</key>
|
||||
<array>
|
||||
@@ -228,6 +242,21 @@
|
||||
<key>LSHandlerRank</key>
|
||||
<string>Alternate</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CFBundleTypeExtensions</key>
|
||||
<array>
|
||||
<string>clj</string>
|
||||
<string>cljs</string>
|
||||
</array>
|
||||
<key>CFBundleTypeIconFile</key>
|
||||
<string>file.icns</string>
|
||||
<key>CFBundleTypeName</key>
|
||||
<string>Clojure source</string>
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>Editor</string>
|
||||
<key>LSHandlerRank</key>
|
||||
<string>Alternate</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CFBundleTypeExtensions</key>
|
||||
<array>
|
||||
@@ -422,6 +451,20 @@
|
||||
<key>LSHandlerRank</key>
|
||||
<string>Alternate</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CFBundleTypeExtensions</key>
|
||||
<array>
|
||||
<string>go</string>
|
||||
</array>
|
||||
<key>CFBundleTypeIconFile</key>
|
||||
<string>file.icns</string>
|
||||
<key>CFBundleTypeName</key>
|
||||
<string>Go source</string>
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>Editor</string>
|
||||
<key>LSHandlerRank</key>
|
||||
<string>Alternate</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CFBundleTypeExtensions</key>
|
||||
<array>
|
||||
@@ -624,6 +667,20 @@
|
||||
<key>LSHandlerRank</key>
|
||||
<string>Alternate</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CFBundleTypeExtensions</key>
|
||||
<array>
|
||||
<string>less</string>
|
||||
</array>
|
||||
<key>CFBundleTypeIconFile</key>
|
||||
<string>file.icns</string>
|
||||
<key>CFBundleTypeName</key>
|
||||
<string>Less source</string>
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>Editor</string>
|
||||
<key>LSHandlerRank</key>
|
||||
<string>Alternate</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CFBundleTypeExtensions</key>
|
||||
<array>
|
||||
@@ -702,6 +759,20 @@
|
||||
<key>LSHandlerRank</key>
|
||||
<string>Alternate</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CFBundleTypeExtensions</key>
|
||||
<array>
|
||||
<string>mk</string>
|
||||
</array>
|
||||
<key>CFBundleTypeIconFile</key>
|
||||
<string>file.icns</string>
|
||||
<key>CFBundleTypeName</key>
|
||||
<string>Makefile source</string>
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>Editor</string>
|
||||
<key>LSHandlerRank</key>
|
||||
<string>Alternate</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CFBundleTypeExtensions</key>
|
||||
<array>
|
||||
@@ -809,6 +880,21 @@
|
||||
<key>LSHandlerRank</key>
|
||||
<string>Alternate</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CFBundleTypeExtensions</key>
|
||||
<array>
|
||||
<string>mustache</string>
|
||||
<string>hbs</string>
|
||||
</array>
|
||||
<key>CFBundleTypeIconFile</key>
|
||||
<string>file.icns</string>
|
||||
<key>CFBundleTypeName</key>
|
||||
<string>Mustache document</string>
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>Editor</string>
|
||||
<key>LSHandlerRank</key>
|
||||
<string>Alternate</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CFBundleTypeExtensions</key>
|
||||
<array>
|
||||
@@ -998,6 +1084,7 @@
|
||||
<key>CFBundleTypeExtensions</key>
|
||||
<array>
|
||||
<string>rhtml</string>
|
||||
<string>erb</string>
|
||||
</array>
|
||||
<key>CFBundleTypeIconFile</key>
|
||||
<string>file.icns</string>
|
||||
@@ -1039,6 +1126,21 @@
|
||||
<key>LSHandlerRank</key>
|
||||
<string>Alternate</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CFBundleTypeExtensions</key>
|
||||
<array>
|
||||
<string>sass</string>
|
||||
<string>scss</string>
|
||||
</array>
|
||||
<key>CFBundleTypeIconFile</key>
|
||||
<string>file.icns</string>
|
||||
<key>CFBundleTypeName</key>
|
||||
<string>Sass source</string>
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>Editor</string>
|
||||
<key>LSHandlerRank</key>
|
||||
<string>Alternate</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CFBundleTypeExtensions</key>
|
||||
<array>
|
||||
@@ -1243,6 +1345,20 @@
|
||||
<key>LSHandlerRank</key>
|
||||
<string>Alternate</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CFBundleTypeExtensions</key>
|
||||
<array>
|
||||
<string>toml</string>
|
||||
</array>
|
||||
<key>CFBundleTypeIconFile</key>
|
||||
<string>file.icns</string>
|
||||
<key>CFBundleTypeName</key>
|
||||
<string>TOML file</string>
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>Editor</string>
|
||||
<key>LSHandlerRank</key>
|
||||
<string>Alternate</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CFBundleTypeExtensions</key>
|
||||
<array>
|
||||
|
||||
3
resources/win/apm.cmd
Normal file
3
resources/win/apm.cmd
Normal file
@@ -0,0 +1,3 @@
|
||||
@echo off
|
||||
|
||||
"%~dp0\..\app\apm\bin\node.exe" "%~dp0\..\app\apm\lib\cli.js" %*
|
||||
@@ -22,12 +22,18 @@ function loadEnvironmentVariables(filePath) {
|
||||
}
|
||||
|
||||
function readEnvironmentVariables() {
|
||||
if (process.platform === 'win32') {
|
||||
loadEnvironmentVariables(path.resolve('/jenkins/config/atomcredentials'));
|
||||
} else if (process.platform === 'darwin') {
|
||||
loadEnvironmentVariables('/var/lib/jenkins/config/atomcredentials');
|
||||
loadEnvironmentVariables('/var/lib/jenkins/config/xcodekeychain');
|
||||
} else if (process.platform === 'linux') {
|
||||
if (process.env.JANKY_SHA1) {
|
||||
if (process.platform === 'win32') {
|
||||
loadEnvironmentVariables(path.resolve('/jenkins/config/atomcredentials'));
|
||||
} else if (process.platform === 'darwin') {
|
||||
loadEnvironmentVariables('/var/lib/jenkins/config/atomcredentials');
|
||||
loadEnvironmentVariables('/var/lib/jenkins/config/xcodekeychain');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function setEnvironmentVariables() {
|
||||
if (process.platform === 'linux') {
|
||||
// Use Clang for building native code, the GCC on Precise is too old.
|
||||
process.env.CC = 'clang';
|
||||
process.env.CXX = 'clang++';
|
||||
@@ -81,6 +87,7 @@ function removeTempFolders() {
|
||||
}
|
||||
|
||||
readEnvironmentVariables();
|
||||
setEnvironmentVariables();
|
||||
removeNodeModules();
|
||||
removeTempFolders();
|
||||
cp.safeExec.bind(global, 'npm install npm --loglevel error', {cwd: path.resolve(__dirname, '..', 'build')}, function() {
|
||||
|
||||
5
script/cibuild.cmd
Normal file
5
script/cibuild.cmd
Normal file
@@ -0,0 +1,5 @@
|
||||
@IF EXIST "%~dp0\node.exe" (
|
||||
"%~dp0\node.exe" "%~dp0\cibuild" %*
|
||||
) ELSE (
|
||||
node "%~dp0\cibuild" %*
|
||||
)
|
||||
@@ -20,10 +20,45 @@ describe "CommandInstaller on #darwin", ->
|
||||
spyOn(CommandInstaller::, 'getResourcesDirectory').andReturn(resourcesPath)
|
||||
spyOn(CommandInstaller::, 'getInstallDirectory').andReturn(installationPath)
|
||||
|
||||
it "shows an error dialog when installing commands interactively fails", ->
|
||||
appDelegate = jasmine.createSpyObj("appDelegate", ["confirm"])
|
||||
installer = new CommandInstaller("2.0.2", appDelegate)
|
||||
spyOn(installer, "installAtomCommand").andCallFake (__, callback) -> callback(new Error("an error"))
|
||||
|
||||
installer.installShellCommandsInteractively()
|
||||
|
||||
expect(appDelegate.confirm).toHaveBeenCalledWith({
|
||||
message: "Failed to install shell commands"
|
||||
detailedMessage: "an error"
|
||||
})
|
||||
|
||||
appDelegate.confirm.reset()
|
||||
installer.installAtomCommand.andCallFake (__, callback) -> callback()
|
||||
spyOn(installer, "installApmCommand").andCallFake (__, callback) -> callback(new Error("another error"))
|
||||
|
||||
installer.installShellCommandsInteractively()
|
||||
|
||||
expect(appDelegate.confirm).toHaveBeenCalledWith({
|
||||
message: "Failed to install shell commands"
|
||||
detailedMessage: "another error"
|
||||
})
|
||||
|
||||
it "shows a success dialog when installing commands interactively succeeds", ->
|
||||
appDelegate = jasmine.createSpyObj("appDelegate", ["confirm"])
|
||||
installer = new CommandInstaller("2.0.2", appDelegate)
|
||||
spyOn(installer, "installAtomCommand").andCallFake (__, callback) -> callback()
|
||||
spyOn(installer, "installApmCommand").andCallFake (__, callback) -> callback()
|
||||
|
||||
installer.installShellCommandsInteractively()
|
||||
|
||||
expect(appDelegate.confirm).toHaveBeenCalledWith({
|
||||
message: "Commands installed."
|
||||
detailedMessage: "The shell commands `atom` and `apm` are installed."
|
||||
})
|
||||
|
||||
describe "when using a stable version of atom", ->
|
||||
beforeEach ->
|
||||
confirm = ->
|
||||
installer = new CommandInstaller("2.0.2", confirm)
|
||||
installer = new CommandInstaller("2.0.2")
|
||||
|
||||
it "symlinks the atom command as 'atom'", ->
|
||||
installedAtomPath = path.join(installationPath, 'atom')
|
||||
|
||||
@@ -589,6 +589,59 @@ describe "Config", ->
|
||||
atom.config.transact ->
|
||||
expect(changeSpy).not.toHaveBeenCalled()
|
||||
|
||||
describe ".transactAsync(callback)", ->
|
||||
changeSpy = null
|
||||
|
||||
beforeEach ->
|
||||
changeSpy = jasmine.createSpy('onDidChange callback')
|
||||
atom.config.onDidChange("foo.bar.baz", changeSpy)
|
||||
|
||||
it "allows only one change event for the duration of the given promise if it gets resolved", ->
|
||||
promiseResult = null
|
||||
transactionPromise = atom.config.transactAsync ->
|
||||
atom.config.set("foo.bar.baz", 1)
|
||||
atom.config.set("foo.bar.baz", 2)
|
||||
atom.config.set("foo.bar.baz", 3)
|
||||
Promise.resolve("a result")
|
||||
|
||||
waitsForPromise -> transactionPromise.then (r) -> promiseResult = r
|
||||
|
||||
runs ->
|
||||
expect(promiseResult).toBe("a result")
|
||||
expect(changeSpy.callCount).toBe(1)
|
||||
expect(changeSpy.argsForCall[0][0]).toEqual(newValue: 3, oldValue: undefined)
|
||||
|
||||
it "allows only one change event for the duration of the given promise if it gets rejected", ->
|
||||
promiseError = null
|
||||
transactionPromise = atom.config.transactAsync ->
|
||||
atom.config.set("foo.bar.baz", 1)
|
||||
atom.config.set("foo.bar.baz", 2)
|
||||
atom.config.set("foo.bar.baz", 3)
|
||||
Promise.reject("an error")
|
||||
|
||||
waitsForPromise -> transactionPromise.catch (e) -> promiseError = e
|
||||
|
||||
runs ->
|
||||
expect(promiseError).toBe("an error")
|
||||
expect(changeSpy.callCount).toBe(1)
|
||||
expect(changeSpy.argsForCall[0][0]).toEqual(newValue: 3, oldValue: undefined)
|
||||
|
||||
it "allows only one change event even when the given callback throws", ->
|
||||
error = new Error("Oops!")
|
||||
promiseError = null
|
||||
transactionPromise = atom.config.transactAsync ->
|
||||
atom.config.set("foo.bar.baz", 1)
|
||||
atom.config.set("foo.bar.baz", 2)
|
||||
atom.config.set("foo.bar.baz", 3)
|
||||
throw error
|
||||
|
||||
waitsForPromise -> transactionPromise.catch (e) -> promiseError = e
|
||||
|
||||
runs ->
|
||||
expect(promiseError).toBe(error)
|
||||
expect(changeSpy.callCount).toBe(1)
|
||||
expect(changeSpy.argsForCall[0][0]).toEqual(newValue: 3, oldValue: undefined)
|
||||
|
||||
describe ".getSources()", ->
|
||||
it "returns an array of all of the config's source names", ->
|
||||
expect(atom.config.getSources()).toEqual([])
|
||||
|
||||
@@ -142,9 +142,13 @@ describe "the `grammars` global", ->
|
||||
expect(atom.grammars.selectGrammar('Rakefile', '').scopeName).toBe 'source.coffee'
|
||||
expect(atom.grammars.selectGrammar('Cakefile', '').scopeName).toBe 'source.ruby'
|
||||
|
||||
it "favors grammars with matching first-line-regexps even if custom file types match the file", ->
|
||||
it "favors user-defined file types over grammars with matching first-line-regexps", ->
|
||||
atom.config.set('core.customFileTypes', 'source.ruby': ['bootstrap'])
|
||||
expect(atom.grammars.selectGrammar('bootstrap', '#!/usr/bin/env node').scopeName).toBe 'source.js'
|
||||
expect(atom.grammars.selectGrammar('bootstrap', '#!/usr/bin/env node').scopeName).toBe 'source.ruby'
|
||||
|
||||
describe "when there is a grammar with a first line pattern, the file type of the file is known, but from a different grammar", ->
|
||||
it "favors file type over the matching pattern", ->
|
||||
expect(atom.grammars.selectGrammar('foo.rb', '#!/usr/bin/env node').scopeName).toBe 'source.ruby'
|
||||
|
||||
describe ".removeGrammar(grammar)", ->
|
||||
it "removes the grammar, so it won't be returned by selectGrammar", ->
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
return unless process.env.ATOM_INTEGRATION_TESTS_ENABLED
|
||||
# Integration tests require a fast machine and, for now, we cannot afford to
|
||||
# run them on Travis.
|
||||
return if process.env.TRAVIS
|
||||
return if process.env.CI
|
||||
|
||||
fs = require "fs-plus"
|
||||
path = require "path"
|
||||
|
||||
@@ -284,6 +284,7 @@ describe "PackageManager", ->
|
||||
expect(Package.prototype.requireMainModule.callCount).toBe 0
|
||||
|
||||
atom.packages.triggerActivationHook('language-fictitious:grammar-used')
|
||||
atom.packages.triggerDeferredActivationHooks()
|
||||
|
||||
waitsForPromise ->
|
||||
promise
|
||||
@@ -433,6 +434,13 @@ describe "PackageManager", ->
|
||||
runs ->
|
||||
expect(atom.keymaps.findKeyBindings(keystrokes: 'ctrl-z', target: element1)).toHaveLength 0
|
||||
|
||||
describe "when setting core.packagesWithKeymapsDisabled", ->
|
||||
it "ignores package names in the array that aren't loaded", ->
|
||||
atom.packages.observePackagesWithKeymapsDisabled()
|
||||
|
||||
expect(-> atom.config.set("core.packagesWithKeymapsDisabled", ["package-does-not-exist"])).not.toThrow()
|
||||
expect(-> atom.config.set("core.packagesWithKeymapsDisabled", [])).not.toThrow()
|
||||
|
||||
describe "when the package's keymaps are disabled and re-enabled after it is activated", ->
|
||||
it "removes and re-adds the keymaps", ->
|
||||
element1 = createTestElement('test-1')
|
||||
|
||||
@@ -168,6 +168,15 @@ describe "Pane", ->
|
||||
pane.activateNextItem()
|
||||
expect(pane.getActiveItem()).toBe item1
|
||||
|
||||
describe "::activateLastItem()", ->
|
||||
it "sets the active item to the last item", ->
|
||||
pane = new Pane(paneParams(items: [new Item("A"), new Item("B"), new Item("C")]))
|
||||
[item1, item2, item3] = pane.getItems()
|
||||
|
||||
expect(pane.getActiveItem()).toBe item1
|
||||
pane.activateLastItem()
|
||||
expect(pane.getActiveItem()).toBe item3
|
||||
|
||||
describe "::moveItemRight() and ::moveItemLeft()", ->
|
||||
it "moves the active item to the right and left, without looping around at either end", ->
|
||||
pane = new Pane(paneParams(items: [new Item("A"), new Item("B"), new Item("C")]))
|
||||
|
||||
@@ -25,27 +25,34 @@ describe "TextEditorPresenter", ->
|
||||
editor.destroy()
|
||||
buffer.destroy()
|
||||
|
||||
buildPresenter = (params={}) ->
|
||||
buildPresenterWithoutMeasurements = (params={}) ->
|
||||
_.defaults params,
|
||||
model: editor
|
||||
explicitHeight: 130
|
||||
contentFrameWidth: 500
|
||||
windowWidth: 500
|
||||
windowHeight: 130
|
||||
boundingClientRect: {left: 0, top: 0, width: 500, height: 130}
|
||||
gutterWidth: 0
|
||||
lineHeight: 10
|
||||
baseCharacterWidth: 10
|
||||
horizontalScrollbarHeight: 10
|
||||
verticalScrollbarWidth: 10
|
||||
scrollTop: 0
|
||||
scrollLeft: 0
|
||||
config: atom.config
|
||||
|
||||
contentFrameWidth: 500
|
||||
presenter = new TextEditorPresenter(params)
|
||||
presenter.setLinesYardstick(new FakeLinesYardstick(editor, presenter))
|
||||
presenter
|
||||
|
||||
buildPresenter = (params={}) ->
|
||||
presenter = buildPresenterWithoutMeasurements(params)
|
||||
presenter.setScrollTop(params.scrollTop) if params.scrollTop?
|
||||
presenter.setScrollLeft(params.scrollLeft) if params.scrollLeft?
|
||||
presenter.setExplicitHeight(params.explicitHeight ? 130)
|
||||
presenter.setWindowSize(params.windowWidth ? 500, params.windowHeight ? 130)
|
||||
presenter.setBoundingClientRect(params.boundingClientRect ? {
|
||||
left: 0
|
||||
top: 0
|
||||
width: 500
|
||||
height: 130
|
||||
})
|
||||
presenter.setGutterWidth(params.gutterWidth ? 0)
|
||||
presenter.setLineHeight(params.lineHeight ? 10)
|
||||
presenter.setBaseCharacterWidth(params.baseCharacterWidth ? 10)
|
||||
presenter.setHorizontalScrollbarHeight(params.horizontalScrollbarHeight ? 10)
|
||||
presenter.setVerticalScrollbarWidth(params.verticalScrollbarWidth ? 10)
|
||||
presenter
|
||||
|
||||
expectValues = (actual, expected) ->
|
||||
for key, value of expected
|
||||
expect(actual[key]).toEqual value
|
||||
@@ -167,16 +174,14 @@ describe "TextEditorPresenter", ->
|
||||
expect(stateFn(presenter).tiles[12]).toBeDefined()
|
||||
|
||||
it "is empty until all of the required measurements are assigned", ->
|
||||
presenter = buildPresenter(explicitHeight: null, lineHeight: null, scrollTop: null)
|
||||
presenter = buildPresenterWithoutMeasurements()
|
||||
expect(stateFn(presenter).tiles).toEqual({})
|
||||
|
||||
presenter.setExplicitHeight(25)
|
||||
expect(stateFn(presenter).tiles).toEqual({})
|
||||
|
||||
# Sets scroll row from model's logical position
|
||||
presenter.setLineHeight(10)
|
||||
expect(stateFn(presenter).tiles).toEqual({})
|
||||
|
||||
presenter.setScrollTop(0)
|
||||
expect(stateFn(presenter).tiles).not.toEqual({})
|
||||
|
||||
it "updates when ::scrollTop changes", ->
|
||||
@@ -619,6 +624,8 @@ describe "TextEditorPresenter", ->
|
||||
describe ".scrollingVertically", ->
|
||||
it "is true for ::stoppedScrollingDelay milliseconds following a changes to ::scrollTop", ->
|
||||
presenter = buildPresenter(scrollTop: 10, stoppedScrollingDelay: 200, explicitHeight: 100)
|
||||
expect(presenter.getState().content.scrollingVertically).toBe true
|
||||
advanceClock(300)
|
||||
expect(presenter.getState().content.scrollingVertically).toBe false
|
||||
expectStateUpdate presenter, -> presenter.setScrollTop(0)
|
||||
expect(presenter.getState().content.scrollingVertically).toBe true
|
||||
@@ -761,7 +768,8 @@ describe "TextEditorPresenter", ->
|
||||
expect(presenter.getState().content.scrollTop).toBe(10)
|
||||
|
||||
it "corresponds to the passed logical coordinates when building the presenter", ->
|
||||
presenter = buildPresenter(scrollRow: 4, lineHeight: 10, explicitHeight: 20)
|
||||
editor.setFirstVisibleScreenRow(4)
|
||||
presenter = buildPresenter(lineHeight: 10, explicitHeight: 20)
|
||||
expect(presenter.getState().content.scrollTop).toBe(40)
|
||||
|
||||
it "tracks the value of ::scrollTop", ->
|
||||
@@ -775,11 +783,11 @@ describe "TextEditorPresenter", ->
|
||||
|
||||
expectStateUpdate presenter, -> presenter.setScrollTop(50)
|
||||
presenter.getState() # commits scroll position
|
||||
expect(editor.getScrollRow()).toBe(5)
|
||||
expect(editor.getFirstVisibleScreenRow()).toBe 5
|
||||
|
||||
expectStateUpdate presenter, -> presenter.setScrollTop(57)
|
||||
presenter.getState() # commits scroll position
|
||||
expect(editor.getScrollRow()).toBe(6)
|
||||
expect(editor.getFirstVisibleScreenRow()).toBe 6
|
||||
|
||||
it "reassigns the scrollTop if it exceeds the max possible value after lines are removed", ->
|
||||
presenter = buildPresenter(scrollTop: 80, lineHeight: 10, explicitHeight: 50, horizontalScrollbarHeight: 0)
|
||||
@@ -888,7 +896,8 @@ describe "TextEditorPresenter", ->
|
||||
expect(presenter.getState().content.scrollLeft).toBe(50)
|
||||
|
||||
it "corresponds to the passed logical coordinates when building the presenter", ->
|
||||
presenter = buildPresenter(scrollColumn: 3, lineHeight: 10, baseCharacterWidth: 10, verticalScrollbarWidth: 10, contentFrameWidth: 500)
|
||||
editor.setFirstVisibleScreenColumn(3)
|
||||
presenter = buildPresenter(lineHeight: 10, baseCharacterWidth: 10, verticalScrollbarWidth: 10, contentFrameWidth: 500)
|
||||
expect(presenter.getState().content.scrollLeft).toBe(30)
|
||||
|
||||
it "tracks the value of ::scrollLeft", ->
|
||||
@@ -902,11 +911,11 @@ describe "TextEditorPresenter", ->
|
||||
|
||||
expectStateUpdate presenter, -> presenter.setScrollLeft(50)
|
||||
presenter.getState() # commits scroll position
|
||||
expect(editor.getScrollColumn()).toBe(5)
|
||||
expect(editor.getFirstVisibleScreenColumn()).toBe 5
|
||||
|
||||
expectStateUpdate presenter, -> presenter.setScrollLeft(57)
|
||||
presenter.getState() # commits scroll position
|
||||
expect(editor.getScrollColumn()).toBe(6)
|
||||
expect(editor.getFirstVisibleScreenColumn()).toBe 6
|
||||
|
||||
it "is always rounded to the nearest integer", ->
|
||||
presenter = buildPresenter(scrollLeft: 10, lineHeight: 10, baseCharacterWidth: 10, verticalScrollbarWidth: 10, contentFrameWidth: 500)
|
||||
@@ -1006,20 +1015,25 @@ describe "TextEditorPresenter", ->
|
||||
|
||||
describe ".backgroundColor", ->
|
||||
it "is assigned to ::backgroundColor unless the editor is mini", ->
|
||||
presenter = buildPresenter(backgroundColor: 'rgba(255, 0, 0, 0)')
|
||||
presenter = buildPresenter()
|
||||
presenter.setBackgroundColor('rgba(255, 0, 0, 0)')
|
||||
expect(presenter.getState().content.backgroundColor).toBe 'rgba(255, 0, 0, 0)'
|
||||
|
||||
editor.setMini(true)
|
||||
presenter = buildPresenter(backgroundColor: 'rgba(255, 0, 0, 0)')
|
||||
presenter = buildPresenter()
|
||||
presenter.setBackgroundColor('rgba(255, 0, 0, 0)')
|
||||
expect(presenter.getState().content.backgroundColor).toBeNull()
|
||||
|
||||
it "updates when ::backgroundColor changes", ->
|
||||
presenter = buildPresenter(backgroundColor: 'rgba(255, 0, 0, 0)')
|
||||
presenter = buildPresenter()
|
||||
presenter.setBackgroundColor('rgba(255, 0, 0, 0)')
|
||||
expect(presenter.getState().content.backgroundColor).toBe 'rgba(255, 0, 0, 0)'
|
||||
expectStateUpdate presenter, -> presenter.setBackgroundColor('rgba(0, 0, 255, 0)')
|
||||
expect(presenter.getState().content.backgroundColor).toBe 'rgba(0, 0, 255, 0)'
|
||||
|
||||
it "updates when ::mini changes", ->
|
||||
presenter = buildPresenter(backgroundColor: 'rgba(255, 0, 0, 0)')
|
||||
presenter = buildPresenter()
|
||||
presenter.setBackgroundColor('rgba(255, 0, 0, 0)')
|
||||
expect(presenter.getState().content.backgroundColor).toBe 'rgba(255, 0, 0, 0)'
|
||||
expectStateUpdate presenter, -> editor.setMini(true)
|
||||
expect(presenter.getState().content.backgroundColor).toBeNull()
|
||||
@@ -1047,6 +1061,7 @@ describe "TextEditorPresenter", ->
|
||||
describe "[tileId].lines[lineId]", -> # line state objects
|
||||
it "includes the state for visible lines in a tile", ->
|
||||
presenter = buildPresenter(explicitHeight: 3, scrollTop: 4, lineHeight: 1, tileSize: 3, stoppedScrollingDelay: 200)
|
||||
presenter.setExplicitHeight(3)
|
||||
|
||||
expect(lineStateForScreenRow(presenter, 2)).toBeUndefined()
|
||||
|
||||
@@ -1320,7 +1335,7 @@ describe "TextEditorPresenter", ->
|
||||
expect(stateForCursor(presenter, 4)).toBeUndefined()
|
||||
|
||||
it "is empty until all of the required measurements are assigned", ->
|
||||
presenter = buildPresenter(explicitHeight: null, lineHeight: null, scrollTop: null, baseCharacterWidth: null, horizontalScrollbarHeight: null)
|
||||
presenter = buildPresenterWithoutMeasurements()
|
||||
expect(presenter.getState().content.cursors).toEqual({})
|
||||
|
||||
presenter.setExplicitHeight(25)
|
||||
@@ -1335,6 +1350,15 @@ describe "TextEditorPresenter", ->
|
||||
presenter.setBaseCharacterWidth(8)
|
||||
expect(presenter.getState().content.cursors).toEqual({})
|
||||
|
||||
presenter.setBoundingClientRect(top: 0, left: 0, width: 500, height: 130)
|
||||
expect(presenter.getState().content.cursors).toEqual({})
|
||||
|
||||
presenter.setWindowSize(500, 130)
|
||||
expect(presenter.getState().content.cursors).toEqual({})
|
||||
|
||||
presenter.setVerticalScrollbarWidth(10)
|
||||
expect(presenter.getState().content.cursors).toEqual({})
|
||||
|
||||
presenter.setHorizontalScrollbarHeight(10)
|
||||
expect(presenter.getState().content.cursors).not.toEqual({})
|
||||
|
||||
@@ -1466,7 +1490,8 @@ describe "TextEditorPresenter", ->
|
||||
it "alternates between true and false twice per ::cursorBlinkPeriod when the editor is focused", ->
|
||||
cursorBlinkPeriod = 100
|
||||
cursorBlinkResumeDelay = 200
|
||||
presenter = buildPresenter({cursorBlinkPeriod, cursorBlinkResumeDelay, focused: true})
|
||||
presenter = buildPresenter({cursorBlinkPeriod, cursorBlinkResumeDelay})
|
||||
presenter.setFocused(true)
|
||||
|
||||
expect(presenter.getState().content.cursorsVisible).toBe true
|
||||
expectStateUpdate presenter, -> advanceClock(cursorBlinkPeriod / 2)
|
||||
@@ -1493,7 +1518,8 @@ describe "TextEditorPresenter", ->
|
||||
it "stops alternating for ::cursorBlinkResumeDelay when a cursor moves or a cursor is added", ->
|
||||
cursorBlinkPeriod = 100
|
||||
cursorBlinkResumeDelay = 200
|
||||
presenter = buildPresenter({cursorBlinkPeriod, cursorBlinkResumeDelay, focused: true})
|
||||
presenter = buildPresenter({cursorBlinkPeriod, cursorBlinkResumeDelay})
|
||||
presenter.setFocused(true)
|
||||
|
||||
expect(presenter.getState().content.cursorsVisible).toBe true
|
||||
expectStateUpdate presenter, -> advanceClock(cursorBlinkPeriod / 2)
|
||||
@@ -1643,7 +1669,7 @@ describe "TextEditorPresenter", ->
|
||||
[[0, 2], [2, 4]],
|
||||
])
|
||||
|
||||
presenter = buildPresenter(explicitHeight: null, lineHeight: null, scrollTop: null, baseCharacterWidth: null, tileSize: 2)
|
||||
presenter = buildPresenterWithoutMeasurements(tileSize: 2)
|
||||
for tileId, tileState of presenter.getState().content.tiles
|
||||
expect(tileState.highlights).toEqual({})
|
||||
|
||||
@@ -1970,7 +1996,7 @@ describe "TextEditorPresenter", ->
|
||||
marker = editor.markBufferRange([[2, 13], [4, 14]], invalidate: 'touch')
|
||||
decoration = editor.decorateMarker(marker, {type: 'overlay', position: 'tail', item})
|
||||
|
||||
presenter = buildPresenter(baseCharacterWidth: null, lineHeight: null, windowWidth: null, windowHeight: null, boundingClientRect: null)
|
||||
presenter = buildPresenterWithoutMeasurements()
|
||||
expect(presenter.getState().content.overlays).toEqual({})
|
||||
|
||||
presenter.setBaseCharacterWidth(10)
|
||||
@@ -1982,6 +2008,12 @@ describe "TextEditorPresenter", ->
|
||||
presenter.setWindowSize(500, 100)
|
||||
expect(presenter.getState().content.overlays).toEqual({})
|
||||
|
||||
presenter.setVerticalScrollbarWidth(10)
|
||||
expect(presenter.getState().content.overlays).toEqual({})
|
||||
|
||||
presenter.setHorizontalScrollbarHeight(10)
|
||||
expect(presenter.getState().content.overlays).toEqual({})
|
||||
|
||||
presenter.setBoundingClientRect({top: 0, left: 0, height: 100, width: 500})
|
||||
expect(presenter.getState().content.overlays).not.toEqual({})
|
||||
|
||||
@@ -2168,7 +2200,8 @@ describe "TextEditorPresenter", ->
|
||||
expect(editor.getRowsPerPage()).toBe(20)
|
||||
|
||||
it "tracks the computed content height if ::autoHeight is true so the editor auto-expands vertically", ->
|
||||
presenter = buildPresenter(explicitHeight: null, autoHeight: true)
|
||||
presenter = buildPresenter(explicitHeight: null)
|
||||
presenter.setAutoHeight(true)
|
||||
expect(presenter.getState().height).toBe editor.getScreenLineCount() * 10
|
||||
|
||||
expectStateUpdate presenter, -> presenter.setAutoHeight(false)
|
||||
@@ -2185,7 +2218,9 @@ describe "TextEditorPresenter", ->
|
||||
|
||||
describe ".focused", ->
|
||||
it "tracks the value of ::focused", ->
|
||||
presenter = buildPresenter(focused: false)
|
||||
presenter = buildPresenter()
|
||||
presenter.setFocused(false)
|
||||
|
||||
expect(presenter.getState().focused).toBe false
|
||||
expectStateUpdate presenter, -> presenter.setFocused(true)
|
||||
expect(presenter.getState().focused).toBe true
|
||||
@@ -2882,7 +2917,9 @@ describe "TextEditorPresenter", ->
|
||||
|
||||
describe ".backgroundColor", ->
|
||||
it "is assigned to ::gutterBackgroundColor if present, and to ::backgroundColor otherwise", ->
|
||||
presenter = buildPresenter(backgroundColor: "rgba(255, 0, 0, 0)", gutterBackgroundColor: "rgba(0, 255, 0, 0)")
|
||||
presenter = buildPresenter()
|
||||
presenter.setBackgroundColor("rgba(255, 0, 0, 0)")
|
||||
presenter.setGutterBackgroundColor("rgba(0, 255, 0, 0)")
|
||||
expect(getStylesForGutterWithName(presenter, 'line-number').backgroundColor).toBe "rgba(0, 255, 0, 0)"
|
||||
expect(getStylesForGutterWithName(presenter, 'test-gutter').backgroundColor).toBe "rgba(0, 255, 0, 0)"
|
||||
|
||||
|
||||
@@ -163,14 +163,8 @@ describe "TextEditor", ->
|
||||
expect(editor.getTitle()).toBe 'untitled'
|
||||
|
||||
describe ".getLongTitle()", ->
|
||||
it "appends the name of the containing directory to the basename of the file", ->
|
||||
expect(editor.getLongTitle()).toBe 'sample.js - fixtures'
|
||||
buffer.setPath(undefined)
|
||||
expect(editor.getLongTitle()).toBe 'untitled'
|
||||
|
||||
describe ".getUniqueTitle()", ->
|
||||
it "returns file name when there is no opened file with identical name", ->
|
||||
expect(editor.getUniqueTitle()).toBe 'sample.js'
|
||||
expect(editor.getLongTitle()).toBe 'sample.js'
|
||||
buffer.setPath(undefined)
|
||||
expect(editor.getLongTitle()).toBe 'untitled'
|
||||
|
||||
@@ -183,8 +177,8 @@ describe "TextEditor", ->
|
||||
atom.workspace.open(path.join('sample-theme-2', 'readme')).then (o) ->
|
||||
editor2 = o
|
||||
runs ->
|
||||
expect(editor1.getUniqueTitle()).toBe 'sample-theme-1/readme'
|
||||
expect(editor2.getUniqueTitle()).toBe 'sample-theme-2/readme'
|
||||
expect(editor1.getLongTitle()).toBe 'sample-theme-1/readme'
|
||||
expect(editor2.getLongTitle()).toBe 'sample-theme-2/readme'
|
||||
|
||||
it "or returns <parent-directory>/.../<filename> when opened files has identical file names", ->
|
||||
editor1 = null
|
||||
@@ -195,8 +189,8 @@ describe "TextEditor", ->
|
||||
atom.workspace.open(path.join('sample-theme-2', 'src', 'js', 'main.js')).then (o) ->
|
||||
editor2 = o
|
||||
runs ->
|
||||
expect(editor1.getUniqueTitle()).toBe 'sample-theme-1/.../main.js'
|
||||
expect(editor2.getUniqueTitle()).toBe 'sample-theme-2/.../main.js'
|
||||
expect(editor1.getLongTitle()).toBe 'sample-theme-1/.../main.js'
|
||||
expect(editor2.getLongTitle()).toBe 'sample-theme-2/.../main.js'
|
||||
|
||||
|
||||
it "notifies ::onDidChangeTitle observers when the underlying buffer path changes", ->
|
||||
@@ -5444,6 +5438,73 @@ describe "TextEditor", ->
|
||||
editor.selectPageUp()
|
||||
expect(editor.getSelectedBufferRanges()).toEqual [[[0, 0], [12, 2]]]
|
||||
|
||||
describe "::setFirstVisibleScreenRow() and ::getFirstVisibleScreenRow()", ->
|
||||
beforeEach ->
|
||||
line = Array(9).join('0123456789')
|
||||
editor.setText([1..100].map(-> line).join('\n'))
|
||||
expect(editor.getLineCount()).toBe 100
|
||||
expect(editor.lineTextForBufferRow(0).length).toBe 80
|
||||
|
||||
describe "when the editor doesn't have a height and lineHeightInPixels", ->
|
||||
it "does not affect the editor's visible row range", ->
|
||||
expect(editor.getVisibleRowRange()).toBeNull()
|
||||
|
||||
editor.setFirstVisibleScreenRow(1)
|
||||
expect(editor.getFirstVisibleScreenRow()).toEqual 1
|
||||
|
||||
editor.setFirstVisibleScreenRow(3)
|
||||
expect(editor.getFirstVisibleScreenRow()).toEqual 3
|
||||
|
||||
expect(editor.getVisibleRowRange()).toBeNull()
|
||||
expect(editor.getLastVisibleScreenRow()).toBeNull()
|
||||
|
||||
describe "when the editor has a height and lineHeightInPixels", ->
|
||||
beforeEach ->
|
||||
atom.config.set('editor.scrollPastEnd', true)
|
||||
editor.setHeight(100, true)
|
||||
editor.setLineHeightInPixels(10)
|
||||
|
||||
it "updates the editor's visible row range", ->
|
||||
editor.setFirstVisibleScreenRow(2)
|
||||
expect(editor.getFirstVisibleScreenRow()).toEqual 2
|
||||
expect(editor.getLastVisibleScreenRow()).toBe 12
|
||||
expect(editor.getVisibleRowRange()).toEqual [2, 12]
|
||||
|
||||
it "notifies ::onDidChangeFirstVisibleScreenRow observers", ->
|
||||
changeCount = 0
|
||||
editor.onDidChangeFirstVisibleScreenRow -> changeCount++
|
||||
|
||||
editor.setFirstVisibleScreenRow(2)
|
||||
expect(changeCount).toBe 1
|
||||
|
||||
editor.setFirstVisibleScreenRow(2)
|
||||
expect(changeCount).toBe 1
|
||||
|
||||
editor.setFirstVisibleScreenRow(3)
|
||||
expect(changeCount).toBe 2
|
||||
|
||||
it "ensures that the top row is less than the buffer's line count", ->
|
||||
editor.setFirstVisibleScreenRow(102)
|
||||
expect(editor.getFirstVisibleScreenRow()).toEqual 99
|
||||
expect(editor.getVisibleRowRange()).toEqual [99, 99]
|
||||
|
||||
it "ensures that the left column is less than the length of the longest screen line", ->
|
||||
editor.setFirstVisibleScreenRow(10)
|
||||
expect(editor.getFirstVisibleScreenRow()).toEqual 10
|
||||
|
||||
editor.setText("\n\n\n")
|
||||
|
||||
editor.setFirstVisibleScreenRow(10)
|
||||
expect(editor.getFirstVisibleScreenRow()).toEqual 3
|
||||
|
||||
describe "when the 'editor.scrollPastEnd' option is set to false", ->
|
||||
it "ensures that the bottom row is less than the buffer's line count", ->
|
||||
atom.config.set('editor.scrollPastEnd', false)
|
||||
|
||||
editor.setFirstVisibleScreenRow(95)
|
||||
expect(editor.getFirstVisibleScreenRow()).toEqual 89
|
||||
expect(editor.getVisibleRowRange()).toEqual [89, 99]
|
||||
|
||||
describe '.get/setPlaceholderText()', ->
|
||||
it 'can be created with placeholderText', ->
|
||||
newEditor = atom.workspace.buildTextEditor(
|
||||
|
||||
@@ -117,7 +117,7 @@ class AtomEnvironment extends Model
|
||||
|
||||
# Call .loadOrCreate instead
|
||||
constructor: (params={}) ->
|
||||
{@blobStore, @applicationDelegate, @window, @document, configDirPath, @enablePersistence} = params
|
||||
{@blobStore, @applicationDelegate, @window, @document, configDirPath, @enablePersistence, onlyLoadBaseStyleSheets} = params
|
||||
|
||||
@state = {version: @constructor.version}
|
||||
|
||||
@@ -183,7 +183,7 @@ class AtomEnvironment extends Model
|
||||
|
||||
@themes.loadBaseStylesheets()
|
||||
@initialStyleElements = @styles.getSnapshot()
|
||||
@themes.initialLoadComplete = true
|
||||
@themes.initialLoadComplete = true if onlyLoadBaseStyleSheets
|
||||
@setBodyPlatformClass()
|
||||
|
||||
@stylesElement = @styles.buildStylesElement()
|
||||
|
||||
@@ -35,7 +35,7 @@ class CommandInstaller
|
||||
process.resourcesPath
|
||||
|
||||
installShellCommandsInteractively: ->
|
||||
showErrorDialog = (error) ->
|
||||
showErrorDialog = (error) =>
|
||||
@applicationDelegate.confirm
|
||||
message: "Failed to install shell commands"
|
||||
detailedMessage: error.message
|
||||
|
||||
@@ -671,17 +671,47 @@ class Config
|
||||
#
|
||||
# * `callback` {Function} to execute while suppressing calls to handlers.
|
||||
transact: (callback) ->
|
||||
@transactDepth++
|
||||
@beginTransaction()
|
||||
try
|
||||
callback()
|
||||
finally
|
||||
@transactDepth--
|
||||
@emitChangeEvent()
|
||||
@endTransaction()
|
||||
|
||||
###
|
||||
Section: Internal methods used by core
|
||||
###
|
||||
|
||||
# Private: Suppress calls to handler functions registered with {::onDidChange}
|
||||
# and {::observe} for the duration of the {Promise} returned by `callback`.
|
||||
# After the {Promise} is either resolved or rejected, handlers will be called
|
||||
# once if the value for their key-path has changed.
|
||||
#
|
||||
# * `callback` {Function} that returns a {Promise}, which will be executed
|
||||
# while suppressing calls to handlers.
|
||||
#
|
||||
# Returns a {Promise} that is either resolved or rejected according to the
|
||||
# `{Promise}` returned by `callback`. If `callback` throws an error, a
|
||||
# rejected {Promise} will be returned instead.
|
||||
transactAsync: (callback) ->
|
||||
@beginTransaction()
|
||||
try
|
||||
endTransaction = (fn) => (args...) =>
|
||||
@endTransaction()
|
||||
fn(args...)
|
||||
result = callback()
|
||||
new Promise (resolve, reject) =>
|
||||
result.then(endTransaction(resolve)).catch(endTransaction(reject))
|
||||
catch error
|
||||
@endTransaction()
|
||||
Promise.reject(error)
|
||||
|
||||
beginTransaction: ->
|
||||
@transactDepth++
|
||||
|
||||
endTransaction: ->
|
||||
@transactDepth--
|
||||
@emitChangeEvent()
|
||||
|
||||
pushAtKeyPath: (keyPath, value) ->
|
||||
arrayValue = @get(keyPath) ? []
|
||||
result = arrayValue.push(value)
|
||||
|
||||
@@ -36,21 +36,21 @@ class GrammarRegistry extends FirstMate.GrammarRegistry
|
||||
if score > highestScore or not bestMatch?
|
||||
bestMatch = grammar
|
||||
highestScore = score
|
||||
else if score is highestScore and bestMatch?.bundledPackage
|
||||
bestMatch = grammar unless grammar.bundledPackage
|
||||
bestMatch
|
||||
|
||||
# Extended: Returns a {Number} representing how well the grammar matches the
|
||||
# `filePath` and `contents`.
|
||||
getGrammarScore: (grammar, filePath, contents) ->
|
||||
return Infinity if @grammarOverrideForPath(filePath) is grammar.scopeName
|
||||
|
||||
contents = fs.readFileSync(filePath, 'utf8') if not contents? and fs.isFileSync(filePath)
|
||||
|
||||
if @grammarOverrideForPath(filePath) is grammar.scopeName
|
||||
2 + (filePath?.length ? 0)
|
||||
else if @grammarMatchesContents(grammar, contents)
|
||||
1 + (filePath?.length ? 0)
|
||||
else
|
||||
@getGrammarPathScore(grammar, filePath)
|
||||
score = @getGrammarPathScore(grammar, filePath)
|
||||
if score > 0 and not grammar.bundledPackage
|
||||
score += 0.25
|
||||
if @grammarMatchesContents(grammar, contents)
|
||||
score += 0.125
|
||||
score
|
||||
|
||||
getGrammarPathScore: (grammar, filePath) ->
|
||||
return -1 unless filePath
|
||||
|
||||
@@ -58,6 +58,7 @@ module.exports = ({blobStore}) ->
|
||||
document.title = "Spec Suite"
|
||||
|
||||
# Avoid throttling of test window by playing silence
|
||||
# See related discussion in https://github.com/atom/atom/pull/9485
|
||||
context = new AudioContext()
|
||||
source = context.createBufferSource()
|
||||
source.connect(context.destination)
|
||||
@@ -69,6 +70,7 @@ module.exports = ({blobStore}) ->
|
||||
buildAtomEnvironment = (params) ->
|
||||
params = cloneObject(params)
|
||||
params.blobStore = blobStore unless params.hasOwnProperty("blobStore")
|
||||
params.onlyLoadBaseStyleSheets = true unless params.hasOwnProperty("onlyLoadBaseStyleSheets")
|
||||
new AtomEnvironment(params)
|
||||
|
||||
promise = testRunner({
|
||||
|
||||
@@ -37,6 +37,7 @@ class PackageManager
|
||||
@emitter = new Emitter
|
||||
@activationHookEmitter = new Emitter
|
||||
@packageDirPaths = []
|
||||
@deferredActivationHooks = []
|
||||
if configDirPath? and not safeMode
|
||||
if @devMode
|
||||
@packageDirPaths.push(path.join(configDirPath, "dev", "packages"))
|
||||
@@ -336,8 +337,10 @@ class PackageManager
|
||||
keymapsToEnable = _.difference(oldValue, newValue)
|
||||
keymapsToDisable = _.difference(newValue, oldValue)
|
||||
|
||||
@getLoadedPackage(packageName).deactivateKeymaps() for packageName in keymapsToDisable when not @isPackageDisabled(packageName)
|
||||
@getLoadedPackage(packageName).activateKeymaps() for packageName in keymapsToEnable when not @isPackageDisabled(packageName)
|
||||
for packageName in keymapsToDisable when not @isPackageDisabled(packageName)
|
||||
@getLoadedPackage(packageName)?.deactivateKeymaps()
|
||||
for packageName in keymapsToEnable when not @isPackageDisabled(packageName)
|
||||
@getLoadedPackage(packageName)?.activateKeymaps()
|
||||
null
|
||||
|
||||
loadPackages: ->
|
||||
@@ -407,6 +410,7 @@ class PackageManager
|
||||
packages = @getLoadedPackagesForTypes(types)
|
||||
promises = promises.concat(activator.activatePackages(packages))
|
||||
Promise.all(promises).then =>
|
||||
@triggerDeferredActivationHooks()
|
||||
@emitter.emit 'did-activate-initial-packages'
|
||||
|
||||
# another type of package manager can handle other package types.
|
||||
@@ -416,11 +420,11 @@ class PackageManager
|
||||
|
||||
activatePackages: (packages) ->
|
||||
promises = []
|
||||
@config.transact =>
|
||||
@config.transactAsync =>
|
||||
for pack in packages
|
||||
promise = @activatePackage(pack.name)
|
||||
promises.push(promise) unless pack.hasActivationCommands()
|
||||
return
|
||||
promises.push(promise) unless pack.activationShouldBeDeferred()
|
||||
Promise.all(promises)
|
||||
@observeDisabledPackages()
|
||||
@observePackagesWithKeymapsDisabled()
|
||||
promises
|
||||
@@ -437,9 +441,17 @@ class PackageManager
|
||||
else
|
||||
Promise.reject(new Error("Failed to load package '#{name}'"))
|
||||
|
||||
triggerDeferredActivationHooks: ->
|
||||
return unless @deferredActivationHooks?
|
||||
@activationHookEmitter.emit(hook) for hook in @deferredActivationHooks
|
||||
@deferredActivationHooks = null
|
||||
|
||||
triggerActivationHook: (hook) ->
|
||||
return new Error("Cannot trigger an empty activation hook") unless hook? and _.isString(hook) and hook.length > 0
|
||||
@activationHookEmitter.emit(hook)
|
||||
if @deferredActivationHooks?
|
||||
@deferredActivationHooks.push hook
|
||||
else
|
||||
@activationHookEmitter.emit(hook)
|
||||
|
||||
onDidTriggerActivationHook: (hook, callback) ->
|
||||
return unless hook? and _.isString(hook) and hook.length > 0
|
||||
|
||||
@@ -312,6 +312,9 @@ class Pane extends Model
|
||||
else
|
||||
@activateItemAtIndex(@items.length - 1)
|
||||
|
||||
activateLastItem: ->
|
||||
@activateItemAtIndex(@items.length - 1)
|
||||
|
||||
# Public: Move the active tab to the right.
|
||||
moveItemRight: ->
|
||||
index = @getActiveItemIndex()
|
||||
@@ -722,7 +725,7 @@ class Pane extends Model
|
||||
@notificationManager.addWarning("Unable to save file: #{error.message}")
|
||||
else if error.code is 'EACCES'
|
||||
addWarningWithPath('Unable to save file: Permission denied')
|
||||
else if error.code in ['EPERM', 'EBUSY', 'UNKNOWN', 'EEXIST']
|
||||
else if error.code in ['EPERM', 'EBUSY', 'UNKNOWN', 'EEXIST', 'ELOOP', 'EAGAIN']
|
||||
addWarningWithPath('Unable to save file', detail: error.message)
|
||||
else if error.code is 'EROFS'
|
||||
addWarningWithPath('Unable to save file: Read-only file system')
|
||||
|
||||
@@ -55,7 +55,7 @@ class Project extends Model
|
||||
###
|
||||
|
||||
deserialize: (state, deserializerManager) ->
|
||||
states.paths = [state.path] if state.path? # backward compatibility
|
||||
state.paths = [state.path] if state.path? # backward compatibility
|
||||
|
||||
@buffers = _.compact state.buffers.map (bufferState) ->
|
||||
# Check that buffer's file path is accessible
|
||||
|
||||
@@ -12,7 +12,7 @@ module.exports = ({commandRegistry, commandInstaller, config}) ->
|
||||
'pane:show-item-6': -> @getModel().getActivePane().activateItemAtIndex(5)
|
||||
'pane:show-item-7': -> @getModel().getActivePane().activateItemAtIndex(6)
|
||||
'pane:show-item-8': -> @getModel().getActivePane().activateItemAtIndex(7)
|
||||
'pane:show-item-9': -> @getModel().getActivePane().activateItemAtIndex(8)
|
||||
'pane:show-item-9': -> @getModel().getActivePane().activateLastItem()
|
||||
'pane:move-item-right': -> @getModel().getActivePane().moveItemRight()
|
||||
'pane:move-item-left': -> @getModel().getActivePane().moveItemLeft()
|
||||
'window:increase-font-size': -> @getModel().increaseFontSize()
|
||||
|
||||
@@ -50,10 +50,6 @@ class TextEditorComponent
|
||||
|
||||
@presenter = new TextEditorPresenter
|
||||
model: @editor
|
||||
scrollTop: 0
|
||||
scrollLeft: 0
|
||||
scrollRow: @editor.getScrollRow()
|
||||
scrollColumn: @editor.getScrollColumn()
|
||||
tileSize: tileSize
|
||||
cursorBlinkPeriod: @cursorBlinkPeriod
|
||||
cursorBlinkResumeDelay: @cursorBlinkResumeDelay
|
||||
|
||||
@@ -13,15 +13,12 @@ class TextEditorPresenter
|
||||
minimumReflowInterval: 200
|
||||
|
||||
constructor: (params) ->
|
||||
{@model, @config, @autoHeight, @explicitHeight, @contentFrameWidth, @scrollTop, @scrollLeft, @scrollColumn, @scrollRow, @boundingClientRect, @windowWidth, @windowHeight, @gutterWidth} = params
|
||||
{horizontalScrollbarHeight, verticalScrollbarWidth} = params
|
||||
{@lineHeight, @baseCharacterWidth, @backgroundColor, @gutterBackgroundColor, @tileSize} = params
|
||||
{@cursorBlinkPeriod, @cursorBlinkResumeDelay, @stoppedScrollingDelay, @focused} = params
|
||||
@measuredHorizontalScrollbarHeight = horizontalScrollbarHeight
|
||||
@measuredVerticalScrollbarWidth = verticalScrollbarWidth
|
||||
@gutterWidth ?= 0
|
||||
@tileSize ?= 6
|
||||
{@model, @config} = params
|
||||
{@cursorBlinkPeriod, @cursorBlinkResumeDelay, @stoppedScrollingDelay, @tileSize} = params
|
||||
{@contentFrameWidth} = params
|
||||
|
||||
@gutterWidth = 0
|
||||
@tileSize ?= 6
|
||||
@realScrollTop = @scrollTop
|
||||
@realScrollLeft = @scrollLeft
|
||||
@disposables = new CompositeDisposable
|
||||
@@ -77,7 +74,6 @@ class TextEditorPresenter
|
||||
@updateVerticalDimensions()
|
||||
@updateScrollbarDimensions()
|
||||
|
||||
@restoreScrollPosition()
|
||||
@commitPendingLogicalScrollTopPosition()
|
||||
@commitPendingScrollTopPosition()
|
||||
|
||||
@@ -218,6 +214,7 @@ class TextEditorPresenter
|
||||
|
||||
@disposables.add @model.onDidAddCursor(@didAddCursor.bind(this))
|
||||
@disposables.add @model.onDidRequestAutoscroll(@requestAutoscroll.bind(this))
|
||||
@disposables.add @model.onDidChangeFirstVisibleScreenRow(@didChangeFirstVisibleScreenRow.bind(this))
|
||||
@observeCursor(cursor) for cursor in @model.getCursors()
|
||||
@disposables.add @model.onDidAddGutter(@didAddGutter.bind(this))
|
||||
return
|
||||
@@ -778,8 +775,7 @@ class TextEditorPresenter
|
||||
if scrollTop isnt @realScrollTop and not Number.isNaN(scrollTop)
|
||||
@realScrollTop = scrollTop
|
||||
@scrollTop = Math.round(scrollTop)
|
||||
@scrollRow = Math.round(@scrollTop / @lineHeight)
|
||||
@model.setScrollRow(@scrollRow)
|
||||
@model.setFirstVisibleScreenRow(Math.round(@scrollTop / @lineHeight), true)
|
||||
|
||||
@updateStartRow()
|
||||
@updateEndRow()
|
||||
@@ -795,8 +791,7 @@ class TextEditorPresenter
|
||||
if scrollLeft isnt @realScrollLeft and not Number.isNaN(scrollLeft)
|
||||
@realScrollLeft = scrollLeft
|
||||
@scrollLeft = Math.round(scrollLeft)
|
||||
@scrollColumn = Math.round(@scrollLeft / @baseCharacterWidth)
|
||||
@model.setScrollColumn(@scrollColumn)
|
||||
@model.setFirstVisibleScreenColumn(Math.round(@scrollLeft / @baseCharacterWidth))
|
||||
|
||||
@emitter.emit 'did-change-scroll-left', @scrollLeft
|
||||
|
||||
@@ -1095,6 +1090,7 @@ class TextEditorPresenter
|
||||
setLineHeight: (lineHeight) ->
|
||||
unless @lineHeight is lineHeight
|
||||
@lineHeight = lineHeight
|
||||
@restoreScrollTopIfNeeded()
|
||||
@model.setLineHeightInPixels(lineHeight)
|
||||
@shouldUpdateHeightState = true
|
||||
@shouldUpdateHorizontalScrollState = true
|
||||
@@ -1122,6 +1118,7 @@ class TextEditorPresenter
|
||||
@halfWidthCharWidth = halfWidthCharWidth
|
||||
@koreanCharWidth = koreanCharWidth
|
||||
@model.setDefaultCharWidth(baseCharacterWidth, doubleWidthCharWidth, halfWidthCharWidth, koreanCharWidth)
|
||||
@restoreScrollLeftIfNeeded()
|
||||
@characterWidthsChanged()
|
||||
|
||||
characterWidthsChanged: ->
|
||||
@@ -1433,6 +1430,9 @@ class TextEditorPresenter
|
||||
|
||||
@emitDidUpdateState()
|
||||
|
||||
didChangeFirstVisibleScreenRow: (screenRow) ->
|
||||
@updateScrollTop(screenRow * @lineHeight)
|
||||
|
||||
getVerticalScrollMarginInPixels: ->
|
||||
Math.round(@model.getVerticalScrollMargin() * @lineHeight)
|
||||
|
||||
@@ -1512,14 +1512,6 @@ class TextEditorPresenter
|
||||
@updateScrollTop(@pendingScrollTop)
|
||||
@pendingScrollTop = null
|
||||
|
||||
restoreScrollPosition: ->
|
||||
return if @hasRestoredScrollPosition or not @hasPixelPositionRequirements()
|
||||
|
||||
@setScrollTop(@scrollRow * @lineHeight) if @scrollRow?
|
||||
@setScrollLeft(@scrollColumn * @baseCharacterWidth) if @scrollColumn?
|
||||
|
||||
@hasRestoredScrollPosition = true
|
||||
|
||||
clearPendingScrollPosition: ->
|
||||
@pendingScrollLogicalPosition = null
|
||||
@pendingScrollTop = null
|
||||
@@ -1531,6 +1523,14 @@ class TextEditorPresenter
|
||||
canScrollTopTo: (scrollTop) ->
|
||||
@scrollTop isnt @constrainScrollTop(scrollTop)
|
||||
|
||||
restoreScrollTopIfNeeded: ->
|
||||
unless @scrollTop?
|
||||
@updateScrollTop(@model.getFirstVisibleScreenRow() * @lineHeight)
|
||||
|
||||
restoreScrollLeftIfNeeded: ->
|
||||
unless @scrollLeft?
|
||||
@updateScrollLeft(@model.getFirstVisibleScreenColumn() * @baseCharacterWidth)
|
||||
|
||||
onDidChangeScrollTop: (callback) ->
|
||||
@emitter.on 'did-change-scroll-top', callback
|
||||
|
||||
|
||||
@@ -54,13 +54,11 @@ GutterContainer = require './gutter-container'
|
||||
# soft wraps and folds to ensure your code interacts with them correctly.
|
||||
module.exports =
|
||||
class TextEditor extends Model
|
||||
callDisplayBufferCreatedHook: false
|
||||
buffer: null
|
||||
languageMode: null
|
||||
cursors: null
|
||||
selections: null
|
||||
suppressSelectionMerging: false
|
||||
updateBatchDepth: 0
|
||||
selectionFlashDuration: 500
|
||||
gutterContainer: null
|
||||
|
||||
@@ -90,7 +88,7 @@ class TextEditor extends Model
|
||||
super
|
||||
|
||||
{
|
||||
@softTabs, @scrollRow, @scrollColumn, initialLine, initialColumn, tabLength,
|
||||
@softTabs, @firstVisibleScreenRow, @firstVisibleScreenColumn, initialLine, initialColumn, tabLength,
|
||||
softWrapped, @displayBuffer, @selectionsMarkerLayer, buffer, suppressCursorCreation,
|
||||
@mini, @placeholderText, lineNumberGutterVisible, largeFileMode, @config,
|
||||
@notificationManager, @packageManager, @clipboard, @viewRegistry, @grammarRegistry,
|
||||
@@ -106,6 +104,8 @@ class TextEditor extends Model
|
||||
throw new Error("Must pass a project parameter when constructing TextEditors") unless @project?
|
||||
throw new Error("Must pass an assert parameter when constructing TextEditors") unless @assert?
|
||||
|
||||
@firstVisibleScreenRow ?= 0
|
||||
@firstVisibleScreenColumn ?= 0
|
||||
@emitter = new Emitter
|
||||
@disposables = new CompositeDisposable
|
||||
@cursors = []
|
||||
@@ -146,8 +146,8 @@ class TextEditor extends Model
|
||||
deserializer: 'TextEditor'
|
||||
id: @id
|
||||
softTabs: @softTabs
|
||||
scrollRow: @getScrollRow()
|
||||
scrollColumn: @getScrollColumn()
|
||||
firstVisibleScreenRow: @getFirstVisibleScreenRow()
|
||||
firstVisibleScreenColumn: @getFirstVisibleScreenColumn()
|
||||
displayBuffer: @displayBuffer.serialize()
|
||||
selectionsMarkerLayerId: @selectionsMarkerLayer.id
|
||||
|
||||
@@ -453,6 +453,9 @@ class TextEditor extends Model
|
||||
onDidChangeCharacterWidths: (callback) ->
|
||||
@displayBuffer.onDidChangeCharacterWidths(callback)
|
||||
|
||||
onDidChangeFirstVisibleScreenRow: (callback, fromView) ->
|
||||
@emitter.on 'did-change-first-visible-screen-row', callback
|
||||
|
||||
onDidChangeScrollTop: (callback) ->
|
||||
Grim.deprecate("This is now a view method. Call TextEditorElement::onDidChangeScrollTop instead.")
|
||||
|
||||
@@ -583,18 +586,18 @@ class TextEditor extends Model
|
||||
else
|
||||
'untitled'
|
||||
|
||||
# Essential: Get unique title for display in other parts of the UI
|
||||
# such as the window title.
|
||||
# Essential: Get unique title for display in other parts of the UI, such as
|
||||
# the window title.
|
||||
#
|
||||
# If the editor's buffer is unsaved, its title is "untitled"
|
||||
# If the editor's buffer is saved, its unique title is formatted as one
|
||||
# of the following,
|
||||
# * "<filename>" when it is the only editing buffer with this file name.
|
||||
# * "<unique-dir-prefix>/.../<filename>", where the "..." may be omitted
|
||||
# if the the direct parent directory is already different.
|
||||
# if the the direct parent directory is already different.
|
||||
#
|
||||
# Returns a {String}
|
||||
getUniqueTitle: ->
|
||||
getLongTitle: ->
|
||||
if sessionPath = @getPath()
|
||||
title = @getTitle()
|
||||
|
||||
@@ -622,22 +625,6 @@ class TextEditor extends Model
|
||||
else
|
||||
'untitled'
|
||||
|
||||
# Essential: Get the editor's long title for display in other parts of the UI
|
||||
# such as the window title.
|
||||
#
|
||||
# If the editor's buffer is saved, its long title is formatted as
|
||||
# "<filename> - <directory>". If it is unsaved, its title is "untitled"
|
||||
#
|
||||
# Returns a {String}.
|
||||
getLongTitle: ->
|
||||
if sessionPath = @getPath()
|
||||
fileName = path.basename(sessionPath)
|
||||
directory = @project.relativize(path.dirname(sessionPath))
|
||||
directory = if directory.length > 0 then directory else path.basename(path.dirname(sessionPath))
|
||||
"#{fileName} - #{directory}"
|
||||
else
|
||||
'untitled'
|
||||
|
||||
# Essential: Returns the {String} path of this editor's text buffer.
|
||||
getPath: -> @buffer.getPath()
|
||||
|
||||
@@ -3130,14 +3117,6 @@ class TextEditor extends Model
|
||||
@placeholderText = placeholderText
|
||||
@emitter.emit 'did-change-placeholder-text', @placeholderText
|
||||
|
||||
getFirstVisibleScreenRow: ->
|
||||
deprecate("This is now a view method. Call TextEditorElement::getFirstVisibleScreenRow instead.")
|
||||
@viewRegistry.getView(this).getVisibleRowRange()[0]
|
||||
|
||||
getLastVisibleScreenRow: ->
|
||||
Grim.deprecate("This is now a view method. Call TextEditorElement::getLastVisibleScreenRow instead.")
|
||||
@viewRegistry.getView(this).getVisibleRowRange()[1]
|
||||
|
||||
pixelPositionForBufferPosition: (bufferPosition) ->
|
||||
Grim.deprecate("This method is deprecated on the model layer. Use `TextEditorElement::pixelPositionForBufferPosition` instead")
|
||||
@viewRegistry.getView(this).pixelPositionForBufferPosition(bufferPosition)
|
||||
@@ -3192,11 +3171,40 @@ class TextEditor extends Model
|
||||
Grim.deprecate("This is now a view method. Call TextEditorElement::getWidth instead.")
|
||||
@displayBuffer.getWidth()
|
||||
|
||||
getScrollRow: -> @scrollRow
|
||||
setScrollRow: (@scrollRow) ->
|
||||
# Experimental: Scroll the editor such that the given screen row is at the
|
||||
# top of the visible area.
|
||||
setFirstVisibleScreenRow: (screenRow, fromView) ->
|
||||
unless fromView
|
||||
maxScreenRow = @getLineCount() - 1
|
||||
unless @config.get('editor.scrollPastEnd')
|
||||
height = @displayBuffer.getHeight()
|
||||
lineHeightInPixels = @displayBuffer.getLineHeightInPixels()
|
||||
if height? and lineHeightInPixels?
|
||||
maxScreenRow -= Math.floor(height / lineHeightInPixels)
|
||||
screenRow = Math.max(Math.min(screenRow, maxScreenRow), 0)
|
||||
|
||||
getScrollColumn: -> @scrollColumn
|
||||
setScrollColumn: (@scrollColumn) ->
|
||||
unless screenRow is @firstVisibleScreenRow
|
||||
@firstVisibleScreenRow = screenRow
|
||||
@emitter.emit 'did-change-first-visible-screen-row', screenRow unless fromView
|
||||
|
||||
getFirstVisibleScreenRow: -> @firstVisibleScreenRow
|
||||
|
||||
getLastVisibleScreenRow: ->
|
||||
height = @displayBuffer.getHeight()
|
||||
lineHeightInPixels = @displayBuffer.getLineHeightInPixels()
|
||||
if height? and lineHeightInPixels?
|
||||
Math.min(@firstVisibleScreenRow + Math.floor(height / lineHeightInPixels), @getLineCount() - 1)
|
||||
else
|
||||
null
|
||||
|
||||
getVisibleRowRange: ->
|
||||
if lastVisibleScreenRow = @getLastVisibleScreenRow()
|
||||
[@firstVisibleScreenRow, lastVisibleScreenRow]
|
||||
else
|
||||
null
|
||||
|
||||
setFirstVisibleScreenColumn: (@firstVisibleScreenColumn) ->
|
||||
getFirstVisibleScreenColumn: -> @firstVisibleScreenColumn
|
||||
|
||||
getScrollTop: ->
|
||||
Grim.deprecate("This is now a view method. Call TextEditorElement::getScrollTop instead.")
|
||||
@@ -3253,11 +3261,6 @@ class TextEditor extends Model
|
||||
|
||||
@viewRegistry.getView(this).getMaxScrollTop()
|
||||
|
||||
getVisibleRowRange: ->
|
||||
Grim.deprecate("This is now a view method. Call TextEditorElement::getVisibleRowRange instead.")
|
||||
|
||||
@viewRegistry.getView(this).getVisibleRowRange()
|
||||
|
||||
intersectsVisibleRowRange: (startRow, endRow) ->
|
||||
Grim.deprecate("This is now a view method. Call TextEditorElement::intersectsVisibleRowRange instead.")
|
||||
|
||||
|
||||
@@ -468,7 +468,7 @@ class Workspace extends Model
|
||||
when 'EACCES'
|
||||
@notificationManager.addWarning("Permission denied '#{error.path}'")
|
||||
return Promise.resolve()
|
||||
when 'EPERM', 'EBUSY', 'ENXIO', 'EIO', 'ENOTCONN', 'UNKNOWN', 'ECONNRESET', 'EINVAL'
|
||||
when 'EPERM', 'EBUSY', 'ENXIO', 'EIO', 'ENOTCONN', 'UNKNOWN', 'ECONNRESET', 'EINVAL', 'EMFILE', 'ENOTDIR'
|
||||
@notificationManager.addWarning("Unable to open '#{error.path ? uri}'", detail: error.message)
|
||||
return Promise.resolve()
|
||||
else
|
||||
|
||||
Reference in New Issue
Block a user