mirror of
https://github.com/atom/atom.git
synced 2026-04-06 03:02:13 -04:00
Merge branch 'master' into as-serialize-history-without-snapshots
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -15,3 +15,4 @@ debug.log
|
||||
docs/output
|
||||
docs/includes
|
||||
spec/fixtures/evil-files/
|
||||
out/
|
||||
|
||||
@@ -6,6 +6,6 @@
|
||||
"url": "https://github.com/atom/atom.git"
|
||||
},
|
||||
"dependencies": {
|
||||
"atom-package-manager": "1.6.0"
|
||||
"atom-package-manager": "1.7.1"
|
||||
}
|
||||
}
|
||||
|
||||
14
package.json
14
package.json
@@ -37,7 +37,7 @@
|
||||
"less-cache": "0.23",
|
||||
"line-top-index": "0.2.0",
|
||||
"marked": "^0.3.4",
|
||||
"nodegit": "0.11.5",
|
||||
"nodegit": "0.11.6",
|
||||
"normalize-package-data": "^2.0.0",
|
||||
"nslog": "^3",
|
||||
"oniguruma": "^5",
|
||||
@@ -72,7 +72,7 @@
|
||||
"one-light-syntax": "1.2.0",
|
||||
"solarized-dark-syntax": "1.0.0",
|
||||
"solarized-light-syntax": "1.0.0",
|
||||
"about": "1.3.1",
|
||||
"about": "1.4.0",
|
||||
"archive-view": "0.61.1",
|
||||
"autocomplete-atom-api": "0.10.0",
|
||||
"autocomplete-css": "0.11.0",
|
||||
@@ -83,13 +83,13 @@
|
||||
"autosave": "0.23.1",
|
||||
"background-tips": "0.26.0",
|
||||
"bookmarks": "0.38.2",
|
||||
"bracket-matcher": "0.80.1",
|
||||
"bracket-matcher": "0.81.0",
|
||||
"command-palette": "0.38.0",
|
||||
"deprecation-cop": "0.54.1",
|
||||
"dev-live-reload": "0.47.0",
|
||||
"encoding-selector": "0.21.0",
|
||||
"exception-reporting": "0.37.0",
|
||||
"find-and-replace": "0.197.3",
|
||||
"find-and-replace": "0.197.4",
|
||||
"fuzzy-finder": "1.0.2",
|
||||
"git-diff": "1.0.0",
|
||||
"go-to-line": "0.30.0",
|
||||
@@ -112,7 +112,7 @@
|
||||
"symbols-view": "0.111.1",
|
||||
"tabs": "0.91.1",
|
||||
"timecop": "0.33.1",
|
||||
"tree-view": "0.201.5",
|
||||
"tree-view": "0.202.0",
|
||||
"update-package-dependencies": "0.10.0",
|
||||
"welcome": "0.34.0",
|
||||
"whitespace": "0.32.2",
|
||||
@@ -129,7 +129,7 @@
|
||||
"language-hyperlink": "0.16.0",
|
||||
"language-java": "0.17.0",
|
||||
"language-javascript": "0.110.0",
|
||||
"language-json": "0.17.4",
|
||||
"language-json": "0.17.5",
|
||||
"language-less": "0.29.0",
|
||||
"language-make": "0.21.0",
|
||||
"language-mustache": "0.13.0",
|
||||
@@ -147,7 +147,7 @@
|
||||
"language-text": "0.7.0",
|
||||
"language-todo": "0.27.0",
|
||||
"language-toml": "0.18.0",
|
||||
"language-xml": "0.34.3",
|
||||
"language-xml": "0.34.4",
|
||||
"language-yaml": "0.25.1"
|
||||
},
|
||||
"private": true,
|
||||
|
||||
@@ -22,30 +22,13 @@ FOR %%a IN (%*) DO (
|
||||
)
|
||||
)
|
||||
|
||||
rem Getting the process ID in cmd of the current cmd process: http://superuser.com/questions/881789/identify-and-kill-batch-script-started-before
|
||||
set T=%TEMP%\atomCmdProcessId-%time::=%.tmp
|
||||
wmic process where (Name="WMIC.exe" AND CommandLine LIKE "%%%TIME%%%") get ParentProcessId /value | find "ParentProcessId" >%T%
|
||||
set /P A=<%T%
|
||||
set PID=%A:~16%
|
||||
del %T%
|
||||
|
||||
IF "%EXPECT_OUTPUT%"=="YES" (
|
||||
SET ELECTRON_ENABLE_LOGGING=YES
|
||||
IF "%WAIT%"=="YES" (
|
||||
"%~dp0\..\..\atom.exe" --pid=%PID% %*
|
||||
rem If the wait flag is set, don't exit this process until Atom tells it to.
|
||||
goto waitLoop
|
||||
powershell -noexit "%~dp0\..\..\atom.exe" --pid=$pid %* ; wait-event
|
||||
) ELSE (
|
||||
"%~dp0\..\..\atom.exe" %*
|
||||
)
|
||||
) ELSE (
|
||||
"%~dp0\..\app\apm\bin\node.exe" "%~dp0\atom.js" %*
|
||||
)
|
||||
|
||||
goto end
|
||||
|
||||
:waitLoop
|
||||
sleep 1
|
||||
goto waitLoop
|
||||
|
||||
:end
|
||||
|
||||
@@ -19,7 +19,9 @@ exports.afterEach = (fn) ->
|
||||
|
||||
waitsForPromise = (fn) ->
|
||||
promise = fn()
|
||||
waitsFor 'spec promise to resolve', 30000, (done) ->
|
||||
# This timeout is 3 minutes. We need to bump it back down once we fix backgrounding
|
||||
# of the renderer process on CI. See https://github.com/atom/electron/issues/4317
|
||||
waitsFor 'spec promise to resolve', 3 * 60 * 1000, (done) ->
|
||||
promise.then(
|
||||
done,
|
||||
(error) ->
|
||||
|
||||
@@ -328,3 +328,18 @@ describe "AtomEnvironment", ->
|
||||
runs ->
|
||||
{releaseVersion} = updateAvailableHandler.mostRecentCall.args[0]
|
||||
expect(releaseVersion).toBe 'version'
|
||||
|
||||
describe "::getReleaseChannel()", ->
|
||||
[version] = []
|
||||
beforeEach ->
|
||||
spyOn(atom, 'getVersion').andCallFake -> version
|
||||
|
||||
it "returns the correct channel based on the version number", ->
|
||||
version = '1.5.6'
|
||||
expect(atom.getReleaseChannel()).toBe 'stable'
|
||||
|
||||
version = '1.5.0-beta10'
|
||||
expect(atom.getReleaseChannel()).toBe 'beta'
|
||||
|
||||
version = '1.7.0-dev-5340c91'
|
||||
expect(atom.getReleaseChannel()).toBe 'dev'
|
||||
|
||||
115
spec/auto-update-manager-spec.js
Normal file
115
spec/auto-update-manager-spec.js
Normal file
@@ -0,0 +1,115 @@
|
||||
'use babel'
|
||||
|
||||
import AutoUpdateManager from '../src/auto-update-manager'
|
||||
import {remote} from 'electron'
|
||||
const electronAutoUpdater = remote.require('electron').autoUpdater
|
||||
|
||||
describe('AutoUpdateManager (renderer)', () => {
|
||||
let autoUpdateManager
|
||||
|
||||
beforeEach(() => {
|
||||
autoUpdateManager = new AutoUpdateManager({
|
||||
applicationDelegate: atom.applicationDelegate
|
||||
})
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
autoUpdateManager.destroy()
|
||||
})
|
||||
|
||||
describe('::onDidBeginCheckingForUpdate', () => {
|
||||
it('subscribes to "did-begin-checking-for-update" event', () => {
|
||||
const spy = jasmine.createSpy('spy')
|
||||
autoUpdateManager.onDidBeginCheckingForUpdate(spy)
|
||||
electronAutoUpdater.emit('checking-for-update')
|
||||
waitsFor(() => {
|
||||
return spy.callCount === 1
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('::onDidBeginDownloadingUpdate', () => {
|
||||
it('subscribes to "did-begin-downloading-update" event', () => {
|
||||
const spy = jasmine.createSpy('spy')
|
||||
autoUpdateManager.onDidBeginDownloadingUpdate(spy)
|
||||
electronAutoUpdater.emit('update-available')
|
||||
waitsFor(() => {
|
||||
return spy.callCount === 1
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('::onDidCompleteDownloadingUpdate', () => {
|
||||
it('subscribes to "did-complete-downloading-update" event', () => {
|
||||
const spy = jasmine.createSpy('spy')
|
||||
autoUpdateManager.onDidCompleteDownloadingUpdate(spy)
|
||||
electronAutoUpdater.emit('update-downloaded', null, null, '1.2.3')
|
||||
waitsFor(() => {
|
||||
return spy.callCount === 1
|
||||
})
|
||||
runs(() => {
|
||||
expect(spy.mostRecentCall.args[0].releaseVersion).toBe('1.2.3')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('::onUpdateNotAvailable', () => {
|
||||
it('subscribes to "update-not-available" event', () => {
|
||||
const spy = jasmine.createSpy('spy')
|
||||
autoUpdateManager.onUpdateNotAvailable(spy)
|
||||
electronAutoUpdater.emit('update-not-available')
|
||||
waitsFor(() => {
|
||||
return spy.callCount === 1
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('::platformSupportsUpdates', () => {
|
||||
let state, releaseChannel
|
||||
it('returns true on OS X and Windows when in stable', () => {
|
||||
spyOn(autoUpdateManager, 'getState').andCallFake(() => state)
|
||||
spyOn(atom, 'getReleaseChannel').andCallFake(() => releaseChannel)
|
||||
|
||||
state = 'idle'
|
||||
releaseChannel = 'stable'
|
||||
expect(autoUpdateManager.platformSupportsUpdates()).toBe(true)
|
||||
|
||||
state = 'idle'
|
||||
releaseChannel = 'dev'
|
||||
expect(autoUpdateManager.platformSupportsUpdates()).toBe(false)
|
||||
|
||||
state = 'unsupported'
|
||||
releaseChannel = 'stable'
|
||||
expect(autoUpdateManager.platformSupportsUpdates()).toBe(false)
|
||||
|
||||
state = 'unsupported'
|
||||
releaseChannel = 'dev'
|
||||
expect(autoUpdateManager.platformSupportsUpdates()).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
describe('::destroy', () => {
|
||||
it('unsubscribes from all events', () => {
|
||||
const spy = jasmine.createSpy('spy')
|
||||
const doneIndicator = jasmine.createSpy('spy')
|
||||
atom.applicationDelegate.onUpdateNotAvailable(doneIndicator)
|
||||
autoUpdateManager.onDidBeginCheckingForUpdate(spy)
|
||||
autoUpdateManager.onDidBeginDownloadingUpdate(spy)
|
||||
autoUpdateManager.onDidCompleteDownloadingUpdate(spy)
|
||||
autoUpdateManager.onUpdateNotAvailable(spy)
|
||||
autoUpdateManager.destroy()
|
||||
electronAutoUpdater.emit('checking-for-update')
|
||||
electronAutoUpdater.emit('update-available')
|
||||
electronAutoUpdater.emit('update-downloaded', null, null, '1.2.3')
|
||||
electronAutoUpdater.emit('update-not-available')
|
||||
|
||||
waitsFor(() => {
|
||||
return doneIndicator.callCount === 1
|
||||
})
|
||||
|
||||
runs(() => {
|
||||
expect(spy.callCount).toBe(0)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -132,13 +132,37 @@ describe "Pane", ->
|
||||
expect(-> pane.addItem('foo')).toThrow()
|
||||
expect(-> pane.addItem(1)).toThrow()
|
||||
|
||||
it "destroys any existing pending item if the new item is pending", ->
|
||||
it "destroys any existing pending item", ->
|
||||
pane = new Pane(paneParams(items: []))
|
||||
itemA = new Item("A")
|
||||
itemB = new Item("B")
|
||||
itemC = new Item("C")
|
||||
pane.addItem(itemA, undefined, false, false)
|
||||
pane.addItem(itemB, undefined, false, true)
|
||||
pane.addItem(itemC, undefined, false, false)
|
||||
expect(itemB.isDestroyed()).toBe true
|
||||
|
||||
it "adds the new item before destroying any existing pending item", ->
|
||||
eventOrder = []
|
||||
|
||||
pane = new Pane(paneParams(items: []))
|
||||
itemA = new Item("A")
|
||||
itemB = new Item("B")
|
||||
pane.addItem(itemA, undefined, false, true)
|
||||
pane.addItem(itemB, undefined, false, true)
|
||||
expect(itemA.isDestroyed()).toBe true
|
||||
|
||||
pane.onDidAddItem ({item}) ->
|
||||
eventOrder.push("add") if item is itemB
|
||||
|
||||
pane.onDidRemoveItem ({item}) ->
|
||||
eventOrder.push("remove") if item is itemA
|
||||
|
||||
pane.addItem(itemB)
|
||||
|
||||
waitsFor ->
|
||||
eventOrder.length is 2
|
||||
|
||||
runs ->
|
||||
expect(eventOrder).toEqual ["add", "remove"]
|
||||
|
||||
describe "::activateItem(item)", ->
|
||||
pane = null
|
||||
|
||||
@@ -21,6 +21,14 @@ describe "Project", ->
|
||||
afterEach ->
|
||||
deserializedProject?.destroy()
|
||||
|
||||
it "does not deserialize paths to non directories", ->
|
||||
deserializedProject = new Project({notificationManager: atom.notifications, packageManager: atom.packages, confirm: atom.confirm})
|
||||
state = atom.project.serialize()
|
||||
state.paths.push('/directory/that/does/not/exist')
|
||||
state.paths.push(path.join(__dirname, 'fixtures', 'sample.js'))
|
||||
deserializedProject.deserialize(state, atom.deserializers)
|
||||
expect(deserializedProject.getPaths()).toEqual(atom.project.getPaths())
|
||||
|
||||
it "does not include unretained buffers in the serialized state", ->
|
||||
waitsForPromise ->
|
||||
atom.project.bufferForPath('a')
|
||||
|
||||
@@ -635,16 +635,28 @@ describe "TextEditorPresenter", ->
|
||||
expectStateUpdate presenter, -> presenter.setExplicitHeight(500)
|
||||
expect(getState(presenter).verticalScrollbar.scrollHeight).toBe 500
|
||||
|
||||
it "adds the computed clientHeight to the computed scrollHeight if editor.scrollPastEnd is true", ->
|
||||
presenter = buildPresenter(scrollTop: 10, explicitHeight: 50, horizontalScrollbarHeight: 10)
|
||||
expectStateUpdate presenter, -> presenter.setScrollTop(300)
|
||||
expect(getState(presenter).verticalScrollbar.scrollHeight).toBe presenter.contentHeight
|
||||
describe "scrollPastEnd", ->
|
||||
it "adds the computed clientHeight to the computed scrollHeight if editor.scrollPastEnd is true", ->
|
||||
presenter = buildPresenter(scrollTop: 10, explicitHeight: 50, horizontalScrollbarHeight: 10)
|
||||
expectStateUpdate presenter, -> presenter.setScrollTop(300)
|
||||
expect(getState(presenter).verticalScrollbar.scrollHeight).toBe presenter.contentHeight
|
||||
|
||||
expectStateUpdate presenter, -> atom.config.set("editor.scrollPastEnd", true)
|
||||
expect(getState(presenter).verticalScrollbar.scrollHeight).toBe presenter.contentHeight + presenter.clientHeight - (presenter.lineHeight * 3)
|
||||
expectStateUpdate presenter, -> atom.config.set("editor.scrollPastEnd", true)
|
||||
expect(getState(presenter).verticalScrollbar.scrollHeight).toBe presenter.contentHeight + presenter.clientHeight - (presenter.lineHeight * 3)
|
||||
|
||||
expectStateUpdate presenter, -> atom.config.set("editor.scrollPastEnd", false)
|
||||
expect(getState(presenter).verticalScrollbar.scrollHeight).toBe presenter.contentHeight
|
||||
expectStateUpdate presenter, -> atom.config.set("editor.scrollPastEnd", false)
|
||||
expect(getState(presenter).verticalScrollbar.scrollHeight).toBe presenter.contentHeight
|
||||
|
||||
it "doesn't add the computed clientHeight to the computed scrollHeight if editor.scrollPastEnd is true but the presenter is created with scrollPastEnd as false", ->
|
||||
presenter = buildPresenter(scrollTop: 10, explicitHeight: 50, horizontalScrollbarHeight: 10, scrollPastEnd: false)
|
||||
expectStateUpdate presenter, -> presenter.setScrollTop(300)
|
||||
expect(getState(presenter).verticalScrollbar.scrollHeight).toBe presenter.contentHeight
|
||||
|
||||
expectStateUpdate presenter, -> atom.config.set("editor.scrollPastEnd", true)
|
||||
expect(getState(presenter).verticalScrollbar.scrollHeight).toBe presenter.contentHeight
|
||||
|
||||
expectStateUpdate presenter, -> atom.config.set("editor.scrollPastEnd", false)
|
||||
expect(getState(presenter).verticalScrollbar.scrollHeight).toBe presenter.contentHeight
|
||||
|
||||
describe ".scrollTop", ->
|
||||
it "tracks the value of ::scrollTop", ->
|
||||
|
||||
@@ -5828,3 +5828,30 @@ describe "TextEditor", ->
|
||||
screenRange: marker1.getRange(),
|
||||
rangeIsReversed: false
|
||||
}
|
||||
|
||||
describe "when the editor is constructed with the showInvisibles option set to false", ->
|
||||
beforeEach ->
|
||||
atom.workspace.destroyActivePane()
|
||||
waitsForPromise ->
|
||||
atom.workspace.open('sample.js', showInvisibles: false).then (o) -> editor = o
|
||||
|
||||
it "ignores invisibles even if editor.showInvisibles is true", ->
|
||||
atom.config.set('editor.showInvisibles', true)
|
||||
invisibles = editor.tokenizedLineForScreenRow(0).invisibles
|
||||
expect(invisibles).toBe(null)
|
||||
|
||||
describe "when the editor is constructed with the grammar option set", ->
|
||||
beforeEach ->
|
||||
atom.workspace.destroyActivePane()
|
||||
waitsForPromise ->
|
||||
atom.packages.activatePackage('language-coffee-script')
|
||||
|
||||
waitsForPromise ->
|
||||
atom.workspace.open('sample.js', grammar: atom.grammars.grammarForScopeName('source.coffee')).then (o) -> editor = o
|
||||
|
||||
it "sets the grammar", ->
|
||||
expect(editor.getGrammar().name).toBe 'CoffeeScript'
|
||||
|
||||
describe "::getElement", ->
|
||||
it "returns an element", ->
|
||||
expect(editor.getElement() instanceof HTMLElement).toBe(true)
|
||||
|
||||
@@ -28,6 +28,12 @@ describe "TooltipManager", ->
|
||||
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()
|
||||
|
||||
it "allows jQuery elements to be passed as the target", ->
|
||||
element2 = document.createElement('div')
|
||||
jasmine.attachToDOM(element2)
|
||||
|
||||
@@ -23,6 +23,15 @@ describe "ViewRegistry", ->
|
||||
component = new TestComponent
|
||||
expect(registry.getView(component)).toBe component.element
|
||||
|
||||
describe "when passed an object with a getElement function", ->
|
||||
it "returns the return value of getElement if it's an instance of HTMLElement", ->
|
||||
class TestComponent
|
||||
getElement: ->
|
||||
@myElement ?= document.createElement('div')
|
||||
|
||||
component = new TestComponent
|
||||
expect(registry.getView(component)).toBe component.myElement
|
||||
|
||||
describe "when passed a model object", ->
|
||||
describe "when a view provider is registered matching the object's constructor", ->
|
||||
it "constructs a view element and assigns the model on it", ->
|
||||
|
||||
@@ -623,6 +623,34 @@ describe "Workspace", ->
|
||||
expect(pane.getItems().length).toBe 2
|
||||
expect(pane.getItems()).toEqual [editor1, editor2]
|
||||
|
||||
describe "when replacing a pending item which is the last item in a second pane", ->
|
||||
it "does not destory the pane even if core.destroyEmptyPanes is on", ->
|
||||
atom.config.set('core.destroyEmptyPanes', true)
|
||||
editor1 = null
|
||||
editor2 = null
|
||||
leftPane = atom.workspace.getActivePane()
|
||||
rightPane = null
|
||||
|
||||
waitsForPromise ->
|
||||
atom.workspace.open('sample.js', pending: true, split: 'right').then (o) ->
|
||||
editor1 = o
|
||||
rightPane = atom.workspace.getActivePane()
|
||||
spyOn rightPane, "destroyed"
|
||||
|
||||
runs ->
|
||||
expect(leftPane).not.toBe rightPane
|
||||
expect(atom.workspace.getActivePane()).toBe rightPane
|
||||
expect(atom.workspace.getActivePane().getItems().length).toBe 1
|
||||
expect(rightPane.getPendingItem()).toBe editor1
|
||||
|
||||
waitsForPromise ->
|
||||
atom.workspace.open('sample.txt', pending: true).then (o) ->
|
||||
editor2 = o
|
||||
|
||||
runs ->
|
||||
expect(rightPane.getPendingItem()).toBe editor2
|
||||
expect(rightPane.destroyed.callCount).toBe 0
|
||||
|
||||
describe "::reopenItem()", ->
|
||||
it "opens the uri associated with the last closed pane that isn't currently open", ->
|
||||
pane = workspace.getActivePane()
|
||||
|
||||
@@ -166,8 +166,7 @@ class ApplicationDelegate
|
||||
|
||||
onDidOpenLocations: (callback) ->
|
||||
outerCallback = (event, message, detail) ->
|
||||
if message is 'open-locations'
|
||||
callback(detail)
|
||||
callback(detail) if message is 'open-locations'
|
||||
|
||||
ipcRenderer.on('message', outerCallback)
|
||||
new Disposable ->
|
||||
@@ -175,8 +174,38 @@ class ApplicationDelegate
|
||||
|
||||
onUpdateAvailable: (callback) ->
|
||||
outerCallback = (event, message, detail) ->
|
||||
if message is 'update-available'
|
||||
callback(detail)
|
||||
# TODO: Yes, this is strange that `onUpdateAvailable` is listening for
|
||||
# `did-begin-downloading-update`. We currently have no mechanism to know
|
||||
# if there is an update, so begin of downloading is a good proxy.
|
||||
callback(detail) if message is 'did-begin-downloading-update'
|
||||
|
||||
ipcRenderer.on('message', outerCallback)
|
||||
new Disposable ->
|
||||
ipcRenderer.removeListener('message', outerCallback)
|
||||
|
||||
onDidBeginDownloadingUpdate: (callback) ->
|
||||
@onUpdateAvailable(callback)
|
||||
|
||||
onDidBeginCheckingForUpdate: (callback) ->
|
||||
outerCallback = (event, message, detail) ->
|
||||
callback(detail) if message is 'checking-for-update'
|
||||
|
||||
ipcRenderer.on('message', outerCallback)
|
||||
new Disposable ->
|
||||
ipcRenderer.removeListener('message', outerCallback)
|
||||
|
||||
onDidCompleteDownloadingUpdate: (callback) ->
|
||||
outerCallback = (event, message, detail) ->
|
||||
# TODO: We could rename this event to `did-complete-downloading-update`
|
||||
callback(detail) if message is 'update-available'
|
||||
|
||||
ipcRenderer.on('message', outerCallback)
|
||||
new Disposable ->
|
||||
ipcRenderer.removeListener('message', outerCallback)
|
||||
|
||||
onUpdateNotAvailable: (callback) ->
|
||||
outerCallback = (event, message, detail) ->
|
||||
callback(detail) if message is 'update-not-available'
|
||||
|
||||
ipcRenderer.on('message', outerCallback)
|
||||
new Disposable ->
|
||||
@@ -206,3 +235,12 @@ class ApplicationDelegate
|
||||
|
||||
disablePinchToZoom: ->
|
||||
webFrame.setZoomLevelLimits(1, 1)
|
||||
|
||||
checkForUpdate: ->
|
||||
ipcRenderer.send('check-for-update')
|
||||
|
||||
restartAndInstallUpdate: ->
|
||||
ipcRenderer.send('install-update')
|
||||
|
||||
getAutoUpdateManagerState: ->
|
||||
ipcRenderer.sendSync('get-auto-update-manager-state')
|
||||
|
||||
@@ -41,6 +41,7 @@ TextEditor = require './text-editor'
|
||||
TextBuffer = require 'text-buffer'
|
||||
Gutter = require './gutter'
|
||||
TextEditorRegistry = require './text-editor-registry'
|
||||
AutoUpdateManager = require './auto-update-manager'
|
||||
|
||||
WorkspaceElement = require './workspace-element'
|
||||
PanelContainerElement = require './panel-container-element'
|
||||
@@ -115,6 +116,9 @@ class AtomEnvironment extends Model
|
||||
# Public: A {TextEditorRegistry} instance
|
||||
textEditors: null
|
||||
|
||||
# Private: An {AutoUpdateManager} instance
|
||||
autoUpdater: null
|
||||
|
||||
saveStateDebounceInterval: 1000
|
||||
|
||||
###
|
||||
@@ -188,6 +192,7 @@ class AtomEnvironment extends Model
|
||||
@themes.workspace = @workspace
|
||||
|
||||
@textEditors = new TextEditorRegistry
|
||||
@autoUpdater = new AutoUpdateManager({@applicationDelegate})
|
||||
|
||||
@config.load()
|
||||
|
||||
@@ -260,8 +265,6 @@ class AtomEnvironment extends Model
|
||||
new PaneAxisElement().initialize(model, env)
|
||||
@views.addViewProvider Pane, (model, env) ->
|
||||
new PaneElement().initialize(model, env)
|
||||
@views.addViewProvider TextEditor, (model, env) ->
|
||||
new TextEditorElement().initialize(model, env)
|
||||
@views.addViewProvider(Gutter, createGutterView)
|
||||
|
||||
registerDefaultOpeners: ->
|
||||
@@ -333,6 +336,7 @@ class AtomEnvironment extends Model
|
||||
@commands.clear()
|
||||
@stylesElement.remove()
|
||||
@config.unobserveUserConfig()
|
||||
@autoUpdater.destroy()
|
||||
|
||||
@uninstallWindowEventHandler()
|
||||
|
||||
@@ -411,6 +415,16 @@ class AtomEnvironment extends Model
|
||||
getVersion: ->
|
||||
@appVersion ?= @getLoadSettings().appVersion
|
||||
|
||||
# Returns the release channel as a {String}. Will return one of `'dev', 'beta', 'stable'`
|
||||
getReleaseChannel: ->
|
||||
version = @getVersion()
|
||||
if version.indexOf('beta') > -1
|
||||
'beta'
|
||||
else if version.indexOf('dev') > -1
|
||||
'dev'
|
||||
else
|
||||
'stable'
|
||||
|
||||
# Public: Returns a {Boolean} that is `true` if the current version is an official release.
|
||||
isReleasedVersion: ->
|
||||
not /\w{7}/.test(@getVersion()) # Check if the release is a 7-character SHA prefix
|
||||
@@ -660,7 +674,7 @@ class AtomEnvironment extends Model
|
||||
@document.body.appendChild(@views.getView(@workspace))
|
||||
@backgroundStylesheet?.remove()
|
||||
|
||||
@watchProjectPath()
|
||||
@watchProjectPaths()
|
||||
|
||||
@packages.activate()
|
||||
@keymaps.loadUserKeymap()
|
||||
@@ -792,7 +806,7 @@ class AtomEnvironment extends Model
|
||||
@themes.load()
|
||||
|
||||
# Notify the browser project of the window's current project path
|
||||
watchProjectPath: ->
|
||||
watchProjectPaths: ->
|
||||
@disposables.add @project.onDidChangePaths =>
|
||||
@applicationDelegate.setRepresentedDirectoryPaths(@project.getPaths())
|
||||
|
||||
@@ -833,7 +847,6 @@ class AtomEnvironment extends Model
|
||||
@applicationDelegate.setTemporaryWindowState(state)
|
||||
savePromise.catch(reject).then(resolve)
|
||||
|
||||
|
||||
loadState: ->
|
||||
if @enablePersistence
|
||||
if stateKey = @getStateKey(@getLoadSettings().initialPaths)
|
||||
@@ -882,6 +895,7 @@ class AtomEnvironment extends Model
|
||||
detail: error.message
|
||||
dismissable: true
|
||||
|
||||
# TODO: We should deprecate the update events here, and use `atom.autoUpdater` instead
|
||||
onUpdateAvailable: (callback) ->
|
||||
@emitter.on 'update-available', callback
|
||||
|
||||
@@ -889,7 +903,8 @@ class AtomEnvironment extends Model
|
||||
@emitter.emit 'update-available', details
|
||||
|
||||
listenForUpdates: ->
|
||||
@disposables.add(@applicationDelegate.onUpdateAvailable(@updateAvailable.bind(this)))
|
||||
# listen for updates available locally (that have been successfully downloaded)
|
||||
@disposables.add(@autoUpdater.onDidCompleteDownloadingUpdate(@updateAvailable.bind(this)))
|
||||
|
||||
setBodyPlatformClass: ->
|
||||
@document.body.classList.add("platform-#{process.platform}")
|
||||
|
||||
73
src/auto-update-manager.js
Normal file
73
src/auto-update-manager.js
Normal file
@@ -0,0 +1,73 @@
|
||||
'use babel'
|
||||
|
||||
import {Emitter, CompositeDisposable} from 'event-kit'
|
||||
|
||||
export default class AutoUpdateManager {
|
||||
constructor ({applicationDelegate}) {
|
||||
this.applicationDelegate = applicationDelegate
|
||||
this.subscriptions = new CompositeDisposable()
|
||||
this.emitter = new Emitter()
|
||||
|
||||
this.subscriptions.add(
|
||||
applicationDelegate.onDidBeginCheckingForUpdate(() => {
|
||||
this.emitter.emit('did-begin-checking-for-update')
|
||||
}),
|
||||
applicationDelegate.onDidBeginDownloadingUpdate(() => {
|
||||
this.emitter.emit('did-begin-downloading-update')
|
||||
}),
|
||||
applicationDelegate.onDidCompleteDownloadingUpdate((details) => {
|
||||
this.emitter.emit('did-complete-downloading-update', details)
|
||||
}),
|
||||
applicationDelegate.onUpdateNotAvailable(() => {
|
||||
this.emitter.emit('update-not-available')
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
destroy () {
|
||||
this.subscriptions.dispose()
|
||||
this.emitter.dispose()
|
||||
}
|
||||
|
||||
checkForUpdate () {
|
||||
this.applicationDelegate.checkForUpdate()
|
||||
}
|
||||
|
||||
restartAndInstallUpdate () {
|
||||
this.applicationDelegate.restartAndInstallUpdate()
|
||||
}
|
||||
|
||||
getState () {
|
||||
return this.applicationDelegate.getAutoUpdateManagerState()
|
||||
}
|
||||
|
||||
platformSupportsUpdates () {
|
||||
return atom.getReleaseChannel() !== 'dev' && this.getState() !== 'unsupported'
|
||||
}
|
||||
|
||||
onDidBeginCheckingForUpdate (callback) {
|
||||
return this.emitter.on('did-begin-checking-for-update', callback)
|
||||
}
|
||||
|
||||
onDidBeginDownloadingUpdate (callback) {
|
||||
return this.emitter.on('did-begin-downloading-update', callback)
|
||||
}
|
||||
|
||||
onDidCompleteDownloadingUpdate (callback) {
|
||||
return this.emitter.on('did-complete-downloading-update', callback)
|
||||
}
|
||||
|
||||
// TODO: When https://github.com/atom/electron/issues/4587 is closed, we can
|
||||
// add an update-available event.
|
||||
// onUpdateAvailable (callback) {
|
||||
// return this.emitter.on('update-available', callback)
|
||||
// }
|
||||
|
||||
onUpdateNotAvailable (callback) {
|
||||
return this.emitter.on('update-not-available', callback)
|
||||
}
|
||||
|
||||
getPlatform () {
|
||||
return process.platform
|
||||
}
|
||||
}
|
||||
@@ -85,16 +85,16 @@ class AtomApplication
|
||||
else
|
||||
@loadState(options) or @openPath(options)
|
||||
|
||||
openWithOptions: ({pathsToOpen, executedFrom, urlsToOpen, test, pidToKillWhenClosed, devMode, safeMode, newWindow, logFile, profileStartup, timeout, clearWindowState, addToLastWindow}) ->
|
||||
openWithOptions: ({initialPaths, pathsToOpen, executedFrom, urlsToOpen, test, pidToKillWhenClosed, devMode, safeMode, newWindow, logFile, profileStartup, timeout, clearWindowState, addToLastWindow}) ->
|
||||
if test
|
||||
@runTests({headless: true, devMode, @resourcePath, executedFrom, pathsToOpen, logFile, timeout})
|
||||
else if pathsToOpen.length > 0
|
||||
@openPaths({pathsToOpen, executedFrom, pidToKillWhenClosed, newWindow, devMode, safeMode, profileStartup, clearWindowState, addToLastWindow})
|
||||
@openPaths({initialPaths, pathsToOpen, executedFrom, pidToKillWhenClosed, newWindow, devMode, safeMode, profileStartup, clearWindowState, addToLastWindow})
|
||||
else if urlsToOpen.length > 0
|
||||
@openUrl({urlToOpen, devMode, safeMode}) for urlToOpen in urlsToOpen
|
||||
else
|
||||
# Always open a editor window if this is the first instance of Atom.
|
||||
@openPath({pidToKillWhenClosed, newWindow, devMode, safeMode, profileStartup, clearWindowState, addToLastWindow})
|
||||
@openPath({initialPaths, pidToKillWhenClosed, newWindow, devMode, safeMode, profileStartup, clearWindowState, addToLastWindow})
|
||||
|
||||
# Public: Removes the {AtomWindow} from the global window list.
|
||||
removeWindow: (window) ->
|
||||
@@ -304,6 +304,15 @@ class AtomApplication
|
||||
ipcMain.on 'execute-javascript-in-dev-tools', (event, code) ->
|
||||
event.sender.devToolsWebContents?.executeJavaScript(code)
|
||||
|
||||
ipcMain.on 'check-for-update', =>
|
||||
@autoUpdateManager.check()
|
||||
|
||||
ipcMain.on 'get-auto-update-manager-state', (event) =>
|
||||
event.returnValue = @autoUpdateManager.getState()
|
||||
|
||||
ipcMain.on 'execute-javascript-in-dev-tools', (event, code) ->
|
||||
event.sender.devToolsWebContents?.executeJavaScript(code)
|
||||
|
||||
setupDockMenu: ->
|
||||
if process.platform is 'darwin'
|
||||
dockMenu = Menu.buildFromTemplate [
|
||||
@@ -409,8 +418,8 @@ class AtomApplication
|
||||
# :profileStartup - Boolean to control creating a profile of the startup time.
|
||||
# :window - {AtomWindow} to open file paths in.
|
||||
# :addToLastWindow - Boolean of whether this should be opened in last focused window.
|
||||
openPath: ({pathToOpen, pidToKillWhenClosed, newWindow, devMode, safeMode, profileStartup, window, clearWindowState, addToLastWindow} = {}) ->
|
||||
@openPaths({pathsToOpen: [pathToOpen], pidToKillWhenClosed, newWindow, devMode, safeMode, profileStartup, window, clearWindowState, addToLastWindow})
|
||||
openPath: ({initialPaths, pathToOpen, pidToKillWhenClosed, newWindow, devMode, safeMode, profileStartup, window, clearWindowState, addToLastWindow} = {}) ->
|
||||
@openPaths({initialPaths, pathsToOpen: [pathToOpen], pidToKillWhenClosed, newWindow, devMode, safeMode, profileStartup, window, clearWindowState, addToLastWindow})
|
||||
|
||||
# Public: Opens multiple paths, in existing windows if possible.
|
||||
#
|
||||
@@ -423,7 +432,7 @@ class AtomApplication
|
||||
# :windowDimensions - Object with height and width keys.
|
||||
# :window - {AtomWindow} to open file paths in.
|
||||
# :addToLastWindow - Boolean of whether this should be opened in last focused window.
|
||||
openPaths: ({pathsToOpen, executedFrom, pidToKillWhenClosed, newWindow, devMode, safeMode, windowDimensions, profileStartup, window, clearWindowState, addToLastWindow}={}) ->
|
||||
openPaths: ({initialPaths, pathsToOpen, executedFrom, pidToKillWhenClosed, newWindow, devMode, safeMode, windowDimensions, profileStartup, window, clearWindowState, addToLastWindow}={}) ->
|
||||
devMode = Boolean(devMode)
|
||||
safeMode = Boolean(safeMode)
|
||||
clearWindowState = Boolean(clearWindowState)
|
||||
@@ -460,7 +469,7 @@ class AtomApplication
|
||||
windowInitializationScript ?= require.resolve('../initialize-application-window')
|
||||
resourcePath ?= @resourcePath
|
||||
windowDimensions ?= @getDimensionsForNewWindow()
|
||||
openedWindow = new AtomWindow({locationsToOpen, windowInitializationScript, resourcePath, devMode, safeMode, windowDimensions, profileStartup, clearWindowState})
|
||||
openedWindow = new AtomWindow({initialPaths, locationsToOpen, windowInitializationScript, resourcePath, devMode, safeMode, windowDimensions, profileStartup, clearWindowState})
|
||||
|
||||
if pidToKillWhenClosed?
|
||||
@pidsToOpenWindows[pidToKillWhenClosed] = openedWindow
|
||||
@@ -503,7 +512,8 @@ class AtomApplication
|
||||
if (states = @storageFolder.load('application.json'))?.length > 0
|
||||
for state in states
|
||||
@openWithOptions(_.extend(options, {
|
||||
pathsToOpen: state.initialPaths
|
||||
initialPaths: state.initialPaths
|
||||
pathsToOpen: state.initialPaths.filter (directoryPath) -> fs.isDirectorySync(directoryPath)
|
||||
urlsToOpen: []
|
||||
devMode: @devMode
|
||||
safeMode: @safeMode
|
||||
|
||||
@@ -17,7 +17,7 @@ class AtomWindow
|
||||
isSpec: null
|
||||
|
||||
constructor: (settings={}) ->
|
||||
{@resourcePath, pathToOpen, locationsToOpen, @isSpec, @headless, @safeMode, @devMode} = settings
|
||||
{@resourcePath, initialPaths, pathToOpen, locationsToOpen, @isSpec, @headless, @safeMode, @devMode} = settings
|
||||
locationsToOpen ?= [{pathToOpen}] if pathToOpen
|
||||
locationsToOpen ?= []
|
||||
|
||||
@@ -47,13 +47,7 @@ class AtomWindow
|
||||
loadSettings.safeMode ?= false
|
||||
loadSettings.atomHome = process.env.ATOM_HOME
|
||||
loadSettings.clearWindowState ?= false
|
||||
|
||||
# Only send to the first non-spec window created
|
||||
if @constructor.includeShellLoadTime and not @isSpec
|
||||
@constructor.includeShellLoadTime = false
|
||||
loadSettings.shellLoadTime ?= Date.now() - global.shellStartTime
|
||||
|
||||
loadSettings.initialPaths =
|
||||
loadSettings.initialPaths ?=
|
||||
for {pathToOpen} in locationsToOpen when pathToOpen
|
||||
if fs.statSyncNoException(pathToOpen).isFile?()
|
||||
path.dirname(pathToOpen)
|
||||
@@ -62,6 +56,13 @@ class AtomWindow
|
||||
|
||||
loadSettings.initialPaths.sort()
|
||||
|
||||
# Only send to the first non-spec window created
|
||||
if @constructor.includeShellLoadTime and not @isSpec
|
||||
@constructor.includeShellLoadTime = false
|
||||
loadSettings.shellLoadTime ?= Date.now() - global.shellStartTime
|
||||
|
||||
@browserWindow.loadSettings = loadSettings
|
||||
|
||||
@browserWindow.once 'window:loaded', =>
|
||||
@emit 'window:loaded'
|
||||
@loaded = true
|
||||
|
||||
@@ -39,16 +39,24 @@ class AutoUpdateManager
|
||||
|
||||
autoUpdater.on 'checking-for-update', =>
|
||||
@setState(CheckingState)
|
||||
@emitWindowEvent('checking-for-update')
|
||||
|
||||
autoUpdater.on 'update-not-available', =>
|
||||
@setState(NoUpdateAvailableState)
|
||||
@emitWindowEvent('update-not-available')
|
||||
|
||||
autoUpdater.on 'update-available', =>
|
||||
@setState(DownladingState)
|
||||
# We use sendMessage to send an event called 'update-available' in 'update-downloaded'
|
||||
# once the update download is complete. This mismatch between the electron
|
||||
# autoUpdater events is unfortunate but in the interest of not changing the
|
||||
# one existing event handled by applicationDelegate
|
||||
@emitWindowEvent('did-begin-downloading-update')
|
||||
@emit('did-begin-download')
|
||||
|
||||
autoUpdater.on 'update-downloaded', (event, releaseNotes, @releaseVersion) =>
|
||||
@setState(UpdateAvailableState)
|
||||
@emitUpdateAvailableEvent(@getWindows()...)
|
||||
@emitUpdateAvailableEvent()
|
||||
|
||||
@config.onDidChange 'core.automaticallyUpdate', ({newValue}) =>
|
||||
if newValue
|
||||
@@ -64,10 +72,14 @@ class AutoUpdateManager
|
||||
when 'linux'
|
||||
@setState(UnsupportedState)
|
||||
|
||||
emitUpdateAvailableEvent: (windows...) ->
|
||||
emitUpdateAvailableEvent: ->
|
||||
return unless @releaseVersion?
|
||||
for atomWindow in windows
|
||||
atomWindow.sendMessage('update-available', {@releaseVersion})
|
||||
@emitWindowEvent('update-available', {@releaseVersion})
|
||||
return
|
||||
|
||||
emitWindowEvent: (eventName, payload) ->
|
||||
for atomWindow in @getWindows()
|
||||
atomWindow.sendMessage(eventName, payload)
|
||||
return
|
||||
|
||||
setState: (state) ->
|
||||
|
||||
@@ -46,7 +46,7 @@ class BufferedNodeProcess extends BufferedProcess
|
||||
|
||||
options ?= {}
|
||||
options.env ?= Object.create(process.env)
|
||||
options.env['ATOM_SHELL_INTERNAL_RUN_AS_NODE'] = 1
|
||||
options.env['ELECTRON_RUN_AS_NODE'] = 1
|
||||
|
||||
args = args?.slice() ? []
|
||||
args.unshift(command)
|
||||
|
||||
@@ -596,7 +596,15 @@ export default class GitRepositoryAsync {
|
||||
.then(([repo, headCommit]) => Promise.all([repo, headCommit.getTree()]))
|
||||
.then(([repo, tree]) => {
|
||||
const options = new Git.DiffOptions()
|
||||
options.contextLines = 0
|
||||
options.flags = Git.Diff.OPTION.DISABLE_PATHSPEC_MATCH
|
||||
options.pathspec = this.relativize(_path, repo.workdir())
|
||||
if (process.platform === 'win32') {
|
||||
// Ignore eol of line differences on windows so that files checked in
|
||||
// as LF don't report every line modified when the text contains CRLF
|
||||
// endings.
|
||||
options.flags |= Git.Diff.OPTION.IGNORE_WHITESPACE_EOL
|
||||
}
|
||||
return Git.Diff.treeToWorkdir(repo, tree, options)
|
||||
})
|
||||
.then(diff => this._getDiffLines(diff))
|
||||
|
||||
@@ -423,10 +423,6 @@ class Pane extends Model
|
||||
|
||||
return if item in @items
|
||||
|
||||
pendingItem = @getPendingItem()
|
||||
@destroyItem(pendingItem) if pendingItem?
|
||||
@setPendingItem(item) if pending
|
||||
|
||||
if typeof item.onDidDestroy is 'function'
|
||||
itemSubscriptions = new CompositeDisposable
|
||||
itemSubscriptions.add item.onDidDestroy => @removeItem(item, false)
|
||||
@@ -437,7 +433,11 @@ class Pane extends Model
|
||||
@subscriptionsPerItem.set item, itemSubscriptions
|
||||
|
||||
@items.splice(index, 0, item)
|
||||
lastPendingItem = @getPendingItem()
|
||||
@setPendingItem(item) if pending
|
||||
|
||||
@emitter.emit 'did-add-item', {item, index, moved}
|
||||
@destroyItem(lastPendingItem) if lastPendingItem?
|
||||
@setActiveItem(item) unless @getActiveItem()?
|
||||
item
|
||||
|
||||
|
||||
@@ -56,6 +56,7 @@ class Project extends Model
|
||||
|
||||
deserialize: (state) ->
|
||||
state.paths = [state.path] if state.path? # backward compatibility
|
||||
state.paths = state.paths.filter (directoryPath) -> fs.isDirectorySync(directoryPath)
|
||||
|
||||
@buffers = _.compact state.buffers.map (bufferState) ->
|
||||
# Check that buffer's file path is accessible
|
||||
|
||||
@@ -43,7 +43,7 @@ class TextEditorComponent
|
||||
@assert domNode?, "TextEditorComponent::domNode was set to null."
|
||||
@domNodeValue = domNode
|
||||
|
||||
constructor: ({@editor, @hostElement, @rootElement, @stylesElement, @useShadowDOM, tileSize, @views, @themes, @config, @workspace, @assert, @grammars}) ->
|
||||
constructor: ({@editor, @hostElement, @rootElement, @stylesElement, @useShadowDOM, tileSize, @views, @themes, @config, @workspace, @assert, @grammars, scrollPastEnd}) ->
|
||||
@tileSize = tileSize if tileSize?
|
||||
@disposables = new CompositeDisposable
|
||||
|
||||
@@ -61,6 +61,7 @@ class TextEditorComponent
|
||||
stoppedScrollingDelay: 200
|
||||
config: @config
|
||||
lineTopIndex: lineTopIndex
|
||||
scrollPastEnd: scrollPastEnd
|
||||
|
||||
@presenter.onDidUpdateState(@requestUpdate)
|
||||
|
||||
|
||||
@@ -17,6 +17,8 @@ class TextEditorElement extends HTMLElement
|
||||
focusOnAttach: false
|
||||
hasTiledRendering: true
|
||||
logicalDisplayBuffer: true
|
||||
scrollPastEnd: true
|
||||
autoHeight: true
|
||||
|
||||
createdCallback: ->
|
||||
# Use globals when the following instance variables aren't set.
|
||||
@@ -38,6 +40,9 @@ class TextEditorElement extends HTMLElement
|
||||
@setAttribute('tabindex', -1)
|
||||
|
||||
initializeContent: (attributes) ->
|
||||
unless @autoHeight
|
||||
@style.height = "100%"
|
||||
|
||||
if @config.get('editor.useShadowDOM')
|
||||
@useShadowDOM = true
|
||||
|
||||
@@ -86,7 +91,7 @@ class TextEditorElement extends HTMLElement
|
||||
@subscriptions.add @component.onDidChangeScrollLeft =>
|
||||
@emitter.emit("did-change-scroll-left", arguments...)
|
||||
|
||||
initialize: (model, {@views, @config, @themes, @workspace, @assert, @styles, @grammars}) ->
|
||||
initialize: (model, {@views, @config, @themes, @workspace, @assert, @styles, @grammars}, @autoHeight = true, @scrollPastEnd = true) ->
|
||||
throw new Error("Must pass a config parameter when initializing TextEditorElements") unless @views?
|
||||
throw new Error("Must pass a config parameter when initializing TextEditorElements") unless @config?
|
||||
throw new Error("Must pass a themes parameter when initializing TextEditorElements") unless @themes?
|
||||
@@ -143,6 +148,7 @@ class TextEditorElement extends HTMLElement
|
||||
workspace: @workspace
|
||||
assert: @assert
|
||||
grammars: @grammars
|
||||
scrollPastEnd: @scrollPastEnd
|
||||
)
|
||||
@rootElement.appendChild(@component.getDomNode())
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ class TextEditorPresenter
|
||||
minimumReflowInterval: 200
|
||||
|
||||
constructor: (params) ->
|
||||
{@model, @config, @lineTopIndex} = params
|
||||
{@model, @config, @lineTopIndex, scrollPastEnd} = params
|
||||
{@cursorBlinkPeriod, @cursorBlinkResumeDelay, @stoppedScrollingDelay, @tileSize} = params
|
||||
{@contentFrameWidth} = params
|
||||
|
||||
@@ -42,6 +42,8 @@ class TextEditorPresenter
|
||||
@startReflowing() if @continuousReflow
|
||||
@updating = false
|
||||
|
||||
@scrollPastEndOverride = scrollPastEnd ? true
|
||||
|
||||
setLinesYardstick: (@linesYardstick) ->
|
||||
|
||||
getLinesYardstick: -> @linesYardstick
|
||||
@@ -661,7 +663,7 @@ class TextEditorPresenter
|
||||
return unless @contentHeight? and @clientHeight?
|
||||
|
||||
contentHeight = @contentHeight
|
||||
if @scrollPastEnd
|
||||
if @scrollPastEnd and @scrollPastEndOverride
|
||||
extraScrollHeight = @clientHeight - (@lineHeight * 3)
|
||||
contentHeight += extraScrollHeight if extraScrollHeight > 0
|
||||
scrollHeight = Math.max(contentHeight, @height)
|
||||
|
||||
@@ -11,6 +11,7 @@ Selection = require './selection'
|
||||
TextMateScopeSelector = require('first-mate').ScopeSelector
|
||||
{Directory} = require "pathwatcher"
|
||||
GutterContainer = require './gutter-container'
|
||||
TextEditorElement = require './text-editor-element'
|
||||
|
||||
# Essential: This class represents all essential editing state for a single
|
||||
# {TextBuffer}, including cursor and selection positions, folds, and soft wraps.
|
||||
@@ -61,6 +62,10 @@ class TextEditor extends Model
|
||||
suppressSelectionMerging: false
|
||||
selectionFlashDuration: 500
|
||||
gutterContainer: null
|
||||
editorElement: null
|
||||
|
||||
Object.defineProperty @prototype, "element",
|
||||
get: -> @getElement()
|
||||
|
||||
@deserialize: (state, atomEnvironment) ->
|
||||
try
|
||||
@@ -95,7 +100,7 @@ class TextEditor extends Model
|
||||
softWrapped, @displayBuffer, @selectionsMarkerLayer, buffer, suppressCursorCreation,
|
||||
@mini, @placeholderText, lineNumberGutterVisible, largeFileMode, @config,
|
||||
@notificationManager, @packageManager, @clipboard, @viewRegistry, @grammarRegistry,
|
||||
@project, @assert, @applicationDelegate
|
||||
@project, @assert, @applicationDelegate, grammar, showInvisibles, @autoHeight, @scrollPastEnd
|
||||
} = params
|
||||
|
||||
throw new Error("Must pass a config parameter when constructing TextEditors") unless @config?
|
||||
@@ -114,11 +119,15 @@ class TextEditor extends Model
|
||||
@cursors = []
|
||||
@cursorsByMarkerId = new Map
|
||||
@selections = []
|
||||
@autoHeight ?= true
|
||||
@scrollPastEnd ?= true
|
||||
@hasTerminatedPendingState = false
|
||||
|
||||
showInvisibles ?= true
|
||||
|
||||
buffer ?= new TextBuffer
|
||||
@displayBuffer ?= new DisplayBuffer({
|
||||
buffer, tabLength, softWrapped, ignoreInvisibles: @mini, largeFileMode,
|
||||
buffer, tabLength, softWrapped, ignoreInvisibles: @mini or not showInvisibles, largeFileMode,
|
||||
@config, @assert, @grammarRegistry, @packageManager
|
||||
})
|
||||
@buffer = @displayBuffer.buffer
|
||||
@@ -147,6 +156,9 @@ class TextEditor extends Model
|
||||
priority: 0
|
||||
visible: lineNumberGutterVisible
|
||||
|
||||
if grammar?
|
||||
@setGrammar(grammar)
|
||||
|
||||
serialize: ->
|
||||
deserializer: 'TextEditor'
|
||||
id: @id
|
||||
@@ -3142,6 +3154,10 @@ class TextEditor extends Model
|
||||
Section: TextEditor Rendering
|
||||
###
|
||||
|
||||
# Get the Element for the editor.
|
||||
getElement: ->
|
||||
@editorElement ?= new TextEditorElement().initialize(this, atom, @autoHeight, @scrollPastEnd)
|
||||
|
||||
# Essential: Retrieves the greyed out placeholder of a mini editor.
|
||||
#
|
||||
# Returns a {String}.
|
||||
@@ -3216,7 +3232,7 @@ class TextEditor extends Model
|
||||
setFirstVisibleScreenRow: (screenRow, fromView) ->
|
||||
unless fromView
|
||||
maxScreenRow = @getScreenLineCount() - 1
|
||||
unless @config.get('editor.scrollPastEnd')
|
||||
unless @config.get('editor.scrollPastEnd') and @scrollPastEnd
|
||||
height = @displayBuffer.getHeight()
|
||||
lineHeightInPixels = @displayBuffer.getLineHeightInPixels()
|
||||
if height? and lineHeightInPixels?
|
||||
|
||||
@@ -63,6 +63,8 @@ class TooltipManager
|
||||
# full list of options. You can also supply the following additional 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.
|
||||
# * `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.
|
||||
|
||||
@@ -64,7 +64,9 @@ Tooltip.prototype.init = function (element, options) {
|
||||
|
||||
if (trigger === 'click') {
|
||||
this.disposables.add(listen(this.element, 'click', this.options.selector, this.toggle.bind(this)))
|
||||
} else if (trigger !== 'manual') {
|
||||
} else if (trigger === 'manual') {
|
||||
this.show()
|
||||
} else {
|
||||
var eventIn, eventOut
|
||||
|
||||
if (trigger === 'hover') {
|
||||
|
||||
@@ -171,6 +171,11 @@ class ViewRegistry
|
||||
if object instanceof HTMLElement
|
||||
return object
|
||||
|
||||
if typeof object?.getElement is 'function'
|
||||
element = object.getElement()
|
||||
if element instanceof HTMLElement
|
||||
return element
|
||||
|
||||
if object?.element instanceof HTMLElement
|
||||
return object.element
|
||||
|
||||
|
||||
@@ -43,6 +43,12 @@ class Workspace extends Model
|
||||
@defaultDirectorySearcher = new DefaultDirectorySearcher()
|
||||
@consumeServices(@packageManager)
|
||||
|
||||
# One cannot simply .bind here since it could be used as a component with
|
||||
# Etch, in which case it'd be `new`d. And when it's `new`d, `this` is always
|
||||
# the newly created object.
|
||||
realThis = this
|
||||
@buildTextEditor = -> Workspace.prototype.buildTextEditor.apply(realThis, arguments)
|
||||
|
||||
@panelContainers =
|
||||
top: new PanelContainer({location: 'top'})
|
||||
left: new PanelContainer({location: 'left'})
|
||||
|
||||
Reference in New Issue
Block a user