Merge branch 'master' into as-deprecate-shadow-dom
@ -1 +1 @@
|
||||
2.7.6
|
||||
2.7.12
|
||||
|
2
atom.sh
@ -28,7 +28,7 @@ while getopts ":wtfvh-:" opt; do
|
||||
REDIRECT_STDERR=1
|
||||
EXPECT_OUTPUT=1
|
||||
;;
|
||||
foreground|test)
|
||||
foreground|benchmark|benchmark-test|test)
|
||||
EXPECT_OUTPUT=1
|
||||
;;
|
||||
esac
|
||||
|
73
benchmarks/benchmark-runner.js
Normal file
@ -0,0 +1,73 @@
|
||||
/** @babel */
|
||||
|
||||
import Chart from 'chart.js'
|
||||
import glob from 'glob'
|
||||
import fs from 'fs-plus'
|
||||
import path from 'path'
|
||||
|
||||
export default async function ({test, benchmarkPaths}) {
|
||||
document.body.style.backgroundColor = '#ffffff'
|
||||
document.body.style.overflow = 'auto'
|
||||
|
||||
let paths = []
|
||||
for (const benchmarkPath of benchmarkPaths) {
|
||||
if (fs.isDirectorySync(benchmarkPath)) {
|
||||
paths = paths.concat(glob.sync(path.join(benchmarkPath, '**', '*.bench.js')))
|
||||
} else {
|
||||
paths.push(benchmarkPath)
|
||||
}
|
||||
}
|
||||
|
||||
while (paths.length > 0) {
|
||||
const benchmark = require(paths.shift())({test})
|
||||
let results
|
||||
if (benchmark instanceof Promise) {
|
||||
results = await benchmark
|
||||
} else {
|
||||
results = benchmark
|
||||
}
|
||||
|
||||
const dataByBenchmarkName = {}
|
||||
for (const {name, duration, x} of results) {
|
||||
dataByBenchmarkName[name] = dataByBenchmarkName[name] || {points: []}
|
||||
dataByBenchmarkName[name].points.push({x, y: duration})
|
||||
}
|
||||
|
||||
const benchmarkContainer = document.createElement('div')
|
||||
document.body.appendChild(benchmarkContainer)
|
||||
for (const key in dataByBenchmarkName) {
|
||||
const data = dataByBenchmarkName[key]
|
||||
if (data.points.length > 1) {
|
||||
const canvas = document.createElement('canvas')
|
||||
benchmarkContainer.appendChild(canvas)
|
||||
const chart = new Chart(canvas, {
|
||||
type: 'line',
|
||||
data: {
|
||||
datasets: [{label: key, fill: false, data: data.points}]
|
||||
},
|
||||
options: {
|
||||
showLines: false,
|
||||
scales: {xAxes: [{type: 'linear', position: 'bottom'}]}
|
||||
}
|
||||
})
|
||||
|
||||
const textualOutput = `${key}:\n\n` + data.points.map((p) => `${p.x}\t${p.y}`).join('\n')
|
||||
console.log(textualOutput)
|
||||
} else {
|
||||
const title = document.createElement('h2')
|
||||
title.textContent = key
|
||||
benchmarkContainer.appendChild(title)
|
||||
const duration = document.createElement('p')
|
||||
duration.textContent = `${data.points[0].y}ms`
|
||||
benchmarkContainer.appendChild(duration)
|
||||
|
||||
const textualOutput = `${key}: ${data.points[0].y}`
|
||||
console.log(textualOutput)
|
||||
}
|
||||
|
||||
global.atom.reset()
|
||||
}
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
90
benchmarks/text-editor-large-file-construction.bench.js
Normal file
@ -0,0 +1,90 @@
|
||||
/** @babel */
|
||||
|
||||
import {TextEditor, TextBuffer} from 'atom'
|
||||
|
||||
const MIN_SIZE_IN_KB = 0 * 1024
|
||||
const MAX_SIZE_IN_KB = 10 * 1024
|
||||
const SIZE_STEP_IN_KB = 1024
|
||||
const LINE_TEXT = 'Lorem ipsum dolor sit amet\n'
|
||||
const TEXT = LINE_TEXT.repeat(Math.ceil(MAX_SIZE_IN_KB * 1024 / LINE_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()
|
||||
|
||||
for (let pane of atom.workspace.getPanes()) {
|
||||
pane.destroy()
|
||||
}
|
||||
|
||||
for (let sizeInKB = MIN_SIZE_IN_KB; sizeInKB < MAX_SIZE_IN_KB; sizeInKB += SIZE_STEP_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})
|
||||
atom.workspace.getActivePane().activateItem(editor)
|
||||
let t1 = window.performance.now()
|
||||
|
||||
data.push({
|
||||
name: 'Opening a large 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 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 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 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))
|
||||
}
|
@ -3,8 +3,6 @@
|
||||
import TextBuffer, {Point, Range} from 'text-buffer'
|
||||
import {File, Directory} from 'pathwatcher'
|
||||
import {Emitter, Disposable, CompositeDisposable} from 'event-kit'
|
||||
import Grim from 'grim'
|
||||
import dedent from 'dedent'
|
||||
import BufferedNodeProcess from '../src/buffered-node-process'
|
||||
import BufferedProcess from '../src/buffered-process'
|
||||
import GitRepository from '../src/git-repository'
|
||||
@ -39,25 +37,7 @@ if (process.platform === 'win32') {
|
||||
// only be exported when not running as a child node process
|
||||
if (process.type === 'renderer') {
|
||||
atomExport.Task = require('../src/task')
|
||||
|
||||
const TextEditor = (params) => {
|
||||
return atom.workspace.buildTextEditor(params)
|
||||
}
|
||||
|
||||
TextEditor.prototype = require('../src/text-editor').prototype
|
||||
|
||||
Object.defineProperty(atomExport, 'TextEditor', {
|
||||
enumerable: true,
|
||||
get () {
|
||||
Grim.deprecate(dedent`
|
||||
The \`TextEditor\` constructor is no longer public.
|
||||
|
||||
To construct a text editor, use \`atom.workspace.buildTextEditor()\`.
|
||||
To check if an object is a text editor, use \`atom.workspace.isTextEditor(object)\`.
|
||||
`)
|
||||
return TextEditor
|
||||
}
|
||||
})
|
||||
atomExport.TextEditor = require('../src/text-editor')
|
||||
}
|
||||
|
||||
export default atomExport
|
||||
|
@ -147,6 +147,7 @@
|
||||
{ label: 'Open In Dev Mode…', command: 'application:open-dev' }
|
||||
{ label: 'Reload Window', command: 'window:reload' }
|
||||
{ label: 'Run Package Specs', command: 'window:run-package-specs' }
|
||||
{ label: 'Run Benchmarks', command: 'window:run-benchmarks' }
|
||||
{ label: 'Toggle Developer Tools', command: 'window:toggle-dev-tools' }
|
||||
]
|
||||
}
|
||||
|
56
package.json
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "atom",
|
||||
"productName": "Atom",
|
||||
"version": "1.12.0-dev",
|
||||
"version": "1.13.0-dev",
|
||||
"description": "A hackable text editor for the 21st Century.",
|
||||
"main": "./src/main-process/main.js",
|
||||
"repository": {
|
||||
@ -15,23 +15,25 @@
|
||||
"electronVersion": "1.3.6",
|
||||
"dependencies": {
|
||||
"async": "0.2.6",
|
||||
"atom-keymap": "7.0.2",
|
||||
"atom-keymap": "7.0.5",
|
||||
"atom-ui": "0.4.1",
|
||||
"babel-core": "5.8.38",
|
||||
"cached-run-in-this-context": "0.4.1",
|
||||
"chai": "3.5.0",
|
||||
"chart.js": "^2.3.0",
|
||||
"clear-cut": "^2.0.1",
|
||||
"coffee-script": "1.11.0",
|
||||
"coffee-script": "1.11.1",
|
||||
"color": "^0.7.3",
|
||||
"dedent": "^0.6.0",
|
||||
"devtron": "1.3.0",
|
||||
"event-kit": "^2.1.0",
|
||||
"find-parent-dir": "^0.3.0",
|
||||
"first-mate": "6.0.0",
|
||||
"first-mate": "6.1.0",
|
||||
"fs-plus": "2.9.2",
|
||||
"fstream": "0.1.24",
|
||||
"fuzzaldrin": "^2.1",
|
||||
"git-utils": "^4.1.2",
|
||||
"glob": "^7.1.1",
|
||||
"grim": "1.5.0",
|
||||
"jasmine-json": "~0.0",
|
||||
"jasmine-tagged": "^1.1.4",
|
||||
@ -60,7 +62,7 @@
|
||||
"sinon": "1.17.4",
|
||||
"source-map-support": "^0.3.2",
|
||||
"temp": "0.8.1",
|
||||
"text-buffer": "9.3.0",
|
||||
"text-buffer": "9.4.0",
|
||||
"typescript-simple": "1.0.0",
|
||||
"underscore-plus": "^1.6.6",
|
||||
"winreg": "^1.2.1",
|
||||
@ -73,35 +75,35 @@
|
||||
"atom-light-ui": "0.45.0",
|
||||
"base16-tomorrow-dark-theme": "1.3.0",
|
||||
"base16-tomorrow-light-theme": "1.3.0",
|
||||
"one-dark-ui": "1.6.1",
|
||||
"one-light-ui": "1.6.1",
|
||||
"one-dark-ui": "1.6.2",
|
||||
"one-light-ui": "1.6.2",
|
||||
"one-dark-syntax": "1.5.0",
|
||||
"one-light-syntax": "1.5.0",
|
||||
"solarized-dark-syntax": "1.0.5",
|
||||
"solarized-light-syntax": "1.0.5",
|
||||
"about": "1.7.0",
|
||||
"archive-view": "0.61.1",
|
||||
"archive-view": "0.62.0",
|
||||
"autocomplete-atom-api": "0.10.0",
|
||||
"autocomplete-css": "0.13.1",
|
||||
"autocomplete-html": "0.7.2",
|
||||
"autocomplete-plus": "2.31.4",
|
||||
"autocomplete-plus": "2.32.0",
|
||||
"autocomplete-snippets": "1.11.0",
|
||||
"autoflow": "0.27.0",
|
||||
"autosave": "0.23.1",
|
||||
"background-tips": "0.26.1",
|
||||
"bookmarks": "0.42.0",
|
||||
"bracket-matcher": "0.82.2",
|
||||
"command-palette": "0.38.0",
|
||||
"command-palette": "0.39.0",
|
||||
"deprecation-cop": "0.54.1",
|
||||
"dev-live-reload": "0.47.0",
|
||||
"encoding-selector": "0.22.0",
|
||||
"exception-reporting": "0.40.0",
|
||||
"find-and-replace": "0.202.0",
|
||||
"find-and-replace": "0.202.1",
|
||||
"fuzzy-finder": "1.4.0",
|
||||
"git-diff": "1.1.0",
|
||||
"go-to-line": "0.31.0",
|
||||
"grammar-selector": "0.48.2",
|
||||
"image-view": "0.59.0",
|
||||
"image-view": "0.60.0",
|
||||
"incompatible-packages": "0.26.1",
|
||||
"keybinding-resolver": "0.35.0",
|
||||
"line-ending-selector": "0.5.0",
|
||||
@ -111,32 +113,32 @@
|
||||
"notifications": "0.65.1",
|
||||
"open-on-github": "1.2.1",
|
||||
"package-generator": "1.0.1",
|
||||
"settings-view": "0.243.0",
|
||||
"settings-view": "0.243.1",
|
||||
"snippets": "1.0.3",
|
||||
"spell-check": "0.68.3",
|
||||
"status-bar": "1.4.1",
|
||||
"spell-check": "0.68.4",
|
||||
"status-bar": "1.6.0",
|
||||
"styleguide": "0.47.2",
|
||||
"symbols-view": "0.113.1",
|
||||
"tabs": "0.102.0",
|
||||
"tabs": "0.103.0",
|
||||
"timecop": "0.33.2",
|
||||
"tree-view": "0.210.0",
|
||||
"update-package-dependencies": "0.10.0",
|
||||
"welcome": "0.35.1",
|
||||
"whitespace": "0.35.0",
|
||||
"wrap-guide": "0.38.2",
|
||||
"language-c": "0.53.2",
|
||||
"language-clojure": "0.22.0",
|
||||
"language-coffee-script": "0.47.3",
|
||||
"language-c": "0.54.0",
|
||||
"language-clojure": "0.22.1",
|
||||
"language-coffee-script": "0.48.0",
|
||||
"language-csharp": "0.12.1",
|
||||
"language-css": "0.40.0",
|
||||
"language-css": "0.40.1",
|
||||
"language-gfm": "0.88.0",
|
||||
"language-git": "0.15.0",
|
||||
"language-go": "0.42.1",
|
||||
"language-html": "0.46.0",
|
||||
"language-go": "0.43.0",
|
||||
"language-html": "0.46.1",
|
||||
"language-hyperlink": "0.16.1",
|
||||
"language-java": "0.24.0",
|
||||
"language-javascript": "0.121.0",
|
||||
"language-json": "0.18.2",
|
||||
"language-javascript": "0.122.0",
|
||||
"language-json": "0.18.3",
|
||||
"language-less": "0.29.6",
|
||||
"language-make": "0.22.2",
|
||||
"language-mustache": "0.13.0",
|
||||
@ -145,7 +147,7 @@
|
||||
"language-php": "0.37.3",
|
||||
"language-property-list": "0.8.0",
|
||||
"language-python": "0.45.1",
|
||||
"language-ruby": "0.70.1",
|
||||
"language-ruby": "0.70.2",
|
||||
"language-ruby-on-rails": "0.25.1",
|
||||
"language-sass": "0.57.0",
|
||||
"language-shellscript": "0.23.0",
|
||||
@ -154,7 +156,7 @@
|
||||
"language-text": "0.7.1",
|
||||
"language-todo": "0.29.1",
|
||||
"language-toml": "0.18.1",
|
||||
"language-xml": "0.34.11",
|
||||
"language-xml": "0.34.12",
|
||||
"language-yaml": "0.27.1"
|
||||
},
|
||||
"private": true,
|
||||
@ -163,8 +165,6 @@
|
||||
"test": "node script/test"
|
||||
},
|
||||
"standard": {
|
||||
"ignore": [],
|
||||
"parser": "babel-eslint",
|
||||
"globals": [
|
||||
"atom",
|
||||
"afterEach",
|
||||
|
Before Width: | Height: | Size: 629 KiB After Width: | Height: | Size: 355 KiB |
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 907 B After Width: | Height: | Size: 838 B |
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 60 KiB After Width: | Height: | Size: 42 KiB |
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 2.1 KiB |
Before Width: | Height: | Size: 4.4 KiB After Width: | Height: | Size: 3.8 KiB |
Before Width: | Height: | Size: 192 KiB After Width: | Height: | Size: 119 KiB |
Before Width: | Height: | Size: 6.8 KiB After Width: | Height: | Size: 5.8 KiB |
Before Width: | Height: | Size: 601 KiB After Width: | Height: | Size: 325 KiB |
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 905 B After Width: | Height: | Size: 809 B |
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 58 KiB After Width: | Height: | Size: 40 KiB |
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 2.1 KiB |
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 3.6 KiB |
Before Width: | Height: | Size: 184 KiB After Width: | Height: | Size: 111 KiB |
Before Width: | Height: | Size: 6.7 KiB After Width: | Height: | Size: 5.7 KiB |
Before Width: | Height: | Size: 628 KiB After Width: | Height: | Size: 357 KiB |
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 944 B After Width: | Height: | Size: 851 B |
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 61 KiB After Width: | Height: | Size: 43 KiB |
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 2.2 KiB |
Before Width: | Height: | Size: 4.5 KiB After Width: | Height: | Size: 3.9 KiB |
Before Width: | Height: | Size: 192 KiB After Width: | Height: | Size: 120 KiB |
Before Width: | Height: | Size: 7.0 KiB After Width: | Height: | Size: 6.0 KiB |
@ -5,14 +5,16 @@ SET WAIT=
|
||||
SET PSARGS=%*
|
||||
|
||||
FOR %%a IN (%*) DO (
|
||||
IF /I "%%a"=="-f" SET EXPECT_OUTPUT=YES
|
||||
IF /I "%%a"=="--foreground" SET EXPECT_OUTPUT=YES
|
||||
IF /I "%%a"=="-h" SET EXPECT_OUTPUT=YES
|
||||
IF /I "%%a"=="--help" SET EXPECT_OUTPUT=YES
|
||||
IF /I "%%a"=="-t" SET EXPECT_OUTPUT=YES
|
||||
IF /I "%%a"=="--test" SET EXPECT_OUTPUT=YES
|
||||
IF /I "%%a"=="-v" SET EXPECT_OUTPUT=YES
|
||||
IF /I "%%a"=="--version" SET EXPECT_OUTPUT=YES
|
||||
IF /I "%%a"=="-f" SET EXPECT_OUTPUT=YES
|
||||
IF /I "%%a"=="--foreground" SET EXPECT_OUTPUT=YES
|
||||
IF /I "%%a"=="-h" SET EXPECT_OUTPUT=YES
|
||||
IF /I "%%a"=="--help" SET EXPECT_OUTPUT=YES
|
||||
IF /I "%%a"=="-t" SET EXPECT_OUTPUT=YES
|
||||
IF /I "%%a"=="--test" SET EXPECT_OUTPUT=YES
|
||||
IF /I "%%a"=="--benchmark" SET EXPECT_OUTPUT=YES
|
||||
IF /I "%%a"=="--benchmark-test" SET EXPECT_OUTPUT=YES
|
||||
IF /I "%%a"=="-v" SET EXPECT_OUTPUT=YES
|
||||
IF /I "%%a"=="--version" SET EXPECT_OUTPUT=YES
|
||||
IF /I "%%a"=="-w" (
|
||||
SET EXPECT_OUTPUT=YES
|
||||
SET WAIT=YES
|
||||
|
@ -12,6 +12,7 @@ const includePathInPackagedApp = require('./include-path-in-packaged-app')
|
||||
module.exports = function () {
|
||||
console.log(`Copying assets to ${CONFIG.intermediateAppPath}`);
|
||||
let srcPaths = [
|
||||
path.join(CONFIG.repositoryRootPath, 'benchmarks', 'benchmark-runner.js'),
|
||||
path.join(CONFIG.repositoryRootPath, 'dot-atom'),
|
||||
path.join(CONFIG.repositoryRootPath, 'exports'),
|
||||
path.join(CONFIG.repositoryRootPath, 'node_modules'),
|
||||
|
@ -6,6 +6,7 @@ const fs = require('fs-extra')
|
||||
const glob = require('glob')
|
||||
const os = require('os')
|
||||
const path = require('path')
|
||||
const spawnSync = require('./spawn-sync')
|
||||
|
||||
const CONFIG = require('../config')
|
||||
|
||||
@ -21,7 +22,8 @@ module.exports = function (packagedAppPath, codeSign) {
|
||||
}
|
||||
|
||||
const certPath = path.join(os.tmpdir(), 'win.p12')
|
||||
if (codeSign && process.env.WIN_P12KEY_URL) {
|
||||
const signing = codeSign && process.env.WIN_P12KEY_URL
|
||||
if (signing) {
|
||||
downloadFileFromGithub(process.env.WIN_P12KEY_URL, certPath)
|
||||
options.certificateFile = certPath
|
||||
options.certificatePassword = process.env.WIN_P12KEY_PASSWORD
|
||||
@ -42,9 +44,30 @@ module.exports = function (packagedAppPath, codeSign) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Squirrel signs its own copy of the executables but we need them for the portable ZIP
|
||||
const extractSignedExes = function() {
|
||||
if (signing) {
|
||||
for (let nupkgPath of glob.sync(`${CONFIG.buildOutputPath}/*-full.nupkg`)) {
|
||||
if (nupkgPath.includes(CONFIG.appMetadata.version)) {
|
||||
console.log(`Extracting signed executables from ${nupkgPath} for use in portable zip`)
|
||||
var atomOutPath = path.join(path.dirname(packagedAppPath), 'Atom')
|
||||
spawnSync('7z.exe', ['e', nupkgPath, 'lib\\net45\\*.exe', '-aoa'], {cwd: atomOutPath})
|
||||
spawnSync(process.env.COMSPEC, ['/c', `move /y ${path.join(atomOutPath, 'squirrel.exe')} ${path.join(atomOutPath, 'update.exe')}`])
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`Creating Windows Installer for ${packagedAppPath}`)
|
||||
return electronInstaller.createWindowsInstaller(options).then(cleanUp, function (error) {
|
||||
console.log(`Windows installer creation failed:\n${error}`)
|
||||
cleanUp()
|
||||
})
|
||||
return electronInstaller.createWindowsInstaller(options)
|
||||
.then(extractSignedExes, function (error) {
|
||||
console.log(`Extracting signed executables failed:\n${error}`)
|
||||
cleanUp()
|
||||
})
|
||||
.then(cleanUp, function (error) {
|
||||
console.log(`Windows installer creation failed:\n${error}`)
|
||||
cleanUp()
|
||||
})
|
||||
}
|
||||
|
@ -25,6 +25,7 @@ module.exports = function () {
|
||||
|
||||
function getPathsToTranspile () {
|
||||
let paths = []
|
||||
paths = paths.concat(glob.sync(path.join(CONFIG.intermediateAppPath, 'benchmarks', '**', '*.js')))
|
||||
paths = paths.concat(glob.sync(path.join(CONFIG.intermediateAppPath, 'exports', '**', '*.js')))
|
||||
paths = paths.concat(glob.sync(path.join(CONFIG.intermediateAppPath, 'src', '**', '*.js')))
|
||||
for (let packageName of Object.keys(CONFIG.appMetadata.packageDependencies)) {
|
||||
|
@ -4,7 +4,6 @@
|
||||
"dependencies": {
|
||||
"async": "2.0.1",
|
||||
"babel-core": "5.8.38",
|
||||
"babel-eslint": "6.1.2",
|
||||
"coffeelint": "1.15.7",
|
||||
"colors": "1.1.2",
|
||||
"csslint": "1.0.2",
|
||||
@ -24,7 +23,7 @@
|
||||
"runas": "3.1.1",
|
||||
"season": "5.3.0",
|
||||
"semver": "5.3.0",
|
||||
"standard": "6.0.0",
|
||||
"standard": "8.4.0",
|
||||
"sync-request": "3.0.1",
|
||||
"tello": "1.0.5",
|
||||
"webdriverio": "2.4.5",
|
||||
|
12
script/test
@ -83,9 +83,19 @@ for (let packageName in CONFIG.appMetadata.packageDependencies) {
|
||||
})
|
||||
}
|
||||
|
||||
function runBenchmarkTests (callback) {
|
||||
const benchmarksPath = path.join(CONFIG.repositoryRootPath, 'benchmarks')
|
||||
const testArguments = ['--benchmark-test', benchmarksPath]
|
||||
|
||||
console.log('Executing benchmark tests'.bold.green)
|
||||
const cp = childProcess.spawn(executablePath, testArguments, {stdio: 'inherit'})
|
||||
cp.on('error', error => { callback(error) })
|
||||
cp.on('close', exitCode => { callback(null, exitCode) })
|
||||
}
|
||||
|
||||
let testSuitesToRun
|
||||
if (process.platform === 'darwin') {
|
||||
testSuitesToRun = [runCoreMainProcessTests, runCoreRenderProcessTests].concat(packageTestSuites)
|
||||
testSuitesToRun = [runCoreMainProcessTests, runCoreRenderProcessTests, runBenchmarkTests].concat(packageTestSuites)
|
||||
} else {
|
||||
testSuitesToRun = [runCoreMainProcessTests]
|
||||
}
|
||||
|
@ -40,11 +40,13 @@ describe "LinesYardstick", ->
|
||||
|
||||
mockLineNodesProvider =
|
||||
lineNodesById: {}
|
||||
|
||||
lineIdForScreenRow: (screenRow) ->
|
||||
editor.screenLineForScreenRow(screenRow).id
|
||||
editor.screenLineForScreenRow(screenRow)?.id
|
||||
|
||||
lineNodeForScreenRow: (screenRow) ->
|
||||
@lineNodesById[@lineIdForScreenRow(screenRow)] ?= buildLineNode(screenRow)
|
||||
if id = @lineIdForScreenRow(screenRow)
|
||||
@lineNodesById[id] ?= buildLineNode(screenRow)
|
||||
|
||||
textNodesForScreenRow: (screenRow) ->
|
||||
lineNode = @lineNodeForScreenRow(screenRow)
|
||||
|
@ -31,6 +31,19 @@ describe "PackageManager", ->
|
||||
it "returns the value of the core.apmPath config setting", ->
|
||||
expect(atom.packages.getApmPath()).toBe "/path/to/apm"
|
||||
|
||||
describe "::loadPackages()", ->
|
||||
beforeEach ->
|
||||
spyOn(atom.packages, 'loadPackage')
|
||||
|
||||
afterEach ->
|
||||
atom.packages.deactivatePackages()
|
||||
atom.packages.unloadPackages()
|
||||
|
||||
it "sets hasLoadedInitialPackages", ->
|
||||
expect(atom.packages.hasLoadedInitialPackages()).toBe false
|
||||
atom.packages.loadPackages()
|
||||
expect(atom.packages.hasLoadedInitialPackages()).toBe true
|
||||
|
||||
describe "::loadPackage(name)", ->
|
||||
beforeEach ->
|
||||
atom.config.set("core.disabledPackages", [])
|
||||
@ -1022,6 +1035,12 @@ describe "PackageManager", ->
|
||||
|
||||
jasmine.restoreDeprecationsSnapshot()
|
||||
|
||||
it "sets hasActivatedInitialPackages", ->
|
||||
spyOn(atom.packages, 'activatePackages')
|
||||
expect(atom.packages.hasActivatedInitialPackages()).toBe false
|
||||
waitsForPromise -> atom.packages.activate()
|
||||
runs -> expect(atom.packages.hasActivatedInitialPackages()).toBe true
|
||||
|
||||
it "activates all the packages, and none of the themes", ->
|
||||
packageActivator = spyOn(atom.packages, 'activatePackages')
|
||||
themeActivator = spyOn(atom.themes, 'activatePackages')
|
||||
|
@ -1,9 +1,11 @@
|
||||
TextEditor = require '../src/text-editor'
|
||||
|
||||
describe "Selection", ->
|
||||
[buffer, editor, selection] = []
|
||||
|
||||
beforeEach ->
|
||||
buffer = atom.project.bufferForPathSync('sample.js')
|
||||
editor = atom.workspace.buildTextEditor(buffer: buffer, tabLength: 2)
|
||||
editor = new TextEditor({buffer: buffer, tabLength: 2})
|
||||
selection = editor.getLastSelection()
|
||||
|
||||
afterEach ->
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
import {it, fit, ffit, fffit, beforeEach, afterEach, conditionPromise} from './async-spec-helpers'
|
||||
import Grim from 'grim'
|
||||
import TextEditor from '../src/text-editor'
|
||||
import TextEditorElement from '../src/text-editor-element'
|
||||
import _, {extend, flatten, last, toArray} from 'underscore-plus'
|
||||
|
||||
@ -4442,7 +4443,7 @@ describe('TextEditorComponent', function () {
|
||||
|
||||
describe('when autoHeight is not assigned on the editor', function () {
|
||||
it('implicitly assigns autoHeight to true and emits a deprecation warning if the editor has its height assigned via an inline style', function () {
|
||||
editor = atom.workspace.buildTextEditor()
|
||||
editor = new TextEditor()
|
||||
element = editor.getElement()
|
||||
element.setUpdatedSynchronously(false)
|
||||
element.style.height = '200px'
|
||||
@ -4457,7 +4458,7 @@ describe('TextEditorComponent', function () {
|
||||
})
|
||||
|
||||
it('implicitly assigns autoHeight to true and emits a deprecation warning if the editor has its height assigned via position absolute with an assigned top and bottom', function () {
|
||||
editor = atom.workspace.buildTextEditor()
|
||||
editor = new TextEditor()
|
||||
element = editor.getElement()
|
||||
element.setUpdatedSynchronously(false)
|
||||
parentElement = document.createElement('div')
|
||||
|
@ -1,3 +1,4 @@
|
||||
TextEditor = require '../src/text-editor'
|
||||
TextEditorElement = require '../src/text-editor-element'
|
||||
{Disposable} = require 'event-kit'
|
||||
|
||||
@ -31,7 +32,7 @@ describe "TextEditorElement", ->
|
||||
describe "when the model is assigned", ->
|
||||
it "adds the 'mini' attribute if .isMini() returns true on the model", ->
|
||||
element = new TextEditorElement
|
||||
model = atom.workspace.buildTextEditor(mini: true)
|
||||
model = new TextEditor({mini: true})
|
||||
element.setModel(model)
|
||||
expect(element.hasAttribute('mini')).toBe true
|
||||
|
||||
@ -50,7 +51,7 @@ describe "TextEditorElement", ->
|
||||
|
||||
describe "when the editor is detached from the DOM and then reattached", ->
|
||||
it "does not render duplicate line numbers", ->
|
||||
editor = atom.workspace.buildTextEditor()
|
||||
editor = new TextEditor
|
||||
editor.setText('1\n2\n3')
|
||||
element = atom.views.getView(editor)
|
||||
|
||||
@ -63,7 +64,7 @@ describe "TextEditorElement", ->
|
||||
expect(element.querySelectorAll('.line-number').length).toBe initialCount
|
||||
|
||||
it "does not render duplicate decorations in custom gutters", ->
|
||||
editor = atom.workspace.buildTextEditor()
|
||||
editor = new TextEditor
|
||||
editor.setText('1\n2\n3')
|
||||
editor.addGutter({name: 'test-gutter'})
|
||||
marker = editor.markBufferRange([[0, 0], [2, 0]])
|
||||
@ -197,7 +198,7 @@ describe "TextEditorElement", ->
|
||||
|
||||
describe "::getMaxScrollTop", ->
|
||||
it "returns the maximum scroll top that can be applied to the element", ->
|
||||
editor = atom.workspace.buildTextEditor()
|
||||
editor = new TextEditor
|
||||
editor.setText('1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16')
|
||||
element = atom.views.getView(editor)
|
||||
element.style.lineHeight = "10px"
|
||||
|
@ -2,6 +2,7 @@ _ = require 'underscore-plus'
|
||||
randomWords = require 'random-words'
|
||||
TextBuffer = require 'text-buffer'
|
||||
{Point, Range} = TextBuffer
|
||||
TextEditor = require '../src/text-editor'
|
||||
TextEditorPresenter = require '../src/text-editor-presenter'
|
||||
FakeLinesYardstick = require './fake-lines-yardstick'
|
||||
LineTopIndex = require 'line-top-index'
|
||||
@ -18,7 +19,7 @@ describe "TextEditorPresenter", ->
|
||||
spyOn(window, "clearInterval").andCallFake window.fakeClearInterval
|
||||
|
||||
buffer = new TextBuffer(filePath: require.resolve('./fixtures/sample.js'))
|
||||
editor = atom.workspace.buildTextEditor({buffer})
|
||||
editor = new TextEditor({buffer})
|
||||
waitsForPromise -> buffer.load()
|
||||
|
||||
afterEach ->
|
||||
@ -474,6 +475,7 @@ describe "TextEditorPresenter", ->
|
||||
waitsForPromise -> atom.packages.activatePackage('language-javascript')
|
||||
|
||||
runs ->
|
||||
editor.setGrammar(atom.grammars.grammarForScopeName('source.js'))
|
||||
maxLineLength = editor.getMaxScreenLineLength()
|
||||
presenter = buildPresenter(contentFrameWidth: 50, baseCharacterWidth: 10)
|
||||
|
||||
@ -758,6 +760,7 @@ describe "TextEditorPresenter", ->
|
||||
waitsForPromise -> atom.packages.activatePackage('language-javascript')
|
||||
|
||||
runs ->
|
||||
editor.setGrammar(atom.grammars.grammarForScopeName('source.js'))
|
||||
editor.setCursorBufferPosition([3, 6])
|
||||
presenter = buildPresenter()
|
||||
expect(getState(presenter).hiddenInput.width).toBe 10
|
||||
@ -917,6 +920,7 @@ describe "TextEditorPresenter", ->
|
||||
waitsForPromise -> atom.packages.activatePackage('language-javascript')
|
||||
|
||||
runs ->
|
||||
editor.setGrammar(atom.grammars.grammarForScopeName('source.js'))
|
||||
maxLineLength = editor.getMaxScreenLineLength()
|
||||
presenter = buildPresenter(contentFrameWidth: 50, baseCharacterWidth: 10)
|
||||
|
||||
@ -1264,6 +1268,8 @@ describe "TextEditorPresenter", ->
|
||||
expectValues lineStateForScreenRow(presenter, 3), {screenRow: 3, tagCodes: editor.screenLineForScreenRow(3).tagCodes}
|
||||
|
||||
it "includes the .endOfLineInvisibles if the editor.showInvisibles config option is true", ->
|
||||
editor.update({showInvisibles: false, invisibles: {eol: 'X'}})
|
||||
|
||||
editor.setText("hello\nworld\r\n")
|
||||
presenter = buildPresenter(explicitHeight: 25, scrollTop: 0, lineHeight: 10)
|
||||
expect(tagsForCodes(presenter, lineStateForScreenRow(presenter, 0).tagCodes).openTags).not.toContain('invisible-character eol')
|
||||
@ -1753,6 +1759,7 @@ describe "TextEditorPresenter", ->
|
||||
atom.packages.activatePackage('language-javascript')
|
||||
|
||||
runs ->
|
||||
editor.setGrammar(atom.grammars.grammarForScopeName('source.js'))
|
||||
editor.setCursorBufferPosition([1, 4])
|
||||
presenter = buildPresenter(explicitHeight: 20)
|
||||
|
||||
@ -2098,6 +2105,7 @@ describe "TextEditorPresenter", ->
|
||||
atom.packages.activatePackage('language-javascript')
|
||||
|
||||
runs ->
|
||||
editor.setGrammar(atom.grammars.grammarForScopeName('source.js'))
|
||||
editor.setSelectedBufferRanges([
|
||||
[[2, 4], [2, 6]],
|
||||
])
|
||||
@ -3588,7 +3596,7 @@ describe "TextEditorPresenter", ->
|
||||
|
||||
performSetup = ->
|
||||
buffer = new TextBuffer
|
||||
editor = atom.workspace.buildTextEditor({buffer})
|
||||
editor = new TextEditor({buffer})
|
||||
editor.setEditorWidthInChars(80)
|
||||
presenterParams =
|
||||
model: editor
|
||||
|
@ -15,15 +15,11 @@ describe('TextEditorRegistry', function () {
|
||||
registry = new TextEditorRegistry({
|
||||
assert: atom.assert,
|
||||
config: atom.config,
|
||||
clipboard: atom.clipboard,
|
||||
grammarRegistry: atom.grammars,
|
||||
packageManager: {deferredActivationHooks: null}
|
||||
})
|
||||
|
||||
editor = new TextEditor({
|
||||
config: atom.config,
|
||||
clipboard: atom.clipboard,
|
||||
})
|
||||
editor = new TextEditor()
|
||||
})
|
||||
|
||||
afterEach(function () {
|
||||
@ -194,10 +190,7 @@ describe('TextEditorRegistry', function () {
|
||||
it('does not update the editor when config settings change for unrelated scope selectors', async function () {
|
||||
await atom.packages.activatePackage('language-javascript')
|
||||
|
||||
const editor2 = new TextEditor({
|
||||
config: atom.config,
|
||||
clipboard: atom.clipboard,
|
||||
})
|
||||
const editor2 = new TextEditor()
|
||||
|
||||
editor2.setGrammar(atom.grammars.selectGrammar('test.js'))
|
||||
|
||||
@ -205,13 +198,13 @@ describe('TextEditorRegistry', function () {
|
||||
registry.maintainConfig(editor2)
|
||||
await initialPackageActivation
|
||||
|
||||
expect(editor.getRootScopeDescriptor().getScopesArray()).toEqual(['text.plain'])
|
||||
expect(editor.getRootScopeDescriptor().getScopesArray()).toEqual(['text.plain.null-grammar'])
|
||||
expect(editor2.getRootScopeDescriptor().getScopesArray()).toEqual(['source.js'])
|
||||
|
||||
expect(editor.getEncoding()).toBe('utf8')
|
||||
expect(editor2.getEncoding()).toBe('utf8')
|
||||
|
||||
atom.config.set('core.fileEncoding', 'utf16le', {scopeSelector: '.text.plain'})
|
||||
atom.config.set('core.fileEncoding', 'utf16le', {scopeSelector: '.text.plain.null-grammar'})
|
||||
atom.config.set('core.fileEncoding', 'utf16be', {scopeSelector: '.source.js'})
|
||||
|
||||
expect(editor.getEncoding()).toBe('utf16le')
|
||||
@ -224,7 +217,6 @@ describe('TextEditorRegistry', function () {
|
||||
registry = new TextEditorRegistry({
|
||||
assert: atom.assert,
|
||||
config: atom.config,
|
||||
clipboard: atom.clipboard,
|
||||
grammarRegistry: atom.grammars,
|
||||
packageManager: {
|
||||
deferredActivationHooks: [],
|
||||
@ -669,10 +661,7 @@ describe('TextEditorRegistry', function () {
|
||||
|
||||
describe('serialization', function () {
|
||||
it('persists editors\' grammar overrides', async function () {
|
||||
const editor2 = new TextEditor({
|
||||
config: atom.config,
|
||||
clipboard: atom.clipboard,
|
||||
})
|
||||
const editor2 = new TextEditor()
|
||||
|
||||
await atom.packages.activatePackage('language-c')
|
||||
await atom.packages.activatePackage('language-html')
|
||||
@ -691,7 +680,6 @@ describe('TextEditorRegistry', function () {
|
||||
const registryCopy = new TextEditorRegistry({
|
||||
assert: atom.assert,
|
||||
config: atom.config,
|
||||
clipboard: atom.clipboard,
|
||||
grammarRegistry: atom.grammars,
|
||||
packageManager: {deferredActivationHooks: null}
|
||||
})
|
||||
|
@ -54,7 +54,6 @@ describe "TextEditor", ->
|
||||
# reusing the same buffer instance
|
||||
editor2 = TextEditor.deserialize(editor.serialize(), {
|
||||
assert: atom.assert,
|
||||
clipboard: atom.clipboard,
|
||||
textEditors: atom.textEditors,
|
||||
project: {
|
||||
bufferForIdSync: (id) -> TextBuffer.deserialize(editor.buffer.serialize())
|
||||
@ -5525,7 +5524,7 @@ describe "TextEditor", ->
|
||||
|
||||
describe "auto height", ->
|
||||
it "returns true by default but can be customized", ->
|
||||
editor = atom.workspace.buildTextEditor()
|
||||
editor = new TextEditor
|
||||
expect(editor.getAutoHeight()).toBe(true)
|
||||
editor.update({autoHeight: false})
|
||||
expect(editor.getAutoHeight()).toBe(false)
|
||||
@ -5543,10 +5542,10 @@ describe "TextEditor", ->
|
||||
|
||||
describe '.get/setPlaceholderText()', ->
|
||||
it 'can be created with placeholderText', ->
|
||||
newEditor = atom.workspace.buildTextEditor(
|
||||
newEditor = new TextEditor({
|
||||
mini: true
|
||||
placeholderText: 'yep'
|
||||
)
|
||||
})
|
||||
expect(newEditor.getPlaceholderText()).toBe 'yep'
|
||||
|
||||
it 'models placeholderText and emits an event when changed', ->
|
||||
@ -5831,11 +5830,7 @@ describe "TextEditor", ->
|
||||
atom.packages.activatePackage('language-coffee-script')
|
||||
|
||||
it "sets the grammar", ->
|
||||
editor = new TextEditor({
|
||||
grammar: atom.grammars.grammarForScopeName('source.coffee')
|
||||
clipboard: atom.clipboard
|
||||
})
|
||||
|
||||
editor = new TextEditor({grammar: atom.grammars.grammarForScopeName('source.coffee')})
|
||||
expect(editor.getGrammar().name).toBe 'CoffeeScript'
|
||||
|
||||
describe "softWrapAtPreferredLineLength", ->
|
||||
|
@ -29,7 +29,7 @@ describe "TokenIterator", ->
|
||||
})
|
||||
tokenizedBuffer.setGrammar(grammar)
|
||||
|
||||
tokenIterator = tokenizedBuffer.tokenizedLineForRow(1).getTokenIterator()
|
||||
tokenIterator = tokenizedBuffer.tokenizedLines[1].getTokenIterator()
|
||||
tokenIterator.next()
|
||||
|
||||
expect(tokenIterator.getBufferStart()).toBe 0
|
||||
|
@ -1,3 +1,4 @@
|
||||
NullGrammar = require '../src/null-grammar'
|
||||
TokenizedBuffer = require '../src/tokenized-buffer'
|
||||
{Point} = TextBuffer = require 'text-buffer'
|
||||
_ = require 'underscore-plus'
|
||||
@ -32,15 +33,8 @@ describe "TokenizedBuffer", ->
|
||||
atom.packages.activatePackage('language-coffee-script')
|
||||
|
||||
it "deserializes it searching among the buffers in the current project", ->
|
||||
tokenizedBufferA = new TokenizedBuffer({
|
||||
buffer, grammarRegistry: atom.grammars, packageManager: atom.packages,
|
||||
assert: atom.assert, tabLength: 2,
|
||||
})
|
||||
tokenizedBufferB = TokenizedBuffer.deserialize(
|
||||
JSON.parse(JSON.stringify(tokenizedBufferA.serialize())),
|
||||
atom
|
||||
)
|
||||
|
||||
tokenizedBufferA = new TokenizedBuffer({buffer, tabLength: 2})
|
||||
tokenizedBufferB = TokenizedBuffer.deserialize(JSON.parse(JSON.stringify(tokenizedBufferA.serialize())), atom)
|
||||
expect(tokenizedBufferB.buffer).toBe(tokenizedBufferA.buffer)
|
||||
|
||||
describe "when the underlying buffer has no path", ->
|
||||
@ -48,25 +42,14 @@ describe "TokenizedBuffer", ->
|
||||
buffer = atom.project.bufferForPathSync(null)
|
||||
|
||||
it "deserializes it searching among the buffers in the current project", ->
|
||||
tokenizedBufferA = new TokenizedBuffer({
|
||||
buffer, grammarRegistry: atom.grammars, packageManager: atom.packages,
|
||||
assert: atom.assert, tabLength: 2,
|
||||
})
|
||||
tokenizedBufferB = TokenizedBuffer.deserialize(
|
||||
JSON.parse(JSON.stringify(tokenizedBufferA.serialize())),
|
||||
atom
|
||||
)
|
||||
|
||||
tokenizedBufferA = new TokenizedBuffer({buffer, tabLength: 2})
|
||||
tokenizedBufferB = TokenizedBuffer.deserialize(JSON.parse(JSON.stringify(tokenizedBufferA.serialize())), atom)
|
||||
expect(tokenizedBufferB.buffer).toBe(tokenizedBufferA.buffer)
|
||||
|
||||
describe "when the buffer is destroyed", ->
|
||||
beforeEach ->
|
||||
buffer = atom.project.bufferForPathSync('sample.js')
|
||||
tokenizedBuffer = new TokenizedBuffer({
|
||||
buffer, grammarRegistry: atom.grammars, packageManager: atom.packages,
|
||||
assert: atom.assert, tabLength: 2,
|
||||
})
|
||||
tokenizedBuffer.setGrammar(atom.grammars.grammarForScopeName('source.js'))
|
||||
tokenizedBuffer = new TokenizedBuffer({buffer, grammar: atom.grammars.grammarForScopeName('source.js'), tabLength: 2})
|
||||
startTokenizing(tokenizedBuffer)
|
||||
|
||||
it "stops tokenization", ->
|
||||
@ -78,11 +61,7 @@ describe "TokenizedBuffer", ->
|
||||
describe "when the buffer contains soft-tabs", ->
|
||||
beforeEach ->
|
||||
buffer = atom.project.bufferForPathSync('sample.js')
|
||||
tokenizedBuffer = new TokenizedBuffer({
|
||||
buffer, grammarRegistry: atom.grammars, packageManager: atom.packages,
|
||||
assert: atom.assert, tabLength: 2,
|
||||
})
|
||||
tokenizedBuffer.setGrammar(atom.grammars.grammarForScopeName('source.js'))
|
||||
tokenizedBuffer = new TokenizedBuffer({buffer, grammar: atom.grammars.grammarForScopeName('source.js'), tabLength: 2})
|
||||
startTokenizing(tokenizedBuffer)
|
||||
|
||||
afterEach ->
|
||||
@ -90,32 +69,29 @@ describe "TokenizedBuffer", ->
|
||||
buffer.release()
|
||||
|
||||
describe "on construction", ->
|
||||
it "initially creates un-tokenized screen lines, then tokenizes lines chunk at a time in the background", ->
|
||||
line0 = tokenizedBuffer.tokenizedLineForRow(0)
|
||||
expect(line0.tokens).toEqual([value: line0.text, scopes: ['source.js']])
|
||||
it "tokenizes lines chunk at a time in the background", ->
|
||||
line0 = tokenizedBuffer.tokenizedLines[0]
|
||||
expect(line0).toBeUndefined()
|
||||
|
||||
line11 = tokenizedBuffer.tokenizedLineForRow(11)
|
||||
expect(line11.tokens).toEqual([value: " return sort(Array.apply(this, arguments));", scopes: ['source.js']])
|
||||
|
||||
# background tokenization has not begun
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(0).ruleStack).toBeUndefined()
|
||||
line11 = tokenizedBuffer.tokenizedLines[11]
|
||||
expect(line11).toBeUndefined()
|
||||
|
||||
# tokenize chunk 1
|
||||
advanceClock()
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(0).ruleStack?).toBeTruthy()
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(4).ruleStack?).toBeTruthy()
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(5).ruleStack?).toBeFalsy()
|
||||
expect(tokenizedBuffer.tokenizedLines[0].ruleStack?).toBeTruthy()
|
||||
expect(tokenizedBuffer.tokenizedLines[4].ruleStack?).toBeTruthy()
|
||||
expect(tokenizedBuffer.tokenizedLines[5]).toBeUndefined()
|
||||
|
||||
# tokenize chunk 2
|
||||
advanceClock()
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(5).ruleStack?).toBeTruthy()
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(9).ruleStack?).toBeTruthy()
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(10).ruleStack?).toBeFalsy()
|
||||
expect(tokenizedBuffer.tokenizedLines[5].ruleStack?).toBeTruthy()
|
||||
expect(tokenizedBuffer.tokenizedLines[9].ruleStack?).toBeTruthy()
|
||||
expect(tokenizedBuffer.tokenizedLines[10]).toBeUndefined()
|
||||
|
||||
# tokenize last chunk
|
||||
advanceClock()
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(10).ruleStack?).toBeTruthy()
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(12).ruleStack?).toBeTruthy()
|
||||
expect(tokenizedBuffer.tokenizedLines[10].ruleStack?).toBeTruthy()
|
||||
expect(tokenizedBuffer.tokenizedLines[12].ruleStack?).toBeTruthy()
|
||||
|
||||
describe "when the buffer is partially tokenized", ->
|
||||
beforeEach ->
|
||||
@ -152,8 +128,8 @@ describe "TokenizedBuffer", ->
|
||||
it "does not attempt to tokenize the lines in the change, and preserves the existing invalid row", ->
|
||||
expect(tokenizedBuffer.firstInvalidRow()).toBe 5
|
||||
buffer.setTextInRange([[6, 0], [7, 0]], "\n\n\n")
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(6).ruleStack?).toBeFalsy()
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(7).ruleStack?).toBeFalsy()
|
||||
expect(tokenizedBuffer.tokenizedLines[6]).toBeUndefined()
|
||||
expect(tokenizedBuffer.tokenizedLines[7]).toBeUndefined()
|
||||
expect(tokenizedBuffer.firstInvalidRow()).toBe 5
|
||||
|
||||
describe "when the buffer is fully tokenized", ->
|
||||
@ -165,101 +141,101 @@ describe "TokenizedBuffer", ->
|
||||
it "updates tokens to reflect the change", ->
|
||||
buffer.setTextInRange([[0, 0], [2, 0]], "foo()\n7\n")
|
||||
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(0).tokens[1]).toEqual(value: '(', scopes: ['source.js', 'meta.function-call.js', 'meta.arguments.js', 'punctuation.definition.arguments.begin.bracket.round.js'])
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(1).tokens[0]).toEqual(value: '7', scopes: ['source.js', 'constant.numeric.decimal.js'])
|
||||
expect(tokenizedBuffer.tokenizedLines[0].tokens[1]).toEqual(value: '(', scopes: ['source.js', 'meta.function-call.js', 'meta.arguments.js', 'punctuation.definition.arguments.begin.bracket.round.js'])
|
||||
expect(tokenizedBuffer.tokenizedLines[1].tokens[0]).toEqual(value: '7', scopes: ['source.js', 'constant.numeric.decimal.js'])
|
||||
# line 2 is unchanged
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(2).tokens[1]).toEqual(value: 'if', scopes: ['source.js', 'keyword.control.js'])
|
||||
expect(tokenizedBuffer.tokenizedLines[2].tokens[1]).toEqual(value: 'if', scopes: ['source.js', 'keyword.control.js'])
|
||||
|
||||
describe "when the change invalidates the tokenization of subsequent lines", ->
|
||||
it "schedules the invalidated lines to be tokenized in the background", ->
|
||||
buffer.insert([5, 30], '/* */')
|
||||
buffer.insert([2, 0], '/*')
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(3).tokens[0].scopes).toEqual ['source.js']
|
||||
expect(tokenizedBuffer.tokenizedLines[3].tokens[0].scopes).toEqual ['source.js']
|
||||
|
||||
advanceClock()
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(3).tokens[0].scopes).toEqual ['source.js', 'comment.block.js']
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(4).tokens[0].scopes).toEqual ['source.js', 'comment.block.js']
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(5).tokens[0].scopes).toEqual ['source.js', 'comment.block.js']
|
||||
expect(tokenizedBuffer.tokenizedLines[3].tokens[0].scopes).toEqual ['source.js', 'comment.block.js']
|
||||
expect(tokenizedBuffer.tokenizedLines[4].tokens[0].scopes).toEqual ['source.js', 'comment.block.js']
|
||||
expect(tokenizedBuffer.tokenizedLines[5].tokens[0].scopes).toEqual ['source.js', 'comment.block.js']
|
||||
|
||||
it "resumes highlighting with the state of the previous line", ->
|
||||
buffer.insert([0, 0], '/*')
|
||||
buffer.insert([5, 0], '*/')
|
||||
|
||||
buffer.insert([1, 0], 'var ')
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(1).tokens[0].scopes).toEqual ['source.js', 'comment.block.js']
|
||||
expect(tokenizedBuffer.tokenizedLines[1].tokens[0].scopes).toEqual ['source.js', 'comment.block.js']
|
||||
|
||||
describe "when lines are both updated and removed", ->
|
||||
it "updates tokens to reflect the change", ->
|
||||
buffer.setTextInRange([[1, 0], [3, 0]], "foo()")
|
||||
|
||||
# previous line 0 remains
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(0).tokens[0]).toEqual(value: 'var', scopes: ['source.js', 'storage.type.var.js'])
|
||||
expect(tokenizedBuffer.tokenizedLines[0].tokens[0]).toEqual(value: 'var', scopes: ['source.js', 'storage.type.var.js'])
|
||||
|
||||
# previous line 3 should be combined with input to form line 1
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(1).tokens[0]).toEqual(value: 'foo', scopes: ['source.js', 'meta.function-call.js', 'entity.name.function.js'])
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(1).tokens[6]).toEqual(value: '=', scopes: ['source.js', 'keyword.operator.assignment.js'])
|
||||
expect(tokenizedBuffer.tokenizedLines[1].tokens[0]).toEqual(value: 'foo', scopes: ['source.js', 'meta.function-call.js', 'entity.name.function.js'])
|
||||
expect(tokenizedBuffer.tokenizedLines[1].tokens[6]).toEqual(value: '=', scopes: ['source.js', 'keyword.operator.assignment.js'])
|
||||
|
||||
# lines below deleted regions should be shifted upward
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(2).tokens[1]).toEqual(value: 'while', scopes: ['source.js', 'keyword.control.js'])
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(3).tokens[1]).toEqual(value: '=', scopes: ['source.js', 'keyword.operator.assignment.js'])
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(4).tokens[1]).toEqual(value: '<', scopes: ['source.js', 'keyword.operator.comparison.js'])
|
||||
expect(tokenizedBuffer.tokenizedLines[2].tokens[1]).toEqual(value: 'while', scopes: ['source.js', 'keyword.control.js'])
|
||||
expect(tokenizedBuffer.tokenizedLines[3].tokens[1]).toEqual(value: '=', scopes: ['source.js', 'keyword.operator.assignment.js'])
|
||||
expect(tokenizedBuffer.tokenizedLines[4].tokens[1]).toEqual(value: '<', scopes: ['source.js', 'keyword.operator.comparison.js'])
|
||||
|
||||
describe "when the change invalidates the tokenization of subsequent lines", ->
|
||||
it "schedules the invalidated lines to be tokenized in the background", ->
|
||||
buffer.insert([5, 30], '/* */')
|
||||
buffer.setTextInRange([[2, 0], [3, 0]], '/*')
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(2).tokens[0].scopes).toEqual ['source.js', 'comment.block.js', 'punctuation.definition.comment.js']
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(3).tokens[0].scopes).toEqual ['source.js']
|
||||
expect(tokenizedBuffer.tokenizedLines[2].tokens[0].scopes).toEqual ['source.js', 'comment.block.js', 'punctuation.definition.comment.js']
|
||||
expect(tokenizedBuffer.tokenizedLines[3].tokens[0].scopes).toEqual ['source.js']
|
||||
|
||||
advanceClock()
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(3).tokens[0].scopes).toEqual ['source.js', 'comment.block.js']
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(4).tokens[0].scopes).toEqual ['source.js', 'comment.block.js']
|
||||
expect(tokenizedBuffer.tokenizedLines[3].tokens[0].scopes).toEqual ['source.js', 'comment.block.js']
|
||||
expect(tokenizedBuffer.tokenizedLines[4].tokens[0].scopes).toEqual ['source.js', 'comment.block.js']
|
||||
|
||||
describe "when lines are both updated and inserted", ->
|
||||
it "updates tokens to reflect the change", ->
|
||||
buffer.setTextInRange([[1, 0], [2, 0]], "foo()\nbar()\nbaz()\nquux()")
|
||||
|
||||
# previous line 0 remains
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(0).tokens[0]).toEqual( value: 'var', scopes: ['source.js', 'storage.type.var.js'])
|
||||
expect(tokenizedBuffer.tokenizedLines[0].tokens[0]).toEqual( value: 'var', scopes: ['source.js', 'storage.type.var.js'])
|
||||
|
||||
# 3 new lines inserted
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(1).tokens[0]).toEqual(value: 'foo', scopes: ['source.js', 'meta.function-call.js', 'entity.name.function.js'])
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(2).tokens[0]).toEqual(value: 'bar', scopes: ['source.js', 'meta.function-call.js', 'entity.name.function.js'])
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(3).tokens[0]).toEqual(value: 'baz', scopes: ['source.js', 'meta.function-call.js', 'entity.name.function.js'])
|
||||
expect(tokenizedBuffer.tokenizedLines[1].tokens[0]).toEqual(value: 'foo', scopes: ['source.js', 'meta.function-call.js', 'entity.name.function.js'])
|
||||
expect(tokenizedBuffer.tokenizedLines[2].tokens[0]).toEqual(value: 'bar', scopes: ['source.js', 'meta.function-call.js', 'entity.name.function.js'])
|
||||
expect(tokenizedBuffer.tokenizedLines[3].tokens[0]).toEqual(value: 'baz', scopes: ['source.js', 'meta.function-call.js', 'entity.name.function.js'])
|
||||
|
||||
# previous line 2 is joined with quux() on line 4
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(4).tokens[0]).toEqual(value: 'quux', scopes: ['source.js', 'meta.function-call.js', 'entity.name.function.js'])
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(4).tokens[4]).toEqual(value: 'if', scopes: ['source.js', 'keyword.control.js'])
|
||||
expect(tokenizedBuffer.tokenizedLines[4].tokens[0]).toEqual(value: 'quux', scopes: ['source.js', 'meta.function-call.js', 'entity.name.function.js'])
|
||||
expect(tokenizedBuffer.tokenizedLines[4].tokens[4]).toEqual(value: 'if', scopes: ['source.js', 'keyword.control.js'])
|
||||
|
||||
# previous line 3 is pushed down to become line 5
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(5).tokens[3]).toEqual(value: '=', scopes: ['source.js', 'keyword.operator.assignment.js'])
|
||||
expect(tokenizedBuffer.tokenizedLines[5].tokens[3]).toEqual(value: '=', scopes: ['source.js', 'keyword.operator.assignment.js'])
|
||||
|
||||
describe "when the change invalidates the tokenization of subsequent lines", ->
|
||||
it "schedules the invalidated lines to be tokenized in the background", ->
|
||||
buffer.insert([5, 30], '/* */')
|
||||
buffer.insert([2, 0], '/*\nabcde\nabcder')
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(2).tokens[0].scopes).toEqual ['source.js', 'comment.block.js', 'punctuation.definition.comment.js']
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(3).tokens[0].scopes).toEqual ['source.js', 'comment.block.js']
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(4).tokens[0].scopes).toEqual ['source.js', 'comment.block.js']
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(5).tokens[0].scopes).toEqual ['source.js']
|
||||
expect(tokenizedBuffer.tokenizedLines[2].tokens[0].scopes).toEqual ['source.js', 'comment.block.js', 'punctuation.definition.comment.js']
|
||||
expect(tokenizedBuffer.tokenizedLines[3].tokens[0].scopes).toEqual ['source.js', 'comment.block.js']
|
||||
expect(tokenizedBuffer.tokenizedLines[4].tokens[0].scopes).toEqual ['source.js', 'comment.block.js']
|
||||
expect(tokenizedBuffer.tokenizedLines[5].tokens[0].scopes).toEqual ['source.js']
|
||||
|
||||
advanceClock() # tokenize invalidated lines in background
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(5).tokens[0].scopes).toEqual ['source.js', 'comment.block.js']
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(6).tokens[0].scopes).toEqual ['source.js', 'comment.block.js']
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(7).tokens[0].scopes).toEqual ['source.js', 'comment.block.js']
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(8).tokens[0].scopes).not.toBe ['source.js', 'comment.block.js']
|
||||
expect(tokenizedBuffer.tokenizedLines[5].tokens[0].scopes).toEqual ['source.js', 'comment.block.js']
|
||||
expect(tokenizedBuffer.tokenizedLines[6].tokens[0].scopes).toEqual ['source.js', 'comment.block.js']
|
||||
expect(tokenizedBuffer.tokenizedLines[7].tokens[0].scopes).toEqual ['source.js', 'comment.block.js']
|
||||
expect(tokenizedBuffer.tokenizedLines[8].tokens[0].scopes).not.toBe ['source.js', 'comment.block.js']
|
||||
|
||||
describe "when there is an insertion that is larger than the chunk size", ->
|
||||
it "tokenizes the initial chunk synchronously, then tokenizes the remaining lines in the background", ->
|
||||
commentBlock = _.multiplyString("// a comment\n", tokenizedBuffer.chunkSize + 2)
|
||||
buffer.insert([0, 0], commentBlock)
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(0).ruleStack?).toBeTruthy()
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(4).ruleStack?).toBeTruthy()
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(5).ruleStack?).toBeFalsy()
|
||||
expect(tokenizedBuffer.tokenizedLines[0].ruleStack?).toBeTruthy()
|
||||
expect(tokenizedBuffer.tokenizedLines[4].ruleStack?).toBeTruthy()
|
||||
expect(tokenizedBuffer.tokenizedLines[5]).toBeUndefined()
|
||||
|
||||
advanceClock()
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(5).ruleStack?).toBeTruthy()
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(6).ruleStack?).toBeTruthy()
|
||||
expect(tokenizedBuffer.tokenizedLines[5].ruleStack?).toBeTruthy()
|
||||
expect(tokenizedBuffer.tokenizedLines[6].ruleStack?).toBeTruthy()
|
||||
|
||||
it "does not break out soft tabs across a scope boundary", ->
|
||||
waitsForPromise ->
|
||||
@ -284,11 +260,7 @@ describe "TokenizedBuffer", ->
|
||||
|
||||
runs ->
|
||||
buffer = atom.project.bufferForPathSync('sample-with-tabs.coffee')
|
||||
tokenizedBuffer = new TokenizedBuffer({
|
||||
buffer, grammarRegistry: atom.grammars, packageManager: atom.packages,
|
||||
assert: atom.assert, tabLength: 2,
|
||||
})
|
||||
tokenizedBuffer.setGrammar(atom.grammars.grammarForScopeName('source.coffee'))
|
||||
tokenizedBuffer = new TokenizedBuffer({buffer, grammar: atom.grammars.grammarForScopeName('source.coffee'), tabLength: 2})
|
||||
startTokenizing(tokenizedBuffer)
|
||||
|
||||
afterEach ->
|
||||
@ -352,7 +324,6 @@ describe "TokenizedBuffer", ->
|
||||
expect(tokenizedHandler.callCount).toBe(1)
|
||||
|
||||
it "retokenizes the buffer", ->
|
||||
|
||||
waitsForPromise ->
|
||||
atom.packages.activatePackage('language-ruby-on-rails')
|
||||
|
||||
@ -362,14 +333,10 @@ describe "TokenizedBuffer", ->
|
||||
runs ->
|
||||
buffer = atom.project.bufferForPathSync()
|
||||
buffer.setText "<div class='name'><%= User.find(2).full_name %></div>"
|
||||
tokenizedBuffer = new TokenizedBuffer({
|
||||
buffer, grammarRegistry: atom.grammars, packageManager: atom.packages,
|
||||
assert: atom.assert, tabLength: 2,
|
||||
})
|
||||
tokenizedBuffer.setGrammar(atom.grammars.selectGrammar('test.erb'))
|
||||
tokenizedBuffer = new TokenizedBuffer({buffer, grammar: atom.grammars.selectGrammar('test.erb'), tabLength: 2})
|
||||
fullyTokenize(tokenizedBuffer)
|
||||
|
||||
{tokens} = tokenizedBuffer.tokenizedLineForRow(0)
|
||||
{tokens} = tokenizedBuffer.tokenizedLines[0]
|
||||
expect(tokens[0]).toEqual value: "<div class='name'>", scopes: ["text.html.ruby"]
|
||||
|
||||
waitsForPromise ->
|
||||
@ -377,7 +344,7 @@ describe "TokenizedBuffer", ->
|
||||
|
||||
runs ->
|
||||
fullyTokenize(tokenizedBuffer)
|
||||
{tokens} = tokenizedBuffer.tokenizedLineForRow(0)
|
||||
{tokens} = tokenizedBuffer.tokenizedLines[0]
|
||||
expect(tokens[0]).toEqual value: '<', scopes: ["text.html.ruby", "meta.tag.block.any.html", "punctuation.definition.tag.begin.html"]
|
||||
|
||||
describe ".tokenForPosition(position)", ->
|
||||
@ -387,11 +354,7 @@ describe "TokenizedBuffer", ->
|
||||
|
||||
it "returns the correct token (regression)", ->
|
||||
buffer = atom.project.bufferForPathSync('sample.js')
|
||||
tokenizedBuffer = new TokenizedBuffer({
|
||||
buffer, grammarRegistry: atom.grammars, packageManager: atom.packages,
|
||||
assert: atom.assert, tabLength: 2,
|
||||
})
|
||||
tokenizedBuffer.setGrammar(atom.grammars.grammarForScopeName('source.js'))
|
||||
tokenizedBuffer = new TokenizedBuffer({buffer, grammar: atom.grammars.grammarForScopeName('source.js'), tabLength: 2})
|
||||
fullyTokenize(tokenizedBuffer)
|
||||
expect(tokenizedBuffer.tokenForPosition([1, 0]).scopes).toEqual ["source.js"]
|
||||
expect(tokenizedBuffer.tokenForPosition([1, 1]).scopes).toEqual ["source.js"]
|
||||
@ -400,16 +363,12 @@ describe "TokenizedBuffer", ->
|
||||
describe ".bufferRangeForScopeAtPosition(selector, position)", ->
|
||||
beforeEach ->
|
||||
buffer = atom.project.bufferForPathSync('sample.js')
|
||||
tokenizedBuffer = new TokenizedBuffer({
|
||||
buffer, grammarRegistry: atom.grammars, packageManager: atom.packages,
|
||||
assert: atom.assert, tabLength: 2,
|
||||
})
|
||||
tokenizedBuffer.setGrammar(atom.grammars.grammarForScopeName('source.js'))
|
||||
tokenizedBuffer = new TokenizedBuffer({buffer, grammar: atom.grammars.grammarForScopeName('source.js'), tabLength: 2})
|
||||
fullyTokenize(tokenizedBuffer)
|
||||
|
||||
describe "when the selector does not match the token at the position", ->
|
||||
it "returns a falsy value", ->
|
||||
expect(tokenizedBuffer.bufferRangeForScopeAtPosition('.bogus', [0, 1])).toBeFalsy()
|
||||
expect(tokenizedBuffer.bufferRangeForScopeAtPosition('.bogus', [0, 1])).toBeUndefined()
|
||||
|
||||
describe "when the selector matches a single token at the position", ->
|
||||
it "returns the range covered by the token", ->
|
||||
@ -423,11 +382,7 @@ describe "TokenizedBuffer", ->
|
||||
describe ".indentLevelForRow(row)", ->
|
||||
beforeEach ->
|
||||
buffer = atom.project.bufferForPathSync('sample.js')
|
||||
tokenizedBuffer = new TokenizedBuffer({
|
||||
buffer, grammarRegistry: atom.grammars, packageManager: atom.packages,
|
||||
assert: atom.assert, tabLength: 2,
|
||||
})
|
||||
tokenizedBuffer.setGrammar(atom.grammars.grammarForScopeName('source.js'))
|
||||
tokenizedBuffer = new TokenizedBuffer({buffer, grammar: atom.grammars.grammarForScopeName('source.js'), tabLength: 2})
|
||||
fullyTokenize(tokenizedBuffer)
|
||||
|
||||
describe "when the line is non-empty", ->
|
||||
@ -469,7 +424,7 @@ describe "TokenizedBuffer", ->
|
||||
|
||||
buffer.insert([12, 0], ' ')
|
||||
expect(tokenizedBuffer.indentLevelForRow(13)).toBe 2
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(14)).not.toBeDefined()
|
||||
expect(tokenizedBuffer.tokenizedLines[14]).not.toBeDefined()
|
||||
|
||||
it "updates the indentLevel of empty lines surrounding a change that inserts lines", ->
|
||||
buffer.insert([7, 0], '\n\n')
|
||||
@ -503,11 +458,7 @@ describe "TokenizedBuffer", ->
|
||||
buffer = atom.project.bufferForPathSync('sample.js')
|
||||
buffer.insert [10, 0], " // multi-line\n // comment\n // block\n"
|
||||
buffer.insert [0, 0], "// multi-line\n// comment\n// block\n"
|
||||
tokenizedBuffer = new TokenizedBuffer({
|
||||
buffer, grammarRegistry: atom.grammars, packageManager: atom.packages,
|
||||
assert: atom.assert, tabLength: 2,
|
||||
})
|
||||
tokenizedBuffer.setGrammar(atom.grammars.grammarForScopeName('source.js'))
|
||||
tokenizedBuffer = new TokenizedBuffer({buffer, grammar: atom.grammars.grammarForScopeName('source.js'), tabLength: 2})
|
||||
fullyTokenize(tokenizedBuffer)
|
||||
|
||||
it "includes the first line of multi-line comments", ->
|
||||
@ -570,40 +521,69 @@ describe "TokenizedBuffer", ->
|
||||
expect(tokenizedBuffer.isFoldableAtRow(7)).toBe false
|
||||
expect(tokenizedBuffer.isFoldableAtRow(8)).toBe false
|
||||
|
||||
describe "::tokenizedLineForRow(row)", ->
|
||||
it "returns the tokenized line for a row, or a placeholder line if it hasn't been tokenized yet", ->
|
||||
buffer = atom.project.bufferForPathSync('sample.js')
|
||||
grammar = atom.grammars.grammarForScopeName('source.js')
|
||||
tokenizedBuffer = new TokenizedBuffer({buffer, grammar, tabLength: 2})
|
||||
line0 = buffer.lineForRow(0)
|
||||
|
||||
jsScopeStartId = grammar.startIdForScope(grammar.scopeName)
|
||||
jsScopeEndId = grammar.endIdForScope(grammar.scopeName)
|
||||
startTokenizing(tokenizedBuffer)
|
||||
expect(tokenizedBuffer.tokenizedLines[0]).toBeUndefined()
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(0).text).toBe(line0)
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(0).tags).toEqual([jsScopeStartId, line0.length, jsScopeEndId])
|
||||
advanceClock(1)
|
||||
expect(tokenizedBuffer.tokenizedLines[0]).not.toBeUndefined()
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(0).text).toBe(line0)
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(0).tags).not.toEqual([jsScopeStartId, line0.length, jsScopeEndId])
|
||||
|
||||
nullScopeStartId = NullGrammar.startIdForScope(NullGrammar.scopeName)
|
||||
nullScopeEndId = NullGrammar.endIdForScope(NullGrammar.scopeName)
|
||||
tokenizedBuffer.setGrammar(NullGrammar)
|
||||
startTokenizing(tokenizedBuffer)
|
||||
expect(tokenizedBuffer.tokenizedLines[0]).toBeUndefined()
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(0).text).toBe(line0)
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(0).tags).toEqual([nullScopeStartId, line0.length, nullScopeEndId])
|
||||
advanceClock(1)
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(0).text).toBe(line0)
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(0).tags).toEqual([nullScopeStartId, line0.length, nullScopeEndId])
|
||||
|
||||
it "returns undefined if the requested row is outside the buffer range", ->
|
||||
buffer = atom.project.bufferForPathSync('sample.js')
|
||||
grammar = atom.grammars.grammarForScopeName('source.js')
|
||||
tokenizedBuffer = new TokenizedBuffer({buffer, grammar, tabLength: 2})
|
||||
fullyTokenize(tokenizedBuffer)
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(999)).toBeUndefined()
|
||||
|
||||
describe "when the buffer is configured with the null grammar", ->
|
||||
it "uses the placeholder tokens and does not actually tokenize using the grammar", ->
|
||||
spyOn(atom.grammars.nullGrammar, 'tokenizeLine').andCallThrough()
|
||||
it "does not actually tokenize using the grammar", ->
|
||||
spyOn(NullGrammar, 'tokenizeLine').andCallThrough()
|
||||
buffer = atom.project.bufferForPathSync('sample.will-use-the-null-grammar')
|
||||
buffer.setText('a\nb\nc')
|
||||
|
||||
tokenizedBuffer = new TokenizedBuffer({
|
||||
buffer, grammarRegistry: atom.grammars, packageManager: atom.packages,
|
||||
assert: atom.assert, tabLength: 2,
|
||||
})
|
||||
tokenizedBuffer = new TokenizedBuffer({buffer, tabLength: 2})
|
||||
tokenizeCallback = jasmine.createSpy('onDidTokenize')
|
||||
tokenizedBuffer.onDidTokenize(tokenizeCallback)
|
||||
|
||||
expect(tokenizedBuffer.tokenizedLines[0]).toBeUndefined()
|
||||
expect(tokenizedBuffer.tokenizedLines[1]).toBeUndefined()
|
||||
expect(tokenizedBuffer.tokenizedLines[2]).toBeUndefined()
|
||||
expect(tokenizeCallback.callCount).toBe(0)
|
||||
expect(NullGrammar.tokenizeLine).not.toHaveBeenCalled()
|
||||
|
||||
fullyTokenize(tokenizedBuffer)
|
||||
|
||||
expect(tokenizeCallback.callCount).toBe 1
|
||||
expect(atom.grammars.nullGrammar.tokenizeLine.callCount).toBe 0
|
||||
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(0).tokens.length).toBe 1
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(0).tokens[0].value).toBe 'a'
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(1).tokens.length).toBe 1
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(1).tokens[0].value).toBe 'b'
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(2).tokens.length).toBe 1
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(2).tokens[0].value).toBe 'c'
|
||||
expect(tokenizedBuffer.tokenizedLines[0]).toBeUndefined()
|
||||
expect(tokenizedBuffer.tokenizedLines[1]).toBeUndefined()
|
||||
expect(tokenizedBuffer.tokenizedLines[2]).toBeUndefined()
|
||||
expect(tokenizeCallback.callCount).toBe(0)
|
||||
expect(NullGrammar.tokenizeLine).not.toHaveBeenCalled()
|
||||
|
||||
describe "text decoration layer API", ->
|
||||
describe "iterator", ->
|
||||
it "iterates over the syntactic scope boundaries", ->
|
||||
buffer = new TextBuffer(text: "var foo = 1 /*\nhello*/var bar = 2\n")
|
||||
tokenizedBuffer = new TokenizedBuffer({
|
||||
buffer, grammarRegistry: atom.grammars, packageManager: atom.packages,
|
||||
assert: atom.assert, tabLength: 2,
|
||||
})
|
||||
tokenizedBuffer.setGrammar(atom.grammars.selectGrammar(".js"))
|
||||
tokenizedBuffer = new TokenizedBuffer({buffer, grammar: atom.grammars.grammarForScopeName("source.js"), tabLength: 2})
|
||||
fullyTokenize(tokenizedBuffer)
|
||||
|
||||
iterator = tokenizedBuffer.buildIterator()
|
||||
@ -655,11 +635,7 @@ describe "TokenizedBuffer", ->
|
||||
|
||||
runs ->
|
||||
buffer = new TextBuffer(text: "# hello\n# world")
|
||||
tokenizedBuffer = new TokenizedBuffer({
|
||||
buffer, grammarRegistry: atom.grammars, packageManager: atom.packages,
|
||||
assert: atom.assert, tabLength: 2,
|
||||
})
|
||||
tokenizedBuffer.setGrammar(atom.grammars.selectGrammar(".coffee"))
|
||||
tokenizedBuffer = new TokenizedBuffer({buffer, grammar: atom.grammars.grammarForScopeName("source.coffee"), tabLength: 2})
|
||||
fullyTokenize(tokenizedBuffer)
|
||||
|
||||
iterator = tokenizedBuffer.buildIterator()
|
||||
@ -688,11 +664,7 @@ describe "TokenizedBuffer", ->
|
||||
})
|
||||
|
||||
buffer = new TextBuffer(text: 'start x\nend x\nx')
|
||||
tokenizedBuffer = new TokenizedBuffer({
|
||||
buffer, grammarRegistry: atom.grammars, packageManager: atom.packages,
|
||||
assert: atom.assert, tabLength: 2,
|
||||
})
|
||||
tokenizedBuffer.setGrammar(grammar)
|
||||
tokenizedBuffer = new TokenizedBuffer({buffer, grammar, tabLength: 2})
|
||||
fullyTokenize(tokenizedBuffer)
|
||||
|
||||
iterator = tokenizedBuffer.buildIterator()
|
||||
|
@ -8,7 +8,7 @@ describe "TooltipManager", ->
|
||||
ctrlY = _.humanizeKeystroke("ctrl-y")
|
||||
|
||||
beforeEach ->
|
||||
manager = new TooltipManager(keymapManager: atom.keymaps)
|
||||
manager = new TooltipManager(keymapManager: atom.keymaps, viewRegistry: atom.views)
|
||||
element = document.createElement('div')
|
||||
element.classList.add('foo')
|
||||
jasmine.attachToDOM(element)
|
||||
@ -16,23 +16,62 @@ describe "TooltipManager", ->
|
||||
hover = (element, fn) ->
|
||||
element.dispatchEvent(new CustomEvent('mouseenter', bubbles: false))
|
||||
element.dispatchEvent(new CustomEvent('mouseover', bubbles: true))
|
||||
advanceClock(manager.defaults.delay.show)
|
||||
advanceClock(manager.hoverDefaults.delay.show)
|
||||
fn()
|
||||
element.dispatchEvent(new CustomEvent('mouseleave', bubbles: false))
|
||||
element.dispatchEvent(new CustomEvent('mouseout', bubbles: true))
|
||||
advanceClock(manager.defaults.delay.hide)
|
||||
advanceClock(manager.hoverDefaults.delay.hide)
|
||||
|
||||
describe "::add(target, options)", ->
|
||||
it "creates a tooltip based on the given options when hovering over the target element", ->
|
||||
manager.add element, title: "Title"
|
||||
hover element, ->
|
||||
expect(document.body.querySelector(".tooltip")).toHaveText("Title")
|
||||
describe "when the trigger is 'hover' (the default)", ->
|
||||
it "creates a tooltip when hovering over the target element", ->
|
||||
manager.add element, title: "Title"
|
||||
hover element, ->
|
||||
expect(document.body.querySelector(".tooltip")).toHaveText("Title")
|
||||
|
||||
it "creates a tooltip immediately if the trigger type is manual", ->
|
||||
disposable = manager.add element, title: "Title", trigger: "manual"
|
||||
expect(document.body.querySelector(".tooltip")).toHaveText("Title")
|
||||
disposable.dispose()
|
||||
expect(document.body.querySelector(".tooltip")).toBeNull()
|
||||
describe "when the trigger is 'manual'", ->
|
||||
it "creates a tooltip immediately and only hides it on dispose", ->
|
||||
disposable = manager.add element, title: "Title", trigger: "manual"
|
||||
expect(document.body.querySelector(".tooltip")).toHaveText("Title")
|
||||
disposable.dispose()
|
||||
expect(document.body.querySelector(".tooltip")).toBeNull()
|
||||
|
||||
describe "when the trigger is 'click'", ->
|
||||
it "shows and hides the tooltip when the target element is clicked", ->
|
||||
disposable = manager.add element, title: "Title", trigger: "click"
|
||||
expect(document.body.querySelector(".tooltip")).toBeNull()
|
||||
element.click()
|
||||
expect(document.body.querySelector(".tooltip")).not.toBeNull()
|
||||
element.click()
|
||||
expect(document.body.querySelector(".tooltip")).toBeNull()
|
||||
|
||||
# Hide the tooltip when clicking anywhere but inside the tooltip element
|
||||
element.click()
|
||||
expect(document.body.querySelector(".tooltip")).not.toBeNull()
|
||||
document.body.querySelector(".tooltip").click()
|
||||
expect(document.body.querySelector(".tooltip")).not.toBeNull()
|
||||
document.body.querySelector(".tooltip").firstChild.click()
|
||||
expect(document.body.querySelector(".tooltip")).not.toBeNull()
|
||||
document.body.click()
|
||||
expect(document.body.querySelector(".tooltip")).toBeNull()
|
||||
|
||||
# Tooltip can show again after hiding due to clicking outside of the tooltip
|
||||
element.click()
|
||||
expect(document.body.querySelector(".tooltip")).not.toBeNull()
|
||||
element.click()
|
||||
expect(document.body.querySelector(".tooltip")).toBeNull()
|
||||
|
||||
it "allows a custom item to be specified for the content of the tooltip", ->
|
||||
tooltipElement = document.createElement('div')
|
||||
manager.add element, item: {element: tooltipElement}
|
||||
hover element, ->
|
||||
expect(tooltipElement.closest(".tooltip")).not.toBeNull()
|
||||
|
||||
it "allows a custom class to be specified for the tooltip", ->
|
||||
tooltipElement = document.createElement('div')
|
||||
manager.add element, title: 'Title', class: 'custom-tooltip-class'
|
||||
hover element, ->
|
||||
expect(document.body.querySelector(".tooltip").classList.contains('custom-tooltip-class')).toBe(true)
|
||||
|
||||
it "allows jQuery elements to be passed as the target", ->
|
||||
element2 = document.createElement('div')
|
||||
@ -52,20 +91,6 @@ describe "TooltipManager", ->
|
||||
hover element, -> expect(document.body.querySelector(".tooltip")).toBeNull()
|
||||
hover element2, -> expect(document.body.querySelector(".tooltip")).toBeNull()
|
||||
|
||||
describe "when a selector is specified", ->
|
||||
it "creates a tooltip when hovering over a descendant of the target that matches the selector", ->
|
||||
child = document.createElement('div')
|
||||
child.classList.add('bar')
|
||||
grandchild = document.createElement('div')
|
||||
element.appendChild(child)
|
||||
child.appendChild(grandchild)
|
||||
|
||||
manager.add element, selector: '.bar', title: 'Bar'
|
||||
|
||||
hover grandchild, ->
|
||||
expect(document.body.querySelector('.tooltip')).toHaveText('Bar')
|
||||
expect(document.body.querySelector('.tooltip')).toBeNull()
|
||||
|
||||
describe "when a keyBindingCommand is specified", ->
|
||||
describe "when a title is specified", ->
|
||||
it "appends the key binding corresponding to the command to the title", ->
|
||||
|
@ -1,6 +1,7 @@
|
||||
{ipcRenderer} = require 'electron'
|
||||
path = require 'path'
|
||||
temp = require('temp').track()
|
||||
{Disposable} = require 'event-kit'
|
||||
|
||||
describe "WorkspaceElement", ->
|
||||
describe "when the workspace element is focused", ->
|
||||
@ -17,9 +18,11 @@ describe "WorkspaceElement", ->
|
||||
it "has a class based on the style of the scrollbar", ->
|
||||
observeCallback = null
|
||||
scrollbarStyle = require 'scrollbar-style'
|
||||
spyOn(scrollbarStyle, 'observePreferredScrollbarStyle').andCallFake (cb) -> observeCallback = cb
|
||||
workspaceElement = atom.views.getView(atom.workspace)
|
||||
spyOn(scrollbarStyle, 'observePreferredScrollbarStyle').andCallFake (cb) ->
|
||||
observeCallback = cb
|
||||
new Disposable(->)
|
||||
|
||||
workspaceElement = atom.views.getView(atom.workspace)
|
||||
observeCallback('legacy')
|
||||
expect(workspaceElement.className).toMatch 'scrollbars-visible-always'
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
path = require 'path'
|
||||
temp = require 'temp'
|
||||
TextEditor = require '../src/text-editor'
|
||||
Workspace = require '../src/workspace'
|
||||
Project = require '../src/project'
|
||||
platform = require './spec-helper-platform'
|
||||
@ -29,7 +30,7 @@ describe "Workspace", ->
|
||||
atom.workspace = new Workspace({
|
||||
config: atom.config, project: atom.project, packageManager: atom.packages,
|
||||
grammarRegistry: atom.grammars, deserializerManager: atom.deserializers,
|
||||
notificationManager: atom.notifications, clipboard: atom.clipboard,
|
||||
notificationManager: atom.notifications,
|
||||
applicationDelegate: atom.applicationDelegate,
|
||||
viewRegistry: atom.views, assert: atom.assert.bind(atom),
|
||||
textEditorRegistry: atom.textEditors
|
||||
@ -795,7 +796,7 @@ describe "Workspace", ->
|
||||
|
||||
describe "::isTextEditor(obj)", ->
|
||||
it "returns true when the passed object is an instance of `TextEditor`", ->
|
||||
expect(workspace.isTextEditor(atom.workspace.buildTextEditor())).toBe(true)
|
||||
expect(workspace.isTextEditor(new TextEditor)).toBe(true)
|
||||
expect(workspace.isTextEditor({getText: -> null})).toBe(false)
|
||||
expect(workspace.isTextEditor(null)).toBe(false)
|
||||
expect(workspace.isTextEditor(undefined)).toBe(false)
|
||||
@ -871,7 +872,7 @@ describe "Workspace", ->
|
||||
workspace2 = new Workspace({
|
||||
config: atom.config, project: atom.project, packageManager: atom.packages,
|
||||
notificationManager: atom.notifications, deserializerManager: atom.deserializers,
|
||||
clipboard: atom.clipboard, viewRegistry: atom.views, grammarRegistry: atom.grammars,
|
||||
viewRegistry: atom.views, grammarRegistry: atom.grammars,
|
||||
applicationDelegate: atom.applicationDelegate, assert: atom.assert.bind(atom),
|
||||
textEditorRegistry: atom.textEditors
|
||||
})
|
||||
@ -934,7 +935,7 @@ describe "Workspace", ->
|
||||
workspace2 = new Workspace({
|
||||
config: atom.config, project: atom.project, packageManager: atom.packages,
|
||||
notificationManager: atom.notifications, deserializerManager: atom.deserializers,
|
||||
clipboard: atom.clipboard, viewRegistry: atom.views, grammarRegistry: atom.grammars,
|
||||
viewRegistry: atom.views, grammarRegistry: atom.grammars,
|
||||
applicationDelegate: atom.applicationDelegate, assert: atom.assert.bind(atom),
|
||||
textEditorRegistry: atom.textEditors
|
||||
})
|
||||
@ -1727,7 +1728,7 @@ describe "Workspace", ->
|
||||
|
||||
describe "when there's no repository for the editor's file", ->
|
||||
it "doesn't do anything", ->
|
||||
editor = atom.workspace.buildTextEditor()
|
||||
editor = new TextEditor
|
||||
editor.setText("stuff")
|
||||
atom.workspace.checkoutHeadRevision(editor)
|
||||
|
||||
|
@ -28,7 +28,6 @@ ThemeManager = require './theme-manager'
|
||||
MenuManager = require './menu-manager'
|
||||
ContextMenuManager = require './context-menu-manager'
|
||||
CommandInstaller = require './command-installer'
|
||||
Clipboard = require './clipboard'
|
||||
Project = require './project'
|
||||
TitleBar = require './title-bar'
|
||||
Workspace = require './workspace'
|
||||
@ -127,7 +126,7 @@ class AtomEnvironment extends Model
|
||||
|
||||
# Call .loadOrCreate instead
|
||||
constructor: (params={}) ->
|
||||
{@blobStore, @applicationDelegate, @window, @document, @configDirPath, @enablePersistence, onlyLoadBaseStyleSheets} = params
|
||||
{@blobStore, @applicationDelegate, @window, @document, @clipboard, @configDirPath, @enablePersistence, onlyLoadBaseStyleSheets} = params
|
||||
|
||||
@unloaded = false
|
||||
@loadTime = null
|
||||
@ -154,7 +153,7 @@ class AtomEnvironment extends Model
|
||||
|
||||
@keymaps = new KeymapManager({@configDirPath, resourcePath, notificationManager: @notifications})
|
||||
|
||||
@tooltips = new TooltipManager(keymapManager: @keymaps)
|
||||
@tooltips = new TooltipManager(keymapManager: @keymaps, viewRegistry: @views)
|
||||
|
||||
@commands = new CommandRegistry
|
||||
@commands.attach(@window)
|
||||
@ -182,20 +181,18 @@ class AtomEnvironment extends Model
|
||||
@packages.setContextMenuManager(@contextMenu)
|
||||
@packages.setThemeManager(@themes)
|
||||
|
||||
@clipboard = new Clipboard()
|
||||
|
||||
@project = new Project({notificationManager: @notifications, packageManager: @packages, @config, @applicationDelegate})
|
||||
|
||||
@commandInstaller = new CommandInstaller(@getVersion(), @applicationDelegate)
|
||||
|
||||
@textEditors = new TextEditorRegistry({
|
||||
@config, grammarRegistry: @grammars, assert: @assert.bind(this), @clipboard,
|
||||
@config, grammarRegistry: @grammars, assert: @assert.bind(this),
|
||||
packageManager: @packages
|
||||
})
|
||||
|
||||
@workspace = new Workspace({
|
||||
@config, @project, packageManager: @packages, grammarRegistry: @grammars, deserializerManager: @deserializers,
|
||||
notificationManager: @notifications, @applicationDelegate, @clipboard, viewRegistry: @views, assert: @assert.bind(this),
|
||||
notificationManager: @notifications, @applicationDelegate, viewRegistry: @views, assert: @assert.bind(this),
|
||||
textEditorRegistry: @textEditors,
|
||||
})
|
||||
|
||||
|
@ -1,68 +1,88 @@
|
||||
path = require 'path'
|
||||
fs = require 'fs-plus'
|
||||
/** @babel */
|
||||
|
||||
# This is loaded by atom.coffee. See https://atom.io/docs/api/latest/Config for
|
||||
# more information about config schemas.
|
||||
module.exports =
|
||||
core:
|
||||
type: 'object'
|
||||
properties:
|
||||
ignoredNames:
|
||||
type: 'array'
|
||||
default: [".git", ".hg", ".svn", ".DS_Store", "._*", "Thumbs.db"]
|
||||
items:
|
||||
import path from 'path'
|
||||
import fs from 'fs-plus'
|
||||
|
||||
// This is loaded by atom-environment.coffee. See
|
||||
// https://atom.io/docs/api/latest/Config for more information about config
|
||||
// schemas.
|
||||
const configSchema = {
|
||||
core: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
ignoredNames: {
|
||||
type: 'array',
|
||||
default: ['.git', '.hg', '.svn', '.DS_Store', '._*', 'Thumbs.db'],
|
||||
items: {
|
||||
type: 'string'
|
||||
},
|
||||
description: 'List of [glob patterns](https://en.wikipedia.org/wiki/Glob_%28programming%29). Files and directories matching these patterns will be ignored by some packages, such as the fuzzy finder and tree view. Individual packages might have additional config settings for ignoring names.'
|
||||
excludeVcsIgnoredPaths:
|
||||
type: 'boolean'
|
||||
default: true
|
||||
title: 'Exclude VCS Ignored Paths'
|
||||
},
|
||||
excludeVcsIgnoredPaths: {
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
title: 'Exclude VCS Ignored Paths',
|
||||
description: 'Files and directories ignored by the current project\'s VCS system will be ignored by some packages, such as the fuzzy finder and find and replace. For example, projects using Git have these paths defined in the .gitignore file. Individual packages might have additional config settings for ignoring VCS ignored files and folders.'
|
||||
followSymlinks:
|
||||
type: 'boolean'
|
||||
default: true
|
||||
},
|
||||
followSymlinks: {
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
description: 'Follow symbolic links when searching files and when opening files with the fuzzy finder.'
|
||||
disabledPackages:
|
||||
type: 'array'
|
||||
default: []
|
||||
items:
|
||||
},
|
||||
disabledPackages: {
|
||||
type: 'array',
|
||||
default: [],
|
||||
|
||||
items: {
|
||||
type: 'string'
|
||||
},
|
||||
|
||||
description: 'List of names of installed packages which are not loaded at startup.'
|
||||
customFileTypes:
|
||||
type: 'object'
|
||||
default: {}
|
||||
description: 'Associates scope names (e.g. `"source.js"`) with arrays of file extensions and file names (e.g. `["Somefile", ".js2"]`)'
|
||||
additionalProperties:
|
||||
type: 'array'
|
||||
items:
|
||||
},
|
||||
customFileTypes: {
|
||||
type: 'object',
|
||||
default: {},
|
||||
description: 'Associates scope names (e.g. `"source.js"`) with arrays of file extensions and file names (e.g. `["Somefile", ".js2"]`)',
|
||||
additionalProperties: {
|
||||
type: 'array',
|
||||
items: {
|
||||
type: 'string'
|
||||
themes:
|
||||
type: 'array'
|
||||
default: ['one-dark-ui', 'one-dark-syntax']
|
||||
items:
|
||||
}
|
||||
}
|
||||
},
|
||||
themes: {
|
||||
type: 'array',
|
||||
default: ['one-dark-ui', 'one-dark-syntax'],
|
||||
items: {
|
||||
type: 'string'
|
||||
},
|
||||
description: 'Names of UI and syntax themes which will be used when Atom starts.'
|
||||
projectHome:
|
||||
type: 'string'
|
||||
default: path.join(fs.getHomeDirectory(), 'github')
|
||||
},
|
||||
projectHome: {
|
||||
type: 'string',
|
||||
default: path.join(fs.getHomeDirectory(), 'github'),
|
||||
description: 'The directory where projects are assumed to be located. Packages created using the Package Generator will be stored here by default.'
|
||||
audioBeep:
|
||||
type: 'boolean'
|
||||
default: true
|
||||
},
|
||||
audioBeep: {
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
description: 'Trigger the system\'s beep sound when certain actions cannot be executed or there are no results.'
|
||||
destroyEmptyPanes:
|
||||
type: 'boolean'
|
||||
default: true
|
||||
title: 'Remove Empty Panes'
|
||||
},
|
||||
destroyEmptyPanes: {
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
title: 'Remove Empty Panes',
|
||||
description: 'When the last tab of a pane is closed, remove that pane as well.'
|
||||
closeEmptyWindows:
|
||||
type: 'boolean'
|
||||
default: true
|
||||
},
|
||||
closeEmptyWindows: {
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
description: 'When a window with no open tabs or panes is given the \'Close Tab\' command, close that window.'
|
||||
fileEncoding:
|
||||
description: 'Default character set encoding to use when reading and writing files.'
|
||||
type: 'string'
|
||||
default: 'utf8'
|
||||
},
|
||||
fileEncoding: {
|
||||
description: 'Default character set encoding to use when reading and writing files.',
|
||||
type: 'string',
|
||||
default: 'utf8',
|
||||
enum: [
|
||||
'cp437',
|
||||
'eucjp',
|
||||
@ -100,179 +120,237 @@ module.exports =
|
||||
'windows1258',
|
||||
'windows866'
|
||||
]
|
||||
openEmptyEditorOnStart:
|
||||
description: 'Automatically open an empty editor on startup.'
|
||||
type: 'boolean'
|
||||
},
|
||||
openEmptyEditorOnStart: {
|
||||
description: 'Automatically open an empty editor on startup.',
|
||||
type: 'boolean',
|
||||
default: true
|
||||
automaticallyUpdate:
|
||||
description: 'Automatically update Atom when a new release is available.'
|
||||
type: 'boolean'
|
||||
},
|
||||
automaticallyUpdate: {
|
||||
description: 'Automatically update Atom when a new release is available.',
|
||||
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'
|
||||
},
|
||||
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',
|
||||
default: true
|
||||
telemetryConsent:
|
||||
description: 'Allow usage statistics and exception reports to be sent to the Atom team to help improve the product.'
|
||||
title: 'Send Telemetry to the Atom Team'
|
||||
type: 'string'
|
||||
default: 'undecided'
|
||||
},
|
||||
telemetryConsent: {
|
||||
description: 'Allow usage statistics and exception reports to be sent to the Atom team to help improve the product.',
|
||||
title: 'Send Telemetry to the Atom Team',
|
||||
type: 'string',
|
||||
default: 'undecided',
|
||||
enum: [
|
||||
{value: 'limited', description: 'Allow limited anonymous usage stats, exception and crash reporting'}
|
||||
{value: 'no', description: 'Do not send any telemetry data'}
|
||||
{value: 'undecided', description: 'Undecided (Atom will ask again next time it is launched)'}
|
||||
{
|
||||
value: 'limited',
|
||||
description: 'Allow limited anonymous usage stats, exception and crash reporting'
|
||||
},
|
||||
{
|
||||
value: 'no',
|
||||
description: 'Do not send any telemetry data'
|
||||
},
|
||||
{
|
||||
value: 'undecided',
|
||||
description: 'Undecided (Atom will ask again next time it is launched)'
|
||||
}
|
||||
]
|
||||
warnOnLargeFileLimit:
|
||||
description: 'Warn before opening files larger than this number of megabytes.'
|
||||
type: 'number'
|
||||
},
|
||||
warnOnLargeFileLimit: {
|
||||
description: 'Warn before opening files larger than this number of megabytes.',
|
||||
type: 'number',
|
||||
default: 20
|
||||
editor:
|
||||
type: 'object'
|
||||
properties:
|
||||
# These settings are used in scoped fashion only. No defaults.
|
||||
commentStart:
|
||||
}
|
||||
}
|
||||
},
|
||||
editor: {
|
||||
type: 'object',
|
||||
// These settings are used in scoped fashion only. No defaults.
|
||||
properties: {
|
||||
commentStart: {
|
||||
type: ['string', 'null']
|
||||
commentEnd:
|
||||
},
|
||||
commentEnd: {
|
||||
type: ['string', 'null']
|
||||
increaseIndentPattern:
|
||||
},
|
||||
increaseIndentPattern: {
|
||||
type: ['string', 'null']
|
||||
decreaseIndentPattern:
|
||||
},
|
||||
decreaseIndentPattern: {
|
||||
type: ['string', 'null']
|
||||
foldEndPattern:
|
||||
},
|
||||
foldEndPattern: {
|
||||
type: ['string', 'null']
|
||||
|
||||
# These can be used as globals or scoped, thus defaults.
|
||||
fontFamily:
|
||||
type: 'string'
|
||||
default: ''
|
||||
},
|
||||
// These can be used as globals or scoped, thus defaults.
|
||||
fontFamily: {
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'The name of the font family used for editor text.'
|
||||
fontSize:
|
||||
type: 'integer'
|
||||
default: 14
|
||||
minimum: 1
|
||||
maximum: 100
|
||||
},
|
||||
fontSize: {
|
||||
type: 'integer',
|
||||
default: 14,
|
||||
minimum: 1,
|
||||
maximum: 100,
|
||||
description: 'Height in pixels of editor text.'
|
||||
lineHeight:
|
||||
type: ['string', 'number']
|
||||
default: 1.5
|
||||
},
|
||||
lineHeight: {
|
||||
type: ['string', 'number'],
|
||||
default: 1.5,
|
||||
description: 'Height of editor lines, as a multiplier of font size.'
|
||||
showInvisibles:
|
||||
type: 'boolean'
|
||||
default: false
|
||||
},
|
||||
showInvisibles: {
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
description: 'Render placeholders for invisible characters, such as tabs, spaces and newlines.'
|
||||
showIndentGuide:
|
||||
type: 'boolean'
|
||||
default: false
|
||||
},
|
||||
showIndentGuide: {
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
description: 'Show indentation indicators in the editor.'
|
||||
showLineNumbers:
|
||||
type: 'boolean'
|
||||
default: true
|
||||
},
|
||||
showLineNumbers: {
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
description: 'Show line numbers in the editor\'s gutter.'
|
||||
atomicSoftTabs:
|
||||
type: 'boolean'
|
||||
default: true
|
||||
},
|
||||
atomicSoftTabs: {
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
description: 'Skip over tab-length runs of leading whitespace when moving the cursor.'
|
||||
autoIndent:
|
||||
type: 'boolean'
|
||||
default: true
|
||||
},
|
||||
autoIndent: {
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
description: 'Automatically indent the cursor when inserting a newline.'
|
||||
autoIndentOnPaste:
|
||||
type: 'boolean'
|
||||
default: true
|
||||
},
|
||||
autoIndentOnPaste: {
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
description: 'Automatically indent pasted text based on the indentation of the previous line.'
|
||||
nonWordCharacters:
|
||||
type: 'string'
|
||||
default: "/\\()\"':,.;<>~!@#$%^&*|+=[]{}`?-…"
|
||||
},
|
||||
nonWordCharacters: {
|
||||
type: 'string',
|
||||
default: "/\\()\"':,.;<>~!@#$%^&*|+=[]{}`?-…",
|
||||
description: 'A string of non-word characters to define word boundaries.'
|
||||
preferredLineLength:
|
||||
type: 'integer'
|
||||
default: 80
|
||||
minimum: 1
|
||||
},
|
||||
preferredLineLength: {
|
||||
type: 'integer',
|
||||
default: 80,
|
||||
minimum: 1,
|
||||
description: 'Identifies the length of a line which is used when wrapping text with the `Soft Wrap At Preferred Line Length` setting enabled, in number of characters.'
|
||||
tabLength:
|
||||
type: 'integer'
|
||||
default: 2
|
||||
minimum: 1
|
||||
},
|
||||
tabLength: {
|
||||
type: 'integer',
|
||||
default: 2,
|
||||
minimum: 1,
|
||||
description: 'Number of spaces used to represent a tab.'
|
||||
softWrap:
|
||||
type: 'boolean'
|
||||
default: false
|
||||
},
|
||||
softWrap: {
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
description: 'Wraps lines that exceed the width of the window. When `Soft Wrap At Preferred Line Length` is set, it will wrap to the number of characters defined by the `Preferred Line Length` setting.'
|
||||
softTabs:
|
||||
type: 'boolean'
|
||||
default: true
|
||||
},
|
||||
softTabs: {
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
description: 'If the `Tab Type` config setting is set to "auto" and autodetection of tab type from buffer content fails, then this config setting determines whether a soft tab or a hard tab will be inserted when the Tab key is pressed.'
|
||||
tabType:
|
||||
type: 'string'
|
||||
default: 'auto'
|
||||
enum: ['auto', 'soft', 'hard']
|
||||
},
|
||||
tabType: {
|
||||
type: 'string',
|
||||
default: 'auto',
|
||||
enum: ['auto', 'soft', 'hard'],
|
||||
description: 'Determine character inserted when Tab key is pressed. Possible values: "auto", "soft" and "hard". When set to "soft" or "hard", soft tabs (spaces) or hard tabs (tab characters) are used. When set to "auto", the editor auto-detects the tab type based on the contents of the buffer (it uses the first leading whitespace on a non-comment line), or uses the value of the Soft Tabs config setting if auto-detection fails.'
|
||||
softWrapAtPreferredLineLength:
|
||||
type: 'boolean'
|
||||
default: false
|
||||
},
|
||||
softWrapAtPreferredLineLength: {
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
description: 'Instead of wrapping lines to the window\'s width, wrap lines to the number of characters defined by the `Preferred Line Length` setting. This will only take effect when the soft wrap config setting is enabled globally or for the current language. **Note:** If you want to hide the wrap guide (the vertical line) you can disable the `wrap-guide` package.'
|
||||
softWrapHangingIndent:
|
||||
type: 'integer'
|
||||
default: 0
|
||||
minimum: 0
|
||||
},
|
||||
softWrapHangingIndent: {
|
||||
type: 'integer',
|
||||
default: 0,
|
||||
minimum: 0,
|
||||
description: 'When soft wrap is enabled, defines length of additional indentation applied to wrapped lines, in number of characters.'
|
||||
scrollSensitivity:
|
||||
type: 'integer'
|
||||
default: 40
|
||||
minimum: 10
|
||||
maximum: 200
|
||||
},
|
||||
scrollSensitivity: {
|
||||
type: 'integer',
|
||||
default: 40,
|
||||
minimum: 10,
|
||||
maximum: 200,
|
||||
description: 'Determines how fast the editor scrolls when using a mouse or trackpad.'
|
||||
scrollPastEnd:
|
||||
type: 'boolean'
|
||||
default: false
|
||||
},
|
||||
scrollPastEnd: {
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
description: 'Allow the editor to be scrolled past the end of the last line.'
|
||||
undoGroupingInterval:
|
||||
type: 'integer'
|
||||
default: 300
|
||||
minimum: 0
|
||||
},
|
||||
undoGroupingInterval: {
|
||||
type: 'integer',
|
||||
default: 300,
|
||||
minimum: 0,
|
||||
description: 'Time interval in milliseconds within which text editing operations will be grouped together in the undo history.'
|
||||
confirmCheckoutHeadRevision:
|
||||
type: 'boolean'
|
||||
default: true
|
||||
title: 'Confirm Checkout HEAD Revision'
|
||||
},
|
||||
confirmCheckoutHeadRevision: {
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
title: 'Confirm Checkout HEAD Revision',
|
||||
description: 'Show confirmation dialog when checking out the HEAD revision and discarding changes to current file since last commit.'
|
||||
invisibles:
|
||||
type: 'object'
|
||||
description: 'A hash of characters Atom will use to render whitespace characters. Keys are whitespace character types, values are rendered characters (use value false to turn off individual whitespace character types).'
|
||||
properties:
|
||||
eol:
|
||||
type: ['boolean', 'string']
|
||||
default: '\u00ac'
|
||||
maximumLength: 1
|
||||
},
|
||||
invisibles: {
|
||||
type: 'object',
|
||||
description: 'A hash of characters Atom will use to render whitespace characters. Keys are whitespace character types, values are rendered characters (use value false to turn off individual whitespace character types).',
|
||||
properties: {
|
||||
eol: {
|
||||
type: ['boolean', 'string'],
|
||||
default: '¬',
|
||||
maximumLength: 1,
|
||||
description: 'Character used to render newline characters (\\n) when the `Show Invisibles` setting is enabled. '
|
||||
space:
|
||||
type: ['boolean', 'string']
|
||||
default: '\u00b7'
|
||||
maximumLength: 1
|
||||
},
|
||||
space: {
|
||||
type: ['boolean', 'string'],
|
||||
default: '·',
|
||||
maximumLength: 1,
|
||||
description: 'Character used to render leading and trailing space characters when the `Show Invisibles` setting is enabled.'
|
||||
tab:
|
||||
type: ['boolean', 'string']
|
||||
default: '\u00bb'
|
||||
maximumLength: 1
|
||||
},
|
||||
tab: {
|
||||
type: ['boolean', 'string'],
|
||||
default: '»',
|
||||
maximumLength: 1,
|
||||
description: 'Character used to render hard tab characters (\\t) when the `Show Invisibles` setting is enabled.'
|
||||
cr:
|
||||
type: ['boolean', 'string']
|
||||
default: '\u00a4'
|
||||
maximumLength: 1
|
||||
},
|
||||
cr: {
|
||||
type: ['boolean', 'string'],
|
||||
default: '¤',
|
||||
maximumLength: 1,
|
||||
description: 'Character used to render carriage return characters (for Microsoft-style line endings) when the `Show Invisibles` setting is enabled.'
|
||||
zoomFontWhenCtrlScrolling:
|
||||
type: 'boolean'
|
||||
default: process.platform isnt 'darwin'
|
||||
}
|
||||
}
|
||||
},
|
||||
zoomFontWhenCtrlScrolling: {
|
||||
type: 'boolean',
|
||||
default: process.platform !== 'darwin',
|
||||
description: 'Change the editor font size when pressing the Ctrl key and scrolling the mouse up/down.'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if process.platform in ['win32', 'linux']
|
||||
module.exports.core.properties.autoHideMenuBar =
|
||||
type: 'boolean'
|
||||
default: false
|
||||
if (['win32', 'linux'].includes(process.platform)) {
|
||||
configSchema.core.properties.autoHideMenuBar = {
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
description: 'Automatically hide the menu bar and toggle it by pressing Alt. This is only supported on Windows & Linux.'
|
||||
}
|
||||
}
|
||||
|
||||
if process.platform is 'darwin'
|
||||
module.exports.core.properties.useCustomTitleBar =
|
||||
type: 'boolean'
|
||||
default: false
|
||||
if (process.platform === 'darwin') {
|
||||
configSchema.core.properties.useCustomTitleBar = {
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
description: 'Use custom, theme-aware title bar.<br>Note: This currently does not include a proxy icon.<br>This setting will require a relaunch of Atom to take effect.'
|
||||
}
|
||||
}
|
||||
|
||||
export default configSchema
|
@ -20,16 +20,21 @@ module.exports = ({blobStore}) ->
|
||||
|
||||
AtomEnvironment = require './atom-environment'
|
||||
ApplicationDelegate = require './application-delegate'
|
||||
Clipboard = require './clipboard'
|
||||
TextEditor = require './text-editor'
|
||||
|
||||
clipboard = new Clipboard
|
||||
TextEditor.setClipboard(clipboard)
|
||||
|
||||
window.atom = new AtomEnvironment({
|
||||
window, document, blobStore,
|
||||
window, document, clipboard, blobStore,
|
||||
applicationDelegate: new ApplicationDelegate,
|
||||
configDirPath: process.env.ATOM_HOME
|
||||
enablePersistence: true
|
||||
configDirPath: process.env.ATOM_HOME,
|
||||
enablePersistence: true,
|
||||
env: process.env
|
||||
})
|
||||
|
||||
atom.startEditorWindow().then ->
|
||||
|
||||
# Workaround for focus getting cleared upon window creation
|
||||
windowFocused = ->
|
||||
window.removeEventListener('focus', windowFocused)
|
||||
|
111
src/initialize-benchmark-window.js
Normal file
@ -0,0 +1,111 @@
|
||||
/** @babel */
|
||||
|
||||
import {remote} from 'electron'
|
||||
import path from 'path'
|
||||
import ipcHelpers from './ipc-helpers'
|
||||
import util from 'util'
|
||||
|
||||
export default async function () {
|
||||
const {getWindowLoadSettings} = require('./window-load-settings-helpers')
|
||||
const {test, headless, resourcePath, benchmarkPaths} = getWindowLoadSettings()
|
||||
try {
|
||||
const Clipboard = require('../src/clipboard')
|
||||
const ApplicationDelegate = require('../src/application-delegate')
|
||||
const AtomEnvironment = require('../src/atom-environment')
|
||||
const TextEditor = require('../src/text-editor')
|
||||
|
||||
const exportsPath = path.join(resourcePath, 'exports')
|
||||
require('module').globalPaths.push(exportsPath) // Add 'exports' to module search path.
|
||||
process.env.NODE_PATH = exportsPath // Set NODE_PATH env variable since tasks may need it.
|
||||
|
||||
document.title = 'Benchmarks'
|
||||
// Allow `document.title` to be assigned in benchmarks without actually changing the window title.
|
||||
let documentTitle = null
|
||||
Object.defineProperty(document, 'title', {
|
||||
get () { return documentTitle },
|
||||
set (title) { documentTitle = title }
|
||||
})
|
||||
|
||||
window.addEventListener('keydown', (event) => {
|
||||
// Reload: cmd-r / ctrl-r
|
||||
if ((event.metaKey || event.ctrlKey) && event.keyCode === 82) {
|
||||
ipcHelpers.call('window-method', 'reload')
|
||||
}
|
||||
|
||||
// Toggle Dev Tools: cmd-alt-i (Mac) / ctrl-shift-i (Linux/Windows)
|
||||
if (event.keyCode === 73) {
|
||||
const isDarwin = process.platform === 'darwin'
|
||||
if ((isDarwin && event.metaKey && event.altKey) || (!isDarwin && event.ctrlKey && event.shiftKey)) {
|
||||
ipcHelpers.call('window-method', 'toggleDevTools')
|
||||
}
|
||||
}
|
||||
|
||||
// Close: cmd-w / ctrl-w
|
||||
if ((event.metaKey || event.ctrlKey) && event.keyCode === 87) {
|
||||
ipcHelpers.call('window-method', 'close')
|
||||
}
|
||||
|
||||
// Copy: cmd-c / ctrl-c
|
||||
if ((event.metaKey || event.ctrlKey) && event.keyCode === 67) {
|
||||
ipcHelpers.call('window-method', 'copy')
|
||||
}
|
||||
}, true)
|
||||
|
||||
const clipboard = new Clipboard()
|
||||
TextEditor.setClipboard(clipboard)
|
||||
|
||||
const applicationDelegate = new ApplicationDelegate()
|
||||
global.atom = new AtomEnvironment({
|
||||
applicationDelegate,
|
||||
window,
|
||||
document,
|
||||
clipboard,
|
||||
configDirPath: process.env.ATOM_HOME,
|
||||
enablePersistence: false
|
||||
})
|
||||
|
||||
// Prevent benchmarks from modifying application menus
|
||||
global.atom.menu.sendToBrowserProcess = function () { }
|
||||
|
||||
if (headless) {
|
||||
Object.defineProperties(process, {
|
||||
stdout: { value: remote.process.stdout },
|
||||
stderr: { value: remote.process.stderr }
|
||||
})
|
||||
|
||||
console.log = function (...args) {
|
||||
const formatted = util.format(...args)
|
||||
process.stdout.write(formatted + '\n')
|
||||
}
|
||||
console.warn = function (...args) {
|
||||
const formatted = util.format(...args)
|
||||
process.stderr.write(formatted + '\n')
|
||||
}
|
||||
console.error = function (...args) {
|
||||
const formatted = util.format(...args)
|
||||
process.stderr.write(formatted + '\n')
|
||||
}
|
||||
} else {
|
||||
remote.getCurrentWindow().show()
|
||||
}
|
||||
|
||||
const benchmarkRunner = require('../benchmarks/benchmark-runner')
|
||||
const statusCode = await benchmarkRunner({test, benchmarkPaths})
|
||||
if (headless) {
|
||||
exitWithStatusCode(statusCode)
|
||||
}
|
||||
} catch (error) {
|
||||
if (headless) {
|
||||
console.error(error.stack || error)
|
||||
exitWithStatusCode(1)
|
||||
} else {
|
||||
ipcHelpers.call('window-method', 'openDevTools')
|
||||
throw error
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function exitWithStatusCode (statusCode) {
|
||||
remote.app.emit('will-quit')
|
||||
remote.process.exit(statusCode)
|
||||
}
|
@ -21,6 +21,8 @@ module.exports = ({blobStore}) ->
|
||||
{getWindowLoadSettings} = require './window-load-settings-helpers'
|
||||
AtomEnvironment = require '../src/atom-environment'
|
||||
ApplicationDelegate = require '../src/application-delegate'
|
||||
Clipboard = require '../src/clipboard'
|
||||
TextEditor = require '../src/text-editor'
|
||||
require '../src/electron-shims'
|
||||
|
||||
{testRunnerPath, legacyTestRunnerPath, headless, logFile, testPaths} = getWindowLoadSettings()
|
||||
@ -58,11 +60,15 @@ module.exports = ({blobStore}) ->
|
||||
|
||||
document.title = "Spec Suite"
|
||||
|
||||
clipboard = new Clipboard
|
||||
TextEditor.setClipboard(clipboard)
|
||||
|
||||
testRunner = require(testRunnerPath)
|
||||
legacyTestRunner = require(legacyTestRunnerPath)
|
||||
buildDefaultApplicationDelegate = -> new ApplicationDelegate()
|
||||
buildAtomEnvironment = (params) ->
|
||||
params = cloneObject(params)
|
||||
params.clipboard = clipboard unless params.hasOwnProperty("clipboard")
|
||||
params.blobStore = blobStore unless params.hasOwnProperty("blobStore")
|
||||
params.onlyLoadBaseStyleSheets = true unless params.hasOwnProperty("onlyLoadBaseStyleSheets")
|
||||
new AtomEnvironment(params)
|
||||
|
@ -2,6 +2,7 @@
|
||||
_ = require 'underscore-plus'
|
||||
{OnigRegExp} = require 'oniguruma'
|
||||
ScopeDescriptor = require './scope-descriptor'
|
||||
NullGrammar = require './null-grammar'
|
||||
|
||||
module.exports =
|
||||
class LanguageMode
|
||||
@ -147,19 +148,19 @@ class LanguageMode
|
||||
rowRange
|
||||
|
||||
rowRangeForCommentAtBufferRow: (bufferRow) ->
|
||||
return unless @editor.tokenizedBuffer.tokenizedLineForRow(bufferRow).isComment()
|
||||
return unless @editor.tokenizedBuffer.tokenizedLines[bufferRow]?.isComment()
|
||||
|
||||
startRow = bufferRow
|
||||
endRow = bufferRow
|
||||
|
||||
if bufferRow > 0
|
||||
for currentRow in [bufferRow-1..0] by -1
|
||||
break unless @editor.tokenizedBuffer.tokenizedLineForRow(currentRow).isComment()
|
||||
break unless @editor.tokenizedBuffer.tokenizedLines[currentRow]?.isComment()
|
||||
startRow = currentRow
|
||||
|
||||
if bufferRow < @buffer.getLastRow()
|
||||
for currentRow in [bufferRow+1..@buffer.getLastRow()] by 1
|
||||
break unless @editor.tokenizedBuffer.tokenizedLineForRow(currentRow).isComment()
|
||||
break unless @editor.tokenizedBuffer.tokenizedLines[currentRow]?.isComment()
|
||||
endRow = currentRow
|
||||
|
||||
return [startRow, endRow] if startRow isnt endRow
|
||||
@ -188,7 +189,7 @@ class LanguageMode
|
||||
# row is a comment.
|
||||
isLineCommentedAtBufferRow: (bufferRow) ->
|
||||
return false unless 0 <= bufferRow <= @editor.getLastBufferRow()
|
||||
@editor.tokenizedBuffer.tokenizedLineForRow(bufferRow).isComment()
|
||||
@editor.tokenizedBuffer.tokenizedLines[bufferRow]?.isComment()
|
||||
|
||||
# Find a row range for a 'paragraph' around specified bufferRow. A paragraph
|
||||
# is a block of text bounded by and empty line or a block of text that is not
|
||||
|
@ -13,19 +13,21 @@ class LinesYardstick
|
||||
measuredRowForPixelPosition: (pixelPosition) ->
|
||||
targetTop = pixelPosition.top
|
||||
row = Math.floor(targetTop / @model.getLineHeightInPixels())
|
||||
row if 0 <= row <= @model.getLastScreenRow()
|
||||
row if 0 <= row
|
||||
|
||||
screenPositionForPixelPosition: (pixelPosition) ->
|
||||
targetTop = pixelPosition.top
|
||||
targetLeft = pixelPosition.left
|
||||
row = @lineTopIndex.rowForPixelPosition(targetTop)
|
||||
targetLeft = 0 if targetTop < 0 or targetLeft < 0
|
||||
targetLeft = Infinity if row > @model.getLastScreenRow()
|
||||
row = Math.min(row, @model.getLastScreenRow())
|
||||
row = Math.max(0, row)
|
||||
|
||||
row = Math.max(0, @lineTopIndex.rowForPixelPosition(targetTop))
|
||||
lineNode = @lineNodesProvider.lineNodeForScreenRow(row)
|
||||
return Point(row, 0) unless lineNode
|
||||
unless lineNode
|
||||
lastScreenRow = @model.getLastScreenRow()
|
||||
if row > lastScreenRow
|
||||
return Point(lastScreenRow, @model.lineLengthForScreenRow(lastScreenRow))
|
||||
else
|
||||
return Point(row, 0)
|
||||
|
||||
targetLeft = pixelPosition.left
|
||||
targetLeft = 0 if targetTop < 0 or targetLeft < 0
|
||||
|
||||
textNodes = @lineNodesProvider.textNodesForScreenRow(row)
|
||||
lineOffset = lineNode.getBoundingClientRect().left
|
||||
|
@ -42,7 +42,7 @@ class AtomApplication
|
||||
# take a few seconds to trigger 'error' event, it could be a bug of node
|
||||
# or atom-shell, before it's fixed we check the existence of socketPath to
|
||||
# speedup startup.
|
||||
if (process.platform isnt 'win32' and not fs.existsSync options.socketPath) or options.test
|
||||
if (process.platform isnt 'win32' and not fs.existsSync options.socketPath) or options.test or options.benchmark or options.benchmarkTest
|
||||
new AtomApplication(options).initialize(options)
|
||||
return
|
||||
|
||||
@ -64,7 +64,7 @@ class AtomApplication
|
||||
|
||||
constructor: (options) ->
|
||||
{@resourcePath, @devResourcePath, @version, @devMode, @safeMode, @socketPath, @logFile, @setPortable, @userDataDir} = options
|
||||
@socketPath = null if options.test
|
||||
@socketPath = null if options.test or options.benchmark or options.benchmarkTest
|
||||
@pidsToOpenWindows = {}
|
||||
@windows = []
|
||||
|
||||
@ -86,7 +86,9 @@ class AtomApplication
|
||||
|
||||
@config.onDidChange 'core.useCustomTitleBar', @promptForRestart.bind(this)
|
||||
|
||||
@autoUpdateManager = new AutoUpdateManager(@version, options.test, @resourcePath, @config)
|
||||
@autoUpdateManager = new AutoUpdateManager(
|
||||
@version, options.test or options.benchmark or options.benchmarkTest, @resourcePath, @config
|
||||
)
|
||||
@applicationMenu = new ApplicationMenu(@version, @autoUpdateManager)
|
||||
@atomProtocolHandler = new AtomProtocolHandler(@resourcePath, @safeMode)
|
||||
|
||||
@ -103,23 +105,41 @@ class AtomApplication
|
||||
Promise.all(windowsClosePromises).then(=> @disposable.dispose())
|
||||
|
||||
launch: (options) ->
|
||||
if options.pathsToOpen?.length > 0 or options.urlsToOpen?.length > 0 or options.test
|
||||
if options.pathsToOpen?.length > 0 or options.urlsToOpen?.length > 0 or options.test or options.benchmark or options.benchmarkTest
|
||||
@openWithOptions(options)
|
||||
else
|
||||
@loadState(options) or @openPath(options)
|
||||
|
||||
openWithOptions: ({initialPaths, pathsToOpen, executedFrom, urlsToOpen, test, pidToKillWhenClosed, devMode, safeMode, newWindow, logFile, profileStartup, timeout, clearWindowState, addToLastWindow, env}) ->
|
||||
openWithOptions: (options) ->
|
||||
{
|
||||
initialPaths, pathsToOpen, executedFrom, urlsToOpen, benchmark,
|
||||
benchmarkTest, test, pidToKillWhenClosed, devMode, safeMode, newWindow,
|
||||
logFile, profileStartup, timeout, clearWindowState, addToLastWindow, env
|
||||
} = options
|
||||
|
||||
app.focus()
|
||||
|
||||
if test
|
||||
@runTests({headless: true, devMode, @resourcePath, executedFrom, pathsToOpen, logFile, timeout, env})
|
||||
@runTests({
|
||||
headless: true, devMode, @resourcePath, executedFrom, pathsToOpen,
|
||||
logFile, timeout, env
|
||||
})
|
||||
else if benchmark or benchmarkTest
|
||||
@runBenchmarks({headless: true, test: benchmarkTest, @resourcePath, executedFrom, pathsToOpen, timeout, env})
|
||||
else if pathsToOpen.length > 0
|
||||
@openPaths({initialPaths, pathsToOpen, executedFrom, pidToKillWhenClosed, newWindow, devMode, safeMode, profileStartup, clearWindowState, addToLastWindow, env})
|
||||
@openPaths({
|
||||
initialPaths, pathsToOpen, executedFrom, pidToKillWhenClosed, newWindow,
|
||||
devMode, safeMode, profileStartup, clearWindowState, addToLastWindow, env
|
||||
})
|
||||
else if urlsToOpen.length > 0
|
||||
@openUrl({urlToOpen, devMode, safeMode, env}) for urlToOpen in urlsToOpen
|
||||
for urlToOpen in urlsToOpen
|
||||
@openUrl({urlToOpen, devMode, safeMode, env})
|
||||
else
|
||||
# Always open a editor window if this is the first instance of Atom.
|
||||
@openPath({initialPaths, pidToKillWhenClosed, newWindow, devMode, safeMode, profileStartup, clearWindowState, addToLastWindow, env})
|
||||
@openPath({
|
||||
initialPaths, pidToKillWhenClosed, newWindow, devMode, safeMode, profileStartup,
|
||||
clearWindowState, addToLastWindow, env
|
||||
})
|
||||
|
||||
# Public: Removes the {AtomWindow} from the global window list.
|
||||
removeWindow: (window) ->
|
||||
@ -280,6 +300,9 @@ class AtomApplication
|
||||
@disposable.add ipcHelpers.on ipcMain, 'run-package-specs', (event, packageSpecPath) =>
|
||||
@runTests({resourcePath: @devResourcePath, pathsToOpen: [packageSpecPath], headless: false})
|
||||
|
||||
@disposable.add ipcHelpers.on ipcMain, 'run-benchmarks', (event, benchmarksPath) =>
|
||||
@runBenchmarks({resourcePath: @devResourcePath, pathsToOpen: [benchmarksPath], headless: false, test: false})
|
||||
|
||||
@disposable.add ipcHelpers.on ipcMain, 'command', (event, command) =>
|
||||
@emit(command)
|
||||
|
||||
@ -651,6 +674,29 @@ class AtomApplication
|
||||
safeMode ?= false
|
||||
new AtomWindow(this, @fileRecoveryService, {windowInitializationScript, resourcePath, headless, isSpec, devMode, testRunnerPath, legacyTestRunnerPath, testPaths, logFile, safeMode, env})
|
||||
|
||||
runBenchmarks: ({headless, test, resourcePath, executedFrom, pathsToOpen, env}) ->
|
||||
if resourcePath isnt @resourcePath and not fs.existsSync(resourcePath)
|
||||
resourcePath = @resourcePath
|
||||
|
||||
try
|
||||
windowInitializationScript = require.resolve(path.resolve(@devResourcePath, 'src', 'initialize-benchmark-window'))
|
||||
catch error
|
||||
windowInitializationScript = require.resolve(path.resolve(__dirname, '..', '..', 'src', 'initialize-benchmark-window'))
|
||||
|
||||
benchmarkPaths = []
|
||||
if pathsToOpen?
|
||||
for pathToOpen in pathsToOpen
|
||||
benchmarkPaths.push(path.resolve(executedFrom, fs.normalize(pathToOpen)))
|
||||
|
||||
if benchmarkPaths.length is 0
|
||||
process.stderr.write 'Error: Specify at least one benchmark path.\n\n'
|
||||
process.exit(1)
|
||||
|
||||
devMode = true
|
||||
isSpec = true
|
||||
safeMode = false
|
||||
new AtomWindow(this, @fileRecoveryService, {windowInitializationScript, resourcePath, headless, test, isSpec, devMode, benchmarkPaths, safeMode, env})
|
||||
|
||||
resolveTestRunnerPath: (testPath) ->
|
||||
FindParentDir ?= require 'find-parent-dir'
|
||||
|
||||
|
@ -19,7 +19,7 @@ if (args.resourcePath) {
|
||||
const stableResourcePath = path.dirname(path.dirname(__dirname))
|
||||
const defaultRepositoryPath = path.join(electron.app.getPath('home'), 'github', 'atom')
|
||||
|
||||
if (args.dev || args.test) {
|
||||
if (args.dev || args.test || args.benchmark || args.benchmarkTest) {
|
||||
if (process.env.ATOM_DEV_RESOURCE_PATH) {
|
||||
resourcePath = process.env.ATOM_DEV_RESOURCE_PATH
|
||||
} else if (fs.statSyncNoException(defaultRepositoryPath)) {
|
||||
|
@ -45,6 +45,8 @@ module.exports = function parseCommandLine (processArgs) {
|
||||
'portable',
|
||||
'Set portable mode. Copies the ~/.atom folder to be a sibling of the installed Atom location if a .atom folder is not already there.'
|
||||
)
|
||||
options.boolean('benchmark').describe('benchmark', 'Open a new window that runs the specified benchmarks.')
|
||||
options.boolean('benchmark-test').describe('benchmark--test', 'Run a faster version of the benchmarks in headless mode.')
|
||||
options.alias('t', 'test').boolean('t').describe('t', 'Run the specified specs and exit with error code on failures.')
|
||||
options.alias('m', 'main-process').boolean('m').describe('m', 'Run the specified specs in the main process.')
|
||||
options.string('timeout').describe(
|
||||
@ -78,6 +80,8 @@ module.exports = function parseCommandLine (processArgs) {
|
||||
const addToLastWindow = args['add']
|
||||
const safeMode = args['safe']
|
||||
const pathsToOpen = args._
|
||||
const benchmark = args['benchmark']
|
||||
const benchmarkTest = args['benchmark-test']
|
||||
const test = args['test']
|
||||
const mainProcess = args['main-process']
|
||||
const timeout = args['timeout']
|
||||
@ -132,10 +136,29 @@ module.exports = function parseCommandLine (processArgs) {
|
||||
devResourcePath = normalizeDriveLetterName(devResourcePath)
|
||||
|
||||
return {
|
||||
resourcePath, devResourcePath, pathsToOpen, urlsToOpen, executedFrom, test,
|
||||
version, pidToKillWhenClosed, devMode, safeMode, newWindow, logFile, socketPath,
|
||||
userDataDir, profileStartup, timeout, setPortable, clearWindowState,
|
||||
addToLastWindow, mainProcess, env: process.env
|
||||
resourcePath,
|
||||
devResourcePath,
|
||||
pathsToOpen,
|
||||
urlsToOpen,
|
||||
executedFrom,
|
||||
test,
|
||||
version,
|
||||
pidToKillWhenClosed,
|
||||
devMode,
|
||||
safeMode,
|
||||
newWindow,
|
||||
logFile,
|
||||
socketPath,
|
||||
userDataDir,
|
||||
profileStartup,
|
||||
timeout,
|
||||
setPortable,
|
||||
clearWindowState,
|
||||
addToLastWindow,
|
||||
mainProcess,
|
||||
benchmark,
|
||||
benchmarkTest,
|
||||
env: process.env
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -57,7 +57,7 @@ module.exports = function start (resourcePath, startTime) {
|
||||
|
||||
if (args.userDataDir != null) {
|
||||
app.setPath('userData', args.userDataDir)
|
||||
} else if (args.test) {
|
||||
} else if (args.test || args.benchmark || args.benchmarkTest) {
|
||||
app.setPath('userData', temp.mkdirSync('atom-test-data'))
|
||||
}
|
||||
|
||||
|
@ -45,7 +45,7 @@ class NativeCompileCache {
|
||||
Module.prototype._compile = function (content, filename) {
|
||||
let moduleSelf = this
|
||||
// remove shebang
|
||||
content = content.replace(/^\#\!.*/, '')
|
||||
content = content.replace(/^#!.*/, '')
|
||||
function require (path) {
|
||||
return moduleSelf.require(path)
|
||||
}
|
||||
|
@ -2,12 +2,39 @@
|
||||
|
||||
import {Disposable} from 'event-kit'
|
||||
|
||||
export default Object.freeze({
|
||||
export default {
|
||||
name: 'Null Grammar',
|
||||
scopeName: 'text.plain',
|
||||
scopeName: 'text.plain.null-grammar',
|
||||
scopeForId (id) {
|
||||
if (id === -1 || id === -2) {
|
||||
return this.scopeName
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
},
|
||||
startIdForScope (scopeName) {
|
||||
if (scopeName === this.scopeName) {
|
||||
return -1
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
},
|
||||
endIdForScope (scopeName) {
|
||||
if (scopeName === this.scopeName) {
|
||||
return -2
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
},
|
||||
tokenizeLine (text) {
|
||||
return {
|
||||
tags: [this.startIdForScope(this.scopeName), text.length, this.endIdForScope(this.scopeName)],
|
||||
ruleStack: null
|
||||
}
|
||||
},
|
||||
onDidUpdate (callback) {
|
||||
return new Disposable(noop)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function noop () {}
|
||||
|
@ -45,6 +45,8 @@ class PackageManager
|
||||
@packageDirPaths.push(path.join(configDirPath, "packages"))
|
||||
|
||||
@packagesCache = require('../package.json')?._atomPackages ? {}
|
||||
@initialPackagesLoaded = false
|
||||
@initialPackagesActivated = false
|
||||
@loadedPackages = {}
|
||||
@activePackages = {}
|
||||
@activatingPackages = {}
|
||||
@ -241,6 +243,9 @@ class PackageManager
|
||||
isPackageActive: (name) ->
|
||||
@getActivePackage(name)?
|
||||
|
||||
# Public: Returns a {Boolean} indicating whether package activation has occurred.
|
||||
hasActivatedInitialPackages: -> @initialPackagesActivated
|
||||
|
||||
###
|
||||
Section: Accessing loaded packages
|
||||
###
|
||||
@ -271,6 +276,9 @@ class PackageManager
|
||||
isPackageLoaded: (name) ->
|
||||
@getLoadedPackage(name)?
|
||||
|
||||
# Public: Returns a {Boolean} indicating whether package loading has occurred.
|
||||
hasLoadedInitialPackages: -> @initialPackagesLoaded
|
||||
|
||||
###
|
||||
Section: Accessing available packages
|
||||
###
|
||||
@ -364,6 +372,7 @@ class PackageManager
|
||||
@config.transact =>
|
||||
@loadPackage(packagePath) for packagePath in packagePaths
|
||||
return
|
||||
@initialPackagesLoaded = true
|
||||
@emitter.emit 'did-load-initial-packages'
|
||||
|
||||
loadPackage: (nameOrPath) ->
|
||||
@ -426,6 +435,7 @@ class PackageManager
|
||||
promises = promises.concat(activator.activatePackages(packages))
|
||||
Promise.all(promises).then =>
|
||||
@triggerDeferredActivationHooks()
|
||||
@initialPackagesActivated = true
|
||||
@emitter.emit 'did-activate-initial-packages'
|
||||
|
||||
# another type of package manager can handle other package types.
|
||||
|
@ -54,6 +54,7 @@ module.exports = ({commandRegistry, commandInstaller, config, notificationManage
|
||||
'application:open-your-stylesheet': -> ipcRenderer.send('command', 'application:open-your-stylesheet')
|
||||
'application:open-license': -> @getModel().openLicense()
|
||||
'window:run-package-specs': -> @runPackageSpecs()
|
||||
'window:run-benchmarks': -> @runBenchmarks()
|
||||
'window:focus-next-pane': -> @getModel().activateNextPane()
|
||||
'window:focus-previous-pane': -> @getModel().activatePreviousPane()
|
||||
'window:focus-pane-above': -> @focusPaneViewAbove()
|
||||
|
@ -14,7 +14,7 @@ class Selection extends Model
|
||||
initialScreenRange: null
|
||||
wordwise: false
|
||||
|
||||
constructor: ({@cursor, @marker, @editor, id, @clipboard}) ->
|
||||
constructor: ({@cursor, @marker, @editor, id}) ->
|
||||
@emitter = new Emitter
|
||||
|
||||
@assignId(id)
|
||||
@ -605,7 +605,7 @@ class Selection extends Model
|
||||
startLevel = @editor.indentLevelForLine(precedingText)
|
||||
|
||||
if maintainClipboard
|
||||
{text: clipboardText, metadata} = @clipboard.readWithMetadata()
|
||||
{text: clipboardText, metadata} = @editor.constructor.clipboard.readWithMetadata()
|
||||
metadata ?= {}
|
||||
unless metadata.selections?
|
||||
metadata.selections = [{
|
||||
@ -618,9 +618,9 @@ class Selection extends Model
|
||||
indentBasis: startLevel,
|
||||
fullLine: fullLine
|
||||
})
|
||||
@clipboard.write([clipboardText, selectionText].join("\n"), metadata)
|
||||
@editor.constructor.clipboard.write([clipboardText, selectionText].join("\n"), metadata)
|
||||
else
|
||||
@clipboard.write(selectionText, {
|
||||
@editor.constructor.clipboard.write(selectionText, {
|
||||
indentBasis: startLevel,
|
||||
fullLine: fullLine
|
||||
})
|
||||
|
@ -9,7 +9,7 @@ class TextEditorPresenter
|
||||
startBlinkingCursorsAfterDelay: null
|
||||
stoppedScrollingTimeoutId: null
|
||||
mouseWheelScreenRow: null
|
||||
overlayDimensions: {}
|
||||
overlayDimensions: null
|
||||
minimumReflowInterval: 200
|
||||
|
||||
constructor: (params) ->
|
||||
@ -31,6 +31,7 @@ class TextEditorPresenter
|
||||
@lineDecorationsByScreenRow = {}
|
||||
@lineNumberDecorationsByScreenRow = {}
|
||||
@customGutterDecorationsByGutterName = {}
|
||||
@overlayDimensions = {}
|
||||
@observedBlockDecorations = new Set()
|
||||
@invalidatedDimensionsByBlockDecoration = new Set()
|
||||
@invalidateAllBlockDecorationsDimensions = false
|
||||
@ -140,6 +141,11 @@ class TextEditorPresenter
|
||||
@shouldUpdateDecorations = true
|
||||
|
||||
observeModel: ->
|
||||
@disposables.add @model.displayLayer.onDidReset =>
|
||||
@spliceBlockDecorationsInRange(0, Infinity, Infinity)
|
||||
@shouldUpdateDecorations = true
|
||||
@emitDidUpdateState()
|
||||
|
||||
@disposables.add @model.displayLayer.onDidChangeSync (changes) =>
|
||||
for change in changes
|
||||
startRow = change.start.row
|
||||
@ -294,24 +300,21 @@ class TextEditorPresenter
|
||||
tileForRow: (row) ->
|
||||
row - (row % @tileSize)
|
||||
|
||||
constrainRow: (row) ->
|
||||
Math.max(0, Math.min(row, @model.getScreenLineCount()))
|
||||
|
||||
getStartTileRow: ->
|
||||
@constrainRow(@tileForRow(@startRow ? 0))
|
||||
@tileForRow(@startRow ? 0)
|
||||
|
||||
getEndTileRow: ->
|
||||
@constrainRow(@tileForRow(@endRow ? 0))
|
||||
@tileForRow(@endRow ? 0)
|
||||
|
||||
isValidScreenRow: (screenRow) ->
|
||||
screenRow >= 0 and screenRow < @model.getScreenLineCount()
|
||||
screenRow >= 0 and screenRow < @model.getApproximateScreenLineCount()
|
||||
|
||||
getScreenRowsToRender: ->
|
||||
startRow = @getStartTileRow()
|
||||
endRow = @constrainRow(@getEndTileRow() + @tileSize)
|
||||
endRow = @getEndTileRow() + @tileSize
|
||||
|
||||
screenRows = [startRow...endRow]
|
||||
longestScreenRow = @model.getLongestScreenRow()
|
||||
longestScreenRow = @model.getApproximateLongestScreenRow()
|
||||
if longestScreenRow?
|
||||
screenRows.push(longestScreenRow)
|
||||
if @screenRowsToMeasure?
|
||||
@ -357,7 +360,7 @@ class TextEditorPresenter
|
||||
zIndex = 0
|
||||
|
||||
for tileStartRow in [@tileForRow(endRow)..@tileForRow(startRow)] by -@tileSize
|
||||
tileEndRow = @constrainRow(tileStartRow + @tileSize)
|
||||
tileEndRow = tileStartRow + @tileSize
|
||||
rowsWithinTile = []
|
||||
|
||||
while screenRowIndex >= 0
|
||||
@ -392,7 +395,7 @@ class TextEditorPresenter
|
||||
visibleTiles[tileStartRow] = true
|
||||
zIndex++
|
||||
|
||||
if @mouseWheelScreenRow? and 0 <= @mouseWheelScreenRow < @model.getScreenLineCount()
|
||||
if @mouseWheelScreenRow? and 0 <= @mouseWheelScreenRow < @model.getApproximateScreenLineCount()
|
||||
mouseWheelTile = @tileForRow(@mouseWheelScreenRow)
|
||||
|
||||
unless visibleTiles[mouseWheelTile]?
|
||||
@ -411,8 +414,7 @@ class TextEditorPresenter
|
||||
visibleLineIds = {}
|
||||
for screenRow in screenRows
|
||||
line = @linesByScreenRow.get(screenRow)
|
||||
unless line?
|
||||
throw new Error("No line exists for row #{screenRow}. Last screen row: #{@model.getLastScreenRow()}")
|
||||
continue unless line?
|
||||
|
||||
visibleLineIds[line.id] = true
|
||||
precedingBlockDecorations = @precedingBlockDecorationsByScreenRowAndId[screenRow] ? {}
|
||||
@ -596,7 +598,9 @@ class TextEditorPresenter
|
||||
visibleLineNumberIds = {}
|
||||
|
||||
for screenRow in screenRows when @isRowRendered(screenRow)
|
||||
lineId = @linesByScreenRow.get(screenRow).id
|
||||
line = @linesByScreenRow.get(screenRow)
|
||||
continue unless line?
|
||||
lineId = line.id
|
||||
{bufferRow, softWrappedAtStart: softWrapped} = @displayLayer.softWrapDescriptorForScreenRow(screenRow)
|
||||
foldable = not softWrapped and @model.isFoldableAtBufferRow(bufferRow)
|
||||
decorationClasses = @lineNumberDecorationClassesForRow(screenRow)
|
||||
@ -623,7 +627,7 @@ class TextEditorPresenter
|
||||
return unless @scrollTop? and @lineHeight? and @height?
|
||||
|
||||
@endRow = Math.min(
|
||||
@model.getScreenLineCount(),
|
||||
@model.getApproximateScreenLineCount(),
|
||||
@lineTopIndex.rowForPixelPosition(@scrollTop + @height + @lineHeight - 1) + 1
|
||||
)
|
||||
|
||||
@ -657,7 +661,7 @@ class TextEditorPresenter
|
||||
updateVerticalDimensions: ->
|
||||
if @lineHeight?
|
||||
oldContentHeight = @contentHeight
|
||||
@contentHeight = Math.round(@lineTopIndex.pixelPositionAfterBlocksForRow(@model.getScreenLineCount()))
|
||||
@contentHeight = Math.round(@lineTopIndex.pixelPositionAfterBlocksForRow(@model.getApproximateScreenLineCount()))
|
||||
|
||||
if @contentHeight isnt oldContentHeight
|
||||
@updateHeight()
|
||||
@ -667,7 +671,7 @@ class TextEditorPresenter
|
||||
updateHorizontalDimensions: ->
|
||||
if @baseCharacterWidth?
|
||||
oldContentWidth = @contentWidth
|
||||
rightmostPosition = @model.getRightmostScreenPosition()
|
||||
rightmostPosition = @model.getApproximateRightmostScreenPosition()
|
||||
@contentWidth = @pixelPositionForScreenPosition(rightmostPosition).left
|
||||
@contentWidth += @scrollLeft
|
||||
@contentWidth += 1 unless @model.isSoftWrapped() # account for cursor width
|
||||
@ -1533,7 +1537,7 @@ class TextEditorPresenter
|
||||
[@startRow, @endRow]
|
||||
|
||||
isRowRendered: (row) ->
|
||||
@getStartTileRow() <= row < @constrainRow(@getEndTileRow() + @tileSize)
|
||||
@getStartTileRow() <= row < @getEndTileRow() + @tileSize
|
||||
|
||||
isOpenTagCode: (tagCode) ->
|
||||
@displayLayer.isOpenTagCode(tagCode)
|
||||
|
@ -39,9 +39,8 @@ const GRAMMAR_SELECTION_RANGE = Range(Point.ZERO, Point(10, 0)).freeze()
|
||||
// done using your editor, be sure to call `dispose` on the returned disposable
|
||||
// to avoid leaking editors.
|
||||
export default class TextEditorRegistry {
|
||||
constructor ({config, grammarRegistry, clipboard, assert, packageManager}) {
|
||||
constructor ({config, grammarRegistry, assert, packageManager}) {
|
||||
this.assert = assert
|
||||
this.clipboard = clipboard
|
||||
this.config = config
|
||||
this.grammarRegistry = grammarRegistry
|
||||
this.scopedSettingsDelegate = new ScopedSettingsDelegate(config)
|
||||
@ -109,10 +108,7 @@ export default class TextEditorRegistry {
|
||||
}
|
||||
|
||||
build (params) {
|
||||
params = Object.assign({
|
||||
clipboard: this.clipboard,
|
||||
assert: this.assert
|
||||
}, params)
|
||||
params = Object.assign({assert: this.assert}, params)
|
||||
|
||||
let scope = null
|
||||
if (params.buffer) {
|
||||
|
@ -2,7 +2,7 @@ _ = require 'underscore-plus'
|
||||
path = require 'path'
|
||||
fs = require 'fs-plus'
|
||||
Grim = require 'grim'
|
||||
{CompositeDisposable, Emitter} = require 'event-kit'
|
||||
{CompositeDisposable, Disposable, Emitter} = require 'event-kit'
|
||||
{Point, Range} = TextBuffer = require 'text-buffer'
|
||||
LanguageMode = require './language-mode'
|
||||
DecorationManager = require './decoration-manager'
|
||||
@ -59,6 +59,9 @@ ZERO_WIDTH_NBSP = '\ufeff'
|
||||
# soft wraps and folds to ensure your code interacts with them correctly.
|
||||
module.exports =
|
||||
class TextEditor extends Model
|
||||
@setClipboard: (clipboard) ->
|
||||
@clipboard = clipboard
|
||||
|
||||
serializationVersion: 1
|
||||
|
||||
buffer: null
|
||||
@ -114,7 +117,6 @@ class TextEditor extends Model
|
||||
if state.displayLayer = state.buffer.getDisplayLayer(state.displayLayerId)
|
||||
state.selectionsMarkerLayer = state.displayLayer.getMarkerLayer(state.selectionsMarkerLayerId)
|
||||
|
||||
state.clipboard = atomEnvironment.clipboard
|
||||
state.assert = atomEnvironment.assert.bind(atomEnvironment)
|
||||
editor = new this(state)
|
||||
if state.registered
|
||||
@ -123,19 +125,20 @@ class TextEditor extends Model
|
||||
editor
|
||||
|
||||
constructor: (params={}) ->
|
||||
unless @constructor.clipboard?
|
||||
throw new Error("Must call TextEditor.setClipboard at least once before creating TextEditor instances")
|
||||
|
||||
super
|
||||
|
||||
{
|
||||
@softTabs, @firstVisibleScreenRow, @firstVisibleScreenColumn, initialLine, initialColumn, tabLength,
|
||||
@softWrapped, @decorationManager, @selectionsMarkerLayer, @buffer, suppressCursorCreation,
|
||||
@mini, @placeholderText, lineNumberGutterVisible, @largeFileMode, @clipboard,
|
||||
@mini, @placeholderText, lineNumberGutterVisible, @largeFileMode,
|
||||
@assert, grammar, @showInvisibles, @autoHeight, @autoWidth, @scrollPastEnd, @editorWidthInChars,
|
||||
@tokenizedBuffer, @displayLayer, @invisibles, @showIndentGuide,
|
||||
@softWrapped, @softWrapAtPreferredLineLength, @preferredLineLength
|
||||
} = params
|
||||
|
||||
throw new Error("Must pass a clipboard parameter when constructing TextEditors") unless @clipboard?
|
||||
|
||||
@assert ?= (condition) -> condition
|
||||
@firstVisibleScreenRow ?= 0
|
||||
@firstVisibleScreenColumn ?= 0
|
||||
@ -147,7 +150,7 @@ class TextEditor extends Model
|
||||
@hasTerminatedPendingState = false
|
||||
|
||||
@mini ?= false
|
||||
@scrollPastEnd ?= true
|
||||
@scrollPastEnd ?= false
|
||||
@showInvisibles ?= true
|
||||
@softTabs ?= true
|
||||
tabLength ?= 2
|
||||
@ -181,6 +184,10 @@ class TextEditor extends Model
|
||||
else
|
||||
@displayLayer = @buffer.addDisplayLayer(displayLayerParams)
|
||||
|
||||
@backgroundWorkHandle = requestIdleCallback(@doBackgroundWork)
|
||||
@disposables.add new Disposable =>
|
||||
cancelIdleCallback(@backgroundWorkHandle) if @backgroundWorkHandle?
|
||||
|
||||
@displayLayer.setTextDecorationLayer(@tokenizedBuffer)
|
||||
@defaultMarkerLayer = @displayLayer.addMarkerLayer()
|
||||
@selectionsMarkerLayer ?= @addMarkerLayer(maintainHistory: true, persistent: true)
|
||||
@ -207,6 +214,13 @@ class TextEditor extends Model
|
||||
priority: 0
|
||||
visible: lineNumberGutterVisible
|
||||
|
||||
doBackgroundWork: (deadline) =>
|
||||
if @displayLayer.doBackgroundWork(deadline)
|
||||
@presenter?.updateVerticalDimensions()
|
||||
@backgroundWorkHandle = requestIdleCallback(@doBackgroundWork)
|
||||
else
|
||||
@backgroundWorkHandle = null
|
||||
|
||||
update: (params) ->
|
||||
displayLayerParams = {}
|
||||
|
||||
@ -391,6 +405,9 @@ class TextEditor extends Model
|
||||
@disposables.add @displayLayer.onDidChangeSync (e) =>
|
||||
@mergeIntersectingSelections()
|
||||
@emitter.emit 'did-change', e
|
||||
@disposables.add @displayLayer.onDidReset =>
|
||||
@mergeIntersectingSelections()
|
||||
@emitter.emit 'did-change', {}
|
||||
|
||||
destroyed: ->
|
||||
@disposables.dispose()
|
||||
@ -705,7 +722,7 @@ class TextEditor extends Model
|
||||
suppressCursorCreation: true,
|
||||
tabLength: @tokenizedBuffer.getTabLength(),
|
||||
@firstVisibleScreenRow, @firstVisibleScreenColumn,
|
||||
@clipboard, @assert, displayLayer, grammar: @getGrammar(),
|
||||
@assert, displayLayer, grammar: @getGrammar(),
|
||||
@autoWidth, @autoHeight
|
||||
})
|
||||
|
||||
@ -907,6 +924,8 @@ class TextEditor extends Model
|
||||
# editor. This accounts for folds.
|
||||
getScreenLineCount: -> @displayLayer.getScreenLineCount()
|
||||
|
||||
getApproximateScreenLineCount: -> @displayLayer.getApproximateScreenLineCount()
|
||||
|
||||
# Essential: Returns a {Number} representing the last zero-indexed buffer row
|
||||
# number of the editor.
|
||||
getLastBufferRow: -> @buffer.getLastRow()
|
||||
@ -953,7 +972,6 @@ class TextEditor extends Model
|
||||
tokens
|
||||
|
||||
screenLineForScreenRow: (screenRow) ->
|
||||
return if screenRow < 0 or screenRow > @getLastScreenRow()
|
||||
@displayLayer.getScreenLines(screenRow, screenRow + 1)[0]
|
||||
|
||||
bufferRowForScreenRow: (screenRow) ->
|
||||
@ -971,10 +989,14 @@ class TextEditor extends Model
|
||||
|
||||
getRightmostScreenPosition: -> @displayLayer.getRightmostScreenPosition()
|
||||
|
||||
getApproximateRightmostScreenPosition: -> @displayLayer.getApproximateRightmostScreenPosition()
|
||||
|
||||
getMaxScreenLineLength: -> @getRightmostScreenPosition().column
|
||||
|
||||
getLongestScreenRow: -> @getRightmostScreenPosition().row
|
||||
|
||||
getApproximateLongestScreenRow: -> @getApproximateRightmostScreenPosition().row
|
||||
|
||||
lineLengthForScreenRow: (screenRow) -> @displayLayer.lineLengthForScreenRow(screenRow)
|
||||
|
||||
# Returns the range for the given buffer row.
|
||||
@ -2718,7 +2740,7 @@ class TextEditor extends Model
|
||||
# Returns the new {Selection}.
|
||||
addSelection: (marker, options={}) ->
|
||||
cursor = @addCursor(marker)
|
||||
selection = new Selection(Object.assign({editor: this, marker, cursor, @clipboard}, options))
|
||||
selection = new Selection(Object.assign({editor: this, marker, cursor}, options))
|
||||
@selections.push(selection)
|
||||
selectionBufferRange = selection.getBufferRange()
|
||||
@mergeIntersectingSelections(preserveFolds: options.preserveFolds)
|
||||
@ -2865,7 +2887,7 @@ class TextEditor extends Model
|
||||
# whitespace.
|
||||
usesSoftTabs: ->
|
||||
for bufferRow in [0..@buffer.getLastRow()]
|
||||
continue if @tokenizedBuffer.tokenizedLineForRow(bufferRow).isComment()
|
||||
continue if @tokenizedBuffer.tokenizedLines[bufferRow]?.isComment()
|
||||
|
||||
line = @buffer.lineForRow(bufferRow)
|
||||
return true if line[0] is ' '
|
||||
@ -3127,7 +3149,7 @@ class TextEditor extends Model
|
||||
#
|
||||
# * `options` (optional) See {Selection::insertText}.
|
||||
pasteText: (options={}) ->
|
||||
{text: clipboardText, metadata} = @clipboard.readWithMetadata()
|
||||
{text: clipboardText, metadata} = @constructor.clipboard.readWithMetadata()
|
||||
return false unless @emitWillInsertTextEvent(clipboardText)
|
||||
|
||||
metadata ?= {}
|
||||
|
@ -36,7 +36,6 @@ class TokenizedBuffer extends Model
|
||||
@tokenIterator = new TokenIterator(this)
|
||||
|
||||
@disposables.add @buffer.registerTextDecorationLayer(this)
|
||||
@rootScopeDescriptor = new ScopeDescriptor(scopes: ['text.plain'])
|
||||
|
||||
@setGrammar(grammar ? NullGrammar)
|
||||
|
||||
@ -95,14 +94,17 @@ class TokenizedBuffer extends Model
|
||||
false
|
||||
|
||||
retokenizeLines: ->
|
||||
lastRow = @buffer.getLastRow()
|
||||
@fullyTokenized = false
|
||||
@tokenizedLines = new Array(lastRow + 1)
|
||||
@tokenizedLines = new Array(@buffer.getLineCount())
|
||||
@invalidRows = []
|
||||
@invalidateRow(0)
|
||||
if @largeFileMode or @grammar.name is 'Null Grammar'
|
||||
@markTokenizationComplete()
|
||||
else
|
||||
@invalidateRow(0)
|
||||
|
||||
setVisible: (@visible) ->
|
||||
@tokenizeInBackground() if @visible
|
||||
if @visible and @grammar.name isnt 'Null Grammar' and not @largeFileMode
|
||||
@tokenizeInBackground()
|
||||
|
||||
getTabLength: -> @tabLength
|
||||
|
||||
@ -117,12 +119,6 @@ class TokenizedBuffer extends Model
|
||||
@tokenizeNextChunk() if @isAlive() and @buffer.isAlive()
|
||||
|
||||
tokenizeNextChunk: ->
|
||||
# Short circuit null grammar which can just use the placeholder tokens
|
||||
if (@grammar.name is 'Null Grammar') and @firstInvalidRow()?
|
||||
@invalidRows = []
|
||||
@markTokenizationComplete()
|
||||
return
|
||||
|
||||
rowsRemaining = @chunkSize
|
||||
|
||||
while @firstInvalidRow()? and rowsRemaining > 0
|
||||
@ -167,8 +163,6 @@ class TokenizedBuffer extends Model
|
||||
return
|
||||
|
||||
invalidateRow: (row) ->
|
||||
return if @largeFileMode
|
||||
|
||||
@invalidRows.push(row)
|
||||
@invalidRows.sort (a, b) -> a - b
|
||||
@tokenizeInBackground()
|
||||
@ -189,18 +183,19 @@ class TokenizedBuffer extends Model
|
||||
start = oldRange.start.row
|
||||
end = oldRange.end.row
|
||||
delta = newRange.end.row - oldRange.end.row
|
||||
oldLineCount = oldRange.end.row - oldRange.start.row + 1
|
||||
newLineCount = newRange.end.row - newRange.start.row + 1
|
||||
|
||||
@updateInvalidRows(start, end, delta)
|
||||
previousEndStack = @stackForRow(end) # used in spill detection below
|
||||
if @largeFileMode or @grammar is NullGrammar
|
||||
newTokenizedLines = @buildPlaceholderTokenizedLinesForRows(start, end + delta)
|
||||
if @largeFileMode or @grammar.name is 'Null Grammar'
|
||||
_.spliceWithArray(@tokenizedLines, start, oldLineCount, new Array(newLineCount))
|
||||
else
|
||||
newTokenizedLines = @buildTokenizedLinesForRows(start, end + delta, @stackForRow(start - 1), @openScopesForRow(start))
|
||||
_.spliceWithArray(@tokenizedLines, start, end - start + 1, newTokenizedLines)
|
||||
|
||||
newEndStack = @stackForRow(end + delta)
|
||||
if newEndStack and not _.isEqual(newEndStack, previousEndStack)
|
||||
@invalidateRow(end + delta + 1)
|
||||
_.spliceWithArray(@tokenizedLines, start, oldLineCount, newTokenizedLines)
|
||||
newEndStack = @stackForRow(end + delta)
|
||||
if newEndStack and not _.isEqual(newEndStack, previousEndStack)
|
||||
@invalidateRow(end + delta + 1)
|
||||
|
||||
isFoldableAtRow: (row) ->
|
||||
if @largeFileMode
|
||||
@ -211,46 +206,39 @@ class TokenizedBuffer extends Model
|
||||
# Returns a {Boolean} indicating whether the given buffer row starts
|
||||
# a a foldable row range due to the code's indentation patterns.
|
||||
isFoldableCodeAtRow: (row) ->
|
||||
# Investigating an exception that's occurring here due to the line being
|
||||
# undefined. This should paper over the problem but we want to figure out
|
||||
# what is happening:
|
||||
tokenizedLine = @tokenizedLineForRow(row)
|
||||
@assert tokenizedLine?, "TokenizedLine is undefined", (error) =>
|
||||
error.metadata = {
|
||||
row: row
|
||||
rowCount: @tokenizedLines.length
|
||||
tokenizedBufferChangeCount: @changeCount
|
||||
bufferChangeCount: @buffer.changeCount
|
||||
}
|
||||
|
||||
return false unless tokenizedLine?
|
||||
|
||||
return false if @buffer.isRowBlank(row) or tokenizedLine.isComment()
|
||||
nextRow = @buffer.nextNonBlankRow(row)
|
||||
return false unless nextRow?
|
||||
|
||||
@indentLevelForRow(nextRow) > @indentLevelForRow(row)
|
||||
if 0 <= row <= @buffer.getLastRow()
|
||||
nextRow = @buffer.nextNonBlankRow(row)
|
||||
tokenizedLine = @tokenizedLines[row]
|
||||
if @buffer.isRowBlank(row) or tokenizedLine?.isComment() or not nextRow?
|
||||
false
|
||||
else
|
||||
@indentLevelForRow(nextRow) > @indentLevelForRow(row)
|
||||
else
|
||||
false
|
||||
|
||||
isFoldableCommentAtRow: (row) ->
|
||||
previousRow = row - 1
|
||||
nextRow = row + 1
|
||||
return false if nextRow > @buffer.getLastRow()
|
||||
|
||||
(row is 0 or not @tokenizedLineForRow(previousRow).isComment()) and
|
||||
@tokenizedLineForRow(row).isComment() and
|
||||
@tokenizedLineForRow(nextRow).isComment()
|
||||
if nextRow > @buffer.getLastRow()
|
||||
false
|
||||
else
|
||||
Boolean(
|
||||
not (@tokenizedLines[previousRow]?.isComment()) and
|
||||
@tokenizedLines[row]?.isComment() and
|
||||
@tokenizedLines[nextRow]?.isComment()
|
||||
)
|
||||
|
||||
buildTokenizedLinesForRows: (startRow, endRow, startingStack, startingopenScopes) ->
|
||||
ruleStack = startingStack
|
||||
openScopes = startingopenScopes
|
||||
stopTokenizingAt = startRow + @chunkSize
|
||||
tokenizedLines = for row in [startRow..endRow]
|
||||
tokenizedLines = for row in [startRow..endRow] by 1
|
||||
if (ruleStack or row is 0) and row < stopTokenizingAt
|
||||
tokenizedLine = @buildTokenizedLineForRow(row, ruleStack, openScopes)
|
||||
ruleStack = tokenizedLine.ruleStack
|
||||
openScopes = @scopesFromTags(openScopes, tokenizedLine.tags)
|
||||
else
|
||||
tokenizedLine = @buildPlaceholderTokenizedLineForRow(row, openScopes)
|
||||
tokenizedLine = undefined
|
||||
tokenizedLine
|
||||
|
||||
if endRow >= stopTokenizingAt
|
||||
@ -259,19 +247,6 @@ class TokenizedBuffer extends Model
|
||||
|
||||
tokenizedLines
|
||||
|
||||
buildPlaceholderTokenizedLinesForRows: (startRow, endRow) ->
|
||||
@buildPlaceholderTokenizedLineForRow(row) for row in [startRow..endRow] by 1
|
||||
|
||||
buildPlaceholderTokenizedLineForRow: (row) ->
|
||||
if @grammar isnt NullGrammar
|
||||
openScopes = [@grammar.startIdForScope(@grammar.scopeName)]
|
||||
else
|
||||
openScopes = []
|
||||
text = @buffer.lineForRow(row)
|
||||
tags = [text.length]
|
||||
lineEnding = @buffer.lineEndingForRow(row)
|
||||
new TokenizedLine({openScopes, text, tags, lineEnding, @tokenIterator})
|
||||
|
||||
buildTokenizedLineForRow: (row, ruleStack, openScopes) ->
|
||||
@buildTokenizedLineForRowWithText(row, @buffer.lineForRow(row), ruleStack, openScopes)
|
||||
|
||||
@ -281,8 +256,14 @@ class TokenizedBuffer extends Model
|
||||
new TokenizedLine({openScopes, text, tags, ruleStack, lineEnding, @tokenIterator})
|
||||
|
||||
tokenizedLineForRow: (bufferRow) ->
|
||||
if 0 <= bufferRow < @tokenizedLines.length
|
||||
@tokenizedLines[bufferRow] ?= @buildPlaceholderTokenizedLineForRow(bufferRow)
|
||||
if 0 <= bufferRow <= @buffer.getLastRow()
|
||||
if tokenizedLine = @tokenizedLines[bufferRow]
|
||||
tokenizedLine
|
||||
else
|
||||
text = @buffer.lineForRow(bufferRow)
|
||||
lineEnding = @buffer.lineEndingForRow(bufferRow)
|
||||
tags = [@grammar.startIdForScope(@grammar.scopeName), text.length, @grammar.endIdForScope(@grammar.scopeName)]
|
||||
@tokenizedLines[bufferRow] = new TokenizedLine({openScopes: [], text, tags, lineEnding, @tokenIterator})
|
||||
|
||||
tokenizedLinesForRows: (startRow, endRow) ->
|
||||
for row in [startRow..endRow] by 1
|
||||
@ -292,8 +273,7 @@ class TokenizedBuffer extends Model
|
||||
@tokenizedLines[bufferRow]?.ruleStack
|
||||
|
||||
openScopesForRow: (bufferRow) ->
|
||||
if bufferRow > 0
|
||||
precedingLine = @tokenizedLineForRow(bufferRow - 1)
|
||||
if precedingLine = @tokenizedLines[bufferRow - 1]
|
||||
@scopesFromTags(precedingLine.openScopes, precedingLine.tags)
|
||||
else
|
||||
[]
|
||||
@ -446,7 +426,7 @@ class TokenizedBuffer extends Model
|
||||
|
||||
logLines: (start=0, end=@buffer.getLastRow()) ->
|
||||
for row in [start..end]
|
||||
line = @tokenizedLineForRow(row).text
|
||||
line = @tokenizedLines[row].text
|
||||
console.log row, line, line.length
|
||||
return
|
||||
|
||||
|
@ -2,7 +2,7 @@ _ = require 'underscore-plus'
|
||||
{Disposable, CompositeDisposable} = require 'event-kit'
|
||||
Tooltip = null
|
||||
|
||||
# Essential: Associates tooltips with HTML elements or selectors.
|
||||
# Essential: Associates tooltips with HTML elements.
|
||||
#
|
||||
# You can get the `TooltipManager` via `atom.tooltips`.
|
||||
#
|
||||
@ -46,25 +46,55 @@ Tooltip = null
|
||||
module.exports =
|
||||
class TooltipManager
|
||||
defaults:
|
||||
delay:
|
||||
show: 1000
|
||||
hide: 100
|
||||
trigger: 'hover'
|
||||
container: 'body'
|
||||
html: true
|
||||
placement: 'auto top'
|
||||
viewportPadding: 2
|
||||
|
||||
constructor: ({@keymapManager}) ->
|
||||
hoverDefaults:
|
||||
{delay: {show: 1000, hide: 100}}
|
||||
|
||||
constructor: ({@keymapManager, @viewRegistry}) ->
|
||||
|
||||
# Essential: Add a tooltip to the given element.
|
||||
#
|
||||
# * `target` An `HTMLElement`
|
||||
# * `options` See http://getbootstrap.com/javascript/#tooltips-options for a
|
||||
# full list of options. You can also supply the following additional options:
|
||||
# * `options` An object with one or more of the following options:
|
||||
# * `title` A {String} or {Function} to use for the text in the tip. If
|
||||
# given a function, `this` will be set to the `target` element.
|
||||
# * `trigger` A {String} that's the same as Bootstrap 'click | hover | focus
|
||||
# | manual', except 'manual' will show the tooltip immediately.
|
||||
# a function is passed, `this` will be set to the `target` element. This
|
||||
# option is mutually exclusive with the `item` option.
|
||||
# * `html` A {Boolean} affecting the interpetation of the `title` option.
|
||||
# If `true` (the default), the `title` string will be interpreted as HTML.
|
||||
# Otherwise it will be interpreted as plain text.
|
||||
# * `item` A view (object with an `.element` property) or a DOM element
|
||||
# containing custom content for the tooltip. This option is mutually
|
||||
# exclusive with the `title` option.
|
||||
# * `class` A {String} with a class to apply to the tooltip element to
|
||||
# enable custom styling.
|
||||
# * `placement` A {String} or {Function} returning a string to indicate
|
||||
# the position of the tooltip relative to `element`. Can be `'top'`,
|
||||
# `'bottom'`, `'left'`, `'right'`, or `'auto'`. When `'auto'` is
|
||||
# specified, it will dynamically reorient the tooltip. For example, if
|
||||
# placement is `'auto left'`, the tooltip will display to the left when
|
||||
# possible, otherwise it will display right.
|
||||
# When a function is used to determine the placement, it is called with
|
||||
# the tooltip DOM node as its first argument and the triggering element
|
||||
# DOM node as its second. The `this` context is set to the tooltip
|
||||
# instance.
|
||||
# * `trigger` A {String} indicating how the tooltip should be displayed.
|
||||
# Choose from one of the following options:
|
||||
# * `'hover'` Show the tooltip when the mouse hovers over the element.
|
||||
# This is the default.
|
||||
# * `'click'` Show the tooltip when the element is clicked. The tooltip
|
||||
# will be hidden after clicking the element again or anywhere else
|
||||
# outside of the tooltip itself.
|
||||
# * `'focus'` Show the tooltip when the element is focused.
|
||||
# * `'manual'` Show the tooltip immediately and only hide it when the
|
||||
# returned disposable is disposed.
|
||||
# * `delay` An object specifying the show and hide delay in milliseconds.
|
||||
# Defaults to `{show: 1000, hide: 100}` if the `trigger` is `hover` and
|
||||
# otherwise defaults to `0` for both values.
|
||||
# * `keyBindingCommand` A {String} containing a command name. If you specify
|
||||
# this option and a key binding exists that matches the command, it will
|
||||
# be appended to the title or rendered alone if no title is specified.
|
||||
@ -92,7 +122,12 @@ class TooltipManager
|
||||
else if keystroke?
|
||||
options.title = getKeystroke(bindings)
|
||||
|
||||
tooltip = new Tooltip(target, _.defaults(options, @defaults))
|
||||
delete options.selector
|
||||
options = _.defaults(options, @defaults)
|
||||
if options.trigger is 'hover'
|
||||
options = _.defaults(options, @hoverDefaults)
|
||||
|
||||
tooltip = new Tooltip(target, options, @viewRegistry)
|
||||
|
||||
hideTooltip = ->
|
||||
tooltip.leave(currentTarget: target)
|
||||
|
@ -7,13 +7,14 @@ const listen = require('./delegated-listener')
|
||||
// This tooltip class is derived from Bootstrap 3, but modified to not require
|
||||
// jQuery, which is an expensive dependency we want to eliminate.
|
||||
|
||||
var Tooltip = function (element, options) {
|
||||
var Tooltip = function (element, options, viewRegistry) {
|
||||
this.options = null
|
||||
this.enabled = null
|
||||
this.timeout = null
|
||||
this.hoverState = null
|
||||
this.element = null
|
||||
this.inState = null
|
||||
this.viewRegistry = viewRegistry
|
||||
|
||||
this.init(element, options)
|
||||
}
|
||||
@ -64,6 +65,14 @@ Tooltip.prototype.init = function (element, options) {
|
||||
|
||||
if (trigger === 'click') {
|
||||
this.disposables.add(listen(this.element, 'click', this.options.selector, this.toggle.bind(this)))
|
||||
this.hideOnClickOutsideOfTooltip = (event) => {
|
||||
const tooltipElement = this.getTooltipElement()
|
||||
if (tooltipElement === event.target) return
|
||||
if (tooltipElement.contains(event.target)) return
|
||||
if (this.element === event.target) return
|
||||
if (this.element.contains(event.target)) return
|
||||
this.hide()
|
||||
}
|
||||
} else if (trigger === 'manual') {
|
||||
this.show()
|
||||
} else {
|
||||
@ -182,8 +191,11 @@ Tooltip.prototype.leave = function (event) {
|
||||
|
||||
Tooltip.prototype.show = function () {
|
||||
if (this.hasContent() && this.enabled) {
|
||||
var tip = this.getTooltipElement()
|
||||
if (this.hideOnClickOutsideOfTooltip) {
|
||||
window.addEventListener('click', this.hideOnClickOutsideOfTooltip, true)
|
||||
}
|
||||
|
||||
var tip = this.getTooltipElement()
|
||||
var tipId = this.getUID('tooltip')
|
||||
|
||||
this.setContent()
|
||||
@ -294,19 +306,33 @@ Tooltip.prototype.replaceArrow = function (delta, dimension, isVertical) {
|
||||
|
||||
Tooltip.prototype.setContent = function () {
|
||||
var tip = this.getTooltipElement()
|
||||
var title = this.getTitle()
|
||||
|
||||
if (this.options.class) {
|
||||
tip.classList.add(this.options.class)
|
||||
}
|
||||
|
||||
var inner = tip.querySelector('.tooltip-inner')
|
||||
if (this.options.html) {
|
||||
inner.innerHTML = title
|
||||
if (this.options.item) {
|
||||
inner.appendChild(this.viewRegistry.getView(this.options.item))
|
||||
} else {
|
||||
inner.textContent = title
|
||||
var title = this.getTitle()
|
||||
if (this.options.html) {
|
||||
inner.innerHTML = title
|
||||
} else {
|
||||
inner.textContent = title
|
||||
}
|
||||
}
|
||||
|
||||
tip.classList.remove('fade', 'in', 'top', 'bottom', 'left', 'right')
|
||||
}
|
||||
|
||||
Tooltip.prototype.hide = function (callback) {
|
||||
this.inState = {}
|
||||
|
||||
if (this.hideOnClickOutsideOfTooltip) {
|
||||
window.removeEventListener('click', this.hideOnClickOutsideOfTooltip, true)
|
||||
}
|
||||
|
||||
this.tip && this.tip.classList.remove('in')
|
||||
|
||||
if (this.hoverState !== 'in') this.tip && this.tip.remove()
|
||||
@ -328,7 +354,7 @@ Tooltip.prototype.fixTitle = function () {
|
||||
}
|
||||
|
||||
Tooltip.prototype.hasContent = function () {
|
||||
return this.getTitle()
|
||||
return this.getTitle() || this.options.item
|
||||
}
|
||||
|
||||
Tooltip.prototype.getCalculatedOffset = function (placement, pos, actualWidth, actualHeight) {
|
||||
@ -436,7 +462,7 @@ Tooltip.prototype.destroy = function () {
|
||||
Tooltip.prototype.getDelegateComponent = function (element) {
|
||||
var component = tooltipComponentsByElement.get(element)
|
||||
if (!component) {
|
||||
component = new Tooltip(element, this.getDelegateOptions())
|
||||
component = new Tooltip(element, this.getDelegateOptions(), this.viewRegistry)
|
||||
tooltipComponentsByElement.set(element, component)
|
||||
}
|
||||
return component
|
||||
|
@ -142,4 +142,13 @@ class WorkspaceElement extends HTMLElement
|
||||
|
||||
ipcRenderer.send('run-package-specs', specPath)
|
||||
|
||||
runBenchmarks: ->
|
||||
if activePath = @workspace.getActivePaneItem()?.getPath?()
|
||||
[projectPath] = @project.relativizePath(activePath)
|
||||
else
|
||||
[projectPath] = @project.getPaths()
|
||||
|
||||
if projectPath
|
||||
ipcRenderer.send('run-benchmarks', path.join(projectPath, 'benchmarks'))
|
||||
|
||||
module.exports = WorkspaceElement = document.registerElement 'atom-workspace', prototype: WorkspaceElement.prototype
|
||||
|
@ -28,7 +28,7 @@ class Workspace extends Model
|
||||
|
||||
{
|
||||
@packageManager, @config, @project, @grammarRegistry, @notificationManager,
|
||||
@clipboard, @viewRegistry, @grammarRegistry, @applicationDelegate, @assert,
|
||||
@viewRegistry, @grammarRegistry, @applicationDelegate, @assert,
|
||||
@deserializerManager, @textEditorRegistry
|
||||
} = params
|
||||
|
||||
|