mirror of
https://github.com/atom/atom.git
synced 2026-01-23 13:58:08 -05:00
Merge branch 'master' into sm-custom-title-bar
This commit is contained in:
@@ -4,7 +4,7 @@ git:
|
||||
matrix:
|
||||
include:
|
||||
- os: linux
|
||||
env: NODE_VERSION=4.4.7 DISPLAY=:99.0 CC=clang CXX=clang++ npm_config_clang=1
|
||||
env: NODE_VERSION=6.9.4 DISPLAY=:99.0 CC=clang CXX=clang++ npm_config_clang=1
|
||||
|
||||
sudo: false
|
||||
|
||||
@@ -19,7 +19,9 @@ install:
|
||||
- npm install -g npm
|
||||
- script/build --create-debian-package --create-rpm-package --compress-artifacts
|
||||
|
||||
script: script/test
|
||||
script:
|
||||
- script/lint
|
||||
- script/test
|
||||
|
||||
cache:
|
||||
directories:
|
||||
|
||||
@@ -93,7 +93,7 @@ repeat these steps to upgrade to future releases.
|
||||
## Building
|
||||
|
||||
* [Linux](./docs/build-instructions/linux.md)
|
||||
* [macOS](./docs/build-instructions/macos.md)
|
||||
* [macOS](./docs/build-instructions/macOS.md)
|
||||
* [FreeBSD](./docs/build-instructions/freebsd.md)
|
||||
* [Windows](./docs/build-instructions/windows.md)
|
||||
|
||||
|
||||
@@ -6,6 +6,6 @@
|
||||
"url": "https://github.com/atom/atom.git"
|
||||
},
|
||||
"dependencies": {
|
||||
"atom-package-manager": "1.15.1"
|
||||
"atom-package-manager": "1.15.3"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ build_script:
|
||||
- script\build.cmd --code-sign --create-windows-installer --compress-artifacts
|
||||
|
||||
test_script:
|
||||
- script\lint.cmd
|
||||
- script\test.cmd
|
||||
|
||||
deploy: off
|
||||
|
||||
@@ -26,7 +26,7 @@ export default async function ({test}) {
|
||||
console.log(text.length / 1024)
|
||||
|
||||
let t0 = window.performance.now()
|
||||
const buffer = new TextBuffer(text)
|
||||
const buffer = new TextBuffer({text})
|
||||
const editor = new TextEditor({buffer, largeFileMode: true})
|
||||
atom.workspace.getActivePane().activateItem(editor)
|
||||
let t1 = window.performance.now()
|
||||
|
||||
97
benchmarks/text-editor-long-lines.bench.js
Normal file
97
benchmarks/text-editor-long-lines.bench.js
Normal file
@@ -0,0 +1,97 @@
|
||||
/** @babel */
|
||||
|
||||
import path from 'path'
|
||||
import fs from 'fs'
|
||||
import {TextEditor, TextBuffer} from 'atom'
|
||||
|
||||
const SIZES_IN_KB = [
|
||||
512,
|
||||
1024,
|
||||
2048
|
||||
]
|
||||
const REPEATED_TEXT = fs.readFileSync(path.join(__dirname, '..', 'spec', 'fixtures', 'sample.js'), 'utf8').replace(/\n/g, '')
|
||||
const TEXT = REPEATED_TEXT.repeat(Math.ceil(SIZES_IN_KB[SIZES_IN_KB.length - 1] * 1024 / REPEATED_TEXT.length))
|
||||
|
||||
export default async function ({test}) {
|
||||
const data = []
|
||||
|
||||
const workspaceElement = atom.views.getView(atom.workspace)
|
||||
document.body.appendChild(workspaceElement)
|
||||
|
||||
atom.packages.loadPackages()
|
||||
await atom.packages.activate()
|
||||
|
||||
console.log(atom.getLoadSettings().resourcePath);
|
||||
|
||||
for (let pane of atom.workspace.getPanes()) {
|
||||
pane.destroy()
|
||||
}
|
||||
|
||||
for (const sizeInKB of SIZES_IN_KB) {
|
||||
const text = TEXT.slice(0, sizeInKB * 1024)
|
||||
console.log(text.length / 1024)
|
||||
|
||||
let t0 = window.performance.now()
|
||||
const buffer = new TextBuffer({text})
|
||||
const editor = new TextEditor({buffer, largeFileMode: true})
|
||||
editor.setGrammar(atom.grammars.grammarForScopeName('source.js'))
|
||||
atom.workspace.getActivePane().activateItem(editor)
|
||||
let t1 = window.performance.now()
|
||||
|
||||
data.push({
|
||||
name: 'Opening a large single-line file',
|
||||
x: sizeInKB,
|
||||
duration: t1 - t0
|
||||
})
|
||||
|
||||
const tickDurations = []
|
||||
for (let i = 0; i < 20; i++) {
|
||||
await timeout(50)
|
||||
t0 = window.performance.now()
|
||||
await timeout(0)
|
||||
t1 = window.performance.now()
|
||||
tickDurations[i] = t1 - t0
|
||||
}
|
||||
|
||||
data.push({
|
||||
name: 'Max time event loop was blocked after opening a large single-line file',
|
||||
x: sizeInKB,
|
||||
duration: Math.max(...tickDurations)
|
||||
})
|
||||
|
||||
t0 = window.performance.now()
|
||||
editor.setCursorScreenPosition(editor.element.screenPositionForPixelPosition({
|
||||
top: 100,
|
||||
left: 30
|
||||
}))
|
||||
t1 = window.performance.now()
|
||||
|
||||
data.push({
|
||||
name: 'Clicking the editor after opening a large single-line file',
|
||||
x: sizeInKB,
|
||||
duration: t1 - t0
|
||||
})
|
||||
|
||||
t0 = window.performance.now()
|
||||
editor.element.setScrollTop(editor.element.getScrollTop() + 100)
|
||||
t1 = window.performance.now()
|
||||
|
||||
data.push({
|
||||
name: 'Scrolling down after opening a large single-line file',
|
||||
x: sizeInKB,
|
||||
duration: t1 - t0
|
||||
})
|
||||
|
||||
editor.destroy()
|
||||
buffer.destroy()
|
||||
await timeout(10000)
|
||||
}
|
||||
|
||||
workspaceElement.remove()
|
||||
|
||||
return data
|
||||
}
|
||||
|
||||
function timeout (duration) {
|
||||
return new Promise((resolve) => setTimeout(resolve, duration))
|
||||
}
|
||||
@@ -16,8 +16,8 @@ general:
|
||||
dependencies:
|
||||
pre:
|
||||
- curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.31.3/install.sh | bash
|
||||
- nvm install 4.4.7
|
||||
- nvm use 4.4.7
|
||||
- nvm install 6.9.4
|
||||
- nvm use 6.9.4
|
||||
- npm install -g npm
|
||||
|
||||
override:
|
||||
|
||||
@@ -71,6 +71,7 @@
|
||||
| [PathWatcher](https://github.com/atom/node-pathwatcher) | [](https://travis-ci.org/atom/node-pathwatcher) | [](https://ci.appveyor.com/project/Atom/node-pathwatcher) | [](https://david-dm.org/atom/node-pathwatcher) |
|
||||
| [Property Accessors](https://github.com/atom/property-accessors) | [](https://travis-ci.org/atom/property-accessors) | [](https://ci.appveyor.com/project/Atom/property-accessors/branch/master) | [](https://david-dm.org/atom/property-accessors) |
|
||||
| [Season](https://github.com/atom/season) | [](https://travis-ci.org/atom/season) | [](https://ci.appveyor.com/project/Atom/season) | [](https://david-dm.org/atom/season) |
|
||||
| [Superstring](https://github.com/atom/superstring) | [](https://travis-ci.org/atom/superstring) | [](https://ci.appveyor.com/project/Atom/superstring/branch/master) | | [](https://david-dm.org/atom/superstring) |
|
||||
| [TextBuffer](https://github.com/atom/text-buffer) | [](https://travis-ci.org/atom/text-buffer) | [](https://ci.appveyor.com/project/Atom/text-buffer/branch/master) | [](https://david-dm.org/atom/text-buffer) |
|
||||
| [Underscore-Plus](https://github.com/atom/underscore-plus) | [](https://travis-ci.org/atom/underscore-plus) | [](https://ci.appveyor.com/project/Atom/underscore-plus/branch/master) | [](https://david-dm.org/atom/underscore-plus) |
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ Ubuntu LTS 12.04 64-bit is the recommended platform.
|
||||
* OS with 64-bit or 32-bit architecture
|
||||
* C++11 toolchain
|
||||
* Git
|
||||
* Node.js 4.4.x or later (we recommend installing it via [nvm](https://github.com/creationix/nvm))
|
||||
* Node.js 6.x (we recommend installing it via [nvm](https://github.com/creationix/nvm))
|
||||
* npm 3.10.x or later (run `npm install -g npm`)
|
||||
* Ensure node-gyp uses python2 (run `npm config set python /usr/bin/python2 -g`, use `sudo` if you didn't install node via nvm)
|
||||
* Development headers for [GNOME Keyring](https://wiki.gnome.org/Projects/GnomeKeyring).
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
## Requirements
|
||||
|
||||
* macOS 10.8 or later
|
||||
* Node.js 4.4.x or later (we recommend installing it via [nvm](https://github.com/creationix/nvm))
|
||||
* Node.js 6.x (we recommend installing it via [nvm](https://github.com/creationix/nvm))
|
||||
* npm 3.10.x or later (run `npm install -g npm`)
|
||||
* Command Line Tools for [Xcode](https://developer.apple.com/xcode/downloads/) (run `xcode-select --install` to install)
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
## Requirements
|
||||
|
||||
* Node.js 4.4.x or later (the architecture of node available to the build system will determine whether you build 32-bit or 64-bit Atom)
|
||||
* Node.js 6.x (the architecture of node available to the build system will determine whether you build 32-bit or 64-bit Atom)
|
||||
* Python v2.7.x
|
||||
* The python.exe must be available at `%SystemDrive%\Python27\python.exe`. If it is installed elsewhere create a symbolic link to the directory containing the python.exe using: `mklink /d %SystemDrive%\Python27 D:\elsewhere\Python27`
|
||||
* 7zip (7z.exe available from the command line) - for creating distribution zip files
|
||||
|
||||
105
package.json
105
package.json
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "atom",
|
||||
"productName": "Atom",
|
||||
"version": "1.15.0-dev",
|
||||
"version": "1.16.0-dev",
|
||||
"description": "A hackable text editor for the 21st Century.",
|
||||
"main": "./src/main-process/main.js",
|
||||
"repository": {
|
||||
@@ -15,10 +15,21 @@
|
||||
"electronVersion": "1.3.13",
|
||||
"dependencies": {
|
||||
"async": "0.2.6",
|
||||
"atom-keymap": "7.1.18",
|
||||
"atom-select-list": "0.0.6",
|
||||
"atom-keymap": "7.1.20",
|
||||
"atom-select-list": "0.0.12",
|
||||
"atom-ui": "0.4.1",
|
||||
"babel-core": "5.8.38",
|
||||
"babel-core": "6.22.1",
|
||||
"babel-plugin-add-module-exports": "0.2.1",
|
||||
"babel-plugin-transform-async-to-generator": "6.22.0",
|
||||
"babel-plugin-transform-class-properties": "6.23.0",
|
||||
"babel-plugin-transform-decorators-legacy": "1.3.4",
|
||||
"babel-plugin-transform-do-expressions": "6.22.0",
|
||||
"babel-plugin-transform-es2015-modules-commonjs": "6.23.0",
|
||||
"babel-plugin-transform-export-extensions": "6.22.0",
|
||||
"babel-plugin-transform-flow-strip-types": "6.22.0",
|
||||
"babel-plugin-transform-function-bind": "6.22.0",
|
||||
"babel-plugin-transform-object-rest-spread": "6.23.0",
|
||||
"babel-plugin-transform-react-jsx": "6.23.0",
|
||||
"cached-run-in-this-context": "0.4.1",
|
||||
"chai": "3.5.0",
|
||||
"chart.js": "^2.3.0",
|
||||
@@ -65,7 +76,7 @@
|
||||
"sinon": "1.17.4",
|
||||
"source-map-support": "^0.3.2",
|
||||
"temp": "0.8.1",
|
||||
"text-buffer": "10.2.3",
|
||||
"text-buffer": "10.3.11",
|
||||
"typescript-simple": "1.0.0",
|
||||
"underscore-plus": "^1.6.6",
|
||||
"winreg": "^1.2.1",
|
||||
@@ -82,85 +93,85 @@
|
||||
"one-light-ui": "1.9.1",
|
||||
"one-dark-syntax": "1.7.1",
|
||||
"one-light-syntax": "1.7.1",
|
||||
"solarized-dark-syntax": "1.1.1",
|
||||
"solarized-light-syntax": "1.1.1",
|
||||
"solarized-dark-syntax": "1.1.2",
|
||||
"solarized-light-syntax": "1.1.2",
|
||||
"about": "1.7.2",
|
||||
"archive-view": "0.62.2",
|
||||
"autocomplete-atom-api": "0.10.0",
|
||||
"autocomplete-css": "0.14.2",
|
||||
"autocomplete-css": "0.15.0",
|
||||
"autocomplete-html": "0.7.2",
|
||||
"autocomplete-plus": "2.34.2",
|
||||
"autocomplete-snippets": "1.11.0",
|
||||
"autoflow": "0.29.0",
|
||||
"autosave": "0.24.0",
|
||||
"background-tips": "0.26.1",
|
||||
"bookmarks": "0.43.4",
|
||||
"bookmarks": "0.44.1",
|
||||
"bracket-matcher": "0.85.2",
|
||||
"command-palette": "0.39.2",
|
||||
"deprecation-cop": "0.55.1",
|
||||
"command-palette": "0.40.2",
|
||||
"deprecation-cop": "0.56.2",
|
||||
"dev-live-reload": "0.47.0",
|
||||
"encoding-selector": "0.22.0",
|
||||
"exception-reporting": "0.40.1",
|
||||
"find-and-replace": "0.206.0",
|
||||
"encoding-selector": "0.23.1",
|
||||
"exception-reporting": "0.41.1",
|
||||
"find-and-replace": "0.206.3",
|
||||
"fuzzy-finder": "1.4.1",
|
||||
"git-diff": "1.2.0",
|
||||
"go-to-line": "0.31.2",
|
||||
"grammar-selector": "0.48.2",
|
||||
"image-view": "0.60.0",
|
||||
"git-diff": "1.3.1",
|
||||
"go-to-line": "0.32.0",
|
||||
"grammar-selector": "0.49.2",
|
||||
"image-view": "0.61.0",
|
||||
"incompatible-packages": "0.26.1",
|
||||
"keybinding-resolver": "0.35.0",
|
||||
"line-ending-selector": "0.5.1",
|
||||
"keybinding-resolver": "0.36.1",
|
||||
"line-ending-selector": "0.6.1",
|
||||
"link": "0.31.2",
|
||||
"markdown-preview": "0.159.2",
|
||||
"metrics": "1.1.2",
|
||||
"notifications": "0.66.1",
|
||||
"markdown-preview": "0.159.7",
|
||||
"metrics": "1.2.0",
|
||||
"notifications": "0.66.2",
|
||||
"open-on-github": "1.2.1",
|
||||
"package-generator": "1.0.2",
|
||||
"settings-view": "0.246.0",
|
||||
"package-generator": "1.1.0",
|
||||
"settings-view": "0.247.0",
|
||||
"snippets": "1.0.5",
|
||||
"spell-check": "0.70.2",
|
||||
"status-bar": "1.7.0",
|
||||
"styleguide": "0.48.0",
|
||||
"spell-check": "0.71.0",
|
||||
"status-bar": "1.8.1",
|
||||
"styleguide": "0.49.2",
|
||||
"symbols-view": "0.114.0",
|
||||
"tabs": "0.104.1",
|
||||
"timecop": "0.34.0",
|
||||
"tree-view": "0.213.1",
|
||||
"timecop": "0.35.0",
|
||||
"tree-view": "0.214.1",
|
||||
"update-package-dependencies": "0.10.0",
|
||||
"welcome": "0.36.0",
|
||||
"whitespace": "0.36.1",
|
||||
"wrap-guide": "0.39.0",
|
||||
"language-c": "0.54.1",
|
||||
"language-clojure": "0.22.1",
|
||||
"language-coffee-script": "0.48.2",
|
||||
"language-csharp": "0.14.0",
|
||||
"welcome": "0.36.1",
|
||||
"whitespace": "0.36.2",
|
||||
"wrap-guide": "0.39.1",
|
||||
"language-c": "0.56.0",
|
||||
"language-clojure": "0.22.2",
|
||||
"language-coffee-script": "0.48.4",
|
||||
"language-csharp": "0.14.1",
|
||||
"language-css": "0.42.0",
|
||||
"language-gfm": "0.88.0",
|
||||
"language-git": "0.19.0",
|
||||
"language-go": "0.43.1",
|
||||
"language-html": "0.47.1",
|
||||
"language-html": "0.47.2",
|
||||
"language-hyperlink": "0.16.1",
|
||||
"language-java": "0.25.0",
|
||||
"language-javascript": "0.125.1",
|
||||
"language-java": "0.26.0",
|
||||
"language-javascript": "0.126.0",
|
||||
"language-json": "0.18.3",
|
||||
"language-less": "0.30.1",
|
||||
"language-make": "0.22.3",
|
||||
"language-mustache": "0.13.1",
|
||||
"language-objective-c": "0.15.1",
|
||||
"language-perl": "0.37.0",
|
||||
"language-php": "0.37.3",
|
||||
"language-php": "0.37.4",
|
||||
"language-property-list": "0.9.0",
|
||||
"language-python": "0.45.1",
|
||||
"language-ruby": "0.70.4",
|
||||
"language-ruby-on-rails": "0.25.1",
|
||||
"language-python": "0.45.2",
|
||||
"language-ruby": "0.70.5",
|
||||
"language-ruby-on-rails": "0.25.2",
|
||||
"language-sass": "0.57.1",
|
||||
"language-shellscript": "0.25.0",
|
||||
"language-source": "0.9.0",
|
||||
"language-sql": "0.25.2",
|
||||
"language-sql": "0.25.3",
|
||||
"language-text": "0.7.1",
|
||||
"language-todo": "0.29.1",
|
||||
"language-toml": "0.18.1",
|
||||
"language-xml": "0.34.15",
|
||||
"language-yaml": "0.27.2"
|
||||
"language-xml": "0.34.16",
|
||||
"language-yaml": "0.28.0"
|
||||
},
|
||||
"private": true,
|
||||
"scripts": {
|
||||
|
||||
@@ -7,7 +7,7 @@ URL: https://atom.io/
|
||||
AutoReqProv: no # Avoid libchromiumcontent.so missing dependency
|
||||
Prefix: <%= installDir %>
|
||||
|
||||
Requires: lsb-core-noarch, libXss.so.1
|
||||
Requires: lsb-core-noarch, libXss.so.1()(64bit)
|
||||
|
||||
%description
|
||||
<%= description %>
|
||||
|
||||
@@ -5,15 +5,17 @@ const path = require('path')
|
||||
const spawnSync = require('./spawn-sync')
|
||||
|
||||
module.exports = function (packagedAppPath) {
|
||||
if (!process.env.ATOM_MAC_CODE_SIGNING_CERT_DOWNLOAD_URL) {
|
||||
if (!process.env.ATOM_MAC_CODE_SIGNING_CERT_DOWNLOAD_URL && !process.env.ATOM_MAC_CODE_SIGNING_CERT_PATH) {
|
||||
console.log('Skipping code signing because the ATOM_MAC_CODE_SIGNING_CERT_DOWNLOAD_URL environment variable is not defined'.gray)
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
const certPath = path.join(os.tmpdir(), 'mac.p12')
|
||||
let certPath = process.env.ATOM_MAC_CODE_SIGNING_CERT_PATH;
|
||||
if (!certPath) {
|
||||
certPath = path.join(os.tmpdir(), 'mac.p12')
|
||||
downloadFileFromGithub(process.env.ATOM_MAC_CODE_SIGNING_CERT_DOWNLOAD_URL, certPath)
|
||||
|
||||
}
|
||||
try {
|
||||
console.log(`Unlocking keychain ${process.env.ATOM_MAC_CODE_SIGNING_KEYCHAIN}`)
|
||||
const unlockArgs = ['unlock-keychain']
|
||||
// For signing on local workstations, password could be entered interactively
|
||||
@@ -31,6 +33,18 @@ module.exports = function (packagedAppPath) {
|
||||
'-T', '/usr/bin/codesign'
|
||||
])
|
||||
|
||||
|
||||
console.log('Running incantation to suppress dialog when signing on macOS Sierra')
|
||||
try {
|
||||
spawnSync('security', [
|
||||
'set-key-partition-list', '-S', 'apple-tool:,apple:', '-s',
|
||||
'-k', process.env.ATOM_MAC_CODE_SIGNING_KEYCHAIN_PASSWORD,
|
||||
process.env.ATOM_MAC_CODE_SIGNING_KEYCHAIN
|
||||
])
|
||||
} catch (e) {
|
||||
console.log('Incantation failed... maybe this isn\'t Sierra?');
|
||||
}
|
||||
|
||||
console.log(`Code-signing application at ${packagedAppPath}`)
|
||||
spawnSync('codesign', [
|
||||
'--deep', '--force', '--verbose',
|
||||
@@ -38,7 +52,9 @@ module.exports = function (packagedAppPath) {
|
||||
'--sign', 'Developer ID Application: GitHub', packagedAppPath
|
||||
], {stdio: 'inherit'})
|
||||
} finally {
|
||||
console.log(`Deleting certificate at ${certPath}`)
|
||||
fs.removeSync(certPath)
|
||||
if (!process.env.ATOM_MAC_CODE_SIGNING_CERT_PATH) {
|
||||
console.log(`Deleting certificate at ${certPath}`)
|
||||
fs.removeSync(certPath)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,15 +18,19 @@ module.exports = function (packagedAppPath, codeSign) {
|
||||
iconUrl: `https://raw.githubusercontent.com/atom/atom/master/resources/app-icons/${CONFIG.channel}/atom.ico`,
|
||||
loadingGif: path.join(CONFIG.repositoryRootPath, 'resources', 'win', 'loading.gif'),
|
||||
outputDirectory: CONFIG.buildOutputPath,
|
||||
remoteReleases: `https://atom.io/api/updates${archSuffix}`,
|
||||
remoteReleases: `https://atom.io/api/updates${archSuffix}?version=${CONFIG.appMetadata.version}`,
|
||||
setupIcon: path.join(CONFIG.repositoryRootPath, 'resources', 'app-icons', CONFIG.channel, 'atom.ico')
|
||||
}
|
||||
|
||||
const certPath = path.join(os.tmpdir(), 'win.p12')
|
||||
const signing = codeSign && process.env.ATOM_WIN_CODE_SIGNING_CERT_DOWNLOAD_URL
|
||||
const signing = codeSign && (process.env.ATOM_WIN_CODE_SIGNING_CERT_DOWNLOAD_URL || process.env.ATOM_WIN_CODE_SIGNING_CERT_PATH)
|
||||
let certPath = process.env.ATOM_WIN_CODE_SIGNING_CERT_PATH;
|
||||
|
||||
if (signing) {
|
||||
downloadFileFromGithub(process.env.ATOM_WIN_CODE_SIGNING_CERT_DOWNLOAD_URL, certPath)
|
||||
if (!certPath) {
|
||||
certPath = path.join(os.tmpdir(), 'win.p12')
|
||||
downloadFileFromGithub(process.env.ATOM_WIN_CODE_SIGNING_CERT_DOWNLOAD_URL, certPath)
|
||||
}
|
||||
|
||||
var signParams = []
|
||||
signParams.push(`/f ${certPath}`) // Signing cert file
|
||||
signParams.push(`/p ${process.env.ATOM_WIN_CODE_SIGNING_CERT_PASSWORD}`) // Signing cert password
|
||||
@@ -39,7 +43,7 @@ module.exports = function (packagedAppPath, codeSign) {
|
||||
}
|
||||
|
||||
const cleanUp = function () {
|
||||
if (fs.existsSync(certPath)) {
|
||||
if (fs.existsSync(certPath) && !process.env.ATOM_WIN_CODE_SIGNING_CERT_PATH) {
|
||||
console.log(`Deleting certificate at ${certPath}`)
|
||||
fs.removeSync(certPath)
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
"description": "Atom build scripts",
|
||||
"dependencies": {
|
||||
"async": "2.0.1",
|
||||
"babel-core": "5.8.38",
|
||||
"coffeelint": "1.15.7",
|
||||
"colors": "1.1.2",
|
||||
"csslint": "1.0.2",
|
||||
|
||||
@@ -142,6 +142,11 @@ describe "AtomEnvironment", ->
|
||||
atom.assert(false, "a == b", (e) -> error = e)
|
||||
expect(error).toBe errors[0]
|
||||
|
||||
describe "if passed metadata", ->
|
||||
it "assigns the metadata on the assertion failure's error object", ->
|
||||
atom.assert(false, "a == b", {foo: 'bar'})
|
||||
expect(errors[0].metadata).toEqual {foo: 'bar'}
|
||||
|
||||
describe "if the condition is true", ->
|
||||
it "does nothing", ->
|
||||
result = atom.assert(true, "a == b")
|
||||
|
||||
@@ -8,8 +8,7 @@ import path from 'path'
|
||||
const temp = require('temp').track()
|
||||
|
||||
describe("AtomPaths", () => {
|
||||
const portableAtomHomePath = path.join(atomPaths.getAppDirectory(), '.atom')
|
||||
console.log(portableAtomHomePath)
|
||||
const portableAtomHomePath = path.join(atomPaths.getAppDirectory(), '..', '.atom')
|
||||
|
||||
afterEach(() => {
|
||||
atomPaths.setAtomHome(app.getPath('home'))
|
||||
|
||||
@@ -67,6 +67,29 @@ describe "BufferedProcess", ->
|
||||
expect(window.onerror.mostRecentCall.args[0]).toContain 'Failed to spawn command `bad-command-nope2`'
|
||||
expect(window.onerror.mostRecentCall.args[4].name).toBe 'BufferedProcessError'
|
||||
|
||||
describe "when autoStart is false", ->
|
||||
it "doesnt start unless start method is called", ->
|
||||
stdout = ''
|
||||
stderr = ''
|
||||
exitCallback = jasmine.createSpy('exit callback')
|
||||
apmProcess = new BufferedProcess
|
||||
autoStart: false
|
||||
command: atom.packages.getApmPath()
|
||||
args: ['-h']
|
||||
options: {}
|
||||
stdout: (lines) -> stdout += lines
|
||||
stderr: (lines) -> stderr += lines
|
||||
exit: exitCallback
|
||||
|
||||
expect(apmProcess.started).not.toBe(true)
|
||||
apmProcess.start()
|
||||
expect(apmProcess.started).toBe(true)
|
||||
|
||||
waitsFor -> exitCallback.callCount is 1
|
||||
runs ->
|
||||
expect(stderr).toContain 'apm - Atom Package Manager'
|
||||
expect(stdout).toEqual ''
|
||||
|
||||
it "calls the specified stdout, stderr, and exit callbacks", ->
|
||||
stdout = ''
|
||||
stderr = ''
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
DecorationManager = require '../src/decoration-manager'
|
||||
|
||||
describe "DecorationManager", ->
|
||||
[decorationManager, buffer, defaultMarkerLayer] = []
|
||||
[decorationManager, buffer, displayLayer, markerLayer1, markerLayer2] = []
|
||||
|
||||
beforeEach ->
|
||||
buffer = atom.project.bufferForPathSync('sample.js')
|
||||
displayLayer = buffer.addDisplayLayer()
|
||||
defaultMarkerLayer = displayLayer.addMarkerLayer()
|
||||
decorationManager = new DecorationManager(displayLayer, defaultMarkerLayer)
|
||||
markerLayer1 = displayLayer.addMarkerLayer()
|
||||
markerLayer2 = displayLayer.addMarkerLayer()
|
||||
decorationManager = new DecorationManager(displayLayer)
|
||||
|
||||
waitsForPromise ->
|
||||
atom.packages.activatePackage('language-javascript')
|
||||
@@ -17,38 +18,53 @@ describe "DecorationManager", ->
|
||||
buffer.release()
|
||||
|
||||
describe "decorations", ->
|
||||
[marker, decoration, decorationProperties] = []
|
||||
[layer1Marker, layer2Marker, layer1MarkerDecoration, layer2MarkerDecoration, decorationProperties] = []
|
||||
beforeEach ->
|
||||
marker = defaultMarkerLayer.markBufferRange([[2, 13], [3, 15]])
|
||||
layer1Marker = markerLayer1.markBufferRange([[2, 13], [3, 15]])
|
||||
decorationProperties = {type: 'line-number', class: 'one'}
|
||||
decoration = decorationManager.decorateMarker(marker, decorationProperties)
|
||||
layer1MarkerDecoration = decorationManager.decorateMarker(layer1Marker, decorationProperties)
|
||||
layer2Marker = markerLayer2.markBufferRange([[2, 13], [3, 15]])
|
||||
layer2MarkerDecoration = decorationManager.decorateMarker(layer2Marker, decorationProperties)
|
||||
|
||||
it "can add decorations associated with markers and remove them", ->
|
||||
expect(decoration).toBeDefined()
|
||||
expect(decoration.getProperties()).toBe decorationProperties
|
||||
expect(decorationManager.decorationForId(decoration.id)).toBe decoration
|
||||
expect(decorationManager.decorationsForScreenRowRange(2, 3)[marker.id][0]).toBe decoration
|
||||
expect(layer1MarkerDecoration).toBeDefined()
|
||||
expect(layer1MarkerDecoration.getProperties()).toBe decorationProperties
|
||||
expect(decorationManager.decorationForId(layer1MarkerDecoration.id)).toBe layer1MarkerDecoration
|
||||
expect(decorationManager.decorationsForScreenRowRange(2, 3)).toEqual {
|
||||
"#{layer1Marker.id}": [layer1MarkerDecoration],
|
||||
"#{layer2Marker.id}": [layer2MarkerDecoration]
|
||||
}
|
||||
|
||||
decoration.destroy()
|
||||
expect(decorationManager.decorationsForScreenRowRange(2, 3)[marker.id]).not.toBeDefined()
|
||||
expect(decorationManager.decorationForId(decoration.id)).not.toBeDefined()
|
||||
layer1MarkerDecoration.destroy()
|
||||
expect(decorationManager.decorationsForScreenRowRange(2, 3)[layer1Marker.id]).not.toBeDefined()
|
||||
expect(decorationManager.decorationForId(layer1MarkerDecoration.id)).not.toBeDefined()
|
||||
layer2MarkerDecoration.destroy()
|
||||
expect(decorationManager.decorationsForScreenRowRange(2, 3)[layer2Marker.id]).not.toBeDefined()
|
||||
expect(decorationManager.decorationForId(layer2MarkerDecoration.id)).not.toBeDefined()
|
||||
|
||||
it "will not fail if the decoration is removed twice", ->
|
||||
decoration.destroy()
|
||||
decoration.destroy()
|
||||
expect(decorationManager.decorationForId(decoration.id)).not.toBeDefined()
|
||||
layer1MarkerDecoration.destroy()
|
||||
layer1MarkerDecoration.destroy()
|
||||
expect(decorationManager.decorationForId(layer1MarkerDecoration.id)).not.toBeDefined()
|
||||
|
||||
it "does not allow destroyed markers to be decorated", ->
|
||||
marker.destroy()
|
||||
layer1Marker.destroy()
|
||||
expect(->
|
||||
decorationManager.decorateMarker(marker, {type: 'overlay', item: document.createElement('div')})
|
||||
decorationManager.decorateMarker(layer1Marker, {type: 'overlay', item: document.createElement('div')})
|
||||
).toThrow("Cannot decorate a destroyed marker")
|
||||
expect(decorationManager.getOverlayDecorations()).toEqual []
|
||||
|
||||
it "does not allow destroyed marker layers to be decorated", ->
|
||||
layer = displayLayer.addMarkerLayer()
|
||||
layer.destroy()
|
||||
expect(->
|
||||
decorationManager.decorateMarkerLayer(layer, {type: 'highlight'})
|
||||
).toThrow("Cannot decorate a destroyed marker layer")
|
||||
|
||||
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: 'line-number', class: 'two'
|
||||
layer1MarkerDecoration.onDidChangeProperties updatedSpy = jasmine.createSpy()
|
||||
layer1MarkerDecoration.setProperties type: 'line-number', class: 'two'
|
||||
|
||||
{oldProperties, newProperties} = updatedSpy.mostRecentCall.args[0]
|
||||
expect(oldProperties).toEqual decorationProperties
|
||||
@@ -56,29 +72,29 @@ describe "DecorationManager", ->
|
||||
|
||||
describe "::getDecorations(properties)", ->
|
||||
it "returns decorations matching the given optional properties", ->
|
||||
expect(decorationManager.getDecorations()).toEqual [decoration]
|
||||
expect(decorationManager.getDecorations()).toEqual [layer1MarkerDecoration, layer2MarkerDecoration]
|
||||
expect(decorationManager.getDecorations(class: 'two').length).toEqual 0
|
||||
expect(decorationManager.getDecorations(class: 'one').length).toEqual 1
|
||||
expect(decorationManager.getDecorations(class: 'one').length).toEqual 2
|
||||
|
||||
describe "::decorateMarker", ->
|
||||
describe "when decorating gutters", ->
|
||||
[marker] = []
|
||||
[layer1Marker] = []
|
||||
|
||||
beforeEach ->
|
||||
marker = defaultMarkerLayer.markBufferRange([[1, 0], [1, 0]])
|
||||
layer1Marker = markerLayer1.markBufferRange([[1, 0], [1, 0]])
|
||||
|
||||
it "creates a decoration that is both of 'line-number' and 'gutter' type when called with the 'line-number' type", ->
|
||||
decorationProperties = {type: 'line-number', class: 'one'}
|
||||
decoration = decorationManager.decorateMarker(marker, decorationProperties)
|
||||
expect(decoration.isType('line-number')).toBe true
|
||||
expect(decoration.isType('gutter')).toBe true
|
||||
expect(decoration.getProperties().gutterName).toBe 'line-number'
|
||||
expect(decoration.getProperties().class).toBe 'one'
|
||||
layer1MarkerDecoration = decorationManager.decorateMarker(layer1Marker, decorationProperties)
|
||||
expect(layer1MarkerDecoration.isType('line-number')).toBe true
|
||||
expect(layer1MarkerDecoration.isType('gutter')).toBe true
|
||||
expect(layer1MarkerDecoration.getProperties().gutterName).toBe 'line-number'
|
||||
expect(layer1MarkerDecoration.getProperties().class).toBe 'one'
|
||||
|
||||
it "creates a decoration that is only of 'gutter' type if called with the 'gutter' type and a 'gutterName'", ->
|
||||
decorationProperties = {type: 'gutter', gutterName: 'test-gutter', class: 'one'}
|
||||
decoration = decorationManager.decorateMarker(marker, decorationProperties)
|
||||
expect(decoration.isType('gutter')).toBe true
|
||||
expect(decoration.isType('line-number')).toBe false
|
||||
expect(decoration.getProperties().gutterName).toBe 'test-gutter'
|
||||
expect(decoration.getProperties().class).toBe 'one'
|
||||
layer1MarkerDecoration = decorationManager.decorateMarker(layer1Marker, decorationProperties)
|
||||
expect(layer1MarkerDecoration.isType('gutter')).toBe true
|
||||
expect(layer1MarkerDecoration.isType('line-number')).toBe false
|
||||
expect(layer1MarkerDecoration.getProperties().gutterName).toBe 'test-gutter'
|
||||
expect(layer1MarkerDecoration.getProperties().class).toBe 'one'
|
||||
|
||||
@@ -28,6 +28,15 @@ describe "DefaultDirectoryProvider", ->
|
||||
directory = provider.directoryForURISync(nonNormalizedPath)
|
||||
expect(directory.getPath()).toEqual tmp
|
||||
|
||||
it "normalizes disk drive letter in path on #win32", ->
|
||||
provider = new DefaultDirectoryProvider()
|
||||
nonNormalizedPath = tmp[0].toLowerCase()+tmp.slice(1)
|
||||
expect(tmp).not.toMatch /^[a-z]:/
|
||||
expect(nonNormalizedPath).toMatch /^[a-z]:/
|
||||
|
||||
directory = provider.directoryForURISync(nonNormalizedPath)
|
||||
expect(directory.getPath()).toEqual tmp
|
||||
|
||||
it "creates a Directory for its parent dir when passed a file", ->
|
||||
provider = new DefaultDirectoryProvider()
|
||||
file = path.join(tmp, "example.txt")
|
||||
|
||||
@@ -1,60 +0,0 @@
|
||||
DOMElementPool = require '../src/dom-element-pool'
|
||||
{contains} = require 'underscore-plus'
|
||||
|
||||
describe "DOMElementPool", ->
|
||||
domElementPool = null
|
||||
|
||||
beforeEach ->
|
||||
domElementPool = new DOMElementPool
|
||||
|
||||
it "builds DOM nodes, recycling them when they are freed", ->
|
||||
[div, span1, span2, span3, span4, span5, textNode] = elements = [
|
||||
domElementPool.buildElement("div")
|
||||
domElementPool.buildElement("span")
|
||||
domElementPool.buildElement("span")
|
||||
domElementPool.buildElement("span")
|
||||
domElementPool.buildElement("span")
|
||||
domElementPool.buildElement("span")
|
||||
domElementPool.buildText("Hello world!")
|
||||
]
|
||||
|
||||
div.appendChild(span1)
|
||||
span1.appendChild(span2)
|
||||
div.appendChild(span3)
|
||||
span3.appendChild(span4)
|
||||
span4.appendChild(textNode)
|
||||
|
||||
domElementPool.freeElementAndDescendants(div)
|
||||
domElementPool.freeElementAndDescendants(span5)
|
||||
|
||||
expect(contains(elements, domElementPool.buildElement("div"))).toBe(true)
|
||||
expect(contains(elements, domElementPool.buildElement("span"))).toBe(true)
|
||||
expect(contains(elements, domElementPool.buildElement("span"))).toBe(true)
|
||||
expect(contains(elements, domElementPool.buildElement("span"))).toBe(true)
|
||||
expect(contains(elements, domElementPool.buildElement("span"))).toBe(true)
|
||||
expect(contains(elements, domElementPool.buildElement("span"))).toBe(true)
|
||||
expect(contains(elements, domElementPool.buildText("another text"))).toBe(true)
|
||||
|
||||
expect(contains(elements, domElementPool.buildElement("div"))).toBe(false)
|
||||
expect(contains(elements, domElementPool.buildElement("span"))).toBe(false)
|
||||
expect(contains(elements, domElementPool.buildText("unexisting"))).toBe(false)
|
||||
|
||||
it "forgets free nodes after being cleared", ->
|
||||
span = domElementPool.buildElement("span")
|
||||
div = domElementPool.buildElement("div")
|
||||
domElementPool.freeElementAndDescendants(span)
|
||||
domElementPool.freeElementAndDescendants(div)
|
||||
|
||||
domElementPool.clear()
|
||||
|
||||
expect(domElementPool.buildElement("span")).not.toBe(span)
|
||||
expect(domElementPool.buildElement("div")).not.toBe(div)
|
||||
|
||||
it "throws an error when trying to free the same node twice", ->
|
||||
div = domElementPool.buildElement("div")
|
||||
domElementPool.freeElementAndDescendants(div)
|
||||
expect(-> domElementPool.freeElementAndDescendants(div)).toThrow()
|
||||
|
||||
it "throws an error when trying to free an invalid element", ->
|
||||
expect(-> domElementPool.freeElementAndDescendants(null)).toThrow()
|
||||
expect(-> domElementPool.freeElementAndDescendants(undefined)).toThrow()
|
||||
112
spec/dom-element-pool-spec.js
Normal file
112
spec/dom-element-pool-spec.js
Normal file
@@ -0,0 +1,112 @@
|
||||
const DOMElementPool = require ('../src/dom-element-pool')
|
||||
|
||||
describe('DOMElementPool', function () {
|
||||
let domElementPool
|
||||
|
||||
beforeEach(() => { domElementPool = new DOMElementPool() })
|
||||
|
||||
it('builds DOM nodes, recycling them when they are freed', function () {
|
||||
let elements
|
||||
const [div, span1, span2, span3, span4, span5, textNode] = Array.from(elements = [
|
||||
domElementPool.buildElement('div', 'foo'),
|
||||
domElementPool.buildElement('span'),
|
||||
domElementPool.buildElement('span'),
|
||||
domElementPool.buildElement('span'),
|
||||
domElementPool.buildElement('span'),
|
||||
domElementPool.buildElement('span'),
|
||||
domElementPool.buildText('Hello world!')
|
||||
])
|
||||
|
||||
expect(div.className).toBe('foo')
|
||||
div.textContent = 'testing'
|
||||
div.style.backgroundColor = 'red'
|
||||
div.dataset.foo = 'bar'
|
||||
|
||||
expect(textNode.textContent).toBe('Hello world!')
|
||||
|
||||
div.appendChild(span1)
|
||||
span1.appendChild(span2)
|
||||
div.appendChild(span3)
|
||||
span3.appendChild(span4)
|
||||
span4.appendChild(textNode)
|
||||
|
||||
domElementPool.freeElementAndDescendants(div)
|
||||
domElementPool.freeElementAndDescendants(span5)
|
||||
|
||||
expect(elements.includes(domElementPool.buildElement('div'))).toBe(true)
|
||||
expect(elements.includes(domElementPool.buildElement('span'))).toBe(true)
|
||||
expect(elements.includes(domElementPool.buildElement('span'))).toBe(true)
|
||||
expect(elements.includes(domElementPool.buildElement('span'))).toBe(true)
|
||||
expect(elements.includes(domElementPool.buildElement('span'))).toBe(true)
|
||||
expect(elements.includes(domElementPool.buildElement('span'))).toBe(true)
|
||||
expect(elements.includes(domElementPool.buildText('another text'))).toBe(true)
|
||||
|
||||
expect(elements.includes(domElementPool.buildElement('div'))).toBe(false)
|
||||
expect(elements.includes(domElementPool.buildElement('span'))).toBe(false)
|
||||
expect(elements.includes(domElementPool.buildText('unexisting'))).toBe(false)
|
||||
|
||||
expect(div.className).toBe('')
|
||||
expect(div.textContent).toBe('')
|
||||
expect(div.style.backgroundColor).toBe('')
|
||||
expect(div.dataset.foo).toBeUndefined()
|
||||
|
||||
expect(textNode.textContent).toBe('another text')
|
||||
})
|
||||
|
||||
it('forgets free nodes after being cleared', function () {
|
||||
const span = domElementPool.buildElement('span')
|
||||
const div = domElementPool.buildElement('div')
|
||||
domElementPool.freeElementAndDescendants(span)
|
||||
domElementPool.freeElementAndDescendants(div)
|
||||
|
||||
domElementPool.clear()
|
||||
|
||||
expect(domElementPool.buildElement('span')).not.toBe(span)
|
||||
expect(domElementPool.buildElement('div')).not.toBe(div)
|
||||
})
|
||||
|
||||
it('does not attempt to free nodes that were not created by the pool', () => {
|
||||
let assertionFailure
|
||||
atom.onDidFailAssertion((error) => assertionFailure = error)
|
||||
|
||||
const foreignDiv = document.createElement('div')
|
||||
const div = domElementPool.buildElement('div')
|
||||
div.appendChild(foreignDiv)
|
||||
domElementPool.freeElementAndDescendants(div)
|
||||
const span = domElementPool.buildElement('span')
|
||||
span.appendChild(foreignDiv)
|
||||
domElementPool.freeElementAndDescendants(span)
|
||||
|
||||
expect(assertionFailure).toBeUndefined()
|
||||
})
|
||||
|
||||
it('fails an assertion when freeing the same element twice', function () {
|
||||
let assertionFailure
|
||||
atom.onDidFailAssertion((error) => assertionFailure = error)
|
||||
|
||||
const div = domElementPool.buildElement('div')
|
||||
div.textContent = 'testing'
|
||||
domElementPool.freeElementAndDescendants(div)
|
||||
expect(assertionFailure).toBeUndefined()
|
||||
domElementPool.freeElementAndDescendants(div)
|
||||
expect(assertionFailure.message).toBe('Assertion failed: The element has already been freed!')
|
||||
expect(assertionFailure.metadata.content).toBe('<div>testing</div>')
|
||||
})
|
||||
|
||||
it('fails an assertion when freeing the same text node twice', function () {
|
||||
let assertionFailure
|
||||
atom.onDidFailAssertion((error) => assertionFailure = error)
|
||||
|
||||
const node = domElementPool.buildText('testing')
|
||||
domElementPool.freeElementAndDescendants(node)
|
||||
expect(assertionFailure).toBeUndefined()
|
||||
domElementPool.freeElementAndDescendants(node)
|
||||
expect(assertionFailure.message).toBe('Assertion failed: The element has already been freed!')
|
||||
expect(assertionFailure.metadata.content).toBe('testing')
|
||||
})
|
||||
|
||||
it('throws an error when trying to free an invalid element', function () {
|
||||
expect(() => domElementPool.freeElementAndDescendants(null)).toThrow()
|
||||
expect(() => domElementPool.freeElementAndDescendants(undefined)).toThrow()
|
||||
})
|
||||
})
|
||||
@@ -78,10 +78,16 @@ describe "LinesYardstick", ->
|
||||
expect(linesYardstick.pixelPositionForScreenPosition(Point(0, 0))).toEqual({left: 0, top: 0})
|
||||
expect(linesYardstick.pixelPositionForScreenPosition(Point(0, 1))).toEqual({left: 7, top: 0})
|
||||
expect(linesYardstick.pixelPositionForScreenPosition(Point(0, 5))).toEqual({left: 38, top: 0})
|
||||
if process.platform is 'darwin' # One pixel off on left on Win32
|
||||
expect(linesYardstick.pixelPositionForScreenPosition(Point(1, 6))).toEqual({left: 43, top: 14})
|
||||
expect(linesYardstick.pixelPositionForScreenPosition(Point(1, 9))).toEqual({left: 72, top: 14})
|
||||
expect(linesYardstick.pixelPositionForScreenPosition(Point(2, Infinity))).toEqual({left: 287.875, top: 28})
|
||||
|
||||
switch process.platform
|
||||
when 'darwin'
|
||||
expect(linesYardstick.pixelPositionForScreenPosition(Point(1, 6))).toEqual({left: 43, top: 14})
|
||||
expect(linesYardstick.pixelPositionForScreenPosition(Point(1, 9))).toEqual({left: 72, top: 14})
|
||||
expect(linesYardstick.pixelPositionForScreenPosition(Point(2, Infinity))).toEqual({left: 287.875, top: 28})
|
||||
when 'win32'
|
||||
expect(linesYardstick.pixelPositionForScreenPosition(Point(1, 6))).toEqual({left: 42, top: 14})
|
||||
expect(linesYardstick.pixelPositionForScreenPosition(Point(1, 9))).toEqual({left: 71, top: 14})
|
||||
expect(linesYardstick.pixelPositionForScreenPosition(Point(2, Infinity))).toEqual({left: 280, top: 28})
|
||||
|
||||
it "reuses already computed pixel positions unless it is invalidated", ->
|
||||
atom.styles.addStyleSheet """
|
||||
@@ -134,28 +140,40 @@ describe "LinesYardstick", ->
|
||||
|
||||
editor.setText(text)
|
||||
|
||||
return unless process.platform is 'darwin' # These numbers are 15 higher on win32 and always integer
|
||||
expect(linesYardstick.pixelPositionForScreenPosition(Point(0, 35)).left).toBe 230.90625
|
||||
expect(linesYardstick.pixelPositionForScreenPosition(Point(0, 36)).left).toBe 237.5
|
||||
expect(linesYardstick.pixelPositionForScreenPosition(Point(0, 37)).left).toBe 244.09375
|
||||
switch process.platform
|
||||
when 'darwin'
|
||||
expect(linesYardstick.pixelPositionForScreenPosition(Point(0, 35)).left).toBe 230.90625
|
||||
expect(linesYardstick.pixelPositionForScreenPosition(Point(0, 36)).left).toBe 237.5
|
||||
expect(linesYardstick.pixelPositionForScreenPosition(Point(0, 37)).left).toBe 244.09375
|
||||
when 'win32'
|
||||
expect(linesYardstick.pixelPositionForScreenPosition(Point(0, 35)).left).toBe 245
|
||||
expect(linesYardstick.pixelPositionForScreenPosition(Point(0, 36)).left).toBe 252
|
||||
expect(linesYardstick.pixelPositionForScreenPosition(Point(0, 37)).left).toBe 259
|
||||
|
||||
if process.platform is 'darwin' # Expectations fail on win32
|
||||
it "handles lines containing a mix of left-to-right and right-to-left characters", ->
|
||||
editor.setText('Persian, locally known as Parsi or Farsi (زبان فارسی), the predominant modern descendant of Old Persian.\n')
|
||||
it "handles lines containing a mix of left-to-right and right-to-left characters", ->
|
||||
editor.setText('Persian, locally known as Parsi or Farsi (زبان فارسی), the predominant modern descendant of Old Persian.\n')
|
||||
|
||||
atom.styles.addStyleSheet """
|
||||
* {
|
||||
font-size: 14px;
|
||||
font-family: monospace;
|
||||
}
|
||||
"""
|
||||
atom.styles.addStyleSheet """
|
||||
* {
|
||||
font-size: 14px;
|
||||
font-family: monospace;
|
||||
}
|
||||
"""
|
||||
|
||||
lineTopIndex = new LineTopIndex({defaultLineHeight: editor.getLineHeightInPixels()})
|
||||
linesYardstick = new LinesYardstick(editor, mockLineNodesProvider, lineTopIndex, atom.grammars)
|
||||
expect(linesYardstick.pixelPositionForScreenPosition(Point(0, 15))).toEqual({left: 126, top: 0})
|
||||
expect(linesYardstick.pixelPositionForScreenPosition(Point(0, 62))).toEqual({left: 521, top: 0})
|
||||
expect(linesYardstick.pixelPositionForScreenPosition(Point(0, 58))).toEqual({left: 487, top: 0})
|
||||
expect(linesYardstick.pixelPositionForScreenPosition(Point(0, Infinity))).toEqual({left: 873.625, top: 0})
|
||||
lineTopIndex = new LineTopIndex({defaultLineHeight: editor.getLineHeightInPixels()})
|
||||
linesYardstick = new LinesYardstick(editor, mockLineNodesProvider, lineTopIndex, atom.grammars)
|
||||
|
||||
switch process.platform
|
||||
when 'darwin'
|
||||
expect(linesYardstick.pixelPositionForScreenPosition(Point(0, 15))).toEqual({left: 126, top: 0})
|
||||
expect(linesYardstick.pixelPositionForScreenPosition(Point(0, 62))).toEqual({left: 521, top: 0})
|
||||
expect(linesYardstick.pixelPositionForScreenPosition(Point(0, 58))).toEqual({left: 487, top: 0})
|
||||
expect(linesYardstick.pixelPositionForScreenPosition(Point(0, Infinity))).toEqual({left: 873.625, top: 0})
|
||||
when 'win32'
|
||||
expect(linesYardstick.pixelPositionForScreenPosition(Point(0, 15))).toEqual({left: 120, top: 0})
|
||||
expect(linesYardstick.pixelPositionForScreenPosition(Point(0, 62))).toEqual({left: 496, top: 0})
|
||||
expect(linesYardstick.pixelPositionForScreenPosition(Point(0, 58))).toEqual({left: 464, top: 0})
|
||||
expect(linesYardstick.pixelPositionForScreenPosition(Point(0, Infinity))).toEqual({left: 832, top: 0})
|
||||
|
||||
describe "::screenPositionForPixelPosition(pixelPosition)", ->
|
||||
it "converts pixel positions to screen positions", ->
|
||||
@@ -176,9 +194,14 @@ describe "LinesYardstick", ->
|
||||
expect(linesYardstick.screenPositionForPixelPosition({top: 46, left: 66.5})).toEqual([3, 9])
|
||||
expect(linesYardstick.screenPositionForPixelPosition({top: 70, left: 99.9})).toEqual([5, 14])
|
||||
expect(linesYardstick.screenPositionForPixelPosition({top: 70, left: 225})).toEqual([5, 30])
|
||||
return unless process.platform is 'darwin' # Following tests are 1 pixel off on Win32
|
||||
expect(linesYardstick.screenPositionForPixelPosition({top: 70, left: 224.2365234375})).toEqual([5, 29])
|
||||
expect(linesYardstick.screenPositionForPixelPosition({top: 84, left: 247.1})).toEqual([6, 33])
|
||||
|
||||
switch process.platform
|
||||
when 'darwin'
|
||||
expect(linesYardstick.screenPositionForPixelPosition({top: 70, left: 224.2365234375})).toEqual([5, 29])
|
||||
expect(linesYardstick.screenPositionForPixelPosition({top: 84, left: 247.1})).toEqual([6, 33])
|
||||
when 'win32'
|
||||
expect(linesYardstick.screenPositionForPixelPosition({top: 70, left: 224.2365234375})).toEqual([5, 30])
|
||||
expect(linesYardstick.screenPositionForPixelPosition({top: 84, left: 247.1})).toEqual([6, 34])
|
||||
|
||||
it "overshoots to the nearest character when text nodes are not spatially contiguous", ->
|
||||
atom.styles.addStyleSheet """
|
||||
|
||||
@@ -196,7 +196,9 @@ describe('AtomApplication', function () {
|
||||
it('persists window state based on the project directories', async function () {
|
||||
const tempDirPath = makeTempDir()
|
||||
const atomApplication = buildAtomApplication()
|
||||
const window1 = atomApplication.launch(parseCommandLine([path.join(tempDirPath, 'new-file')]))
|
||||
const nonExistentFilePath = path.join(tempDirPath, 'new-file')
|
||||
|
||||
const window1 = atomApplication.launch(parseCommandLine([nonExistentFilePath]))
|
||||
await evalInWebContents(window1.browserWindow.webContents, function (sendBackToMainProcess) {
|
||||
atom.workspace.observeActivePaneItem(function (textEditor) {
|
||||
if (textEditor) {
|
||||
@@ -205,17 +207,31 @@ describe('AtomApplication', function () {
|
||||
}
|
||||
})
|
||||
})
|
||||
await window1.saveState()
|
||||
window1.close()
|
||||
await window1.closedPromise
|
||||
|
||||
const window2 = atomApplication.launch(parseCommandLine([path.join(tempDirPath)]))
|
||||
// Restore unsaved state when opening the directory itself
|
||||
const window2 = atomApplication.launch(parseCommandLine([tempDirPath]))
|
||||
await window2.loadedPromise
|
||||
const window2Text = await evalInWebContents(window2.browserWindow.webContents, function (sendBackToMainProcess) {
|
||||
atom.workspace.observeActivePaneItem(function (textEditor) {
|
||||
if (textEditor) sendBackToMainProcess(textEditor.getText())
|
||||
})
|
||||
const textEditor = atom.workspace.getActiveTextEditor()
|
||||
textEditor.moveToBottom()
|
||||
textEditor.insertText(' How are you?')
|
||||
sendBackToMainProcess(textEditor.getText())
|
||||
})
|
||||
assert.equal(window2Text, 'Hello World! How are you?')
|
||||
await window2.saveState()
|
||||
window2.close()
|
||||
await window2.closedPromise
|
||||
|
||||
assert.equal(window2Text, 'Hello World!')
|
||||
// Restore unsaved state when opening a path to a non-existent file in the directory
|
||||
const window3 = atomApplication.launch(parseCommandLine([path.join(tempDirPath, 'another-non-existent-file')]))
|
||||
await window3.loadedPromise
|
||||
const window3Texts = await evalInWebContents(window3.browserWindow.webContents, function (sendBackToMainProcess, nonExistentFilePath) {
|
||||
sendBackToMainProcess(atom.workspace.getTextEditors().map(editor => editor.getText()))
|
||||
})
|
||||
assert.include(window3Texts, 'Hello World! How are you?')
|
||||
})
|
||||
|
||||
it('shows all directories in the tree view when multiple directory paths are passed to Atom', async function () {
|
||||
@@ -260,7 +276,7 @@ describe('AtomApplication', function () {
|
||||
})
|
||||
assert.equal(window1EditorTitle, 'untitled')
|
||||
|
||||
const window2 = atomApplication.launch(parseCommandLine([]))
|
||||
const window2 = atomApplication.openWithOptions(parseCommandLine([]))
|
||||
await focusWindow(window2)
|
||||
const window2EditorTitle = await evalInWebContents(window1.browserWindow.webContents, function (sendBackToMainProcess) {
|
||||
sendBackToMainProcess(atom.workspace.getActiveTextEditor().getTitle())
|
||||
@@ -472,7 +488,7 @@ describe('AtomApplication', function () {
|
||||
}
|
||||
|
||||
let channelIdCounter = 0
|
||||
function evalInWebContents (webContents, source) {
|
||||
function evalInWebContents (webContents, source, ...args) {
|
||||
const channelId = 'eval-result-' + channelIdCounter++
|
||||
return new Promise(function (resolve) {
|
||||
electron.ipcMain.on(channelId, receiveResult)
|
||||
|
||||
@@ -112,7 +112,7 @@ describe("FileRecoveryService", () => {
|
||||
const mockWindow = {}
|
||||
const filePath = temp.path()
|
||||
fs.writeFileSync(filePath, "content")
|
||||
fs.chmodSync(filePath, 0444)
|
||||
fs.chmodSync(filePath, 0o444)
|
||||
|
||||
let logs = []
|
||||
this.stub(console, 'log', (message) => logs.push(message))
|
||||
|
||||
@@ -27,12 +27,12 @@ describe "PackageManager", ->
|
||||
apmPath += ".cmd"
|
||||
expect(atom.packages.getApmPath()).toBe apmPath
|
||||
|
||||
describe "when the core.apmPath setting is set", ->
|
||||
beforeEach ->
|
||||
atom.config.set("core.apmPath", "/path/to/apm")
|
||||
describe "when the core.apmPath setting is set", ->
|
||||
beforeEach ->
|
||||
atom.config.set("core.apmPath", "/path/to/apm")
|
||||
|
||||
it "returns the value of the core.apmPath config setting", ->
|
||||
expect(atom.packages.getApmPath()).toBe "/path/to/apm"
|
||||
it "returns the value of the core.apmPath config setting", ->
|
||||
expect(atom.packages.getApmPath()).toBe "/path/to/apm"
|
||||
|
||||
describe "::loadPackages()", ->
|
||||
beforeEach ->
|
||||
@@ -57,11 +57,13 @@ describe "PackageManager", ->
|
||||
expect(pack.metadata.name).toBe "package-with-index"
|
||||
|
||||
it "returns the package if it has an invalid keymap", ->
|
||||
spyOn(atom, 'inSpecMode').andReturn(false)
|
||||
pack = atom.packages.loadPackage("package-with-broken-keymap")
|
||||
expect(pack instanceof Package).toBe true
|
||||
expect(pack.metadata.name).toBe "package-with-broken-keymap"
|
||||
|
||||
it "returns the package if it has an invalid stylesheet", ->
|
||||
spyOn(atom, 'inSpecMode').andReturn(false)
|
||||
pack = atom.packages.loadPackage("package-with-invalid-styles")
|
||||
expect(pack instanceof Package).toBe true
|
||||
expect(pack.metadata.name).toBe "package-with-invalid-styles"
|
||||
@@ -75,6 +77,7 @@ describe "PackageManager", ->
|
||||
expect(addErrorHandler.argsForCall[1][0].options.packageName).toEqual "package-with-invalid-styles"
|
||||
|
||||
it "returns null if the package has an invalid package.json", ->
|
||||
spyOn(atom, 'inSpecMode').andReturn(false)
|
||||
addErrorHandler = jasmine.createSpy()
|
||||
atom.notifications.onDidAddNotification(addErrorHandler)
|
||||
expect(atom.packages.loadPackage("package-with-broken-package-json")).toBeNull()
|
||||
@@ -107,6 +110,7 @@ describe "PackageManager", ->
|
||||
|
||||
describe "when the package is deprecated", ->
|
||||
it "returns null", ->
|
||||
spyOn(console, 'warn')
|
||||
expect(atom.packages.loadPackage(path.join(__dirname, 'fixtures', 'packages', 'wordcount'))).toBeNull()
|
||||
expect(atom.packages.isDeprecatedPackage('wordcount', '2.1.9')).toBe true
|
||||
expect(atom.packages.isDeprecatedPackage('wordcount', '2.2.0')).toBe true
|
||||
@@ -392,6 +396,7 @@ describe "PackageManager", ->
|
||||
expect(mainModule.activate.callCount).toBe 1
|
||||
|
||||
it "adds a notification when the activation commands are invalid", ->
|
||||
spyOn(atom, 'inSpecMode').andReturn(false)
|
||||
addErrorHandler = jasmine.createSpy()
|
||||
atom.notifications.onDidAddNotification(addErrorHandler)
|
||||
expect(-> atom.packages.activatePackage('package-with-invalid-activation-commands')).not.toThrow()
|
||||
@@ -400,6 +405,7 @@ describe "PackageManager", ->
|
||||
expect(addErrorHandler.argsForCall[0][0].options.packageName).toEqual "package-with-invalid-activation-commands"
|
||||
|
||||
it "adds a notification when the context menu is invalid", ->
|
||||
spyOn(atom, 'inSpecMode').andReturn(false)
|
||||
addErrorHandler = jasmine.createSpy()
|
||||
atom.notifications.onDidAddNotification(addErrorHandler)
|
||||
expect(-> atom.packages.activatePackage('package-with-invalid-context-menu')).not.toThrow()
|
||||
@@ -546,8 +552,9 @@ describe "PackageManager", ->
|
||||
waitsFor -> activatedPackage?
|
||||
runs -> expect(activatedPackage.name).toBe 'package-with-main'
|
||||
|
||||
describe "when the package throws an error while loading", ->
|
||||
describe "when the package's main module throws an error on load", ->
|
||||
it "adds a notification instead of throwing an exception", ->
|
||||
spyOn(atom, 'inSpecMode').andReturn(false)
|
||||
atom.config.set("core.disabledPackages", [])
|
||||
addErrorHandler = jasmine.createSpy()
|
||||
atom.notifications.onDidAddNotification(addErrorHandler)
|
||||
@@ -556,6 +563,11 @@ describe "PackageManager", ->
|
||||
expect(addErrorHandler.argsForCall[0][0].message).toContain("Failed to load the package-that-throws-an-exception package")
|
||||
expect(addErrorHandler.argsForCall[0][0].options.packageName).toEqual "package-that-throws-an-exception"
|
||||
|
||||
it "re-throws the exception in test mode", ->
|
||||
atom.config.set("core.disabledPackages", [])
|
||||
addErrorHandler = jasmine.createSpy()
|
||||
expect(-> atom.packages.activatePackage("package-that-throws-an-exception")).toThrow("This package throws an exception")
|
||||
|
||||
describe "when the package is not found", ->
|
||||
it "rejects the promise", ->
|
||||
atom.config.set("core.disabledPackages", [])
|
||||
@@ -893,6 +905,7 @@ describe "PackageManager", ->
|
||||
|
||||
describe "::serialize", ->
|
||||
it "does not serialize packages that threw an error during activation", ->
|
||||
spyOn(atom, 'inSpecMode').andReturn(false)
|
||||
spyOn(console, 'warn')
|
||||
badPack = null
|
||||
waitsForPromise ->
|
||||
@@ -940,6 +953,7 @@ describe "PackageManager", ->
|
||||
atom.packages.unloadPackages()
|
||||
|
||||
it "calls `deactivate` on the package's main module if activate was successful", ->
|
||||
spyOn(atom, 'inSpecMode').andReturn(false)
|
||||
pack = null
|
||||
waitsForPromise ->
|
||||
atom.packages.activatePackage("package-with-deactivate").then (p) -> pack = p
|
||||
@@ -1028,6 +1042,7 @@ describe "PackageManager", ->
|
||||
|
||||
describe "::activate()", ->
|
||||
beforeEach ->
|
||||
spyOn(atom, 'inSpecMode').andReturn(false)
|
||||
jasmine.snapshotDeprecations()
|
||||
spyOn(console, 'warn')
|
||||
atom.packages.loadPackages()
|
||||
@@ -1042,6 +1057,7 @@ describe "PackageManager", ->
|
||||
jasmine.restoreDeprecationsSnapshot()
|
||||
|
||||
it "sets hasActivatedInitialPackages", ->
|
||||
spyOn(atom.styles, 'getUserStyleSheetPath').andReturn(null)
|
||||
spyOn(atom.packages, 'activatePackages')
|
||||
expect(atom.packages.hasActivatedInitialPackages()).toBe false
|
||||
waitsForPromise -> atom.packages.activate()
|
||||
|
||||
@@ -614,3 +614,7 @@ describe "Project", ->
|
||||
|
||||
randomPath = path.join("some", "random", "path")
|
||||
expect(atom.project.contains(randomPath)).toBe false
|
||||
|
||||
describe ".resolvePath(uri)", ->
|
||||
it "normalizes disk drive letter in passed path on #win32", ->
|
||||
expect(atom.project.resolvePath("d:\\file.txt")).toEqual "D:\\file.txt"
|
||||
|
||||
@@ -98,6 +98,13 @@ describe('StyleManager', () => {
|
||||
])
|
||||
})
|
||||
|
||||
it('does not transform CSS rules with invalid syntax', () => {
|
||||
styleManager.addStyleSheet("atom-text-editor::shadow .class-1 { font-family: inval'id }")
|
||||
expect(Array.from(styleManager.getStyleElements()[0].sheet.cssRules).map((r) => r.selectorText)).toEqual([
|
||||
'atom-text-editor::shadow .class-1'
|
||||
])
|
||||
})
|
||||
|
||||
it('does not throw exceptions on rules with no selectors', () => {
|
||||
styleManager.addStyleSheet('@media screen {font-size: 10px}', {context: 'atom-text-editor'})
|
||||
})
|
||||
|
||||
@@ -1347,7 +1347,19 @@ describe('TextEditorComponent', function () {
|
||||
expect(cursorsNode.classList.contains('blink-off')).toBe(true)
|
||||
})
|
||||
|
||||
it('does not render cursors that are associated with non-empty selections', function () {
|
||||
it('renders cursors that are associated with empty selections', function () {
|
||||
editor.update({showCursorOnSelection: true})
|
||||
editor.setSelectedScreenRange([[0, 4], [4, 6]])
|
||||
editor.addCursorAtScreenPosition([6, 8])
|
||||
runAnimationFrames()
|
||||
let cursorNodes = componentNode.querySelectorAll('.cursor')
|
||||
expect(cursorNodes.length).toBe(2)
|
||||
expect(cursorNodes[0].style['-webkit-transform']).toBe('translate(' + (Math.round(6 * charWidth)) + 'px, ' + (4 * lineHeightInPixels) + 'px)')
|
||||
expect(cursorNodes[1].style['-webkit-transform']).toBe('translate(' + (Math.round(8 * charWidth)) + 'px, ' + (6 * lineHeightInPixels) + 'px)')
|
||||
})
|
||||
|
||||
it('does not render cursors that are associated with non-empty selections when showCursorOnSelection is false', function () {
|
||||
editor.update({showCursorOnSelection: false})
|
||||
editor.setSelectedScreenRange([[0, 4], [4, 6]])
|
||||
editor.addCursorAtScreenPosition([6, 8])
|
||||
runAnimationFrames()
|
||||
@@ -1735,11 +1747,13 @@ describe('TextEditorComponent', function () {
|
||||
})
|
||||
|
||||
describe('block decorations rendering', function () {
|
||||
let markerLayer
|
||||
|
||||
function createBlockDecorationBeforeScreenRow(screenRow, {className}) {
|
||||
let item = document.createElement("div")
|
||||
item.className = className || ""
|
||||
let blockDecoration = editor.decorateMarker(
|
||||
editor.markScreenPosition([screenRow, 0], {invalidate: "never"}),
|
||||
markerLayer.markScreenPosition([screenRow, 0], {invalidate: "never"}),
|
||||
{type: "block", item: item, position: "before"}
|
||||
)
|
||||
return [item, blockDecoration]
|
||||
@@ -1749,13 +1763,14 @@ describe('TextEditorComponent', function () {
|
||||
let item = document.createElement("div")
|
||||
item.className = className || ""
|
||||
let blockDecoration = editor.decorateMarker(
|
||||
editor.markScreenPosition([screenRow, 0], {invalidate: "never"}),
|
||||
markerLayer.markScreenPosition([screenRow, 0], {invalidate: "never"}),
|
||||
{type: "block", item: item, position: "after"}
|
||||
)
|
||||
return [item, blockDecoration]
|
||||
}
|
||||
|
||||
beforeEach(function () {
|
||||
markerLayer = editor.addMarkerLayer()
|
||||
wrapperNode.style.height = 5 * lineHeightInPixels + 'px'
|
||||
editor.update({autoHeight: false})
|
||||
component.measureDimensions()
|
||||
|
||||
@@ -1583,6 +1583,7 @@ describe "TextEditorPresenter", ->
|
||||
getState(presenter).content.cursors[presenter.model.getCursors()[cursorIndex].id]
|
||||
|
||||
it "contains pixelRects for empty selections that are visible on screen", ->
|
||||
editor.update({showCursorOnSelection: false})
|
||||
editor.setSelectedBufferRanges([
|
||||
[[1, 2], [1, 2]],
|
||||
[[2, 4], [2, 4]],
|
||||
@@ -1627,6 +1628,7 @@ describe "TextEditorPresenter", ->
|
||||
expect(getState(presenter).content.cursors).not.toEqual({})
|
||||
|
||||
it "updates when block decorations change", ->
|
||||
editor.update({showCursorOnSelection: false})
|
||||
editor.setSelectedBufferRanges([
|
||||
[[1, 2], [1, 2]],
|
||||
[[2, 4], [2, 4]],
|
||||
@@ -1704,6 +1706,7 @@ describe "TextEditorPresenter", ->
|
||||
expect(stateForCursor(presenter, 0)).toEqual {top: 20, left: 10 * 22, width: 10, height: 10}
|
||||
|
||||
it "updates when ::explicitHeight changes", ->
|
||||
editor.update({showCursorOnSelection: false})
|
||||
editor.setSelectedBufferRanges([
|
||||
[[1, 2], [1, 2]],
|
||||
[[2, 4], [2, 4]],
|
||||
@@ -1757,6 +1760,7 @@ describe "TextEditorPresenter", ->
|
||||
expect(stateForCursor(presenter, 0)).toEqual {top: 1 * 10, left: (3 * 10) + 20, width: 20, height: 10}
|
||||
|
||||
it "updates when cursors are added, moved, hidden, shown, or destroyed", ->
|
||||
editor.update({showCursorOnSelection: false})
|
||||
editor.setSelectedBufferRanges([
|
||||
[[1, 2], [1, 2]],
|
||||
[[3, 4], [3, 5]]
|
||||
|
||||
@@ -436,6 +436,19 @@ describe('TextEditorRegistry', function () {
|
||||
expect(editor.hasAtomicSoftTabs()).toBe(true)
|
||||
})
|
||||
|
||||
it('enables or disables cursor on selection visibility based on the config', async function () {
|
||||
editor.update({showCursorOnSelection: true})
|
||||
expect(editor.getShowCursorOnSelection()).toBe(true)
|
||||
|
||||
atom.config.set('editor.showCursorOnSelection', false)
|
||||
registry.maintainConfig(editor)
|
||||
await initialPackageActivation
|
||||
expect(editor.getShowCursorOnSelection()).toBe(false)
|
||||
|
||||
atom.config.set('editor.showCursorOnSelection', true)
|
||||
expect(editor.getShowCursorOnSelection()).toBe(true)
|
||||
})
|
||||
|
||||
it('enables or disables line numbers based on the config', async function () {
|
||||
editor.update({showLineNumbers: true})
|
||||
expect(editor.showLineNumbers).toBe(true)
|
||||
|
||||
@@ -89,7 +89,11 @@ describe "TextEditor", ->
|
||||
|
||||
describe ".copy()", ->
|
||||
it "returns a different editor with the same initial state", ->
|
||||
editor.update({autoHeight: false, autoWidth: true})
|
||||
expect(editor.getAutoHeight()).toBeFalsy()
|
||||
expect(editor.getAutoWidth()).toBeFalsy()
|
||||
expect(editor.getShowCursorOnSelection()).toBeTruthy()
|
||||
|
||||
editor.update({autoHeight: true, autoWidth: true, showCursorOnSelection: false})
|
||||
editor.setSelectedBufferRange([[1, 2], [3, 4]])
|
||||
editor.addSelectionForBufferRange([[5, 6], [7, 8]], reversed: true)
|
||||
editor.firstVisibleScreenRow = 5
|
||||
@@ -105,7 +109,8 @@ describe "TextEditor", ->
|
||||
expect(editor2.getFirstVisibleScreenColumn()).toBe 5
|
||||
expect(editor2.isFoldedAtBufferRow(4)).toBeTruthy()
|
||||
expect(editor2.getAutoWidth()).toBeTruthy()
|
||||
expect(editor2.getAutoHeight()).toBeFalsy()
|
||||
expect(editor2.getAutoHeight()).toBeTruthy()
|
||||
expect(editor2.getShowCursorOnSelection()).toBeFalsy()
|
||||
|
||||
# editor2 can now diverge from its origin edit session
|
||||
editor2.getLastSelection().setBufferRange([[2, 1], [4, 3]])
|
||||
@@ -1186,6 +1191,15 @@ describe "TextEditor", ->
|
||||
editor.getLastSelection().destroy()
|
||||
expect(editor.getLastSelection().getBufferRange()).toEqual([[0, 0], [0, 0]])
|
||||
|
||||
it "doesn't get stuck in a infinite loop when called from ::onDidAddCursor after the last selection has been destroyed (regression)", ->
|
||||
callCount = 0
|
||||
editor.getLastSelection().destroy()
|
||||
editor.onDidAddCursor (cursor) ->
|
||||
callCount++
|
||||
editor.getLastSelection()
|
||||
expect(editor.getLastSelection().getBufferRange()).toEqual([[0, 0], [0, 0]])
|
||||
expect(callCount).toBe(1)
|
||||
|
||||
describe ".getSelections()", ->
|
||||
it "creates a new selection at (0, 0) if the last selection has been destroyed", ->
|
||||
editor.getLastSelection().destroy()
|
||||
@@ -1858,7 +1872,7 @@ describe "TextEditor", ->
|
||||
[[4, 25], [4, 29]]
|
||||
]
|
||||
for cursor in editor.getCursors()
|
||||
expect(cursor.isVisible()).toBeFalsy()
|
||||
expect(cursor.isVisible()).toBeTruthy()
|
||||
|
||||
it "skips lines that are too short to create a non-empty selection", ->
|
||||
editor.setSelectedBufferRange([[3, 31], [3, 38]])
|
||||
@@ -1991,7 +2005,7 @@ describe "TextEditor", ->
|
||||
[[2, 37], [2, 40]]
|
||||
]
|
||||
for cursor in editor.getCursors()
|
||||
expect(cursor.isVisible()).toBeFalsy()
|
||||
expect(cursor.isVisible()).toBeTruthy()
|
||||
|
||||
it "skips lines that are too short to create a non-empty selection", ->
|
||||
editor.setSelectedBufferRange([[6, 31], [6, 38]])
|
||||
@@ -2161,6 +2175,54 @@ describe "TextEditor", ->
|
||||
editor.setCursorScreenPosition([3, 3])
|
||||
expect(selection.isEmpty()).toBeTruthy()
|
||||
|
||||
describe "cursor visibility while there is a selection", ->
|
||||
describe "when showCursorOnSelection is true", ->
|
||||
it "is visible while there is no selection", ->
|
||||
expect(selection.isEmpty()).toBeTruthy()
|
||||
expect(editor.getShowCursorOnSelection()).toBeTruthy()
|
||||
expect(editor.getCursors().length).toBe 1
|
||||
expect(editor.getCursors()[0].isVisible()).toBeTruthy()
|
||||
|
||||
it "is visible while there is a selection", ->
|
||||
expect(selection.isEmpty()).toBeTruthy()
|
||||
editor.setSelectedBufferRange([[1, 2], [1, 5]])
|
||||
expect(selection.isEmpty()).toBeFalsy()
|
||||
expect(editor.getCursors().length).toBe 1
|
||||
expect(editor.getCursors()[0].isVisible()).toBeTruthy()
|
||||
|
||||
it "is visible while there are multiple selections", ->
|
||||
expect(editor.getSelections().length).toBe 1
|
||||
editor.setSelectedBufferRanges([[[1, 2], [1, 5]], [[2, 2], [2, 5]]])
|
||||
expect(editor.getSelections().length).toBe 2
|
||||
expect(editor.getCursors().length).toBe 2
|
||||
expect(editor.getCursors()[0].isVisible()).toBeTruthy()
|
||||
expect(editor.getCursors()[1].isVisible()).toBeTruthy()
|
||||
|
||||
describe "when showCursorOnSelection is false", ->
|
||||
it "is visible while there is no selection", ->
|
||||
editor.update({showCursorOnSelection: false})
|
||||
expect(selection.isEmpty()).toBeTruthy()
|
||||
expect(editor.getShowCursorOnSelection()).toBeFalsy()
|
||||
expect(editor.getCursors().length).toBe 1
|
||||
expect(editor.getCursors()[0].isVisible()).toBeTruthy()
|
||||
|
||||
it "is not visible while there is a selection", ->
|
||||
editor.update({showCursorOnSelection: false})
|
||||
expect(selection.isEmpty()).toBeTruthy()
|
||||
editor.setSelectedBufferRange([[1, 2], [1, 5]])
|
||||
expect(selection.isEmpty()).toBeFalsy()
|
||||
expect(editor.getCursors().length).toBe 1
|
||||
expect(editor.getCursors()[0].isVisible()).toBeFalsy()
|
||||
|
||||
it "is not visible while there are multiple selections", ->
|
||||
editor.update({showCursorOnSelection: false})
|
||||
expect(editor.getSelections().length).toBe 1
|
||||
editor.setSelectedBufferRanges([[[1, 2], [1, 5]], [[2, 2], [2, 5]]])
|
||||
expect(editor.getSelections().length).toBe 2
|
||||
expect(editor.getCursors().length).toBe 2
|
||||
expect(editor.getCursors()[0].isVisible()).toBeFalsy()
|
||||
expect(editor.getCursors()[1].isVisible()).toBeFalsy()
|
||||
|
||||
it "does not share selections between different edit sessions for the same buffer", ->
|
||||
editor2 = null
|
||||
waitsForPromise ->
|
||||
|
||||
@@ -4,6 +4,7 @@ temp = require('temp').track()
|
||||
|
||||
describe "atom.themes", ->
|
||||
beforeEach ->
|
||||
spyOn(atom, 'inSpecMode').andReturn(false)
|
||||
spyOn(console, 'warn')
|
||||
|
||||
afterEach ->
|
||||
|
||||
@@ -74,14 +74,14 @@ describe "WorkspaceElement", ->
|
||||
atom.config.set('editor.fontSize', 12)
|
||||
|
||||
# Zoom out
|
||||
editorElement.dispatchEvent(new WheelEvent('mousewheel', {
|
||||
editorElement.querySelector('span').dispatchEvent(new WheelEvent('mousewheel', {
|
||||
wheelDeltaY: -10,
|
||||
ctrlKey: true
|
||||
}))
|
||||
expect(atom.config.get('editor.fontSize')).toBe(11)
|
||||
|
||||
# Zoom in
|
||||
editorElement.dispatchEvent(new WheelEvent('mousewheel', {
|
||||
editorElement.querySelector('span').dispatchEvent(new WheelEvent('mousewheel', {
|
||||
wheelDeltaY: 10,
|
||||
ctrlKey: true
|
||||
}))
|
||||
@@ -95,13 +95,13 @@ describe "WorkspaceElement", ->
|
||||
expect(atom.config.get('editor.fontSize')).toBe(12)
|
||||
|
||||
# No ctrl key
|
||||
workspaceElement.dispatchEvent(new WheelEvent('mousewheel', {
|
||||
editorElement.querySelector('span').dispatchEvent(new WheelEvent('mousewheel', {
|
||||
wheelDeltaY: 10,
|
||||
}))
|
||||
expect(atom.config.get('editor.fontSize')).toBe(12)
|
||||
|
||||
atom.config.set('editor.zoomFontWhenCtrlScrolling', false)
|
||||
editorElement.dispatchEvent(new WheelEvent('mousewheel', {
|
||||
editorElement.querySelector('span').dispatchEvent(new WheelEvent('mousewheel', {
|
||||
wheelDeltaY: 10,
|
||||
ctrlKey: true
|
||||
}))
|
||||
|
||||
@@ -886,8 +886,12 @@ describe "Workspace", ->
|
||||
|
||||
describe "document.title", ->
|
||||
describe "when there is no item open", ->
|
||||
it "sets the title to 'untitled'", ->
|
||||
expect(document.title).toMatch ///^untitled///
|
||||
it "sets the title to the project path", ->
|
||||
expect(document.title).toMatch escapeStringRegex(fs.tildify(atom.project.getPaths()[0]))
|
||||
|
||||
it "sets the title to 'untitled' if there is no project path", ->
|
||||
atom.project.setPaths([])
|
||||
expect(document.title).toMatch /^untitled/
|
||||
|
||||
describe "when the active pane item's path is not inside a project path", ->
|
||||
beforeEach ->
|
||||
@@ -948,10 +952,10 @@ describe "Workspace", ->
|
||||
expect(document.title).toMatch ///^#{item.getTitle()}\ \u2014\ #{pathEscaped}///
|
||||
|
||||
describe "when the last pane item is removed", ->
|
||||
it "updates the title to be untitled", ->
|
||||
it "updates the title to the project's first path", ->
|
||||
atom.workspace.getActivePane().destroy()
|
||||
expect(atom.workspace.getActivePaneItem()).toBeUndefined()
|
||||
expect(document.title).toMatch ///^untitled///
|
||||
expect(document.title).toMatch escapeStringRegex(fs.tildify(atom.project.getPaths()[0]))
|
||||
|
||||
describe "when an inactive pane's item changes", ->
|
||||
it "does not update the title", ->
|
||||
|
||||
@@ -2,10 +2,12 @@ _ = require 'underscore-plus'
|
||||
{screen, ipcRenderer, remote, shell, webFrame} = require 'electron'
|
||||
ipcHelpers = require './ipc-helpers'
|
||||
{Disposable} = require 'event-kit'
|
||||
{getWindowLoadSettings, setWindowLoadSettings} = require './window-load-settings-helpers'
|
||||
getWindowLoadSettings = require './get-window-load-settings'
|
||||
|
||||
module.exports =
|
||||
class ApplicationDelegate
|
||||
getWindowLoadSettings: -> getWindowLoadSettings()
|
||||
|
||||
open: (params) ->
|
||||
ipcRenderer.send('open', params)
|
||||
|
||||
@@ -109,10 +111,7 @@ class ApplicationDelegate
|
||||
ipcRenderer.send("add-recent-document", filename)
|
||||
|
||||
setRepresentedDirectoryPaths: (paths) ->
|
||||
loadSettings = getWindowLoadSettings()
|
||||
loadSettings['initialPaths'] = paths
|
||||
setWindowLoadSettings(loadSettings)
|
||||
ipcRenderer.send("did-change-paths")
|
||||
ipcHelpers.call('window-method', 'setRepresentedDirectoryPaths', paths)
|
||||
|
||||
setAutoHideWindowMenuBar: (autoHide) ->
|
||||
ipcHelpers.call('window-method', 'setAutoHideMenuBar', autoHide)
|
||||
@@ -149,13 +148,9 @@ class ApplicationDelegate
|
||||
showMessageDialog: (params) ->
|
||||
|
||||
showSaveDialog: (params) ->
|
||||
if _.isString(params)
|
||||
params = defaultPath: params
|
||||
else
|
||||
params = _.clone(params)
|
||||
params.title ?= 'Save File'
|
||||
params.defaultPath ?= getWindowLoadSettings().initialPaths[0]
|
||||
remote.dialog.showSaveDialog remote.getCurrentWindow(), params
|
||||
if typeof params is 'string'
|
||||
params = {defaultPath: params}
|
||||
@getCurrentWindow().showSaveDialog(params)
|
||||
|
||||
playBeepSound: ->
|
||||
shell.beep()
|
||||
|
||||
@@ -11,7 +11,6 @@ Model = require './model'
|
||||
WindowEventHandler = require './window-event-handler'
|
||||
StateStore = require './state-store'
|
||||
StorageFolder = require './storage-folder'
|
||||
{getWindowLoadSettings} = require './window-load-settings-helpers'
|
||||
registerDefaultCommands = require './register-default-commands'
|
||||
{updateProcessEnv} = require './update-process-env'
|
||||
|
||||
@@ -458,7 +457,7 @@ class AtomEnvironment extends Model
|
||||
#
|
||||
# Returns an {Object} containing all the load setting key/value pairs.
|
||||
getLoadSettings: ->
|
||||
getWindowLoadSettings()
|
||||
@applicationDelegate.getWindowLoadSettings()
|
||||
|
||||
###
|
||||
Section: Managing The Atom Window
|
||||
@@ -828,12 +827,17 @@ class AtomEnvironment extends Model
|
||||
Section: Private
|
||||
###
|
||||
|
||||
assert: (condition, message, callback) ->
|
||||
assert: (condition, message, callbackOrMetadata) ->
|
||||
return true if condition
|
||||
|
||||
error = new Error("Assertion failed: #{message}")
|
||||
Error.captureStackTrace(error, @assert)
|
||||
callback?(error)
|
||||
|
||||
if callbackOrMetadata?
|
||||
if typeof callbackOrMetadata is 'function'
|
||||
callbackOrMetadata?(error)
|
||||
else
|
||||
error.metadata = callbackOrMetadata
|
||||
|
||||
@emitter.emit 'did-fail-assertion', error
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ const hasWriteAccess = (dir) => {
|
||||
const getAppDirectory = () => {
|
||||
switch (process.platform) {
|
||||
case 'darwin':
|
||||
return path.join(process.execPath.substring(0, process.execPath.indexOf('.app')), '..')
|
||||
return process.execPath.substring(0, process.execPath.indexOf('.app') + 4)
|
||||
case 'linux':
|
||||
case 'win32':
|
||||
return path.join(process.execPath, '..')
|
||||
@@ -27,7 +27,7 @@ const getAppDirectory = () => {
|
||||
module.exports = {
|
||||
setAtomHome: (homePath) => {
|
||||
// When a read-writeable .atom folder exists above app use that
|
||||
const portableHomePath = path.join(getAppDirectory(), '.atom')
|
||||
const portableHomePath = path.join(getAppDirectory(), '..', '.atom')
|
||||
if (fs.existsSync(portableHomePath)) {
|
||||
if (hasWriteAccess(portableHomePath)) {
|
||||
process.env.ATOM_HOME = portableHomePath
|
||||
|
||||
20
src/babel.js
20
src/babel.js
@@ -6,6 +6,7 @@ var defaultOptions = require('../static/babelrc.json')
|
||||
|
||||
var babel = null
|
||||
var babelVersionDirectory = null
|
||||
var options = null
|
||||
|
||||
var PREFIXES = [
|
||||
'/** @babel */',
|
||||
@@ -47,16 +48,27 @@ exports.compile = function (sourceCode, filePath) {
|
||||
var noop = function () {}
|
||||
Logger.prototype.debug = noop
|
||||
Logger.prototype.verbose = noop
|
||||
|
||||
options = {ast: false, babelrc: false}
|
||||
for (var key in defaultOptions) {
|
||||
if (key === 'plugins') {
|
||||
const plugins = []
|
||||
for (const [pluginName, pluginOptions] of defaultOptions[key]) {
|
||||
plugins.push([require.resolve(`babel-plugin-${pluginName}`), pluginOptions])
|
||||
}
|
||||
options[key] = plugins
|
||||
} else {
|
||||
options[key] = defaultOptions[key]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (process.platform === 'win32') {
|
||||
filePath = 'file:///' + path.resolve(filePath).replace(/\\/g, '/')
|
||||
}
|
||||
|
||||
var options = {filename: filePath}
|
||||
for (var key in defaultOptions) {
|
||||
options[key] = defaultOptions[key]
|
||||
}
|
||||
options.filename = filePath
|
||||
|
||||
return babel.transform(sourceCode, options).code
|
||||
}
|
||||
|
||||
|
||||
@@ -46,18 +46,34 @@ export default class BufferedProcess {
|
||||
// * `exit` {Function} (optional) The callback which receives a single
|
||||
// argument containing the exit status.
|
||||
// * `code` {Number}
|
||||
constructor ({command, args, options = {}, stdout, stderr, exit} = {}) {
|
||||
// * `autoStart` {Boolean} (optional) Whether the command will automatically start
|
||||
// when this BufferedProcess is created. Defaults to true. When set to false you
|
||||
// must call the `start` method to start the process.
|
||||
constructor ({command, args, options = {}, stdout, stderr, exit, autoStart = true} = {}) {
|
||||
this.emitter = new Emitter()
|
||||
this.command = command
|
||||
// Related to joyent/node#2318
|
||||
if (process.platform === 'win32' && options.shell === undefined) {
|
||||
this.spawnWithEscapedWindowsArgs(command, args, options)
|
||||
} else {
|
||||
this.spawn(command, args, options)
|
||||
this.args = args
|
||||
this.options = options
|
||||
this.stdout = stdout
|
||||
this.stderr = stderr
|
||||
this.exit = exit
|
||||
if (autoStart === true) {
|
||||
this.start()
|
||||
}
|
||||
|
||||
this.killed = false
|
||||
this.handleEvents(stdout, stderr, exit)
|
||||
}
|
||||
|
||||
start () {
|
||||
if (this.started === true) return
|
||||
|
||||
this.started = true
|
||||
// Related to joyent/node#2318
|
||||
if (process.platform === 'win32' && this.options.shell === undefined) {
|
||||
this.spawnWithEscapedWindowsArgs(this.command, this.args, this.options)
|
||||
} else {
|
||||
this.spawn(this.command, this.args, this.options)
|
||||
}
|
||||
this.handleEvents(this.stdout, this.stderr, this.exit)
|
||||
}
|
||||
|
||||
// Windows has a bunch of special rules that node still doesn't take care of for you
|
||||
|
||||
@@ -68,6 +68,12 @@ const configSchema = {
|
||||
default: true,
|
||||
description: 'Trigger the system\'s beep sound when certain actions cannot be executed or there are no results.'
|
||||
},
|
||||
closeDeletedFileTabs: {
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
title: 'Close Deleted File Tabs',
|
||||
description: 'Close corresponding editors when a file is deleted outside Atom.'
|
||||
},
|
||||
destroyEmptyPanes: {
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
@@ -84,42 +90,166 @@ const configSchema = {
|
||||
type: 'string',
|
||||
default: 'utf8',
|
||||
enum: [
|
||||
'cp437',
|
||||
'cp850',
|
||||
'cp866',
|
||||
'eucjp',
|
||||
'euckr',
|
||||
'gbk',
|
||||
'iso88591',
|
||||
'iso885910',
|
||||
'iso885913',
|
||||
'iso885914',
|
||||
'iso885915',
|
||||
'iso885916',
|
||||
'iso88592',
|
||||
'iso88593',
|
||||
'iso88594',
|
||||
'iso88595',
|
||||
'iso88596',
|
||||
'iso88597',
|
||||
'iso88597',
|
||||
'iso88598',
|
||||
'koi8r',
|
||||
'koi8u',
|
||||
'macroman',
|
||||
'shiftjis',
|
||||
'utf16be',
|
||||
'utf16le',
|
||||
'utf8',
|
||||
'windows1250',
|
||||
'windows1251',
|
||||
'windows1252',
|
||||
'windows1253',
|
||||
'windows1254',
|
||||
'windows1255',
|
||||
'windows1256',
|
||||
'windows1257',
|
||||
'windows1258'
|
||||
{
|
||||
value: 'iso88596',
|
||||
description: 'Arabic (ISO 8859-6)'
|
||||
},
|
||||
{
|
||||
value: 'windows1256',
|
||||
description: 'Arabic (Windows 1256)'
|
||||
},
|
||||
{
|
||||
value: 'iso88594',
|
||||
description: 'Baltic (ISO 8859-4)'
|
||||
},
|
||||
{
|
||||
value: 'windows1257',
|
||||
description: 'Baltic (Windows 1257)'
|
||||
},
|
||||
{
|
||||
value: 'iso885914',
|
||||
description: 'Celtic (ISO 8859-14)'
|
||||
},
|
||||
{
|
||||
value: 'iso88592',
|
||||
description: 'Central European (ISO 8859-2)'
|
||||
},
|
||||
{
|
||||
value: 'windows1250',
|
||||
description: 'Central European (Windows 1250)'
|
||||
},
|
||||
{
|
||||
value: 'gb18030',
|
||||
description: 'Chinese (GB18030)'
|
||||
},
|
||||
{
|
||||
value: 'gbk',
|
||||
description: 'Chinese (GBK)'
|
||||
},
|
||||
{
|
||||
value: 'cp950',
|
||||
description: 'Traditional Chinese (Big5)'
|
||||
},
|
||||
{
|
||||
value: 'big5hkscs',
|
||||
description: 'Traditional Chinese (Big5-HKSCS)'
|
||||
},
|
||||
{
|
||||
value: 'cp866',
|
||||
description: 'Cyrillic (CP 866)'
|
||||
},
|
||||
{
|
||||
value: 'iso88595',
|
||||
description: 'Cyrillic (ISO 8859-5)'
|
||||
},
|
||||
{
|
||||
value: 'koi8r',
|
||||
description: 'Cyrillic (KOI8-R)'
|
||||
},
|
||||
{
|
||||
value: 'koi8u',
|
||||
description: 'Cyrillic (KOI8-U)'
|
||||
},
|
||||
{
|
||||
value: 'windows1251',
|
||||
description: 'Cyrillic (Windows 1251)'
|
||||
},
|
||||
{
|
||||
value: 'cp437',
|
||||
description: 'DOS (CP 437)'
|
||||
},
|
||||
{
|
||||
value: 'cp850',
|
||||
description: 'DOS (CP 850)'
|
||||
},
|
||||
{
|
||||
value: 'iso885913',
|
||||
description: 'Estonian (ISO 8859-13)'
|
||||
},
|
||||
{
|
||||
value: 'iso88597',
|
||||
description: 'Greek (ISO 8859-7)'
|
||||
},
|
||||
{
|
||||
value: 'windows1253',
|
||||
description: 'Greek (Windows 1253)'
|
||||
},
|
||||
{
|
||||
value: 'iso88598',
|
||||
description: 'Hebrew (ISO 8859-8)'
|
||||
},
|
||||
{
|
||||
value: 'windows1255',
|
||||
description: 'Hebrew (Windows 1255)'
|
||||
},
|
||||
{
|
||||
value: 'cp932',
|
||||
description: 'Japanese (CP 932)'
|
||||
},
|
||||
{
|
||||
value: 'eucjp',
|
||||
description: 'Japanese (EUC-JP)'
|
||||
},
|
||||
{
|
||||
value: 'shiftjis',
|
||||
description: 'Japanese (Shift JIS)'
|
||||
},
|
||||
{
|
||||
value: 'euckr',
|
||||
description: 'Korean (EUC-KR)'
|
||||
},
|
||||
{
|
||||
value: 'iso885910',
|
||||
description: 'Nordic (ISO 8859-10)'
|
||||
},
|
||||
{
|
||||
value: 'iso885916',
|
||||
description: 'Romanian (ISO 8859-16)'
|
||||
},
|
||||
{
|
||||
value: 'iso88599',
|
||||
description: 'Turkish (ISO 8859-9)'
|
||||
},
|
||||
{
|
||||
value: 'windows1254',
|
||||
description: 'Turkish (Windows 1254)'
|
||||
},
|
||||
{
|
||||
value: 'utf8',
|
||||
description: 'Unicode (UTF-8)'
|
||||
},
|
||||
{
|
||||
value: 'utf16le',
|
||||
description: 'Unicode (UTF-16 LE)'
|
||||
},
|
||||
{
|
||||
value: 'utf16be',
|
||||
description: 'Unicode (UTF-16 BE)'
|
||||
},
|
||||
{
|
||||
value: 'windows1258',
|
||||
description: 'Vietnamese (Windows 1258)'
|
||||
},
|
||||
{
|
||||
value: 'iso88591',
|
||||
description: 'Western (ISO 8859-1)'
|
||||
},
|
||||
{
|
||||
value: 'iso88593',
|
||||
description: 'Western (ISO 8859-3)'
|
||||
},
|
||||
{
|
||||
value: 'iso885915',
|
||||
description: 'Western (ISO 8859-15)'
|
||||
},
|
||||
{
|
||||
value: 'macroman',
|
||||
description: 'Western (Mac Roman)'
|
||||
},
|
||||
{
|
||||
value: 'windows1252',
|
||||
description: 'Western (Windows 1252)'
|
||||
}
|
||||
]
|
||||
},
|
||||
openEmptyEditorOnStart: {
|
||||
@@ -142,6 +272,12 @@ const configSchema = {
|
||||
type: 'boolean',
|
||||
default: true
|
||||
},
|
||||
useProxySettingsWhenCallingApm: {
|
||||
title: 'Use Proxy Settings When Calling APM',
|
||||
description: 'Use detected proxy settings when calling the `apm` command-line tool.',
|
||||
type: 'boolean',
|
||||
default: true
|
||||
},
|
||||
allowPendingPaneItems: {
|
||||
description: 'Allow items to be previewed without adding them to a pane permanently, such as when single clicking files in the tree view.',
|
||||
type: 'boolean',
|
||||
@@ -211,6 +347,11 @@ const configSchema = {
|
||||
default: 1.5,
|
||||
description: 'Height of editor lines, as a multiplier of font size.'
|
||||
},
|
||||
showCursorOnSelection: {
|
||||
type: 'boolean',
|
||||
'default': true,
|
||||
description: 'Show cursor while there is a selection.'
|
||||
},
|
||||
showInvisibles: {
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
|
||||
@@ -336,6 +336,31 @@ ScopeDescriptor = require './scope-descriptor'
|
||||
# order: 2
|
||||
# ```
|
||||
#
|
||||
# ## Manipulating values outside your configuration schema
|
||||
#
|
||||
# It is possible to manipulate(`get`, `set`, `observe` etc) values that do not
|
||||
# appear in your configuration schema. For example, if the config schema of the
|
||||
# package 'some-package' is
|
||||
#
|
||||
# ```coffee
|
||||
# config:
|
||||
# someSetting:
|
||||
# type: 'boolean'
|
||||
# default: false
|
||||
# ```
|
||||
#
|
||||
# You can still do the following
|
||||
#
|
||||
# ```coffee
|
||||
# let otherSetting = atom.config.get('some-package.otherSetting')
|
||||
# atom.config.set('some-package.stillAnotherSetting', otherSetting * 5)
|
||||
# ```
|
||||
#
|
||||
# In other words, if a function asks for a `key-path`, that path doesn't have to
|
||||
# be described in the config schema for the package or any package. However, as
|
||||
# highlighted in the best practices section, you are advised against doing the
|
||||
# above.
|
||||
#
|
||||
# ## Best practices
|
||||
#
|
||||
# * Don't depend on (or write to) configuration keys outside of your keypath.
|
||||
|
||||
@@ -12,15 +12,18 @@ EmptyLineRegExp = /(\r\n[\t ]*\r\n)|(\n[\t ]*\n)/g
|
||||
# of a {DisplayMarker}.
|
||||
module.exports =
|
||||
class Cursor extends Model
|
||||
showCursorOnSelection: null
|
||||
screenPosition: null
|
||||
bufferPosition: null
|
||||
goalColumn: null
|
||||
visible: true
|
||||
|
||||
# Instantiated by a {TextEditor}
|
||||
constructor: ({@editor, @marker, id}) ->
|
||||
constructor: ({@editor, @marker, @showCursorOnSelection, id}) ->
|
||||
@emitter = new Emitter
|
||||
|
||||
@showCursorOnSelection ?= true
|
||||
|
||||
@assignId(id)
|
||||
@updateVisibility()
|
||||
|
||||
@@ -575,7 +578,10 @@ class Cursor extends Model
|
||||
isVisible: -> @visible
|
||||
|
||||
updateVisibility: ->
|
||||
@setVisible(@marker.getBufferRange().isEmpty())
|
||||
if @showCursorOnSelection
|
||||
@setVisible(true)
|
||||
else
|
||||
@setVisible(@marker.getBufferRange().isEmpty())
|
||||
|
||||
###
|
||||
Section: Comparing to another cursor
|
||||
@@ -645,6 +651,11 @@ class Cursor extends Model
|
||||
Section: Private
|
||||
###
|
||||
|
||||
setShowCursorOnSelection: (value) ->
|
||||
if value isnt @showCursorOnSelection
|
||||
@showCursorOnSelection = value
|
||||
@updateVisibility()
|
||||
|
||||
getNonWordCharacters: ->
|
||||
@editor.getNonWordCharacters(@getScopeDescriptor().getScopesArray())
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ class DecorationManager extends Model
|
||||
didUpdateDecorationsEventScheduled: false
|
||||
updatedSynchronously: false
|
||||
|
||||
constructor: (@displayLayer, @defaultMarkerLayer) ->
|
||||
constructor: (@displayLayer) ->
|
||||
super
|
||||
|
||||
@emitter = new Emitter
|
||||
@@ -71,9 +71,11 @@ class DecorationManager extends Model
|
||||
|
||||
decorationsForScreenRowRange: (startScreenRow, endScreenRow) ->
|
||||
decorationsByMarkerId = {}
|
||||
for marker in @defaultMarkerLayer.findMarkers(intersectsScreenRowRange: [startScreenRow, endScreenRow])
|
||||
if decorations = @decorationsByMarkerId[marker.id]
|
||||
decorationsByMarkerId[marker.id] = decorations
|
||||
for layerId of @decorationCountsByLayerId
|
||||
layer = @displayLayer.getMarkerLayer(layerId)
|
||||
for marker in layer.findMarkers(intersectsScreenRowRange: [startScreenRow, endScreenRow])
|
||||
if decorations = @decorationsByMarkerId[marker.id]
|
||||
decorationsByMarkerId[marker.id] = decorations
|
||||
decorationsByMarkerId
|
||||
|
||||
decorationsStateForScreenRowRange: (startScreenRow, endScreenRow) ->
|
||||
@@ -104,7 +106,14 @@ class DecorationManager extends Model
|
||||
decorationsState
|
||||
|
||||
decorateMarker: (marker, decorationParams) ->
|
||||
throw new Error("Cannot decorate a destroyed marker") if marker.isDestroyed()
|
||||
if marker.isDestroyed()
|
||||
error = new Error("Cannot decorate a destroyed marker")
|
||||
error.metadata = {markerLayerIsDestroyed: marker.layer.isDestroyed()}
|
||||
if marker.destroyStackTrace?
|
||||
error.metadata.destroyStackTrace = marker.destroyStackTrace
|
||||
if marker.bufferMarker?.destroyStackTrace?
|
||||
error.metadata.destroyStackTrace = marker.bufferMarker?.destroyStackTrace
|
||||
throw error
|
||||
marker = @displayLayer.getMarkerLayer(marker.layer.id).getMarker(marker.id)
|
||||
decoration = new Decoration(marker, this, decorationParams)
|
||||
@decorationsByMarkerId[marker.id] ?= []
|
||||
@@ -117,6 +126,7 @@ class DecorationManager extends Model
|
||||
decoration
|
||||
|
||||
decorateMarkerLayer: (markerLayer, decorationParams) ->
|
||||
throw new Error("Cannot decorate a destroyed marker layer") if markerLayer.isDestroyed()
|
||||
decoration = new LayerDecoration(markerLayer, this, decorationParams)
|
||||
@layerDecorationsByMarkerLayerId[markerLayer.id] ?= []
|
||||
@layerDecorationsByMarkerLayerId[markerLayer.id].push(decoration)
|
||||
|
||||
@@ -15,7 +15,7 @@ class DefaultDirectoryProvider
|
||||
# * {Directory} if the given URI is compatible with this provider.
|
||||
# * `null` if the given URI is not compatibile with this provider.
|
||||
directoryForURISync: (uri) ->
|
||||
normalizedPath = path.normalize(uri)
|
||||
normalizedPath = @normalizePath(uri)
|
||||
{host} = url.parse(uri)
|
||||
directoryPath = if host
|
||||
uri
|
||||
@@ -42,3 +42,17 @@ class DefaultDirectoryProvider
|
||||
# * `null` if the given URI is not compatibile with this provider.
|
||||
directoryForURI: (uri) ->
|
||||
Promise.resolve(@directoryForURISync(uri))
|
||||
|
||||
# Public: Normalizes path.
|
||||
#
|
||||
# * `uri` {String} The path that should be normalized.
|
||||
#
|
||||
# Returns a {String} with normalized path.
|
||||
normalizePath: (uri) ->
|
||||
# Normalize disk drive letter on Windows to avoid opening two buffers for the same file
|
||||
pathWithNormalizedDiskDriveLetter =
|
||||
if process.platform is 'win32' and matchData = uri.match(/^([a-z]):/)
|
||||
"#{matchData[1].toUpperCase()}#{uri.slice(1)}"
|
||||
else
|
||||
uri
|
||||
path.normalize(pathWithNormalizedDiskDriveLetter)
|
||||
|
||||
@@ -1,55 +0,0 @@
|
||||
module.exports =
|
||||
class DOMElementPool
|
||||
constructor: ->
|
||||
@freeElementsByTagName = {}
|
||||
@freedElements = new Set
|
||||
|
||||
clear: ->
|
||||
@freedElements.clear()
|
||||
for tagName, freeElements of @freeElementsByTagName
|
||||
freeElements.length = 0
|
||||
return
|
||||
|
||||
build: (tagName, factory, reset) ->
|
||||
element = @freeElementsByTagName[tagName]?.pop()
|
||||
element ?= factory()
|
||||
reset(element)
|
||||
@freedElements.delete(element)
|
||||
element
|
||||
|
||||
buildElement: (tagName, className) ->
|
||||
factory = -> document.createElement(tagName)
|
||||
reset = (element) ->
|
||||
delete element.dataset[dataId] for dataId of element.dataset
|
||||
element.removeAttribute("style")
|
||||
if className?
|
||||
element.className = className
|
||||
else
|
||||
element.removeAttribute("class")
|
||||
@build(tagName, factory, reset)
|
||||
|
||||
buildText: (textContent) ->
|
||||
factory = -> document.createTextNode(textContent)
|
||||
reset = (element) -> element.textContent = textContent
|
||||
@build("#text", factory, reset)
|
||||
|
||||
freeElementAndDescendants: (element) ->
|
||||
@free(element)
|
||||
@freeDescendants(element)
|
||||
|
||||
freeDescendants: (element) ->
|
||||
for descendant in element.childNodes by -1
|
||||
@free(descendant)
|
||||
@freeDescendants(descendant)
|
||||
return
|
||||
|
||||
free: (element) ->
|
||||
throw new Error("The element cannot be null or undefined.") unless element?
|
||||
throw new Error("The element has already been freed!") if @freedElements.has(element)
|
||||
|
||||
tagName = element.nodeName.toLowerCase()
|
||||
@freeElementsByTagName[tagName] ?= []
|
||||
@freeElementsByTagName[tagName].push(element)
|
||||
@freedElements.add(element)
|
||||
|
||||
element.remove()
|
||||
89
src/dom-element-pool.js
Normal file
89
src/dom-element-pool.js
Normal file
@@ -0,0 +1,89 @@
|
||||
module.exports =
|
||||
class DOMElementPool {
|
||||
constructor () {
|
||||
this.managedElements = new Set()
|
||||
this.freeElementsByTagName = new Map()
|
||||
this.freedElements = new Set()
|
||||
}
|
||||
|
||||
clear () {
|
||||
this.managedElements.clear()
|
||||
this.freedElements.clear()
|
||||
this.freeElementsByTagName.clear()
|
||||
}
|
||||
|
||||
buildElement (tagName, className) {
|
||||
const elements = this.freeElementsByTagName.get(tagName)
|
||||
let element = elements ? elements.pop() : null
|
||||
if (element) {
|
||||
for (let dataId in element.dataset) { delete element.dataset[dataId] }
|
||||
element.removeAttribute('style')
|
||||
if (className) {
|
||||
element.className = className
|
||||
} else {
|
||||
element.removeAttribute('class')
|
||||
}
|
||||
while (element.firstChild) {
|
||||
element.removeChild(element.firstChild)
|
||||
}
|
||||
this.freedElements.delete(element)
|
||||
} else {
|
||||
element = document.createElement(tagName)
|
||||
if (className) {
|
||||
element.className = className
|
||||
}
|
||||
this.managedElements.add(element)
|
||||
}
|
||||
return element
|
||||
}
|
||||
|
||||
buildText (textContent) {
|
||||
const elements = this.freeElementsByTagName.get('#text')
|
||||
let element = elements ? elements.pop() : null
|
||||
if (element) {
|
||||
element.textContent = textContent
|
||||
this.freedElements.delete(element)
|
||||
} else {
|
||||
element = document.createTextNode(textContent)
|
||||
this.managedElements.add(element)
|
||||
}
|
||||
return element
|
||||
}
|
||||
|
||||
freeElementAndDescendants (element) {
|
||||
this.free(element)
|
||||
element.remove()
|
||||
}
|
||||
|
||||
freeDescendants (element) {
|
||||
while (element.firstChild) {
|
||||
this.free(element.firstChild)
|
||||
element.removeChild(element.firstChild)
|
||||
}
|
||||
}
|
||||
|
||||
free (element) {
|
||||
if (element == null) { throw new Error('The element cannot be null or undefined.') }
|
||||
if (!this.managedElements.has(element)) return
|
||||
if (this.freedElements.has(element)) {
|
||||
atom.assert(false, 'The element has already been freed!', {
|
||||
content: element instanceof window.Text ? element.textContent : element.outerHTML
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
const tagName = element.nodeName.toLowerCase()
|
||||
let elements = this.freeElementsByTagName.get(tagName)
|
||||
if (!elements) {
|
||||
elements = []
|
||||
this.freeElementsByTagName.set(tagName, elements)
|
||||
}
|
||||
elements.push(element)
|
||||
this.freedElements.add(element)
|
||||
|
||||
for (let i = element.childNodes.length - 1; i >= 0; i--) {
|
||||
const descendant = element.childNodes[i]
|
||||
this.free(descendant)
|
||||
}
|
||||
}
|
||||
}
|
||||
10
src/get-window-load-settings.js
Normal file
10
src/get-window-load-settings.js
Normal file
@@ -0,0 +1,10 @@
|
||||
const {remote} = require('electron')
|
||||
|
||||
let windowLoadSettings = null
|
||||
|
||||
module.exports = () => {
|
||||
if (!windowLoadSettings) {
|
||||
windowLoadSettings = remote.getCurrentWindow().loadSettings
|
||||
}
|
||||
return windowLoadSettings
|
||||
}
|
||||
@@ -61,6 +61,19 @@ export class HistoryManager {
|
||||
this.didChangeProjects()
|
||||
}
|
||||
|
||||
removeProject (paths) {
|
||||
if (paths.length === 0) return
|
||||
|
||||
let project = this.getProject(paths)
|
||||
if (!project) return
|
||||
|
||||
let index = this.projects.indexOf(project)
|
||||
this.projects.splice(index, 1)
|
||||
|
||||
this.saveState()
|
||||
this.didChangeProjects()
|
||||
}
|
||||
|
||||
getProject (paths) {
|
||||
for (var i = 0; i < this.projects.length; i++) {
|
||||
if (arrayEquivalent(paths, this.projects[i].paths)) {
|
||||
|
||||
@@ -3,7 +3,7 @@ module.exports = ({blobStore}) ->
|
||||
{updateProcessEnv} = require('./update-process-env')
|
||||
path = require 'path'
|
||||
require './window'
|
||||
{getWindowLoadSettings} = require './window-load-settings-helpers'
|
||||
getWindowLoadSettings = require './get-window-load-settings'
|
||||
{ipcRenderer} = require 'electron'
|
||||
{resourcePath, devMode, env} = getWindowLoadSettings()
|
||||
require './electron-shims'
|
||||
|
||||
@@ -6,7 +6,7 @@ import ipcHelpers from './ipc-helpers'
|
||||
import util from 'util'
|
||||
|
||||
export default async function () {
|
||||
const {getWindowLoadSettings} = require('./window-load-settings-helpers')
|
||||
const getWindowLoadSettings = require('./get-window-load-settings')
|
||||
const {test, headless, resourcePath, benchmarkPaths} = getWindowLoadSettings()
|
||||
try {
|
||||
const Clipboard = require('../src/clipboard')
|
||||
|
||||
@@ -18,7 +18,7 @@ module.exports = ({blobStore}) ->
|
||||
try
|
||||
path = require 'path'
|
||||
{ipcRenderer} = require 'electron'
|
||||
{getWindowLoadSettings} = require './window-load-settings-helpers'
|
||||
getWindowLoadSettings = require './get-window-load-settings'
|
||||
CompileCache = require './compile-cache'
|
||||
AtomEnvironment = require '../src/atom-environment'
|
||||
ApplicationDelegate = require '../src/application-delegate'
|
||||
|
||||
@@ -15,6 +15,7 @@ exports.on = function (emitter, eventName, callback) {
|
||||
exports.call = function (channel, ...args) {
|
||||
if (!ipcRenderer) {
|
||||
ipcRenderer = require('electron').ipcRenderer
|
||||
ipcRenderer.setMaxListeners(20)
|
||||
}
|
||||
|
||||
var responseChannel = getResponseChannel(channel)
|
||||
|
||||
@@ -593,8 +593,7 @@ class AtomApplication
|
||||
states = []
|
||||
for window in @windows
|
||||
unless window.isSpec
|
||||
if loadSettings = window.getLoadSettings()
|
||||
states.push(initialPaths: loadSettings.initialPaths)
|
||||
states.push({initialPaths: window.representedDirectoryPaths})
|
||||
if states.length > 0 or allowEmpty
|
||||
@storageFolder.storeSync('application.json', states)
|
||||
|
||||
|
||||
@@ -52,9 +52,7 @@ class AtomWindow
|
||||
if @shouldHideTitleBar()
|
||||
options.frame = false
|
||||
|
||||
@browserWindow = new BrowserWindow options
|
||||
@atomApplication.addWindow(this)
|
||||
|
||||
@browserWindow = new BrowserWindow(options)
|
||||
@handleEvents()
|
||||
|
||||
loadSettings = Object.assign({}, settings)
|
||||
@@ -66,11 +64,15 @@ class AtomWindow
|
||||
loadSettings.clearWindowState ?= false
|
||||
loadSettings.initialPaths ?=
|
||||
for {pathToOpen} in locationsToOpen when pathToOpen
|
||||
if fs.statSyncNoException(pathToOpen).isFile?()
|
||||
path.dirname(pathToOpen)
|
||||
else
|
||||
stat = fs.statSyncNoException(pathToOpen) or null
|
||||
if stat?.isDirectory()
|
||||
pathToOpen
|
||||
|
||||
else
|
||||
parentDirectory = path.dirname(pathToOpen)
|
||||
if stat?.isFile() or fs.existsSync(parentDirectory)
|
||||
parentDirectory
|
||||
else
|
||||
pathToOpen
|
||||
loadSettings.initialPaths.sort()
|
||||
|
||||
# Only send to the first non-spec window created
|
||||
@@ -78,33 +80,31 @@ class AtomWindow
|
||||
@constructor.includeShellLoadTime = false
|
||||
loadSettings.shellLoadTime ?= Date.now() - global.shellStartTime
|
||||
|
||||
@representedDirectoryPaths = loadSettings.initialPaths
|
||||
@env = loadSettings.env if loadSettings.env?
|
||||
|
||||
@browserWindow.loadSettings = loadSettings
|
||||
|
||||
@browserWindow.on 'window:loaded', =>
|
||||
@emit 'window:loaded'
|
||||
@resolveLoadedPromise()
|
||||
|
||||
@setLoadSettings(loadSettings)
|
||||
@env = loadSettings.env if loadSettings.env?
|
||||
@browserWindow.loadURL url.format
|
||||
protocol: 'file'
|
||||
pathname: "#{@resourcePath}/static/index.html"
|
||||
slashes: true
|
||||
|
||||
@browserWindow.showSaveDialog = @showSaveDialog.bind(this)
|
||||
|
||||
@browserWindow.focusOnWebView() if @isSpec
|
||||
@browserWindow.temporaryState = {windowDimensions} if windowDimensions?
|
||||
|
||||
hasPathToOpen = not (locationsToOpen.length is 1 and not locationsToOpen[0].pathToOpen?)
|
||||
@openLocations(locationsToOpen) if hasPathToOpen and not @isSpecWindow()
|
||||
|
||||
setLoadSettings: (loadSettings) ->
|
||||
@browserWindow.loadURL url.format
|
||||
protocol: 'file'
|
||||
pathname: "#{@resourcePath}/static/index.html"
|
||||
slashes: true
|
||||
hash: encodeURIComponent(JSON.stringify(loadSettings))
|
||||
@atomApplication.addWindow(this)
|
||||
|
||||
getLoadSettings: ->
|
||||
if @browserWindow.webContents? and not @browserWindow.webContents.isLoading()
|
||||
hash = url.parse(@browserWindow.webContents.getURL()).hash.substr(1)
|
||||
JSON.parse(decodeURIComponent(hash))
|
||||
|
||||
hasProjectPath: -> @getLoadSettings().initialPaths?.length > 0
|
||||
hasProjectPath: -> @representedDirectoryPaths.length > 0
|
||||
|
||||
setupContextMenu: ->
|
||||
ContextMenu = require './context-menu'
|
||||
@@ -118,7 +118,7 @@ class AtomWindow
|
||||
true
|
||||
|
||||
containsPath: (pathToCheck) ->
|
||||
@getLoadSettings()?.initialPaths?.some (projectPath) ->
|
||||
@representedDirectoryPaths.some (projectPath) ->
|
||||
if not projectPath
|
||||
false
|
||||
else if not pathToCheck
|
||||
@@ -281,6 +281,13 @@ class AtomWindow
|
||||
@saveState().then => @browserWindow.reload()
|
||||
@loadedPromise
|
||||
|
||||
showSaveDialog: (params) ->
|
||||
params = Object.assign({
|
||||
title: 'Save File',
|
||||
defaultPath: @representedDirectoryPaths[0]
|
||||
}, params)
|
||||
dialog.showSaveDialog(this, params)
|
||||
|
||||
toggleDevTools: -> @browserWindow.toggleDevTools()
|
||||
|
||||
openDevTools: -> @browserWindow.openDevTools()
|
||||
@@ -291,4 +298,8 @@ class AtomWindow
|
||||
|
||||
setRepresentedFilename: (representedFilename) -> @browserWindow.setRepresentedFilename(representedFilename)
|
||||
|
||||
setRepresentedDirectoryPaths: (@representedDirectoryPaths) ->
|
||||
@representedDirectoryPaths.sort()
|
||||
@atomApplication.saveState()
|
||||
|
||||
copy: -> @browserWindow.copy()
|
||||
|
||||
@@ -22,7 +22,7 @@ class AutoUpdateManager
|
||||
setupAutoUpdater: ->
|
||||
if process.platform is 'win32'
|
||||
archSuffix = if process.arch is 'ia32' then '' else '-' + process.arch
|
||||
@feedUrl = "https://atom.io/api/updates#{archSuffix}"
|
||||
@feedUrl = "https://atom.io/api/updates#{archSuffix}?version=#{@version}"
|
||||
autoUpdater = require './auto-updater-win32'
|
||||
else
|
||||
@feedUrl = "https://atom.io/api/updates?version=#{@version}"
|
||||
|
||||
@@ -106,14 +106,14 @@ module.exports = function parseCommandLine (processArgs) {
|
||||
|
||||
if (args['resource-path']) {
|
||||
devMode = true
|
||||
resourcePath = args['resource-path']
|
||||
devResourcePath = args['resource-path']
|
||||
}
|
||||
|
||||
if (test) {
|
||||
devMode = true
|
||||
}
|
||||
|
||||
if (devMode && !resourcePath) {
|
||||
if (devMode) {
|
||||
resourcePath = devResourcePath
|
||||
}
|
||||
|
||||
|
||||
@@ -711,6 +711,9 @@ class Package
|
||||
incompatibleNativeModules
|
||||
|
||||
handleError: (message, error) ->
|
||||
if atom.inSpecMode()
|
||||
throw error
|
||||
|
||||
if error.filename and error.location and (error instanceof SyntaxError)
|
||||
location = "#{error.filename}:#{error.location.first_line + 1}:#{error.location.first_column + 1}"
|
||||
detail = "#{error.message} in #{location}"
|
||||
|
||||
@@ -21,7 +21,6 @@ class Project extends Model
|
||||
constructor: ({@notificationManager, packageManager, config, @applicationDelegate}) ->
|
||||
@emitter = new Emitter
|
||||
@buffers = []
|
||||
@paths = []
|
||||
@rootDirectories = []
|
||||
@repositories = []
|
||||
@directoryProviders = []
|
||||
@@ -32,7 +31,9 @@ class Project extends Model
|
||||
|
||||
destroyed: ->
|
||||
buffer.destroy() for buffer in @buffers
|
||||
@setPaths([])
|
||||
repository?.destroy() for repository in @repositories
|
||||
@rootDirectories = []
|
||||
@repositories = []
|
||||
|
||||
reset: (packageManager) ->
|
||||
@emitter.dispose()
|
||||
@@ -62,6 +63,9 @@ class Project extends Model
|
||||
fs.closeSync(fs.openSync(bufferState.filePath, 'r'))
|
||||
catch error
|
||||
return unless error.code is 'ENOENT'
|
||||
unless bufferState.shouldDestroyOnFileDelete?
|
||||
bufferState.shouldDestroyOnFileDelete =
|
||||
-> atom.config.get('core.closeDeletedFileTabs')
|
||||
TextBuffer.deserialize(bufferState)
|
||||
|
||||
@subscribeToBuffer(buffer) for buffer in @buffers
|
||||
@@ -205,7 +209,7 @@ class Project extends Model
|
||||
removePath: (projectPath) ->
|
||||
# The projectPath may be a URI, in which case it should not be normalized.
|
||||
unless projectPath in @getPaths()
|
||||
projectPath = path.normalize(projectPath)
|
||||
projectPath = @defaultDirectoryProvider.normalizePath(projectPath)
|
||||
|
||||
indexToRemove = null
|
||||
for directory, i in @rootDirectories
|
||||
@@ -233,11 +237,10 @@ class Project extends Model
|
||||
uri
|
||||
else
|
||||
if fs.isAbsolute(uri)
|
||||
path.normalize(fs.resolveHome(uri))
|
||||
|
||||
@defaultDirectoryProvider.normalizePath(fs.resolveHome(uri))
|
||||
# TODO: what should we do here when there are multiple directories?
|
||||
else if projectPath = @getPaths()[0]
|
||||
path.normalize(fs.resolveHome(path.join(projectPath, uri)))
|
||||
@defaultDirectoryProvider.normalizePath(fs.resolveHome(path.join(projectPath, uri)))
|
||||
else
|
||||
undefined
|
||||
|
||||
@@ -360,9 +363,14 @@ class Project extends Model
|
||||
else
|
||||
@buildBuffer(absoluteFilePath)
|
||||
|
||||
shouldDestroyBufferOnFileDelete: ->
|
||||
atom.config.get('core.closeDeletedFileTabs')
|
||||
|
||||
# Still needed when deserializing a tokenized buffer
|
||||
buildBufferSync: (absoluteFilePath) ->
|
||||
buffer = new TextBuffer({filePath: absoluteFilePath})
|
||||
buffer = new TextBuffer({
|
||||
filePath: absoluteFilePath
|
||||
shouldDestroyOnFileDelete: @shouldDestroyBufferOnFileDelete})
|
||||
@addBuffer(buffer)
|
||||
buffer.loadSync()
|
||||
buffer
|
||||
@@ -374,7 +382,9 @@ class Project extends Model
|
||||
#
|
||||
# Returns a {Promise} that resolves to the {TextBuffer}.
|
||||
buildBuffer: (absoluteFilePath) ->
|
||||
buffer = new TextBuffer({filePath: absoluteFilePath})
|
||||
buffer = new TextBuffer({
|
||||
filePath: absoluteFilePath
|
||||
shouldDestroyOnFileDelete: @shouldDestroyBufferOnFileDelete})
|
||||
@addBuffer(buffer)
|
||||
buffer.load()
|
||||
.then((buffer) -> buffer)
|
||||
|
||||
@@ -19,6 +19,8 @@ export default class ReopenProjectMenuManager {
|
||||
}),
|
||||
commands.add('atom-workspace', { 'application:reopen-project': this.reopenProjectCommand.bind(this) })
|
||||
)
|
||||
|
||||
this.applyWindowsJumpListRemovals()
|
||||
}
|
||||
|
||||
reopenProjectCommand (e) {
|
||||
@@ -49,9 +51,30 @@ export default class ReopenProjectMenuManager {
|
||||
this.updateWindowsJumpList()
|
||||
}
|
||||
|
||||
static taskDescription (paths) {
|
||||
return paths.map(path => `${ReopenProjectMenuManager.betterBaseName(path)} (${path})`).join(' ')
|
||||
}
|
||||
|
||||
// Windows users can right-click Atom taskbar and remove project from the jump list.
|
||||
// We have to honor that or the group stops working. As we only get a partial list
|
||||
// each time we remove them from history entirely.
|
||||
applyWindowsJumpListRemovals () {
|
||||
if (process.platform !== 'win32') return
|
||||
if (this.app === undefined) {
|
||||
this.app = require('remote').app
|
||||
}
|
||||
|
||||
const removed = this.app.getJumpListSettings().removedItems.map(i => i.description)
|
||||
if (removed.length === 0) return
|
||||
for (let project of this.historyManager.getProjects()) {
|
||||
if (removed.includes(ReopenProjectMenuManager.taskDescription(project.paths))) {
|
||||
this.historyManager.removeProject(project.paths)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
updateWindowsJumpList () {
|
||||
if (process.platform !== 'win32') return
|
||||
|
||||
if (this.app === undefined) {
|
||||
this.app = require('remote').app
|
||||
}
|
||||
@@ -64,7 +87,7 @@ export default class ReopenProjectMenuManager {
|
||||
({
|
||||
type: 'task',
|
||||
title: project.paths.map(ReopenProjectMenuManager.betterBaseName).join(', '),
|
||||
description: project.paths.map(path => `${ReopenProjectMenuManager.betterBaseName(path)} (${path})`).join(' '),
|
||||
description: ReopenProjectMenuManager.taskDescription(project.paths),
|
||||
program: process.execPath,
|
||||
args: project.paths.map(path => `"${path}"`).join(' '),
|
||||
iconPath: path.join(path.dirname(process.execPath), 'resources', 'cli', 'folder.ico'),
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
module.exports =
|
||||
class StateStore {
|
||||
constructor (databaseName, version) {
|
||||
this.connected = false
|
||||
this.dbPromise = new Promise((resolve) => {
|
||||
let dbOpenRequest = indexedDB.open(databaseName, version)
|
||||
dbOpenRequest.onupgradeneeded = (event) => {
|
||||
@@ -10,15 +11,21 @@ class StateStore {
|
||||
db.createObjectStore('states')
|
||||
}
|
||||
dbOpenRequest.onsuccess = () => {
|
||||
this.connected = true
|
||||
resolve(dbOpenRequest.result)
|
||||
}
|
||||
dbOpenRequest.onerror = (error) => {
|
||||
console.error('Could not connect to indexedDB', error)
|
||||
this.connected = false
|
||||
resolve(null)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
isConnected () {
|
||||
return this.connected
|
||||
}
|
||||
|
||||
connect () {
|
||||
return this.dbPromise.then((db) => !!db)
|
||||
}
|
||||
|
||||
@@ -250,59 +250,70 @@ module.exports = class StyleManager {
|
||||
|
||||
function transformDeprecatedShadowDOMSelectors (css, context) {
|
||||
const transformedSelectors = []
|
||||
const transformedSource = postcss.parse(css)
|
||||
transformedSource.walkRules((rule) => {
|
||||
const transformedSelector = selectorParser((selectors) => {
|
||||
selectors.each((selector) => {
|
||||
const firstNode = selector.nodes[0]
|
||||
if (context === 'atom-text-editor' && firstNode.type === 'pseudo' && firstNode.value === ':host') {
|
||||
const atomTextEditorElementNode = selectorParser.tag({value: 'atom-text-editor'})
|
||||
firstNode.replaceWith(atomTextEditorElementNode)
|
||||
}
|
||||
|
||||
let previousNodeIsAtomTextEditor = false
|
||||
let targetsAtomTextEditorShadow = context === 'atom-text-editor'
|
||||
let previousNode
|
||||
selector.each((node) => {
|
||||
if (targetsAtomTextEditorShadow && node.type === 'class') {
|
||||
if (DEPRECATED_SYNTAX_SELECTORS.has(node.value)) {
|
||||
node.value = `syntax--${node.value}`
|
||||
}
|
||||
} else {
|
||||
if (previousNodeIsAtomTextEditor && node.type === 'pseudo' && node.value === '::shadow') {
|
||||
node.type = 'className'
|
||||
node.value = '.editor'
|
||||
targetsAtomTextEditorShadow = true
|
||||
}
|
||||
}
|
||||
|
||||
previousNode = node
|
||||
if (node.type === 'combinator') {
|
||||
previousNodeIsAtomTextEditor = false
|
||||
} else if (previousNode.type === 'tag' && previousNode.value === 'atom-text-editor') {
|
||||
previousNodeIsAtomTextEditor = true
|
||||
}
|
||||
})
|
||||
})
|
||||
}).process(rule.selector, {lossless: true}).result
|
||||
if (transformedSelector !== rule.selector) {
|
||||
transformedSelectors.push({before: rule.selector, after: transformedSelector})
|
||||
rule.selector = transformedSelector
|
||||
}
|
||||
})
|
||||
let deprecationMessage
|
||||
if (transformedSelectors.length > 0) {
|
||||
deprecationMessage = 'Starting from Atom v1.13.0, the contents of `atom-text-editor` elements '
|
||||
deprecationMessage += 'are no longer encapsulated within a shadow DOM boundary. '
|
||||
deprecationMessage += 'This means you should stop using `:host` and `::shadow` '
|
||||
deprecationMessage += 'pseudo-selectors, and prepend all your syntax selectors with `syntax--`. '
|
||||
deprecationMessage += 'To prevent breakage with existing style sheets, Atom will automatically '
|
||||
deprecationMessage += 'upgrade the following selectors:\n\n'
|
||||
deprecationMessage += transformedSelectors
|
||||
.map((selector) => `* \`${selector.before}\` => \`${selector.after}\``)
|
||||
.join('\n\n') + '\n\n'
|
||||
deprecationMessage += 'Automatic translation of selectors will be removed in a few release cycles to minimize startup time. '
|
||||
deprecationMessage += 'Please, make sure to upgrade the above selectors as soon as possible.'
|
||||
let transformedSource
|
||||
try {
|
||||
transformedSource = postcss.parse(css)
|
||||
} catch (e) {
|
||||
transformedSource = null
|
||||
}
|
||||
|
||||
if (transformedSource) {
|
||||
transformedSource.walkRules((rule) => {
|
||||
const transformedSelector = selectorParser((selectors) => {
|
||||
selectors.each((selector) => {
|
||||
const firstNode = selector.nodes[0]
|
||||
if (context === 'atom-text-editor' && firstNode.type === 'pseudo' && firstNode.value === ':host') {
|
||||
const atomTextEditorElementNode = selectorParser.tag({value: 'atom-text-editor'})
|
||||
firstNode.replaceWith(atomTextEditorElementNode)
|
||||
}
|
||||
|
||||
let previousNodeIsAtomTextEditor = false
|
||||
let targetsAtomTextEditorShadow = context === 'atom-text-editor'
|
||||
let previousNode
|
||||
selector.each((node) => {
|
||||
if (targetsAtomTextEditorShadow && node.type === 'class') {
|
||||
if (DEPRECATED_SYNTAX_SELECTORS.has(node.value)) {
|
||||
node.value = `syntax--${node.value}`
|
||||
}
|
||||
} else {
|
||||
if (previousNodeIsAtomTextEditor && node.type === 'pseudo' && node.value === '::shadow') {
|
||||
node.type = 'className'
|
||||
node.value = '.editor'
|
||||
targetsAtomTextEditorShadow = true
|
||||
}
|
||||
}
|
||||
|
||||
previousNode = node
|
||||
if (node.type === 'combinator') {
|
||||
previousNodeIsAtomTextEditor = false
|
||||
} else if (previousNode.type === 'tag' && previousNode.value === 'atom-text-editor') {
|
||||
previousNodeIsAtomTextEditor = true
|
||||
}
|
||||
})
|
||||
})
|
||||
}).process(rule.selector, {lossless: true}).result
|
||||
if (transformedSelector !== rule.selector) {
|
||||
transformedSelectors.push({before: rule.selector, after: transformedSelector})
|
||||
rule.selector = transformedSelector
|
||||
}
|
||||
})
|
||||
let deprecationMessage
|
||||
if (transformedSelectors.length > 0) {
|
||||
deprecationMessage = 'Starting from Atom v1.13.0, the contents of `atom-text-editor` elements '
|
||||
deprecationMessage += 'are no longer encapsulated within a shadow DOM boundary. '
|
||||
deprecationMessage += 'This means you should stop using `:host` and `::shadow` '
|
||||
deprecationMessage += 'pseudo-selectors, and prepend all your syntax selectors with `syntax--`. '
|
||||
deprecationMessage += 'To prevent breakage with existing style sheets, Atom will automatically '
|
||||
deprecationMessage += 'upgrade the following selectors:\n\n'
|
||||
deprecationMessage += transformedSelectors
|
||||
.map((selector) => `* \`${selector.before}\` => \`${selector.after}\``)
|
||||
.join('\n\n') + '\n\n'
|
||||
deprecationMessage += 'Automatic translation of selectors will be removed in a few release cycles to minimize startup time. '
|
||||
deprecationMessage += 'Please, make sure to upgrade the above selectors as soon as possible.'
|
||||
}
|
||||
return {source: transformedSource.toString(), deprecationMessage}
|
||||
} else {
|
||||
// CSS was malformed so we don't transform it.
|
||||
return {source: css}
|
||||
}
|
||||
return {source: transformedSource.toString(), deprecationMessage}
|
||||
}
|
||||
|
||||
@@ -108,7 +108,10 @@ class TextEditorElement extends HTMLElement
|
||||
|
||||
buildModel: ->
|
||||
@setModel(@workspace.buildTextEditor(
|
||||
buffer: new TextBuffer(@textContent)
|
||||
buffer: new TextBuffer({
|
||||
text: @textContent
|
||||
shouldDestroyOnFileDelete:
|
||||
-> atom.config.get('core.closeDeletedFileTabs')})
|
||||
softWrapped: false
|
||||
tabLength: 2
|
||||
softTabs: true
|
||||
|
||||
@@ -622,6 +622,18 @@ class TextEditorPresenter
|
||||
return unless @scrollTop? and @lineHeight?
|
||||
|
||||
@startRow = Math.max(0, @lineTopIndex.rowForPixelPosition(@scrollTop))
|
||||
atom.assert(
|
||||
Number.isFinite(@startRow),
|
||||
'Invalid start row',
|
||||
(error) =>
|
||||
error.metadata = {
|
||||
startRow: @startRow?.toString(),
|
||||
scrollTop: @scrollTop?.toString(),
|
||||
scrollHeight: @scrollHeight?.toString(),
|
||||
clientHeight: @clientHeight?.toString(),
|
||||
lineHeight: @lineHeight?.toString()
|
||||
}
|
||||
)
|
||||
|
||||
updateEndRow: ->
|
||||
return unless @scrollTop? and @lineHeight? and @height?
|
||||
|
||||
@@ -11,6 +11,7 @@ const EDITOR_PARAMS_BY_SETTING_KEY = [
|
||||
['editor.showInvisibles', 'showInvisibles'],
|
||||
['editor.tabLength', 'tabLength'],
|
||||
['editor.invisibles', 'invisibles'],
|
||||
['editor.showCursorOnSelection', 'showCursorOnSelection'],
|
||||
['editor.showIndentGuide', 'showIndentGuide'],
|
||||
['editor.showLineNumbers', 'showLineNumbers'],
|
||||
['editor.softWrap', 'softWrapped'],
|
||||
|
||||
@@ -16,12 +16,11 @@ TextEditorElement = require './text-editor-element'
|
||||
{isDoubleWidthCharacter, isHalfWidthCharacter, isKoreanCharacter, isWrapBoundary} = require './text-utils'
|
||||
|
||||
ZERO_WIDTH_NBSP = '\ufeff'
|
||||
MAX_SCREEN_LINE_LENGTH = 500
|
||||
|
||||
# Essential: This class represents all essential editing state for a single
|
||||
# {TextBuffer}, including cursor and selection positions, folds, and soft wraps.
|
||||
# If you're manipulating the state of an editor, use this class. If you're
|
||||
# interested in the visual appearance of editors, use {TextEditorElement}
|
||||
# instead.
|
||||
# If you're manipulating the state of an editor, use this class.
|
||||
#
|
||||
# A single {TextBuffer} can belong to multiple editors. For example, if the
|
||||
# same file is open in two different panes, Atom creates a separate editor for
|
||||
@@ -67,6 +66,7 @@ class TextEditor extends Model
|
||||
buffer: null
|
||||
languageMode: null
|
||||
cursors: null
|
||||
showCursorOnSelection: null
|
||||
selections: null
|
||||
suppressSelectionMerging: false
|
||||
selectionFlashDuration: 500
|
||||
@@ -133,7 +133,8 @@ class TextEditor extends Model
|
||||
@mini, @placeholderText, lineNumberGutterVisible, @largeFileMode,
|
||||
@assert, grammar, @showInvisibles, @autoHeight, @autoWidth, @scrollPastEnd, @editorWidthInChars,
|
||||
@tokenizedBuffer, @displayLayer, @invisibles, @showIndentGuide,
|
||||
@softWrapped, @softWrapAtPreferredLineLength, @preferredLineLength
|
||||
@softWrapped, @softWrapAtPreferredLineLength, @preferredLineLength,
|
||||
@showCursorOnSelection
|
||||
} = params
|
||||
|
||||
@assert ?= (condition) -> condition
|
||||
@@ -153,13 +154,15 @@ class TextEditor extends Model
|
||||
tabLength ?= 2
|
||||
@autoIndent ?= true
|
||||
@autoIndentOnPaste ?= true
|
||||
@showCursorOnSelection ?= true
|
||||
@undoGroupingInterval ?= 300
|
||||
@nonWordCharacters ?= "/\\()\"':,.;<>~!@#$%^&*|+=[]{}`?-…"
|
||||
@softWrapped ?= false
|
||||
@softWrapAtPreferredLineLength ?= false
|
||||
@preferredLineLength ?= 80
|
||||
|
||||
@buffer ?= new TextBuffer
|
||||
@buffer ?= new TextBuffer({shouldDestroyOnFileDelete: ->
|
||||
atom.config.get('core.closeDeletedFileTabs')})
|
||||
@tokenizedBuffer ?= new TokenizedBuffer({
|
||||
grammar, tabLength, @buffer, @largeFileMode, @assert
|
||||
})
|
||||
@@ -190,8 +193,9 @@ class TextEditor extends Model
|
||||
@displayLayer.setTextDecorationLayer(@tokenizedBuffer)
|
||||
@defaultMarkerLayer = @displayLayer.addMarkerLayer()
|
||||
@selectionsMarkerLayer ?= @addMarkerLayer(maintainHistory: true, persistent: true)
|
||||
@selectionsMarkerLayer.trackDestructionInOnDidCreateMarkerCallbacks = true
|
||||
|
||||
@decorationManager = new DecorationManager(@displayLayer, @defaultMarkerLayer)
|
||||
@decorationManager = new DecorationManager(@displayLayer)
|
||||
@decorateMarkerLayer(@displayLayer.foldsMarkerLayer, {type: 'line-number', class: 'folded'})
|
||||
|
||||
for marker in @selectionsMarkerLayer.getMarkers()
|
||||
@@ -342,6 +346,12 @@ class TextEditor extends Model
|
||||
if value isnt @autoWidth
|
||||
@autoWidth = value
|
||||
@presenter?.didChangeAutoWidth()
|
||||
|
||||
when 'showCursorOnSelection'
|
||||
if value isnt @showCursorOnSelection
|
||||
@showCursorOnSelection = value
|
||||
cursor.setShowCursorOnSelection(value) for cursor in @getCursors()
|
||||
|
||||
else
|
||||
throw new TypeError("Invalid TextEditor parameter: '#{param}'")
|
||||
|
||||
@@ -722,7 +732,7 @@ class TextEditor extends Model
|
||||
tabLength: @tokenizedBuffer.getTabLength(),
|
||||
@firstVisibleScreenRow, @firstVisibleScreenColumn,
|
||||
@assert, displayLayer, grammar: @getGrammar(),
|
||||
@autoWidth, @autoHeight
|
||||
@autoWidth, @autoHeight, @showCursorOnSelection
|
||||
})
|
||||
|
||||
# Controls visibility based on the given {Boolean}.
|
||||
@@ -892,7 +902,7 @@ class TextEditor extends Model
|
||||
# Determine whether the user should be prompted to save before closing
|
||||
# this editor.
|
||||
shouldPromptToSave: ({windowCloseRequested, projectHasPaths}={}) ->
|
||||
if windowCloseRequested and projectHasPaths
|
||||
if windowCloseRequested and projectHasPaths and atom.stateStore.isConnected()
|
||||
false
|
||||
else
|
||||
@isModified() and not @buffer.hasMultipleEditors()
|
||||
@@ -2269,13 +2279,12 @@ class TextEditor extends Model
|
||||
|
||||
# Add a cursor based on the given {DisplayMarker}.
|
||||
addCursor: (marker) ->
|
||||
cursor = new Cursor(editor: this, marker: marker)
|
||||
cursor = new Cursor(editor: this, marker: marker, showCursorOnSelection: @showCursorOnSelection)
|
||||
@cursors.push(cursor)
|
||||
@cursorsByMarkerId.set(marker.id, cursor)
|
||||
@decorateMarker(marker, type: 'line-number', class: 'cursor-line')
|
||||
@decorateMarker(marker, type: 'line-number', class: 'cursor-line-no-selection', onlyHead: true, onlyEmpty: true)
|
||||
@decorateMarker(marker, type: 'line', class: 'cursor-line', onlyEmpty: true)
|
||||
@emitter.emit 'did-add-cursor', cursor
|
||||
cursor
|
||||
|
||||
moveCursors: (fn) ->
|
||||
@@ -2764,6 +2773,7 @@ class TextEditor extends Model
|
||||
if selection.intersectsBufferRange(selectionBufferRange)
|
||||
return selection
|
||||
else
|
||||
@emitter.emit 'did-add-cursor', cursor
|
||||
@emitter.emit 'did-add-selection', selection
|
||||
selection
|
||||
|
||||
@@ -2956,7 +2966,7 @@ class TextEditor extends Model
|
||||
else
|
||||
@getEditorWidthInChars()
|
||||
else
|
||||
Infinity
|
||||
MAX_SCREEN_LINE_LENGTH
|
||||
|
||||
###
|
||||
Section: Indentation
|
||||
@@ -3466,6 +3476,11 @@ class TextEditor extends Model
|
||||
# Returns a positive {Number}.
|
||||
getScrollSensitivity: -> @scrollSensitivity
|
||||
|
||||
# Experimental: Does this editor show cursors while there is a selection?
|
||||
#
|
||||
# Returns a positive {Boolean}.
|
||||
getShowCursorOnSelection: -> @showCursorOnSelection
|
||||
|
||||
# Experimental: Are line numbers enabled for this editor?
|
||||
#
|
||||
# Returns a {Boolean}
|
||||
|
||||
@@ -8,6 +8,8 @@ ScopeDescriptor = require './scope-descriptor'
|
||||
TokenizedBufferIterator = require './tokenized-buffer-iterator'
|
||||
NullGrammar = require './null-grammar'
|
||||
|
||||
MAX_LINE_LENGTH_TO_TOKENIZE = 500
|
||||
|
||||
module.exports =
|
||||
class TokenizedBuffer extends Model
|
||||
grammar: null
|
||||
@@ -251,6 +253,8 @@ class TokenizedBuffer extends Model
|
||||
|
||||
buildTokenizedLineForRowWithText: (row, text, ruleStack = @stackForRow(row - 1), openScopes = @openScopesForRow(row)) ->
|
||||
lineEnding = @buffer.lineEndingForRow(row)
|
||||
if text.length > MAX_LINE_LENGTH_TO_TOKENIZE
|
||||
text = text.slice(0, MAX_LINE_LENGTH_TO_TOKENIZE)
|
||||
{tags, ruleStack} = @grammar.tokenizeLine(text, ruleStack, row is 0, false)
|
||||
new TokenizedLine({openScopes, text, tags, ruleStack, lineEnding, @tokenIterator})
|
||||
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
windowLoadSettings = null
|
||||
|
||||
exports.getWindowLoadSettings = ->
|
||||
windowLoadSettings ?= JSON.parse(window.decodeURIComponent(window.location.hash.substr(1)))
|
||||
|
||||
exports.setWindowLoadSettings = (settings) ->
|
||||
windowLoadSettings = settings
|
||||
location.hash = encodeURIComponent(JSON.stringify(settings))
|
||||
@@ -102,7 +102,7 @@ class WorkspaceElement extends HTMLElement
|
||||
getModel: -> @model
|
||||
|
||||
handleMousewheel: (event) ->
|
||||
if event.ctrlKey and @config.get('editor.zoomFontWhenCtrlScrolling') and event.target.matches('atom-text-editor')
|
||||
if event.ctrlKey and @config.get('editor.zoomFontWhenCtrlScrolling') and event.target.closest('atom-text-editor')?
|
||||
if event.wheelDeltaY > 0
|
||||
@model.increaseFontSize()
|
||||
else if event.wheelDeltaY < 0
|
||||
|
||||
@@ -182,7 +182,7 @@ class Workspace extends Model
|
||||
projectPath = _.find projectPaths, (projectPath) ->
|
||||
itemPath is projectPath or itemPath?.startsWith(projectPath + path.sep)
|
||||
itemTitle ?= "untitled"
|
||||
projectPath ?= if itemPath then path.dirname(itemPath) else null
|
||||
projectPath ?= if itemPath then path.dirname(itemPath) else projectPaths[0]
|
||||
if projectPath?
|
||||
projectPath = fs.tildify(projectPath)
|
||||
|
||||
|
||||
@@ -1,7 +1,16 @@
|
||||
{
|
||||
"breakConfig": true,
|
||||
"sourceMap": "inline",
|
||||
"blacklist": ["es6.forOf", "useStrict"],
|
||||
"optional": ["asyncToGenerator"],
|
||||
"stage": 0
|
||||
"plugins": [
|
||||
["add-module-exports", {}],
|
||||
["transform-async-to-generator", {}],
|
||||
["transform-decorators-legacy", {}],
|
||||
["transform-class-properties", {}],
|
||||
["transform-es2015-modules-commonjs", {"strictMode": false}],
|
||||
["transform-export-extensions", {}],
|
||||
["transform-do-expressions", {}],
|
||||
["transform-function-bind", {}],
|
||||
["transform-object-rest-spread", {}],
|
||||
["transform-flow-strip-types", {}],
|
||||
["transform-react-jsx", {}]
|
||||
]
|
||||
}
|
||||
|
||||
@@ -2,9 +2,8 @@
|
||||
var path = require('path')
|
||||
var FileSystemBlobStore = require('../src/file-system-blob-store')
|
||||
var NativeCompileCache = require('../src/native-compile-cache')
|
||||
var getWindowLoadSettings = require('../src/get-window-load-settings')
|
||||
|
||||
var loadSettings = null
|
||||
var loadSettingsError = null
|
||||
var blobStore = null
|
||||
|
||||
window.onload = function () {
|
||||
@@ -25,20 +24,16 @@
|
||||
// Normalize to make sure drive letter case is consistent on Windows
|
||||
process.resourcesPath = path.normalize(process.resourcesPath)
|
||||
|
||||
if (loadSettingsError) {
|
||||
throw loadSettingsError
|
||||
}
|
||||
|
||||
var devMode = loadSettings.devMode || !loadSettings.resourcePath.startsWith(process.resourcesPath + path.sep)
|
||||
var devMode = getWindowLoadSettings().devMode || !getWindowLoadSettings().resourcePath.startsWith(process.resourcesPath + path.sep)
|
||||
|
||||
if (devMode) {
|
||||
setupDeprecatedPackages()
|
||||
}
|
||||
|
||||
if (loadSettings.profileStartup) {
|
||||
profileStartup(loadSettings, Date.now() - startTime)
|
||||
if (getWindowLoadSettings().profileStartup) {
|
||||
profileStartup(Date.now() - startTime)
|
||||
} else {
|
||||
setupWindow(loadSettings)
|
||||
setupWindow()
|
||||
setLoadTime(Date.now() - startTime)
|
||||
}
|
||||
} catch (error) {
|
||||
@@ -61,23 +56,23 @@
|
||||
console.error(error.stack || error)
|
||||
}
|
||||
|
||||
function setupWindow (loadSettings) {
|
||||
function setupWindow () {
|
||||
var CompileCache = require('../src/compile-cache')
|
||||
CompileCache.setAtomHomeDirectory(process.env.ATOM_HOME)
|
||||
|
||||
var ModuleCache = require('../src/module-cache')
|
||||
ModuleCache.register(loadSettings)
|
||||
ModuleCache.add(loadSettings.resourcePath)
|
||||
ModuleCache.register(getWindowLoadSettings())
|
||||
ModuleCache.add(getWindowLoadSettings().resourcePath)
|
||||
|
||||
// By explicitly passing the app version here, we could save the call
|
||||
// of "require('remote').require('app').getVersion()".
|
||||
var startCrashReporter = require('../src/crash-reporter-start')
|
||||
startCrashReporter({_version: loadSettings.appVersion})
|
||||
startCrashReporter({_version: getWindowLoadSettings().appVersion})
|
||||
|
||||
setupVmCompatibility()
|
||||
setupCsonCache(CompileCache.getCacheDirectory())
|
||||
|
||||
var initialize = require(loadSettings.windowInitializationScript)
|
||||
var initialize = require(getWindowLoadSettings().windowInitializationScript)
|
||||
return initialize({blobStore: blobStore}).then(function () {
|
||||
require('electron').ipcRenderer.send('window-command', 'window:loaded')
|
||||
})
|
||||
@@ -105,11 +100,11 @@
|
||||
}
|
||||
}
|
||||
|
||||
function profileStartup (loadSettings, initialTime) {
|
||||
function profileStartup (initialTime) {
|
||||
function profile () {
|
||||
console.profile('startup')
|
||||
var startTime = Date.now()
|
||||
setupWindow(loadSettings).then(function () {
|
||||
setupWindow().then(function () {
|
||||
setLoadTime(Date.now() - startTime + initialTime)
|
||||
console.profileEnd('startup')
|
||||
console.log('Switch to the Profiles tab to view the created startup profile')
|
||||
@@ -125,16 +120,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
function parseLoadSettings () {
|
||||
var rawLoadSettings = decodeURIComponent(window.location.hash.substr(1))
|
||||
try {
|
||||
loadSettings = JSON.parse(rawLoadSettings)
|
||||
} catch (error) {
|
||||
console.error('Failed to parse load settings: ' + rawLoadSettings)
|
||||
loadSettingsError = error
|
||||
}
|
||||
}
|
||||
|
||||
var setupAtomHome = function () {
|
||||
if (process.env.ATOM_HOME) {
|
||||
return
|
||||
@@ -143,11 +128,10 @@
|
||||
// Ensure ATOM_HOME is always set before anything else is required
|
||||
// This is because of a difference in Linux not inherited between browser and render processes
|
||||
// https://github.com/atom/atom/issues/5412
|
||||
if (loadSettings && loadSettings.atomHome) {
|
||||
process.env.ATOM_HOME = loadSettings.atomHome
|
||||
if (getWindowLoadSettings() && getWindowLoadSettings().atomHome) {
|
||||
process.env.ATOM_HOME = getWindowLoadSettings().atomHome
|
||||
}
|
||||
}
|
||||
|
||||
parseLoadSettings()
|
||||
setupAtomHome()
|
||||
})()
|
||||
|
||||
Reference in New Issue
Block a user