mirror of
https://github.com/atom/atom.git
synced 2026-04-06 03:02:13 -04:00
Merge branch 'master' into ns-mb-detangle-editor
This commit is contained in:
@@ -5,11 +5,12 @@ Ubuntu LTS 12.04 64-bit is the recommended platform.
|
||||
## Requirements
|
||||
|
||||
* OS with 64-bit or 32-bit architecture
|
||||
* C++ toolchain
|
||||
* C++11 toolchain
|
||||
* [Git](https://git-scm.com/)
|
||||
* [Node.js](https://nodejs.org/en/download/) (0.10.x or above)
|
||||
* [npm](https://www.npmjs.com/) v1.4.x or above (automatically bundled with Node.js)
|
||||
* Node.js (4.x or above) (Can be installed via [nvm](https://github.com/creationix/nvm)).
|
||||
* [npm](https://www.npmjs.com/) v3.10.5 or above (automatically bundled with Node.js)
|
||||
* `npm -v` to check the version.
|
||||
* `npm install -g npm` to upgrade if necessary.
|
||||
* `npm config set python /usr/bin/python2 -g` to ensure that gyp uses python2.
|
||||
* You might need to run this command as `sudo`, depending on how you have set up [npm](https://github.com/joyent/node/wiki/Installing-Node.js-via-package-manager#ubuntu-mint-elementary-os).
|
||||
* development headers for [GNOME Keyring](https://wiki.gnome.org/Projects/GnomeKeyring)
|
||||
@@ -17,10 +18,18 @@ Ubuntu LTS 12.04 64-bit is the recommended platform.
|
||||
### Ubuntu / Debian
|
||||
|
||||
* `sudo apt-get install build-essential git libgnome-keyring-dev fakeroot`
|
||||
* Instructions for [Node.js](https://github.com/nodejs/node-v0.x-archive/wiki/Installing-Node.js-via-package-manager#debian-and-ubuntu-based-linux-distributions).
|
||||
* Make sure the command `node` is available after Node.js installation (some systems install it as `nodejs`).
|
||||
* Use `which node` to check if it is available.
|
||||
* Use `sudo update-alternatives --install /usr/bin/node node /usr/bin/nodejs 10` to update it.
|
||||
* Install Node.js and npm:
|
||||
* Install [nvm](https://github.com/creationix/nvm).
|
||||
* Run `nvm install 4` to install Node 4.x.
|
||||
* Run `npm install -g npm` to upgrade to the latest npm.
|
||||
* You may need to install a newer C++ compiler with C++11 support if script/bootstrap has errors:
|
||||
```sh
|
||||
sudo add-apt-repository ppa:ubuntu-toolchain-r/test
|
||||
sudo apt-get update
|
||||
sudo apt-get install gcc-5 g++-5
|
||||
sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-5 80 --slave /usr/bin/g++ g++ /usr/bin/g++-5
|
||||
sudo update-alternatives --config gcc # choose gcc-5 from the list
|
||||
```
|
||||
|
||||
### Fedora / CentOS / RHEL
|
||||
|
||||
|
||||
@@ -58,7 +58,7 @@
|
||||
"sinon": "1.17.4",
|
||||
"source-map-support": "^0.3.2",
|
||||
"temp": "0.8.1",
|
||||
"text-buffer": "9.2.3",
|
||||
"text-buffer": "9.2.6",
|
||||
"typescript-simple": "1.0.0",
|
||||
"underscore-plus": "^1.6.6",
|
||||
"winreg": "^1.2.1",
|
||||
@@ -87,13 +87,13 @@
|
||||
"autoflow": "0.27.0",
|
||||
"autosave": "0.23.1",
|
||||
"background-tips": "0.26.1",
|
||||
"bookmarks": "0.41.0",
|
||||
"bookmarks": "0.41.1",
|
||||
"bracket-matcher": "0.82.1",
|
||||
"command-palette": "0.38.0",
|
||||
"deprecation-cop": "0.54.1",
|
||||
"dev-live-reload": "0.47.0",
|
||||
"encoding-selector": "0.22.0",
|
||||
"exception-reporting": "0.38.1",
|
||||
"exception-reporting": "0.38.2",
|
||||
"find-and-replace": "0.201.0",
|
||||
"fuzzy-finder": "1.3.0",
|
||||
"git-diff": "1.1.0",
|
||||
|
||||
@@ -197,6 +197,21 @@ class AtomReporter
|
||||
time = "0#{time}" if time.length < 3
|
||||
@time.textContent = "#{time[0...-2]}.#{time[-2..]}s"
|
||||
|
||||
specTitle: (spec) ->
|
||||
parentDescs = []
|
||||
s = spec.suite
|
||||
while s
|
||||
parentDescs.unshift(s.description)
|
||||
s = s.parentSuite
|
||||
|
||||
suiteString = ""
|
||||
indent = ""
|
||||
for desc in parentDescs
|
||||
suiteString += indent + desc + "\n"
|
||||
indent += " "
|
||||
|
||||
"#{suiteString} #{indent} it #{spec.description}"
|
||||
|
||||
addSpecs: (specs) ->
|
||||
coreSpecs = 0
|
||||
bundledPackageSpecs = 0
|
||||
@@ -204,6 +219,7 @@ class AtomReporter
|
||||
for spec in specs
|
||||
symbol = document.createElement('li')
|
||||
symbol.setAttribute('id', "spec-summary-#{spec.id}")
|
||||
symbol.setAttribute('title', @specTitle(spec))
|
||||
symbol.className = "spec-summary pending"
|
||||
switch spec.specType
|
||||
when 'core'
|
||||
|
||||
@@ -1,159 +1,85 @@
|
||||
'use babel'
|
||||
/** @babel */
|
||||
/* eslint-env jasmine */
|
||||
|
||||
import child_process from 'child_process'
|
||||
import environmentHelpers from '../src/environment-helpers'
|
||||
import os from 'os'
|
||||
import updateProcessEnv from '../src/update-process-env'
|
||||
import dedent from 'dedent'
|
||||
|
||||
describe('Environment handling', () => {
|
||||
let originalEnv
|
||||
let options
|
||||
describe('updateProcessEnv(launchEnv)', function () {
|
||||
let originalProcessEnv, originalProcessPlatform
|
||||
|
||||
beforeEach(() => {
|
||||
originalEnv = process.env
|
||||
delete process._originalEnv
|
||||
options = {
|
||||
platform: process.platform,
|
||||
env: Object.assign({}, process.env)
|
||||
}
|
||||
beforeEach(function () {
|
||||
originalProcessEnv = process.env
|
||||
originalProcessPlatform = process.platform
|
||||
process.env = {}
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
process.env = originalEnv
|
||||
delete process._originalEnv
|
||||
afterEach(function () {
|
||||
process.env = originalProcessEnv
|
||||
process.platform = originalProcessPlatform
|
||||
})
|
||||
|
||||
describe('on macOS, when PWD is not set', () => {
|
||||
beforeEach(() => {
|
||||
options.platform = 'darwin'
|
||||
})
|
||||
describe('when the launch environment appears to come from a shell', function () {
|
||||
it('updates process.env to match the launch environment', function () {
|
||||
process.env = {
|
||||
WILL_BE_DELETED: 'hi',
|
||||
NODE_ENV: 'the-node-env',
|
||||
NODE_PATH: '/the/node/path',
|
||||
}
|
||||
const initialProcessEnv = process.env
|
||||
|
||||
describe('needsPatching', () => {
|
||||
it('returns true if PWD is unset', () => {
|
||||
delete options.env.PWD
|
||||
expect(environmentHelpers.needsPatching(options)).toBe(true)
|
||||
options.env.PWD = undefined
|
||||
expect(environmentHelpers.needsPatching(options)).toBe(true)
|
||||
options.env.PWD = null
|
||||
expect(environmentHelpers.needsPatching(options)).toBe(true)
|
||||
options.env.PWD = false
|
||||
expect(environmentHelpers.needsPatching(options)).toBe(true)
|
||||
updateProcessEnv({PWD: '/the/dir', KEY1: 'value1', KEY2: 'value2'})
|
||||
expect(process.env).toEqual({
|
||||
PWD: '/the/dir',
|
||||
KEY1: 'value1',
|
||||
KEY2: 'value2',
|
||||
NODE_ENV: 'the-node-env',
|
||||
NODE_PATH: '/the/node/path',
|
||||
})
|
||||
|
||||
it('returns false if PWD is set', () => {
|
||||
options.env.PWD = 'xterm'
|
||||
expect(environmentHelpers.needsPatching(options)).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
describe('normalize', () => {
|
||||
it('changes process.env if PWD is unset', () => {
|
||||
if (process.platform === 'win32') {
|
||||
return
|
||||
}
|
||||
delete options.env.PWD
|
||||
environmentHelpers.normalize(options)
|
||||
expect(process._originalEnv).toBeDefined()
|
||||
expect(process._originalEnv).toBeTruthy()
|
||||
expect(process.env).toBeDefined()
|
||||
expect(process.env).toBeTruthy()
|
||||
expect(process.env.PWD).toBeDefined()
|
||||
expect(process.env.PWD).toBeTruthy()
|
||||
expect(process.env.PATH).toBeDefined()
|
||||
expect(process.env.PATH).toBeTruthy()
|
||||
expect(process.env.ATOM_HOME).toBeDefined()
|
||||
expect(process.env.ATOM_HOME).toBeTruthy()
|
||||
})
|
||||
// See #11302. On Windows, `process.env` is a magic object that offers
|
||||
// case-insensitive environment variable matching, so we cannot replace it
|
||||
// with another object.
|
||||
expect(process.env).toBe(initialProcessEnv)
|
||||
})
|
||||
})
|
||||
|
||||
describe('on a platform other than macOS', () => {
|
||||
beforeEach(() => {
|
||||
options.platform = 'penguin'
|
||||
})
|
||||
describe('when the launch environment does not come from a shell', function () {
|
||||
describe('on osx', function () {
|
||||
it('updates process.env to match the environment in the user\'s login shell', function () {
|
||||
process.platform = 'darwin'
|
||||
process.env.SHELL = '/my/custom/bash'
|
||||
|
||||
describe('needsPatching', () => {
|
||||
it('returns false if PWD is set or unset', () => {
|
||||
delete options.env.PWD
|
||||
expect(environmentHelpers.needsPatching(options)).toBe(false)
|
||||
options.env.PWD = undefined
|
||||
expect(environmentHelpers.needsPatching(options)).toBe(false)
|
||||
options.env.PWD = null
|
||||
expect(environmentHelpers.needsPatching(options)).toBe(false)
|
||||
options.env.PWD = false
|
||||
expect(environmentHelpers.needsPatching(options)).toBe(false)
|
||||
options.env.PWD = '/'
|
||||
expect(environmentHelpers.needsPatching(options)).toBe(false)
|
||||
})
|
||||
|
||||
it('returns false for linux', () => {
|
||||
options.platform = 'linux'
|
||||
options.PWD = '/'
|
||||
expect(environmentHelpers.needsPatching(options)).toBe(false)
|
||||
})
|
||||
|
||||
it('returns false for windows', () => {
|
||||
options.platform = 'win32'
|
||||
options.PWD = 'c:\\'
|
||||
expect(environmentHelpers.needsPatching(options)).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
describe('normalize', () => {
|
||||
it('does not change the environment', () => {
|
||||
if (process.platform === 'win32') {
|
||||
return
|
||||
}
|
||||
delete options.env.PWD
|
||||
environmentHelpers.normalize(options)
|
||||
expect(process._originalEnv).toBeUndefined()
|
||||
expect(process.env).toBeDefined()
|
||||
expect(process.env).toBeTruthy()
|
||||
expect(process.env.PATH).toBeDefined()
|
||||
expect(process.env.PATH).toBeTruthy()
|
||||
expect(process.env.PWD).toBeUndefined()
|
||||
expect(process.env.PATH).toBe(originalEnv.PATH)
|
||||
expect(process.env.ATOM_HOME).toBeDefined()
|
||||
expect(process.env.ATOM_HOME).toBeTruthy()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('getFromShell', () => {
|
||||
describe('when things are configured properly', () => {
|
||||
beforeEach(() => {
|
||||
spyOn(child_process, 'spawnSync').andReturn({
|
||||
stdout: 'FOO=BAR' + os.EOL + 'TERM=xterm-something' + os.EOL +
|
||||
'PATH=/usr/bin:/bin:/usr/sbin:/sbin:/crazy/path'
|
||||
stdout: dedent`
|
||||
FOO=BAR=BAZ=QUUX
|
||||
TERM=xterm-something
|
||||
PATH=/usr/bin:/bin:/usr/sbin:/sbin:/crazy/path
|
||||
`
|
||||
})
|
||||
})
|
||||
|
||||
it('returns an object containing the information from the user\'s shell environment', () => {
|
||||
let env = environmentHelpers.getFromShell()
|
||||
expect(env.FOO).toEqual('BAR')
|
||||
expect(env.TERM).toEqual('xterm-something')
|
||||
expect(env.PATH).toEqual('/usr/bin:/bin:/usr/sbin:/sbin:/crazy/path')
|
||||
updateProcessEnv(process.env)
|
||||
expect(child_process.spawnSync.mostRecentCall.args[0]).toBe('/my/custom/bash')
|
||||
expect(process.env).toEqual({
|
||||
FOO: 'BAR=BAZ=QUUX',
|
||||
TERM: 'xterm-something',
|
||||
PATH: '/usr/bin:/bin:/usr/sbin:/sbin:/crazy/path'
|
||||
})
|
||||
|
||||
// Doesn't error
|
||||
updateProcessEnv(null)
|
||||
})
|
||||
})
|
||||
|
||||
describe('when an error occurs launching the shell', () => {
|
||||
beforeEach(() => {
|
||||
spyOn(child_process, 'spawnSync').andReturn({
|
||||
error: new Error('testing when an error occurs')
|
||||
})
|
||||
})
|
||||
describe('not on osx', function () {
|
||||
it('does not update process.env', function () {
|
||||
process.platform = 'win32'
|
||||
spyOn(child_process, 'spawnSync')
|
||||
process.env = {FOO: 'bar'}
|
||||
|
||||
it('returns undefined', () => {
|
||||
expect(environmentHelpers.getFromShell()).toBeUndefined()
|
||||
})
|
||||
|
||||
it('leaves the environment as-is when normalize() is called', () => {
|
||||
options.platform = 'darwin'
|
||||
delete options.env.PWD
|
||||
expect(environmentHelpers.needsPatching(options)).toBe(true)
|
||||
environmentHelpers.normalize(options)
|
||||
expect(process.env).toBeDefined()
|
||||
expect(process._originalEnv).toBeUndefined()
|
||||
updateProcessEnv(process.env)
|
||||
expect(child_process.spawnSync).not.toHaveBeenCalled()
|
||||
expect(process.env).toEqual({FOO: 'bar'})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -5600,6 +5600,13 @@ describe "TextEditor", ->
|
||||
rangeIsReversed: false
|
||||
}
|
||||
|
||||
it "does not throw errors after the marker's containing layer is destroyed", ->
|
||||
layer = editor.addMarkerLayer()
|
||||
marker = layer.markBufferRange([[2, 4], [6, 8]])
|
||||
decoration = editor.decorateMarker(marker, type: 'highlight', class: 'foo')
|
||||
layer.destroy()
|
||||
editor.decorationsStateForScreenRowRange(0, 5)
|
||||
|
||||
describe "::decorateMarkerLayer", ->
|
||||
it "based on the markers in the layer, includes multiple decoration objects with the same properties and different ranges in the object returned from ::decorationsStateForScreenRowRange", ->
|
||||
layer1 = editor.getBuffer().addMarkerLayer()
|
||||
@@ -5753,6 +5760,17 @@ describe "TextEditor", ->
|
||||
editor.setEditorWidthInChars(10)
|
||||
expect(editor.lineTextForScreenRow(0)).toBe 'var '
|
||||
|
||||
describe "the softWrapHangingIndent setting", ->
|
||||
it "controls how much extra indentation is applied to soft-wrapped lines", ->
|
||||
editor.setText('123456789')
|
||||
editor.setEditorWidthInChars(8)
|
||||
atom.config.set('editor.softWrap', true)
|
||||
atom.config.set('editor.softWrapHangingIndent', 2)
|
||||
expect(editor.lineTextForScreenRow(1)).toEqual ' 9'
|
||||
|
||||
atom.config.set('editor.softWrapHangingIndent', 4)
|
||||
expect(editor.lineTextForScreenRow(1)).toEqual ' 9'
|
||||
|
||||
describe "::getElement", ->
|
||||
it "returns an element", ->
|
||||
expect(editor.getElement() instanceof HTMLElement).toBe(true)
|
||||
|
||||
29
spec/title-bar-spec.coffee
Normal file
29
spec/title-bar-spec.coffee
Normal file
@@ -0,0 +1,29 @@
|
||||
TitleBar = require '../src/title-bar'
|
||||
|
||||
describe "TitleBar", ->
|
||||
it "updates the title based on document.title when the active pane item changes", ->
|
||||
titleBar = new TitleBar({
|
||||
workspace: atom.workspace,
|
||||
themes: atom.themes,
|
||||
applicationDelegate: atom.applicationDelegate,
|
||||
})
|
||||
|
||||
expect(titleBar.element.querySelector('.title').textContent).toBe document.title
|
||||
initialTitle = document.title
|
||||
|
||||
atom.workspace.getActivePane().activateItem({
|
||||
getTitle: -> 'Test Title'
|
||||
})
|
||||
|
||||
expect(document.title).not.toBe(initialTitle)
|
||||
expect(titleBar.element.querySelector('.title').textContent).toBe document.title
|
||||
|
||||
it "can update the sheet offset for the current window based on its height", ->
|
||||
titleBar = new TitleBar({
|
||||
workspace: atom.workspace,
|
||||
themes: atom.themes,
|
||||
applicationDelegate: atom.applicationDelegate,
|
||||
})
|
||||
expect(->
|
||||
titleBar.updateWindowSheetOffset()
|
||||
).not.toThrow()
|
||||
@@ -206,6 +206,7 @@ describe "WindowEventHandler", ->
|
||||
webContentsSpy = jasmine.createSpyObj("webContents", ["copy", "paste"])
|
||||
spyOn(atom.applicationDelegate, "getCurrentWindow").andReturn({
|
||||
webContents: webContentsSpy
|
||||
on: ->
|
||||
})
|
||||
|
||||
nativeKeyBindingsInput = document.createElement("input")
|
||||
|
||||
@@ -31,6 +31,7 @@ ContextMenuManager = require './context-menu-manager'
|
||||
CommandInstaller = require './command-installer'
|
||||
Clipboard = require './clipboard'
|
||||
Project = require './project'
|
||||
TitleBar = require './title-bar'
|
||||
Workspace = require './workspace'
|
||||
PanelContainer = require './panel-container'
|
||||
Panel = require './panel'
|
||||
@@ -196,6 +197,7 @@ class AtomEnvironment extends Model
|
||||
notificationManager: @notifications, @applicationDelegate, @clipboard, viewRegistry: @views, assert: @assert.bind(this),
|
||||
textEditorRegistry: @textEditors,
|
||||
})
|
||||
|
||||
@themes.workspace = @workspace
|
||||
|
||||
@autoUpdater = new AutoUpdateManager({@applicationDelegate})
|
||||
@@ -554,10 +556,6 @@ class AtomEnvironment extends Model
|
||||
# Extended: Set the full screen state of the current window.
|
||||
setFullScreen: (fullScreen=false) ->
|
||||
@applicationDelegate.setWindowFullScreen(fullScreen)
|
||||
if fullScreen
|
||||
@document.body.classList.add("fullscreen")
|
||||
else
|
||||
@document.body.classList.remove("fullscreen")
|
||||
|
||||
# Extended: Toggle the full screen state of the current window.
|
||||
toggleFullScreen: ->
|
||||
@@ -683,6 +681,9 @@ class AtomEnvironment extends Model
|
||||
@deserialize(state) if state?
|
||||
@deserializeTimings.atom = Date.now() - startTime
|
||||
|
||||
if process.platform is 'darwin' and @config.get('core.useCustomTitleBar')
|
||||
@workspace.addHeaderPanel({item: new TitleBar({@workspace, @themes, @applicationDelegate})})
|
||||
|
||||
@document.body.appendChild(@views.getView(@workspace))
|
||||
@backgroundStylesheet?.remove()
|
||||
|
||||
|
||||
@@ -261,3 +261,9 @@ if process.platform in ['win32', 'linux']
|
||||
type: 'boolean'
|
||||
default: false
|
||||
description: 'Automatically hide the menu bar and toggle it by pressing Alt. This is only supported on Windows & Linux.'
|
||||
|
||||
if process.platform is 'darwin'
|
||||
module.exports.core.properties.useCustomTitleBar =
|
||||
type: 'boolean'
|
||||
default: false
|
||||
description: 'Use custom, theme-aware title-bar.<br />Note: Note: This currently does not include a proxy icon.<br />This setting will require a relaunch of Atom to take effect.'
|
||||
|
||||
@@ -144,7 +144,7 @@ class DecorationManager extends Model
|
||||
else
|
||||
delete @overlayDecorationsById[decoration.id]
|
||||
|
||||
didDestroyDecoration: (decoration) ->
|
||||
didDestroyMarkerDecoration: (decoration) ->
|
||||
{marker} = decoration
|
||||
return unless decorations = @decorationsByMarkerId[marker.id]
|
||||
index = decorations.indexOf(decoration)
|
||||
|
||||
@@ -69,16 +69,16 @@ class Decoration
|
||||
@destroyed = false
|
||||
@markerDestroyDisposable = @marker.onDidDestroy => @destroy()
|
||||
|
||||
# Essential: Destroy this marker.
|
||||
# Essential: Destroy this marker decoration.
|
||||
#
|
||||
# If you own the marker, you should use {DisplayMarker::destroy} which will destroy
|
||||
# this decoration.
|
||||
# You can also destroy the marker if you own it, which will destroy this
|
||||
# decoration.
|
||||
destroy: ->
|
||||
return if @destroyed
|
||||
@markerDestroyDisposable.dispose()
|
||||
@markerDestroyDisposable = null
|
||||
@destroyed = true
|
||||
@decorationManager.didDestroyDecoration(this)
|
||||
@decorationManager.didDestroyMarkerDecoration(this)
|
||||
@emitter.emit 'did-destroy'
|
||||
@emitter.dispose()
|
||||
|
||||
|
||||
@@ -1,116 +0,0 @@
|
||||
'use babel'
|
||||
|
||||
import {spawnSync} from 'child_process'
|
||||
import os from 'os'
|
||||
|
||||
// Gets a dump of the user's configured shell environment.
|
||||
//
|
||||
// Returns the output of the `env` command or `undefined` if there was an error.
|
||||
function getRawShellEnv () {
|
||||
let shell = getUserShell()
|
||||
|
||||
// The `-ilc` set of options was tested to work with the macOS v10.11
|
||||
// default-installed versions of bash, zsh, sh, and ksh. It *does not*
|
||||
// work with csh or tcsh.
|
||||
let results = spawnSync(shell, ['-ilc', 'env'], {encoding: 'utf8'})
|
||||
if (results.error || !results.stdout || results.stdout.length <= 0) {
|
||||
return
|
||||
}
|
||||
|
||||
return results.stdout
|
||||
}
|
||||
|
||||
function getUserShell () {
|
||||
if (process.env.SHELL) {
|
||||
return process.env.SHELL
|
||||
}
|
||||
|
||||
return '/bin/bash'
|
||||
}
|
||||
|
||||
// Gets the user's configured shell environment.
|
||||
//
|
||||
// Returns a copy of the user's shell enviroment.
|
||||
function getFromShell () {
|
||||
let shellEnvText = getRawShellEnv()
|
||||
if (!shellEnvText) {
|
||||
return
|
||||
}
|
||||
|
||||
let env = {}
|
||||
|
||||
for (let line of shellEnvText.split(os.EOL)) {
|
||||
if (line.includes('=')) {
|
||||
let components = line.split('=')
|
||||
if (components.length === 2) {
|
||||
env[components[0]] = components[1]
|
||||
} else {
|
||||
let k = components.shift()
|
||||
let v = components.join('=')
|
||||
env[k] = v
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return env
|
||||
}
|
||||
|
||||
function needsPatching (options = { platform: process.platform, env: process.env }) {
|
||||
if (options.platform === 'darwin' && !options.env.PWD) {
|
||||
let shell = getUserShell()
|
||||
if (shell.endsWith('csh') || shell.endsWith('tcsh') || shell.endsWith('fish')) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// Fix for #11302 because `process.env` on Windows is a magic object that offers case-insensitive
|
||||
// environment variable matching. By always cloning to `process.env` we prevent breaking the
|
||||
// underlying functionality.
|
||||
function clone (to, from) {
|
||||
for (var key in to) {
|
||||
// Don't erase NODE_ENV. Fixes #12024
|
||||
if (key !== 'NODE_ENV') {
|
||||
delete to[key]
|
||||
}
|
||||
}
|
||||
|
||||
Object.assign(to, from)
|
||||
}
|
||||
|
||||
function normalize (options = {}) {
|
||||
if (options && options.env) {
|
||||
clone(process.env, options.env)
|
||||
}
|
||||
|
||||
if (!options.env) {
|
||||
options.env = process.env
|
||||
}
|
||||
|
||||
if (!options.platform) {
|
||||
options.platform = process.platform
|
||||
}
|
||||
|
||||
if (needsPatching(options)) {
|
||||
// Patch the `process.env` on startup to fix the problem first documented
|
||||
// in #4126. Retain the original in case someone needs it.
|
||||
let shellEnv = getFromShell()
|
||||
if (shellEnv && shellEnv.PATH) {
|
||||
process._originalEnv = Object.assign({}, process.env)
|
||||
clone(process.env, shellEnv)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function replace (env) {
|
||||
if (!env || !env.PATH) {
|
||||
return
|
||||
}
|
||||
|
||||
clone(process.env, env)
|
||||
}
|
||||
|
||||
export default { getFromShell, needsPatching, normalize, replace }
|
||||
@@ -1,15 +1,13 @@
|
||||
# Like sands through the hourglass, so are the days of our lives.
|
||||
module.exports = ({blobStore}) ->
|
||||
environmentHelpers = require('./environment-helpers')
|
||||
updateProcessEnv = require('./update-process-env')
|
||||
path = require 'path'
|
||||
require './window'
|
||||
{getWindowLoadSettings} = require './window-load-settings-helpers'
|
||||
{ipcRenderer} = require 'electron'
|
||||
{resourcePath, isSpec, devMode, env} = getWindowLoadSettings()
|
||||
|
||||
# Set baseline environment
|
||||
environmentHelpers.normalize({env: env})
|
||||
env = process.env
|
||||
updateProcessEnv(env)
|
||||
|
||||
# Add application-specific exports to module search path.
|
||||
exportsPath = path.join(resourcePath, 'exports')
|
||||
@@ -37,5 +35,5 @@ module.exports = ({blobStore}) ->
|
||||
setTimeout (-> document.querySelector('atom-workspace').focus()), 0
|
||||
window.addEventListener('focus', windowFocused)
|
||||
ipcRenderer.on('environment', (event, env) ->
|
||||
environmentHelpers.replace(env)
|
||||
updateProcessEnv(env)
|
||||
)
|
||||
|
||||
@@ -76,6 +76,8 @@ class AtomApplication
|
||||
@config.setSchema null, {type: 'object', properties: _.clone(require('../config-schema'))}
|
||||
@config.load()
|
||||
|
||||
@config.onDidChange 'core.useCustomTitleBar', @promptForRelaunch
|
||||
|
||||
@autoUpdateManager = new AutoUpdateManager(@version, options.test, @resourcePath, @config)
|
||||
@applicationMenu = new ApplicationMenu(@version, @autoUpdateManager)
|
||||
@atomProtocolHandler = new AtomProtocolHandler(@resourcePath, @safeMode)
|
||||
@@ -87,6 +89,7 @@ class AtomApplication
|
||||
@setupDockMenu()
|
||||
@storageFolder = new StorageFolder(process.env.ATOM_HOME)
|
||||
|
||||
|
||||
if options.pathsToOpen?.length > 0 or options.urlsToOpen?.length > 0 or options.test
|
||||
@openWithOptions(options)
|
||||
else
|
||||
@@ -706,3 +709,15 @@ class AtomApplication
|
||||
openOptions.defaultPath = path
|
||||
|
||||
dialog.showOpenDialog(parentWindow, openOptions, callback)
|
||||
|
||||
promptForRelaunch: ->
|
||||
chosen = dialog.showMessageBox BrowserWindow.getFocusedWindow(),
|
||||
type: 'warning'
|
||||
title: 'Relaunch required'
|
||||
message: "You will need to relaunch Atom for this change to take effect."
|
||||
buttons: ['Quit Atom', 'Cancel']
|
||||
if chosen is 0
|
||||
# once we're using electron v.1.2.2
|
||||
# app.relaunch()
|
||||
app.quit()
|
||||
|
||||
|
||||
@@ -40,6 +40,9 @@ class AtomWindow
|
||||
if process.platform is 'linux'
|
||||
options.icon = @constructor.iconPath
|
||||
|
||||
if @shouldHideTitleBar()
|
||||
options.titleBarStyle = 'hidden'
|
||||
|
||||
@browserWindow = new BrowserWindow options
|
||||
global.atomApplication.addWindow(this)
|
||||
|
||||
@@ -199,6 +202,11 @@ class AtomWindow
|
||||
[width, height] = @browserWindow.getSize()
|
||||
{x, y, width, height}
|
||||
|
||||
shouldHideTitleBar: ->
|
||||
not @isSpec and
|
||||
process.platform is 'darwin' and
|
||||
global.atomApplication.config.get('core.useCustomTitleBar')
|
||||
|
||||
close: -> @browserWindow.close()
|
||||
|
||||
focus: -> @browserWindow.focus()
|
||||
|
||||
@@ -113,9 +113,11 @@ function setupAtomHome ({setPortable}) {
|
||||
|
||||
try {
|
||||
atomHome = fs.realpathSync(atomHome)
|
||||
} finally {
|
||||
process.env.ATOM_HOME = atomHome
|
||||
} catch (e) {
|
||||
// Don't throw an error if atomHome doesn't exist.
|
||||
}
|
||||
|
||||
process.env.ATOM_HOME = atomHome
|
||||
}
|
||||
|
||||
function setupCompileCache () {
|
||||
|
||||
@@ -245,7 +245,8 @@ class TextEditor extends Model
|
||||
tabLength: @getTabLength(),
|
||||
ratioForCharacter: @ratioForCharacter.bind(this),
|
||||
isWrapBoundary: isWrapBoundary,
|
||||
foldCharacter: ZERO_WIDTH_NBSP
|
||||
foldCharacter: ZERO_WIDTH_NBSP,
|
||||
softWrapHangingIndent: @config.get('editor.softWrapHangingIndent', scope: @getRootScopeDescriptor())
|
||||
})
|
||||
|
||||
destroyed: ->
|
||||
|
||||
21
src/title-bar.coffee
Normal file
21
src/title-bar.coffee
Normal file
@@ -0,0 +1,21 @@
|
||||
module.exports =
|
||||
class TitleBar
|
||||
constructor: ({@workspace, @themes, @applicationDelegate}) ->
|
||||
@element = document.createElement('div')
|
||||
@element.classList.add('title-bar')
|
||||
|
||||
@titleElement = document.createElement('div')
|
||||
@titleElement.classList.add('title')
|
||||
@element.appendChild(@titleElement)
|
||||
|
||||
@workspace.onDidChangeActivePaneItem => @updateTitle()
|
||||
@themes.onDidChangeActiveThemes => @updateWindowSheetOffset()
|
||||
|
||||
@updateTitle()
|
||||
@updateWindowSheetOffset()
|
||||
|
||||
updateTitle: ->
|
||||
@titleElement.textContent = document.title
|
||||
|
||||
updateWindowSheetOffset: ->
|
||||
@applicationDelegate.getCurrentWindow().setSheetOffset(@element.offsetHeight)
|
||||
49
src/update-process-env.js
Normal file
49
src/update-process-env.js
Normal file
@@ -0,0 +1,49 @@
|
||||
/** @babel */
|
||||
|
||||
import {spawnSync} from 'child_process'
|
||||
|
||||
const ENVIRONMENT_VARIABLES_TO_PRESERVE = new Set(['NODE_ENV', 'NODE_PATH'])
|
||||
|
||||
export default function updateProcessEnv (launchEnv) {
|
||||
let envToAssign
|
||||
if (launchEnv && launchEnv.PWD) {
|
||||
envToAssign = launchEnv
|
||||
} else {
|
||||
if (process.platform === 'darwin') {
|
||||
envToAssign = getEnvFromShell()
|
||||
}
|
||||
}
|
||||
|
||||
if (envToAssign) {
|
||||
for (let key in process.env) {
|
||||
if (!ENVIRONMENT_VARIABLES_TO_PRESERVE.has(key)) {
|
||||
delete process.env[key]
|
||||
}
|
||||
}
|
||||
|
||||
for (let key in envToAssign) {
|
||||
if (!ENVIRONMENT_VARIABLES_TO_PRESERVE.has(key)) {
|
||||
process.env[key] = envToAssign[key]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getEnvFromShell () {
|
||||
let shell = process.env.SHELL
|
||||
if (shell && (shell.endsWith('/bash') || shell.endsWith('/sh'))) {
|
||||
let {stdout} = spawnSync(shell, ['-ilc', 'env'], {encoding: 'utf8'})
|
||||
if (stdout) {
|
||||
let result = {}
|
||||
for (let line of stdout.split('\n')) {
|
||||
if (line.includes('=')) {
|
||||
let components = line.split('=')
|
||||
let key = components.shift()
|
||||
let value = components.join('=')
|
||||
result[key] = value
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -23,6 +23,15 @@ class WindowEventHandler
|
||||
@subscriptions.add listen(@document, 'click', 'a', @handleLinkClick)
|
||||
@subscriptions.add listen(@document, 'submit', 'form', @handleFormSubmit)
|
||||
|
||||
browserWindow = @applicationDelegate.getCurrentWindow()
|
||||
browserWindow.on 'enter-full-screen', @handleEnterFullScreen
|
||||
@subscriptions.add new Disposable =>
|
||||
browserWindow.removeListener('enter-full-screen', @handleEnterFullScreen)
|
||||
|
||||
browserWindow.on 'leave-full-screen', @handleLeaveFullScreen
|
||||
@subscriptions.add new Disposable =>
|
||||
browserWindow.removeListener('leave-full-screen', @handleLeaveFullScreen)
|
||||
|
||||
@subscriptions.add @atomEnvironment.commands.add @window,
|
||||
'window:toggle-full-screen': @handleWindowToggleFullScreen
|
||||
'window:close': @handleWindowClose
|
||||
@@ -136,6 +145,12 @@ class WindowEventHandler
|
||||
@document.body.classList.add('is-blurred')
|
||||
@atomEnvironment.storeWindowDimensions()
|
||||
|
||||
handleEnterFullScreen: =>
|
||||
@document.body.classList.add("fullscreen")
|
||||
|
||||
handleLeaveFullScreen: =>
|
||||
@document.body.classList.remove("fullscreen")
|
||||
|
||||
handleWindowBeforeunload: =>
|
||||
confirmed = @atomEnvironment.workspace?.confirmClose(windowCloseRequested: true)
|
||||
if confirmed and not @reloadRequested and not @atomEnvironment.inSpecMode() and @atomEnvironment.getCurrentWindow().isWebViewFocused()
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
@import "panes";
|
||||
@import "syntax";
|
||||
@import "text-editor-light";
|
||||
@import "title-bar";
|
||||
@import "workspace-view";
|
||||
|
||||
// Atom UI library
|
||||
|
||||
41
static/title-bar.less
Normal file
41
static/title-bar.less
Normal file
@@ -0,0 +1,41 @@
|
||||
@import "ui-variables";
|
||||
|
||||
@title-bar-text-size: 13px;
|
||||
@title-bar-height: 23px;
|
||||
@title-bar-background-color: @base-background-color;
|
||||
@title-bar-border-color: @base-border-color;
|
||||
|
||||
body.fullscreen .title-bar {
|
||||
margin-top: -@title-bar-height;
|
||||
}
|
||||
|
||||
.title-bar {
|
||||
height: @title-bar-height;
|
||||
transition: margin-top 160ms;
|
||||
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
font-size: @title-bar-text-size;
|
||||
-webkit-user-select: none;
|
||||
-webkit-app-region: drag;
|
||||
|
||||
padding: 0 70px;
|
||||
overflow: hidden;
|
||||
|
||||
.title {
|
||||
flex: 0 1 auto;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
background-color: @title-bar-background-color;
|
||||
border-bottom: 1px solid @title-bar-border-color;
|
||||
|
||||
.is-blurred & {
|
||||
color: @text-color-subtle;
|
||||
}
|
||||
}
|
||||
@@ -79,7 +79,6 @@
|
||||
|
||||
@tab-height: 30px;
|
||||
|
||||
|
||||
// Other
|
||||
|
||||
@font-family: 'BlinkMacSystemFont', 'Lucida Grande', 'Segoe UI', Ubuntu, Cantarell, sans-serif;
|
||||
|
||||
Reference in New Issue
Block a user