Merge back master

This commit is contained in:
abe33
2015-11-04 18:26:27 +01:00
18 changed files with 936 additions and 416 deletions

View File

@@ -18,6 +18,7 @@
"atom-keymap": "^6.1.0",
"babel-core": "^5.8.21",
"bootstrap": "^3.3.4",
"cached-run-in-this-context": "0.4.0",
"clear-cut": "^2.0.1",
"coffee-script": "1.8.0",
"color": "^0.7.3",
@@ -86,7 +87,7 @@
"dev-live-reload": "0.47.0",
"encoding-selector": "0.21.0",
"exception-reporting": "0.37.0",
"find-and-replace": "0.189.0",
"find-and-replace": "0.190.0",
"fuzzy-finder": "0.93.0",
"git-diff": "0.57.0",
"go-to-line": "0.30.0",
@@ -108,9 +109,9 @@
"status-bar": "0.80.0",
"styleguide": "0.45.0",
"symbols-view": "0.110.0",
"tabs": "0.87.0",
"tabs": "0.88.0",
"timecop": "0.33.0",
"tree-view": "0.195.0",
"tree-view": "0.196.0",
"update-package-dependencies": "0.10.0",
"welcome": "0.32.0",
"whitespace": "0.32.0",
@@ -119,7 +120,7 @@
"language-clojure": "0.18.0",
"language-coffee-script": "0.43.0",
"language-csharp": "0.11.0",
"language-css": "0.34.0",
"language-css": "0.35.0",
"language-gfm": "0.81.0",
"language-git": "0.10.0",
"language-go": "0.39.0",

View File

@@ -215,6 +215,17 @@ describe "AtomEnvironment", ->
expect(atom.project.getPaths()).toEqual(initialPaths)
describe "::unloadEditorWindow()", ->
it "saves the BlobStore so it can be loaded after reload", ->
configDirPath = temp.mkdirSync()
fakeBlobStore = jasmine.createSpyObj("blob store", ["save"])
atomEnvironment = new AtomEnvironment({applicationDelegate: atom.applicationDelegate, enablePersistence: true, configDirPath, blobStore: fakeBlobStore, window, document})
atomEnvironment.unloadEditorWindow()
expect(fakeBlobStore.save).toHaveBeenCalled()
atomEnvironment.destroy()
it "saves the serialized state of the window so it can be deserialized after reload", ->
atomEnvironment = new AtomEnvironment({applicationDelegate: atom.applicationDelegate, window, document})
spyOn(atomEnvironment, 'saveStateSync')

View File

@@ -0,0 +1,68 @@
path = require "path"
fs = require 'fs-plus'
temp = require "temp"
AtomPortable = require "../src/browser/atom-portable"
portableModeCommonPlatformBehavior = (platform) ->
describe "with ATOM_HOME environment variable", ->
it "returns false", ->
expect(AtomPortable.isPortableInstall(platform, "C:\\some\\path")).toBe false
describe "without ATOM_HOME environment variable", ->
environmentAtomHome = undefined
portableAtomHomePath = path.join(path.dirname(process.execPath), "..", ".atom")
portableAtomHomeNaturallyExists = fs.existsSync(portableAtomHomePath)
portableAtomHomeBackupPath = "#{portableAtomHomePath}.temp"
beforeEach ->
fs.renameSync(portableAtomHomePath, portableAtomHomeBackupPath) if fs.existsSync(portableAtomHomePath)
afterEach ->
if portableAtomHomeNaturallyExists
fs.renameSync(portableAtomHomeBackupPath, portableAtomHomePath) if not fs.existsSync(portableAtomHomePath)
else
fs.removeSync(portableAtomHomePath) if fs.existsSync(portableAtomHomePath)
fs.removeSync(portableAtomHomeBackupPath) if fs.existsSync(portableAtomHomeBackupPath)
describe "with .atom directory sibling to exec", ->
beforeEach ->
fs.mkdirSync(portableAtomHomePath) if not fs.existsSync(portableAtomHomePath)
describe "without .atom directory sibling to exec", ->
beforeEach ->
fs.removeSync(portableAtomHomePath) if fs.existsSync(portableAtomHomePath)
it "returns false", ->
expect(AtomPortable.isPortableInstall(platform, environmentAtomHome)).toBe false
describe "Set Portable Mode on #win32", ->
portableAtomHomePath = path.join(path.dirname(process.execPath), "..", ".atom")
portableAtomHomeNaturallyExists = fs.existsSync(portableAtomHomePath)
portableAtomHomeBackupPath = "#{portableAtomHomePath}.temp"
beforeEach ->
fs.renameSync(portableAtomHomePath, portableAtomHomeBackupPath) if fs.existsSync(portableAtomHomePath)
afterEach ->
if portableAtomHomeNaturallyExists
fs.renameSync(portableAtomHomeBackupPath, portableAtomHomePath) if not fs.existsSync(portableAtomHomePath)
else
fs.removeSync(portableAtomHomePath) if fs.existsSync(portableAtomHomePath)
fs.removeSync(portableAtomHomeBackupPath) if fs.existsSync(portableAtomHomeBackupPath)
it "creates a portable home directory", ->
expect(fs.existsSync(portableAtomHomePath)).toBe false
AtomPortable.setPortable(process.env.ATOM_HOME)
expect(fs.existsSync(portableAtomHomePath)).toBe true
describe "Check for Portable Mode", ->
describe "Windows", ->
portableModeCommonPlatformBehavior "win32"
describe "Mac", ->
it "returns false", ->
expect(AtomPortable.isPortableInstall("darwin", "darwin")).toBe false
describe "Linux", ->
portableModeCommonPlatformBehavior "linux"

View File

@@ -0,0 +1,69 @@
temp = require 'temp'
FileSystemBlobStore = require '../src/file-system-blob-store'
describe "FileSystemBlobStore", ->
[storageDirectory, blobStore] = []
beforeEach ->
storageDirectory = temp.path()
blobStore = FileSystemBlobStore.load(storageDirectory)
it "is empty when the file doesn't exist", ->
expect(blobStore.get("foo")).toBeUndefined()
expect(blobStore.get("bar")).toBeUndefined()
it "allows to read and write buffers from/to memory without persisting them", ->
blobStore.set("foo", new Buffer("foo"))
blobStore.set("bar", new Buffer("bar"))
expect(blobStore.get("foo")).toEqual(new Buffer("foo"))
expect(blobStore.get("bar")).toEqual(new Buffer("bar"))
it "persists buffers when saved and retrieves them on load, giving priority to in-memory ones", ->
blobStore.set("foo", new Buffer("foo"))
blobStore.set("bar", new Buffer("bar"))
blobStore.save()
blobStore = FileSystemBlobStore.load(storageDirectory)
expect(blobStore.get("foo")).toEqual(new Buffer("foo"))
expect(blobStore.get("bar")).toEqual(new Buffer("bar"))
blobStore.set("foo", new Buffer("changed"))
expect(blobStore.get("foo")).toEqual(new Buffer("changed"))
it "persists both in-memory and previously stored buffers when saved", ->
blobStore.set("foo", new Buffer("foo"))
blobStore.set("bar", new Buffer("bar"))
blobStore.save()
blobStore = FileSystemBlobStore.load(storageDirectory)
blobStore.set("bar", new Buffer("changed"))
blobStore.set("qux", new Buffer("qux"))
blobStore.save()
blobStore = FileSystemBlobStore.load(storageDirectory)
expect(blobStore.get("foo")).toEqual(new Buffer("foo"))
expect(blobStore.get("bar")).toEqual(new Buffer("changed"))
expect(blobStore.get("qux")).toEqual(new Buffer("qux"))
it "allows to delete keys from both memory and stored buffers", ->
blobStore.set("a", new Buffer("a"))
blobStore.set("b", new Buffer("b"))
blobStore.save()
blobStore = FileSystemBlobStore.load(storageDirectory)
blobStore.set("b", new Buffer("b"))
blobStore.set("c", new Buffer("c"))
blobStore.delete("b")
blobStore.delete("c")
blobStore.save()
blobStore = FileSystemBlobStore.load(storageDirectory)
expect(blobStore.get("a")).toEqual(new Buffer("a"))
expect(blobStore.get("b")).toBeUndefined()
expect(blobStore.get("c")).toBeUndefined()

1
spec/fixtures/native-cache/file-1.js vendored Normal file
View File

@@ -0,0 +1 @@
module.exports = function () { return 1; }

1
spec/fixtures/native-cache/file-2.js vendored Normal file
View File

@@ -0,0 +1 @@
module.exports = function () { return 2; }

1
spec/fixtures/native-cache/file-3.js vendored Normal file
View File

@@ -0,0 +1 @@
module.exports = function () { return 3; }

View File

@@ -0,0 +1,47 @@
describe "NativeCompileCache", ->
nativeCompileCache = require '../src/native-compile-cache'
[fakeCacheStore, cachedFiles] = []
beforeEach ->
cachedFiles = []
fakeCacheStore = jasmine.createSpyObj("cache store", ["set", "get", "has", "delete"])
nativeCompileCache.setCacheStore(fakeCacheStore)
nativeCompileCache.install()
it "writes and reads from the cache storage when requiring files", ->
fakeCacheStore.has.andReturn(false)
fakeCacheStore.set.andCallFake (filename, cacheBuffer) ->
cachedFiles.push({filename, cacheBuffer})
fn1 = require('./fixtures/native-cache/file-1')
fn2 = require('./fixtures/native-cache/file-2')
expect(cachedFiles.length).toBe(2)
expect(cachedFiles[0].filename).toBe(require.resolve('./fixtures/native-cache/file-1'))
expect(cachedFiles[0].cacheBuffer).toBeInstanceOf(Uint8Array)
expect(cachedFiles[0].cacheBuffer.length).toBeGreaterThan(0)
expect(fn1()).toBe(1)
expect(cachedFiles[1].filename).toBe(require.resolve('./fixtures/native-cache/file-2'))
expect(cachedFiles[1].cacheBuffer).toBeInstanceOf(Uint8Array)
expect(cachedFiles[1].cacheBuffer.length).toBeGreaterThan(0)
expect(fn2()).toBe(2)
fakeCacheStore.has.andReturn(true)
fakeCacheStore.get.andReturn(cachedFiles[0].cacheBuffer)
fakeCacheStore.set.reset()
fn1 = require('./fixtures/native-cache/file-1')
expect(fakeCacheStore.set).not.toHaveBeenCalled()
expect(fn1()).toBe(1)
it "deletes previously cached code when the cache is not valid", ->
fakeCacheStore.has.andReturn(true)
fakeCacheStore.get.andCallFake -> new Buffer("an invalid cache")
fn3 = require('./fixtures/native-cache/file-3')
expect(fakeCacheStore.delete).toHaveBeenCalledWith(require.resolve('./fixtures/native-cache/file-3'))
expect(fn3()).toBe(3)

View File

@@ -858,6 +858,185 @@ describe "TextEditor", ->
editor.moveToBeginningOfNextWord()
expect(editor.getCursorBufferPosition()).toEqual [11, 9]
describe ".moveToPreviousSubwordBoundary", ->
it "does not move the cursor when there is no previous subword boundary", ->
editor.setText('')
editor.moveToPreviousSubwordBoundary()
expect(editor.getCursorBufferPosition()).toEqual([0, 0])
it "stops at word and underscore boundaries", ->
editor.setText("sub_word \n")
editor.setCursorBufferPosition([0, 9])
editor.moveToPreviousSubwordBoundary()
expect(editor.getCursorBufferPosition()).toEqual([0, 8])
editor.moveToPreviousSubwordBoundary()
expect(editor.getCursorBufferPosition()).toEqual([0, 4])
editor.moveToPreviousSubwordBoundary()
expect(editor.getCursorBufferPosition()).toEqual([0, 0])
editor.setText(" word\n")
editor.setCursorBufferPosition([0, 3])
editor.moveToPreviousSubwordBoundary()
expect(editor.getCursorBufferPosition()).toEqual([0, 1])
it "stops at camelCase boundaries", ->
editor.setText(" getPreviousWord\n")
editor.setCursorBufferPosition([0, 16])
editor.moveToPreviousSubwordBoundary()
expect(editor.getCursorBufferPosition()).toEqual([0, 12])
editor.moveToPreviousSubwordBoundary()
expect(editor.getCursorBufferPosition()).toEqual([0, 4])
editor.moveToPreviousSubwordBoundary()
expect(editor.getCursorBufferPosition()).toEqual([0, 1])
it "skips consecutive non-word characters", ->
editor.setText("e, => \n")
editor.setCursorBufferPosition([0, 6])
editor.moveToPreviousSubwordBoundary()
expect(editor.getCursorBufferPosition()).toEqual([0, 3])
editor.moveToPreviousSubwordBoundary()
expect(editor.getCursorBufferPosition()).toEqual([0, 1])
it "skips consecutive uppercase characters", ->
editor.setText(" AAADF \n")
editor.setCursorBufferPosition([0, 7])
editor.moveToPreviousSubwordBoundary()
expect(editor.getCursorBufferPosition()).toEqual([0, 6])
editor.moveToPreviousSubwordBoundary()
expect(editor.getCursorBufferPosition()).toEqual([0, 1])
editor.setText("ALPhA\n")
editor.setCursorBufferPosition([0, 4])
editor.moveToPreviousSubwordBoundary()
expect(editor.getCursorBufferPosition()).toEqual([0, 2])
it "skips consecutive numbers", ->
editor.setText(" 88 \n")
editor.setCursorBufferPosition([0, 4])
editor.moveToPreviousSubwordBoundary()
expect(editor.getCursorBufferPosition()).toEqual([0, 3])
editor.moveToPreviousSubwordBoundary()
expect(editor.getCursorBufferPosition()).toEqual([0, 1])
it "works with multiple cursors", ->
editor.setText("curOp\ncursorOptions\n")
editor.setCursorBufferPosition([0, 8])
editor.addCursorAtBufferPosition([1, 13])
[cursor1, cursor2] = editor.getCursors()
editor.moveToPreviousSubwordBoundary()
expect(cursor1.getBufferPosition()).toEqual([0, 3])
expect(cursor2.getBufferPosition()).toEqual([1, 6])
it "works with non-English characters", ->
editor.setText("supåTøåst \n")
editor.setCursorBufferPosition([0, 9])
editor.moveToPreviousSubwordBoundary()
expect(editor.getCursorBufferPosition()).toEqual([0, 4])
editor.setText("supaÖast \n")
editor.setCursorBufferPosition([0, 8])
editor.moveToPreviousSubwordBoundary()
expect(editor.getCursorBufferPosition()).toEqual([0, 4])
describe ".moveToNextSubwordBoundary", ->
it "does not move the cursor when there is no next subword boundary", ->
editor.setText('')
editor.moveToNextSubwordBoundary()
expect(editor.getCursorBufferPosition()).toEqual([0, 0])
it "stops at word and underscore boundaries", ->
editor.setText(" sub_word \n")
editor.setCursorBufferPosition([0, 0])
editor.moveToNextSubwordBoundary()
expect(editor.getCursorBufferPosition()).toEqual([0, 1])
editor.moveToNextSubwordBoundary()
expect(editor.getCursorBufferPosition()).toEqual([0, 4])
editor.moveToNextSubwordBoundary()
expect(editor.getCursorBufferPosition()).toEqual([0, 9])
editor.setText("word \n")
editor.setCursorBufferPosition([0, 0])
editor.moveToNextSubwordBoundary()
expect(editor.getCursorBufferPosition()).toEqual([0, 4])
it "stops at camelCase boundaries", ->
editor.setText("getPreviousWord \n")
editor.setCursorBufferPosition([0, 0])
editor.moveToNextSubwordBoundary()
expect(editor.getCursorBufferPosition()).toEqual([0, 3])
editor.moveToNextSubwordBoundary()
expect(editor.getCursorBufferPosition()).toEqual([0, 11])
editor.moveToNextSubwordBoundary()
expect(editor.getCursorBufferPosition()).toEqual([0, 15])
it "skips consecutive non-word characters", ->
editor.setText(", => \n")
editor.setCursorBufferPosition([0, 0])
editor.moveToNextSubwordBoundary()
expect(editor.getCursorBufferPosition()).toEqual([0, 1])
editor.moveToNextSubwordBoundary()
expect(editor.getCursorBufferPosition()).toEqual([0, 4])
it "skips consecutive uppercase characters", ->
editor.setText(" AAADF \n")
editor.setCursorBufferPosition([0, 0])
editor.moveToNextSubwordBoundary()
expect(editor.getCursorBufferPosition()).toEqual([0, 1])
editor.moveToNextSubwordBoundary()
expect(editor.getCursorBufferPosition()).toEqual([0, 6])
editor.setText("ALPhA\n")
editor.setCursorBufferPosition([0, 0])
editor.moveToNextSubwordBoundary()
expect(editor.getCursorBufferPosition()).toEqual([0, 2])
it "skips consecutive numbers", ->
editor.setText(" 88 \n")
editor.setCursorBufferPosition([0, 0])
editor.moveToNextSubwordBoundary()
expect(editor.getCursorBufferPosition()).toEqual([0, 1])
editor.moveToNextSubwordBoundary()
expect(editor.getCursorBufferPosition()).toEqual([0, 3])
it "works with multiple cursors", ->
editor.setText("curOp\ncursorOptions\n")
editor.setCursorBufferPosition([0, 0])
editor.addCursorAtBufferPosition([1, 0])
[cursor1, cursor2] = editor.getCursors()
editor.moveToNextSubwordBoundary()
expect(cursor1.getBufferPosition()).toEqual([0, 3])
expect(cursor2.getBufferPosition()).toEqual([1, 6])
it "works with non-English characters", ->
editor.setText("supåTøåst \n")
editor.setCursorBufferPosition([0, 0])
editor.moveToNextSubwordBoundary()
expect(editor.getCursorBufferPosition()).toEqual([0, 4])
editor.setText("supaÖast \n")
editor.setCursorBufferPosition([0, 0])
editor.moveToNextSubwordBoundary()
expect(editor.getCursorBufferPosition()).toEqual([0, 4])
describe ".moveToBeginningOfNextParagraph()", ->
it "moves the cursor before the first line of the next paragraph", ->
editor.setCursorBufferPosition [0, 6]
@@ -1330,6 +1509,128 @@ describe "TextEditor", ->
expect(selection4.getBufferRange()).toEqual [[3, 30], [3, 31]]
expect(selection4.isReversed()).toBeFalsy()
describe ".selectToPreviousSubwordBoundary", ->
it "selects subwords", ->
editor.setText("")
editor.insertText("_word\n")
editor.insertText(" getPreviousWord\n")
editor.insertText("e, => \n")
editor.insertText(" 88 \n")
editor.setCursorBufferPosition([0, 5])
editor.addCursorAtBufferPosition([1, 7])
editor.addCursorAtBufferPosition([2, 5])
editor.addCursorAtBufferPosition([3, 3])
[selection1, selection2, selection3, selection4] = editor.getSelections()
editor.selectToPreviousSubwordBoundary()
expect(selection1.getBufferRange()).toEqual([[0, 1], [0, 5]])
expect(selection1.isReversed()).toBeTruthy()
expect(selection2.getBufferRange()).toEqual([[1, 4], [1, 7]])
expect(selection2.isReversed()).toBeTruthy()
expect(selection3.getBufferRange()).toEqual([[2, 3], [2, 5]])
expect(selection3.isReversed()).toBeTruthy()
expect(selection4.getBufferRange()).toEqual([[3, 1], [3, 3]])
expect(selection4.isReversed()).toBeTruthy()
describe ".selectToNextSubwordBoundary", ->
it "selects subwords", ->
editor.setText("")
editor.insertText("word_\n")
editor.insertText("getPreviousWord\n")
editor.insertText("e, => \n")
editor.insertText(" 88 \n")
editor.setCursorBufferPosition([0, 1])
editor.addCursorAtBufferPosition([1, 7])
editor.addCursorAtBufferPosition([2, 2])
editor.addCursorAtBufferPosition([3, 1])
[selection1, selection2, selection3, selection4] = editor.getSelections()
editor.selectToNextSubwordBoundary()
expect(selection1.getBufferRange()).toEqual([[0, 1], [0, 4]])
expect(selection1.isReversed()).toBeFalsy()
expect(selection2.getBufferRange()).toEqual([[1, 7], [1, 11]])
expect(selection2.isReversed()).toBeFalsy()
expect(selection3.getBufferRange()).toEqual([[2, 2], [2, 5]])
expect(selection3.isReversed()).toBeFalsy()
expect(selection4.getBufferRange()).toEqual([[3, 1], [3, 3]])
expect(selection4.isReversed()).toBeFalsy()
describe ".deleteToBeginningOfSubword", ->
it "deletes subwords", ->
editor.setText("")
editor.insertText("_word\n")
editor.insertText(" getPreviousWord\n")
editor.insertText("e, => \n")
editor.insertText(" 88 \n")
editor.setCursorBufferPosition([0, 5])
editor.addCursorAtBufferPosition([1, 7])
editor.addCursorAtBufferPosition([2, 5])
editor.addCursorAtBufferPosition([3, 3])
[cursor1, cursor2, cursor3, cursor4] = editor.getCursors()
editor.deleteToBeginningOfSubword()
expect(buffer.lineForRow(0)).toBe('_')
expect(buffer.lineForRow(1)).toBe(' getviousWord')
expect(buffer.lineForRow(2)).toBe('e, ')
expect(buffer.lineForRow(3)).toBe(' ')
expect(cursor1.getBufferPosition()).toEqual([0, 1])
expect(cursor2.getBufferPosition()).toEqual([1, 4])
expect(cursor3.getBufferPosition()).toEqual([2, 3])
expect(cursor4.getBufferPosition()).toEqual([3, 1])
editor.deleteToBeginningOfSubword()
expect(buffer.lineForRow(0)).toBe('')
expect(buffer.lineForRow(1)).toBe(' viousWord')
expect(buffer.lineForRow(2)).toBe('e ')
expect(buffer.lineForRow(3)).toBe(' ')
expect(cursor1.getBufferPosition()).toEqual([0, 0])
expect(cursor2.getBufferPosition()).toEqual([1, 1])
expect(cursor3.getBufferPosition()).toEqual([2, 1])
expect(cursor4.getBufferPosition()).toEqual([3, 0])
editor.deleteToBeginningOfSubword()
expect(buffer.lineForRow(0)).toBe('')
expect(buffer.lineForRow(1)).toBe('viousWord')
expect(buffer.lineForRow(2)).toBe(' ')
expect(buffer.lineForRow(3)).toBe('')
expect(cursor1.getBufferPosition()).toEqual([0, 0])
expect(cursor2.getBufferPosition()).toEqual([1, 0])
expect(cursor3.getBufferPosition()).toEqual([2, 0])
expect(cursor4.getBufferPosition()).toEqual([2, 1])
describe ".deleteToEndOfSubword", ->
it "deletes subwords", ->
editor.setText("")
editor.insertText("word_\n")
editor.insertText("getPreviousWord \n")
editor.insertText("e, => \n")
editor.insertText(" 88 \n")
editor.setCursorBufferPosition([0, 0])
editor.addCursorAtBufferPosition([1, 0])
editor.addCursorAtBufferPosition([2, 2])
editor.addCursorAtBufferPosition([3, 0])
[cursor1, cursor2, cursor3, cursor4] = editor.getCursors()
editor.deleteToEndOfSubword()
expect(buffer.lineForRow(0)).toBe('_')
expect(buffer.lineForRow(1)).toBe('PreviousWord ')
expect(buffer.lineForRow(2)).toBe('e, ')
expect(buffer.lineForRow(3)).toBe('88 ')
expect(cursor1.getBufferPosition()).toEqual([0, 0])
expect(cursor2.getBufferPosition()).toEqual([1, 0])
expect(cursor3.getBufferPosition()).toEqual([2, 2])
expect(cursor4.getBufferPosition()).toEqual([3, 0])
editor.deleteToEndOfSubword()
expect(buffer.lineForRow(0)).toBe('')
expect(buffer.lineForRow(1)).toBe('Word ')
expect(buffer.lineForRow(2)).toBe('e,')
expect(buffer.lineForRow(3)).toBe(' ')
expect(cursor1.getBufferPosition()).toEqual([0, 0])
expect(cursor2.getBufferPosition()).toEqual([1, 0])
expect(cursor3.getBufferPosition()).toEqual([2, 2])
expect(cursor4.getBufferPosition()).toEqual([3, 0])
describe ".selectWordsContainingCursors()", ->
describe "when the cursor is inside a word", ->
it "selects the entire word", ->
@@ -4849,6 +5150,36 @@ describe "TextEditor", ->
"""
expect(editor.getSelectedBufferRange()).toEqual [[13, 0], [14, 2]]
describe ".moveLineUp()", ->
it "moves the line under the cursor up", ->
editor.setCursorBufferPosition([1, 0])
editor.moveLineUp()
expect(editor.getTextInBufferRange([[0, 0], [0, 30]])).toBe " var sort = function(items) {"
expect(editor.indentationForBufferRow(0)).toBe 1
expect(editor.indentationForBufferRow(1)).toBe 0
it "updates the line's indentation when the editor.autoIndent setting is true", ->
atom.config.set('editor.autoIndent', true)
editor.setCursorBufferPosition([1, 0])
editor.moveLineUp()
expect(editor.indentationForBufferRow(0)).toBe 0
expect(editor.indentationForBufferRow(1)).toBe 0
describe ".moveLineDown()", ->
it "moves the line under the cursor down", ->
editor.setCursorBufferPosition([0, 0])
editor.moveLineDown()
expect(editor.getTextInBufferRange([[1, 0], [1, 31]])).toBe "var quicksort = function () {"
expect(editor.indentationForBufferRow(0)).toBe 1
expect(editor.indentationForBufferRow(1)).toBe 0
it "updates the line's indentation when the editor.autoIndent setting is true", ->
atom.config.set('editor.autoIndent', true)
editor.setCursorBufferPosition([0, 0])
editor.moveLineDown()
expect(editor.indentationForBufferRow(0)).toBe 1
expect(editor.indentationForBufferRow(1)).toBe 2
describe ".shouldPromptToSave()", ->
it "returns false when an edit session's buffer is in use by more than one session", ->
jasmine.unspy(editor, 'shouldPromptToSave')
@@ -5150,307 +5481,6 @@ describe "TextEditor", ->
waitsForPromise -> editor.checkoutHeadRevision()
describe ".moveToPreviousSubwordBoundary", ->
it "does not move the cursor when there is no previous subword boundary", ->
editor.setText('')
editor.moveToPreviousSubwordBoundary()
expect(editor.getCursorBufferPosition()).toEqual([0, 0])
it "stops at word and underscore boundaries", ->
editor.setText("sub_word \n")
editor.setCursorBufferPosition([0, 9])
editor.moveToPreviousSubwordBoundary()
expect(editor.getCursorBufferPosition()).toEqual([0, 8])
editor.moveToPreviousSubwordBoundary()
expect(editor.getCursorBufferPosition()).toEqual([0, 4])
editor.moveToPreviousSubwordBoundary()
expect(editor.getCursorBufferPosition()).toEqual([0, 0])
editor.setText(" word\n")
editor.setCursorBufferPosition([0, 3])
editor.moveToPreviousSubwordBoundary()
expect(editor.getCursorBufferPosition()).toEqual([0, 1])
it "stops at camelCase boundaries", ->
editor.setText(" getPreviousWord\n")
editor.setCursorBufferPosition([0, 16])
editor.moveToPreviousSubwordBoundary()
expect(editor.getCursorBufferPosition()).toEqual([0, 12])
editor.moveToPreviousSubwordBoundary()
expect(editor.getCursorBufferPosition()).toEqual([0, 4])
editor.moveToPreviousSubwordBoundary()
expect(editor.getCursorBufferPosition()).toEqual([0, 1])
it "skips consecutive non-word characters", ->
editor.setText("e, => \n")
editor.setCursorBufferPosition([0, 6])
editor.moveToPreviousSubwordBoundary()
expect(editor.getCursorBufferPosition()).toEqual([0, 3])
editor.moveToPreviousSubwordBoundary()
expect(editor.getCursorBufferPosition()).toEqual([0, 1])
it "skips consecutive uppercase characters", ->
editor.setText(" AAADF \n")
editor.setCursorBufferPosition([0, 7])
editor.moveToPreviousSubwordBoundary()
expect(editor.getCursorBufferPosition()).toEqual([0, 6])
editor.moveToPreviousSubwordBoundary()
expect(editor.getCursorBufferPosition()).toEqual([0, 1])
editor.setText("ALPhA\n")
editor.setCursorBufferPosition([0, 4])
editor.moveToPreviousSubwordBoundary()
expect(editor.getCursorBufferPosition()).toEqual([0, 2])
it "skips consecutive numbers", ->
editor.setText(" 88 \n")
editor.setCursorBufferPosition([0, 4])
editor.moveToPreviousSubwordBoundary()
expect(editor.getCursorBufferPosition()).toEqual([0, 3])
editor.moveToPreviousSubwordBoundary()
expect(editor.getCursorBufferPosition()).toEqual([0, 1])
it "works with multiple cursors", ->
editor.setText("curOp\ncursorOptions\n")
editor.setCursorBufferPosition([0, 8])
editor.addCursorAtBufferPosition([1, 13])
[cursor1, cursor2] = editor.getCursors()
editor.moveToPreviousSubwordBoundary()
expect(cursor1.getBufferPosition()).toEqual([0, 3])
expect(cursor2.getBufferPosition()).toEqual([1, 6])
it "works with non-English characters", ->
editor.setText("supåTøåst \n")
editor.setCursorBufferPosition([0, 9])
editor.moveToPreviousSubwordBoundary()
expect(editor.getCursorBufferPosition()).toEqual([0, 4])
editor.setText("supaÖast \n")
editor.setCursorBufferPosition([0, 8])
editor.moveToPreviousSubwordBoundary()
expect(editor.getCursorBufferPosition()).toEqual([0, 4])
describe ".moveToNextSubwordBoundary", ->
it "does not move the cursor when there is no next subword boundary", ->
editor.setText('')
editor.moveToNextSubwordBoundary()
expect(editor.getCursorBufferPosition()).toEqual([0, 0])
it "stops at word and underscore boundaries", ->
editor.setText(" sub_word \n")
editor.setCursorBufferPosition([0, 0])
editor.moveToNextSubwordBoundary()
expect(editor.getCursorBufferPosition()).toEqual([0, 1])
editor.moveToNextSubwordBoundary()
expect(editor.getCursorBufferPosition()).toEqual([0, 4])
editor.moveToNextSubwordBoundary()
expect(editor.getCursorBufferPosition()).toEqual([0, 9])
editor.setText("word \n")
editor.setCursorBufferPosition([0, 0])
editor.moveToNextSubwordBoundary()
expect(editor.getCursorBufferPosition()).toEqual([0, 4])
it "stops at camelCase boundaries", ->
editor.setText("getPreviousWord \n")
editor.setCursorBufferPosition([0, 0])
editor.moveToNextSubwordBoundary()
expect(editor.getCursorBufferPosition()).toEqual([0, 3])
editor.moveToNextSubwordBoundary()
expect(editor.getCursorBufferPosition()).toEqual([0, 11])
editor.moveToNextSubwordBoundary()
expect(editor.getCursorBufferPosition()).toEqual([0, 15])
it "skips consecutive non-word characters", ->
editor.setText(", => \n")
editor.setCursorBufferPosition([0, 0])
editor.moveToNextSubwordBoundary()
expect(editor.getCursorBufferPosition()).toEqual([0, 1])
editor.moveToNextSubwordBoundary()
expect(editor.getCursorBufferPosition()).toEqual([0, 4])
it "skips consecutive uppercase characters", ->
editor.setText(" AAADF \n")
editor.setCursorBufferPosition([0, 0])
editor.moveToNextSubwordBoundary()
expect(editor.getCursorBufferPosition()).toEqual([0, 1])
editor.moveToNextSubwordBoundary()
expect(editor.getCursorBufferPosition()).toEqual([0, 6])
editor.setText("ALPhA\n")
editor.setCursorBufferPosition([0, 0])
editor.moveToNextSubwordBoundary()
expect(editor.getCursorBufferPosition()).toEqual([0, 2])
it "skips consecutive numbers", ->
editor.setText(" 88 \n")
editor.setCursorBufferPosition([0, 0])
editor.moveToNextSubwordBoundary()
expect(editor.getCursorBufferPosition()).toEqual([0, 1])
editor.moveToNextSubwordBoundary()
expect(editor.getCursorBufferPosition()).toEqual([0, 3])
it "works with multiple cursors", ->
editor.setText("curOp\ncursorOptions\n")
editor.setCursorBufferPosition([0, 0])
editor.addCursorAtBufferPosition([1, 0])
[cursor1, cursor2] = editor.getCursors()
editor.moveToNextSubwordBoundary()
expect(cursor1.getBufferPosition()).toEqual([0, 3])
expect(cursor2.getBufferPosition()).toEqual([1, 6])
it "works with non-English characters", ->
editor.setText("supåTøåst \n")
editor.setCursorBufferPosition([0, 0])
editor.moveToNextSubwordBoundary()
expect(editor.getCursorBufferPosition()).toEqual([0, 4])
editor.setText("supaÖast \n")
editor.setCursorBufferPosition([0, 0])
editor.moveToNextSubwordBoundary()
expect(editor.getCursorBufferPosition()).toEqual([0, 4])
describe ".selectToPreviousSubwordBoundary", ->
it "selects subwords", ->
editor.setText("")
editor.insertText("_word\n")
editor.insertText(" getPreviousWord\n")
editor.insertText("e, => \n")
editor.insertText(" 88 \n")
editor.setCursorBufferPosition([0, 5])
editor.addCursorAtBufferPosition([1, 7])
editor.addCursorAtBufferPosition([2, 5])
editor.addCursorAtBufferPosition([3, 3])
[selection1, selection2, selection3, selection4] = editor.getSelections()
editor.selectToPreviousSubwordBoundary()
expect(selection1.getBufferRange()).toEqual([[0, 1], [0, 5]])
expect(selection1.isReversed()).toBeTruthy()
expect(selection2.getBufferRange()).toEqual([[1, 4], [1, 7]])
expect(selection2.isReversed()).toBeTruthy()
expect(selection3.getBufferRange()).toEqual([[2, 3], [2, 5]])
expect(selection3.isReversed()).toBeTruthy()
expect(selection4.getBufferRange()).toEqual([[3, 1], [3, 3]])
expect(selection4.isReversed()).toBeTruthy()
describe ".selectToNextSubwordBoundary", ->
it "selects subwords", ->
editor.setText("")
editor.insertText("word_\n")
editor.insertText("getPreviousWord\n")
editor.insertText("e, => \n")
editor.insertText(" 88 \n")
editor.setCursorBufferPosition([0, 1])
editor.addCursorAtBufferPosition([1, 7])
editor.addCursorAtBufferPosition([2, 2])
editor.addCursorAtBufferPosition([3, 1])
[selection1, selection2, selection3, selection4] = editor.getSelections()
editor.selectToNextSubwordBoundary()
expect(selection1.getBufferRange()).toEqual([[0, 1], [0, 4]])
expect(selection1.isReversed()).toBeFalsy()
expect(selection2.getBufferRange()).toEqual([[1, 7], [1, 11]])
expect(selection2.isReversed()).toBeFalsy()
expect(selection3.getBufferRange()).toEqual([[2, 2], [2, 5]])
expect(selection3.isReversed()).toBeFalsy()
expect(selection4.getBufferRange()).toEqual([[3, 1], [3, 3]])
expect(selection4.isReversed()).toBeFalsy()
describe ".deleteToBeginningOfSubword", ->
it "deletes subwords", ->
editor.setText("")
editor.insertText("_word\n")
editor.insertText(" getPreviousWord\n")
editor.insertText("e, => \n")
editor.insertText(" 88 \n")
editor.setCursorBufferPosition([0, 5])
editor.addCursorAtBufferPosition([1, 7])
editor.addCursorAtBufferPosition([2, 5])
editor.addCursorAtBufferPosition([3, 3])
[cursor1, cursor2, cursor3, cursor4] = editor.getCursors()
editor.deleteToBeginningOfSubword()
expect(buffer.lineForRow(0)).toBe('_')
expect(buffer.lineForRow(1)).toBe(' getviousWord')
expect(buffer.lineForRow(2)).toBe('e, ')
expect(buffer.lineForRow(3)).toBe(' ')
expect(cursor1.getBufferPosition()).toEqual([0, 1])
expect(cursor2.getBufferPosition()).toEqual([1, 4])
expect(cursor3.getBufferPosition()).toEqual([2, 3])
expect(cursor4.getBufferPosition()).toEqual([3, 1])
editor.deleteToBeginningOfSubword()
expect(buffer.lineForRow(0)).toBe('')
expect(buffer.lineForRow(1)).toBe(' viousWord')
expect(buffer.lineForRow(2)).toBe('e ')
expect(buffer.lineForRow(3)).toBe(' ')
expect(cursor1.getBufferPosition()).toEqual([0, 0])
expect(cursor2.getBufferPosition()).toEqual([1, 1])
expect(cursor3.getBufferPosition()).toEqual([2, 1])
expect(cursor4.getBufferPosition()).toEqual([3, 0])
editor.deleteToBeginningOfSubword()
expect(buffer.lineForRow(0)).toBe('')
expect(buffer.lineForRow(1)).toBe('viousWord')
expect(buffer.lineForRow(2)).toBe(' ')
expect(buffer.lineForRow(3)).toBe('')
expect(cursor1.getBufferPosition()).toEqual([0, 0])
expect(cursor2.getBufferPosition()).toEqual([1, 0])
expect(cursor3.getBufferPosition()).toEqual([2, 0])
expect(cursor4.getBufferPosition()).toEqual([2, 1])
describe ".deleteToEndOfSubword", ->
it "deletes subwords", ->
editor.setText("")
editor.insertText("word_\n")
editor.insertText("getPreviousWord \n")
editor.insertText("e, => \n")
editor.insertText(" 88 \n")
editor.setCursorBufferPosition([0, 0])
editor.addCursorAtBufferPosition([1, 0])
editor.addCursorAtBufferPosition([2, 2])
editor.addCursorAtBufferPosition([3, 0])
[cursor1, cursor2, cursor3, cursor4] = editor.getCursors()
editor.deleteToEndOfSubword()
expect(buffer.lineForRow(0)).toBe('_')
expect(buffer.lineForRow(1)).toBe('PreviousWord ')
expect(buffer.lineForRow(2)).toBe('e, ')
expect(buffer.lineForRow(3)).toBe('88 ')
expect(cursor1.getBufferPosition()).toEqual([0, 0])
expect(cursor2.getBufferPosition()).toEqual([1, 0])
expect(cursor3.getBufferPosition()).toEqual([2, 2])
expect(cursor4.getBufferPosition()).toEqual([3, 0])
editor.deleteToEndOfSubword()
expect(buffer.lineForRow(0)).toBe('')
expect(buffer.lineForRow(1)).toBe('Word ')
expect(buffer.lineForRow(2)).toBe('e,')
expect(buffer.lineForRow(3)).toBe(' ')
expect(cursor1.getBufferPosition()).toEqual([0, 0])
expect(cursor2.getBufferPosition()).toEqual([1, 0])
expect(cursor3.getBufferPosition()).toEqual([2, 2])
expect(cursor4.getBufferPosition()).toEqual([3, 0])
describe 'gutters', ->
describe 'the TextEditor constructor', ->
it 'creates a line-number gutter', ->

View File

@@ -1,5 +1,6 @@
crypto = require 'crypto'
path = require 'path'
ipc = require 'ipc'
_ = require 'underscore-plus'
{deprecate} = require 'grim'
@@ -116,7 +117,7 @@ class AtomEnvironment extends Model
# Call .loadOrCreate instead
constructor: (params={}) ->
{@applicationDelegate, @window, @document, configDirPath, @enablePersistence} = params
{@blobStore, @applicationDelegate, @window, @document, configDirPath, @enablePersistence} = params
@state = {version: @constructor.version}
@@ -203,6 +204,15 @@ class AtomEnvironment extends Model
@observeAutoHideMenuBar()
checkPortableHomeWritable = ->
responseChannel = "check-portable-home-writable-response"
ipc.on responseChannel, (response) ->
ipc.removeAllListeners(responseChannel)
atom.notifications.addWarning("#{response.message.replace(/([\\\.+\\-_#!])/g, '\\$1')}") if not response.writable
ipc.send('check-portable-home-writable', responseChannel)
checkPortableHomeWritable()
setConfigSchema: ->
@config.setSchema null, {type: 'object', properties: _.clone(require('./config-schema'))}
@@ -306,6 +316,7 @@ class AtomEnvironment extends Model
@project = null
@commands.clear()
@stylesElement.remove()
@config.unobserveUserConfig()
@uninstallWindowEventHandler()
@@ -636,6 +647,7 @@ class AtomEnvironment extends Model
@state.packageStates = @packages.packageStates
@state.fullScreen = @isFullScreen()
@saveStateSync()
@saveBlobStoreSync()
openInitialEmptyEditorIfNecessary: ->
return unless @config.get('core.openEmptyEditorOnStart')
@@ -759,6 +771,11 @@ class AtomEnvironment extends Model
showSaveDialogSync: (options={}) ->
@applicationDelegate.showSaveDialog(options)
saveBlobStoreSync: ->
return unless @enablePersistence
@blobStore.save()
saveStateSync: ->
return unless @enablePersistence

View File

@@ -0,0 +1,35 @@
fs = require 'fs-plus'
path = require 'path'
ipc = require 'ipc'
module.exports =
class AtomPortable
@getPortableAtomHomePath: ->
execDirectoryPath = path.dirname(process.execPath)
path.join(execDirectoryPath, '..', '.atom')
@setPortable: (existingAtomHome) ->
fs.copySync(existingAtomHome, @getPortableAtomHomePath())
@isPortableInstall: (platform, environmentAtomHome, defaultHome) ->
return false unless platform in ['linux', 'win32']
return false if environmentAtomHome
return false if not fs.existsSync(@getPortableAtomHomePath())
# currently checking only that the directory exists and is writable,
# probably want to do some integrity checks on contents in future
@isPortableAtomHomePathWritable(defaultHome)
@isPortableAtomHomePathWritable: (defaultHome) ->
writable = false
message = ""
try
writePermissionTestFile = path.join(@getPortableAtomHomePath(), "write.test")
fs.writeFileSync(writePermissionTestFile, "test") if not fs.existsSync(writePermissionTestFile)
fs.removeSync(writePermissionTestFile)
writable = true
catch error
message = "Failed to use portable Atom home directory (#{@getPortableAtomHomePath()}). Using the default instead (#{defaultHome}). #{error.message}"
ipc.on 'check-portable-home-writable', (event) ->
event.sender.send 'check-portable-home-writable-response', {writable, message}
writable

View File

@@ -49,6 +49,7 @@ class AtomWindow
loadSettings.resourcePath = @resourcePath
loadSettings.devMode ?= false
loadSettings.safeMode ?= false
loadSettings.atomHome = process.env.ATOM_HOME
# Only send to the first non-spec window created
if @constructor.includeShellLoadTime and not @isSpec

View File

@@ -12,15 +12,14 @@ yargs = require 'yargs'
console.log = require 'nslog'
start = ->
setupAtomHome()
args = parseCommandLine()
setupAtomHome(args)
setupCompileCache()
return if handleStartupEventWithSquirrel()
# NB: This prevents Win10 from showing dupe items in the taskbar
app.setAppUserModelId('com.squirrel.atom.atom')
args = parseCommandLine()
addPathToOpen = (event, pathToOpen) ->
event.preventDefault()
args.pathsToOpen.push(pathToOpen)
@@ -57,11 +56,25 @@ handleStartupEventWithSquirrel = ->
setupCrashReporter = ->
crashReporter.start(productName: 'Atom', companyName: 'GitHub')
setupAtomHome = ->
setupAtomHome = ({setPortable}) ->
return if process.env.ATOM_HOME
atomHome = path.join(app.getHomeDir(), '.atom')
AtomPortable = require './atom-portable'
if setPortable and not AtomPortable.isPortableInstall(process.platform, process.env.ATOM_HOME, atomHome)
try
AtomPortable.setPortable(atomHome)
catch error
console.log("Failed copying portable directory '#{atomHome}' to '#{AtomPortable.getPortableAtomHomePath()}'")
console.log("#{error.message} #{error.stack}")
if AtomPortable.isPortableInstall(process.platform, process.env.ATOM_HOME, atomHome)
atomHome = AtomPortable.getPortableAtomHomePath()
try
atomHome = fs.realpathSync(atomHome)
process.env.ATOM_HOME = atomHome
setupCompileCache = ->
@@ -100,6 +113,7 @@ parseCommandLine = ->
options.boolean('profile-startup').describe('profile-startup', 'Create a profile of the startup execution time.')
options.alias('r', 'resource-path').string('r').describe('r', 'Set the path to the Atom source directory and enable dev-mode.')
options.boolean('safe').describe('safe', 'Do not load packages from ~/.atom/packages or ~/.atom/dev/packages.')
options.boolean('portable').describe('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.alias('t', 'test').boolean('t').describe('t', 'Run the specified specs and exit with error code on failures.')
options.string('timeout').describe('timeout', 'When in test mode, waits until the specified time (in minutes) and kills the process (exit code: 130).')
options.alias('v', 'version').boolean('v').describe('v', 'Print the version.')
@@ -129,6 +143,7 @@ parseCommandLine = ->
profileStartup = args['profile-startup']
urlsToOpen = []
devResourcePath = process.env.ATOM_DEV_RESOURCE_PATH ? path.join(app.getHomeDir(), 'github', 'atom')
setPortable = args.portable
if args['resource-path']
devMode = true
@@ -149,6 +164,6 @@ parseCommandLine = ->
{resourcePath, devResourcePath, pathsToOpen, urlsToOpen, executedFrom, test,
version, pidToKillWhenClosed, devMode, safeMode, newWindow,
logFile, socketPath, profileStartup, timeout}
logFile, socketPath, profileStartup, timeout, setPortable}
start()

View File

@@ -0,0 +1,111 @@
'use strict'
const fs = require('fs-plus')
const path = require('path')
module.exports =
class FileSystemBlobStore {
static load (directory) {
let instance = new FileSystemBlobStore(directory)
instance.load()
return instance
}
constructor (directory) {
this.inMemoryBlobs = new Map()
this.blobFilename = path.join(directory, 'BLOB')
this.blobMapFilename = path.join(directory, 'MAP')
this.lockFilename = path.join(directory, 'LOCK')
this.storedBlob = new Buffer(0)
this.storedBlobMap = {}
}
load () {
if (!fs.existsSync(this.blobMapFilename)) {
return
}
if (!fs.existsSync(this.blobFilename)) {
return
}
this.storedBlob = fs.readFileSync(this.blobFilename)
this.storedBlobMap = JSON.parse(fs.readFileSync(this.blobMapFilename))
}
save () {
let dump = this.getDump()
let blobToStore = Buffer.concat(dump[0])
let mapToStore = JSON.stringify(dump[1])
let acquiredLock = false
try {
fs.writeFileSync(this.lockFilename, 'LOCK', {flag: 'wx'})
acquiredLock = true
fs.writeFileSync(this.blobFilename, blobToStore)
fs.writeFileSync(this.blobMapFilename, mapToStore)
} catch (error) {
// Swallow the exception silently only if we fail to acquire the lock.
if (error.code !== 'EEXIST') {
throw error
}
} finally {
if (acquiredLock) {
fs.unlinkSync(this.lockFilename)
}
}
}
has (key) {
return this.inMemoryBlobs.hasOwnProperty(key) || this.storedBlobMap.hasOwnProperty(key)
}
get (key) {
return this.getFromMemory(key) || this.getFromStorage(key)
}
set (key, buffer) {
return this.inMemoryBlobs.set(key, buffer)
}
delete (key) {
this.inMemoryBlobs.delete(key)
delete this.storedBlobMap[key]
}
getFromMemory (key) {
return this.inMemoryBlobs.get(key)
}
getFromStorage (key) {
if (!this.storedBlobMap[key]) {
return
}
return this.storedBlob.slice.apply(this.storedBlob, this.storedBlobMap[key])
}
getDump () {
let buffers = []
let blobMap = {}
let currentBufferStart = 0
function dump (key, getBufferByKey) {
let buffer = getBufferByKey(key)
buffers.push(buffer)
blobMap[key] = [currentBufferStart, currentBufferStart + buffer.length]
currentBufferStart += buffer.length
}
for (let key of this.inMemoryBlobs.keys()) {
dump(key, this.getFromMemory.bind(this))
}
for (let key of Object.keys(this.storedBlobMap)) {
if (!blobMap[key]) {
dump(key, this.getFromStorage.bind(this))
}
}
return [buffers, blobMap]
}
}

View File

@@ -1,34 +1,34 @@
# Like sands through the hourglass, so are the days of our lives.
module.exports = ({blobStore}) ->
path = require 'path'
require './window'
{getWindowLoadSettings} = require './window-load-settings-helpers'
path = require 'path'
require './window'
{getWindowLoadSettings} = require './window-load-settings-helpers'
{resourcePath, isSpec, devMode} = getWindowLoadSettings()
{resourcePath, isSpec, devMode} = getWindowLoadSettings()
# Add application-specific exports to module search path.
exportsPath = path.join(resourcePath, 'exports')
require('module').globalPaths.push(exportsPath)
process.env.NODE_PATH = exportsPath
# Add application-specific exports to module search path.
exportsPath = path.join(resourcePath, 'exports')
require('module').globalPaths.push(exportsPath)
process.env.NODE_PATH = exportsPath
# Make React faster
process.env.NODE_ENV ?= 'production' unless devMode
# Make React faster
process.env.NODE_ENV ?= 'production' unless devMode
AtomEnvironment = require './atom-environment'
ApplicationDelegate = require './application-delegate'
window.atom = new AtomEnvironment({
window, document, blobStore,
applicationDelegate: new ApplicationDelegate,
configDirPath: process.env.ATOM_HOME
enablePersistence: true
})
AtomEnvironment = require './atom-environment'
ApplicationDelegate = require './application-delegate'
window.atom = new AtomEnvironment({
window, document,
applicationDelegate: new ApplicationDelegate,
configDirPath: process.env.ATOM_HOME
enablePersistence: true
})
atom.displayWindow()
atom.loadStateSync()
atom.startEditorWindow()
atom.displayWindow()
atom.loadStateSync()
atom.startEditorWindow()
# Workaround for focus getting cleared upon window creation
windowFocused = ->
window.removeEventListener('focus', windowFocused)
setTimeout (-> document.querySelector('atom-workspace').focus()), 0
window.addEventListener('focus', windowFocused)
# Workaround for focus getting cleared upon window creation
windowFocused = ->
window.removeEventListener('focus', windowFocused)
setTimeout (-> document.querySelector('atom-workspace').focus()), 0
window.addEventListener('focus', windowFocused)

View File

@@ -1,69 +1,78 @@
# Start the crash reporter before anything else.
require('crash-reporter').start(productName: 'Atom', companyName: 'GitHub')
remote = require 'remote'
cloneObject = (object) ->
clone = {}
clone[key] = value for key, value of object
clone
exitWithStatusCode = (status) ->
remote.require('app').emit('will-quit')
remote.process.exit(status)
module.exports = ({blobStore}) ->
# Start the crash reporter before anything else.
require('crash-reporter').start(productName: 'Atom', companyName: 'GitHub')
remote = require 'remote'
try
path = require 'path'
ipc = require 'ipc'
{getWindowLoadSettings} = require './window-load-settings-helpers'
AtomEnvironment = require '../src/atom-environment'
ApplicationDelegate = require '../src/application-delegate'
exitWithStatusCode = (status) ->
remote.require('app').emit('will-quit')
remote.process.exit(status)
{testRunnerPath, legacyTestRunnerPath, headless, logFile, testPaths} = getWindowLoadSettings()
try
path = require 'path'
ipc = require 'ipc'
{getWindowLoadSettings} = require './window-load-settings-helpers'
AtomEnvironment = require '../src/atom-environment'
ApplicationDelegate = require '../src/application-delegate'
if headless
# Override logging in headless mode so it goes to the console, regardless
# of the --enable-logging flag to Electron.
console.log = (args...) ->
ipc.send 'write-to-stdout', args.join(' ') + '\n'
console.warn = (args...) ->
ipc.send 'write-to-stderr', args.join(' ') + '\n'
console.error = (args...) ->
ipc.send 'write-to-stderr', args.join(' ') + '\n'
else
# Show window synchronously so a focusout doesn't fire on input elements
# that are focused in the very first spec run.
remote.getCurrentWindow().show()
{testRunnerPath, legacyTestRunnerPath, headless, logFile, testPaths} = getWindowLoadSettings()
handleKeydown = (event) ->
# Reload: cmd-r / ctrl-r
if (event.metaKey or event.ctrlKey) and event.keyCode is 82
ipc.send('call-window-method', 'restart')
if headless
# Override logging in headless mode so it goes to the console, regardless
# of the --enable-logging flag to Electron.
console.log = (args...) ->
ipc.send 'write-to-stdout', args.join(' ') + '\n'
console.warn = (args...) ->
ipc.send 'write-to-stderr', args.join(' ') + '\n'
console.error = (args...) ->
ipc.send 'write-to-stderr', args.join(' ') + '\n'
else
# Show window synchronously so a focusout doesn't fire on input elements
# that are focused in the very first spec run.
remote.getCurrentWindow().show()
# Toggle Dev Tools: cmd-alt-i / ctrl-alt-i
if (event.metaKey or event.ctrlKey) and event.altKey and event.keyCode is 73
ipc.send('call-window-method', 'toggleDevTools')
handleKeydown = (event) ->
# Reload: cmd-r / ctrl-r
if (event.metaKey or event.ctrlKey) and event.keyCode is 82
ipc.send('call-window-method', 'restart')
# Reload: cmd-w / ctrl-w
if (event.metaKey or event.ctrlKey) and event.keyCode is 87
ipc.send('call-window-method', 'close')
# Toggle Dev Tools: cmd-alt-i / ctrl-alt-i
if (event.metaKey or event.ctrlKey) and event.altKey and event.keyCode is 73
ipc.send('call-window-method', 'toggleDevTools')
window.addEventListener('keydown', handleKeydown, true)
# Reload: cmd-w / ctrl-w
if (event.metaKey or event.ctrlKey) and event.keyCode is 87
ipc.send('call-window-method', 'close')
# Add 'exports' to module search path.
exportsPath = path.join(getWindowLoadSettings().resourcePath, 'exports')
require('module').globalPaths.push(exportsPath)
process.env.NODE_PATH = exportsPath # Set NODE_PATH env variable since tasks may need it.
window.addEventListener('keydown', handleKeydown, true)
document.title = "Spec Suite"
# Add 'exports' to module search path.
exportsPath = path.join(getWindowLoadSettings().resourcePath, 'exports')
require('module').globalPaths.push(exportsPath)
process.env.NODE_PATH = exportsPath # Set NODE_PATH env variable since tasks may need it.
testRunner = require(testRunnerPath)
legacyTestRunner = require(legacyTestRunnerPath)
buildAtomEnvironment = (params) -> new AtomEnvironment(params)
buildDefaultApplicationDelegate = (params) -> new ApplicationDelegate()
document.title = "Spec Suite"
promise = testRunner({
logFile, headless, testPaths, buildAtomEnvironment, buildDefaultApplicationDelegate, legacyTestRunner
})
testRunner = require(testRunnerPath)
legacyTestRunner = require(legacyTestRunnerPath)
buildDefaultApplicationDelegate = -> new ApplicationDelegate()
buildAtomEnvironment = (params) ->
params = cloneObject(params)
params.blobStore = blobStore unless params.hasOwnProperty("blobStore")
new AtomEnvironment(params)
promise.then(exitWithStatusCode) if getWindowLoadSettings().headless
catch error
if getWindowLoadSettings().headless
console.error(error.stack ? error)
exitWithStatusCode(1)
else
throw error
promise = testRunner({
logFile, headless, testPaths, buildAtomEnvironment, buildDefaultApplicationDelegate, legacyTestRunner
})
promise.then(exitWithStatusCode) if getWindowLoadSettings().headless
catch error
if getWindowLoadSettings().headless
console.error(error.stack ? error)
exitWithStatusCode(1)
else
throw error

101
src/native-compile-cache.js Normal file
View File

@@ -0,0 +1,101 @@
'use strict'
const Module = require('module')
const path = require('path')
const cachedVm = require('cached-run-in-this-context')
class NativeCompileCache {
constructor () {
this.cacheStore = null
this.previousModuleCompile = null
}
setCacheStore (store) {
this.cacheStore = store
}
install () {
this.savePreviousModuleCompile()
this.overrideModuleCompile()
}
uninstall () {
this.restorePreviousModuleCompile()
}
savePreviousModuleCompile () {
this.previousModuleCompile = Module.prototype._compile
}
overrideModuleCompile () {
let cacheStore = this.cacheStore
let resolvedArgv = null
// Here we override Node's module.js
// (https://github.com/atom/node/blob/atom/lib/module.js#L378), changing
// only the bits that affect compilation in order to use the cached one.
Module.prototype._compile = function (content, filename) {
let self = this
// remove shebang
content = content.replace(/^\#\!.*/, '')
function require (path) {
return self.require(path)
}
require.resolve = function (request) {
return Module._resolveFilename(request, self)
}
require.main = process.mainModule
// Enable support to add extra extension types
require.extensions = Module._extensions
require.cache = Module._cache
let dirname = path.dirname(filename)
// create wrapper function
let wrapper = Module.wrap(content)
let compiledWrapper = null
if (cacheStore.has(filename)) {
let buffer = cacheStore.get(filename)
let compilationResult = cachedVm.runInThisContextCached(wrapper, filename, buffer)
compiledWrapper = compilationResult.result
if (compilationResult.wasRejected) {
cacheStore.delete(filename)
}
} else {
let compilationResult = cachedVm.runInThisContext(wrapper, filename)
if (compilationResult.cacheBuffer) {
cacheStore.set(filename, compilationResult.cacheBuffer)
}
compiledWrapper = compilationResult.result
}
if (global.v8debug) {
if (!resolvedArgv) {
// we enter the repl if we're not given a filename argument.
if (process.argv[1]) {
resolvedArgv = Module._resolveFilename(process.argv[1], null)
} else {
resolvedArgv = 'repl'
}
}
// Set breakpoint on module start
if (filename === resolvedArgv) {
// Installing this dummy debug event listener tells V8 to start
// the debugger. Without it, the setBreakPoint() fails with an
// 'illegal access' error.
global.v8debug.Debug.setListener(function () {})
global.v8debug.Debug.setBreakPoint(compiledWrapper, 0, 0)
}
}
let args = [self.exports, require, self, filename, dirname, process, global]
return compiledWrapper.apply(self.exports, args)
}
}
restorePreviousModuleCompile () {
Module.prototype._compile = this.previousModuleCompile
}
}
module.exports = new NativeCompileCache()

View File

@@ -1,9 +1,11 @@
(function () {
var fs = require('fs')
var path = require('path')
var FileSystemBlobStore = require('../src/file-system-blob-store')
var NativeCompileCache = require('../src/native-compile-cache')
var loadSettings = null
var loadSettingsError = null
var blobStore = null
window.onload = function () {
try {
@@ -13,8 +15,11 @@
console.error('Unhandled promise rejection %o with error: %o', promise, error)
})
// Ensure ATOM_HOME is always set before anything else is required
setupAtomHome()
blobStore = FileSystemBlobStore.load(
path.join(process.env.ATOM_HOME, 'blob-store/')
)
NativeCompileCache.setCacheStore(blobStore)
NativeCompileCache.install()
// Normalize to make sure drive letter case is consistent on Windows
process.resourcesPath = path.normalize(process.resourcesPath)
@@ -76,28 +81,11 @@
setupVmCompatibility()
setupCsonCache(CompileCache.getCacheDirectory())
require(loadSettings.windowInitializationScript)
var initialize = require(loadSettings.windowInitializationScript)
initialize({blobStore: blobStore})
require('ipc').sendChannel('window-command', 'window:loaded')
}
function setupAtomHome () {
if (!process.env.ATOM_HOME) {
var home
if (process.platform === 'win32') {
home = process.env.USERPROFILE
} else {
home = process.env.HOME
}
var atomHome = path.join(home, '.atom')
try {
atomHome = fs.realpathSync(atomHome)
} catch (error) {
// Ignore since the path might just not exist yet.
}
process.env.ATOM_HOME = atomHome
}
}
function setupCsonCache (cacheDir) {
require('season').setCacheDir(path.join(cacheDir, 'cson'))
}
@@ -181,6 +169,20 @@
}, false)
}
var setupAtomHome = function () {
if (process.env.ATOM_HOME) {
return
}
// Ensure ATOM_HOME is always set before anything else is required
// This is because of a difference in Linux not inherited between browser and render processes
// https://github.com/atom/atom/issues/5412
if (loadSettings && loadSettings.atomHome) {
process.env.ATOM_HOME = loadSettings.atomHome
}
}
parseLoadSettings()
setupAtomHome()
setupWindowBackground()
})()