Merge branch 'master' into wl-electron-37

This commit is contained in:
Antonio Scandurra
2016-05-17 09:30:36 +02:00
17 changed files with 371 additions and 239 deletions

View File

@@ -1,24 +1,46 @@
# Contributor Code of Conduct
# Contributor Covenant Code of Conduct
As contributors and maintainers of this project, and in the interest of fostering an open and welcoming community, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities.
## Our Pledge
We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, religion, or nationality.
In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
- The use of sexualized language or imagery
- Personal attacks
- Trolling or insulting/derogatory comments
- Public or private harassment
- Publishing other's private information, such as physical or electronic addresses, without explicit permission
- Other unethical or unprofessional conduct
* The use of sexualized language or imagery and unwelcome sexual attention or advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
By adopting this Code of Conduct, project maintainers commit themselves to fairly and consistently applying these principles to every aspect of managing this project. Project maintainers who do not follow or enforce the Code of Conduct may be permanently removed from the project team.
## Scope
This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community.
This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting a project maintainer at [atom@github.com](mailto:atom@github.com). All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. Maintainers are obligated to maintain confidentiality with regard to the reporter of an incident.
## Enforcement
This Code of Conduct is adapted from the Contributor Covenant, version 1.3.0, available from http://contributor-covenant.org/version/1/3/0/
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at [atom@github.com](mailto:atom@github.com). All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
[homepage]: http://contributor-covenant.org
[version]: http://contributor-covenant.org/version/1/4/

View File

@@ -62,7 +62,7 @@ Here's a list of the big ones:
* [git-diff](https://github.com/atom/git-diff) - Git change indicators shown in the editor's gutter.
* [language-javascript](https://github.com/atom/language-javascript) - all bundled languages are packages too, and each one has a separate package `language-[name]`. Use these for feedback on syntax highlighting issues that only appear for a specific language.
* [one-dark-ui](https://github.com/atom/one-dark-ui) - the default UI styling for anything but the text editor. UI theme packages (i.e. packages with a `-ui` suffix) provide only styling and it's possible that a bundled package is responsible for a UI issue. There are other other bundled UI themes, such as [one-light-ui](https://github.com/atom/one-light-ui).
* [one-dark-syntax](https://github.com/atom/one-dark-syntax) - the default syntax highlighting styles applied for all languages. There are other other bundled syntax themes, such as [solarized-dark](https://github.com/atom/solarized-dark). You should use these packages for reporting issues that appear in many languages, but disappear if you change to another syntax theme.
* [one-dark-syntax](https://github.com/atom/one-dark-syntax) - the default syntax highlighting styles applied for all languages. There are other other bundled syntax themes, such as [solarized-dark-syntax](https://github.com/atom/solarized-dark-syntax). You should use these packages for reporting issues that appear in many languages, but disappear if you change to another syntax theme.
* [apm](https://github.com/atom/apm) - the `apm` command line tool (Atom Package Manager). You should use this repository for any contributions related to the `apm` tool and to publishing packages.
* [atom.io](https://github.com/atom/atom.io) - the repository for feedback on the [Atom.io website](https://atom.io) and the [Atom.io package API](https://github.com/atom/atom/blob/master/docs/apm-rest-api.md) used by [apm](https://github.com/atom/apm).

View File

@@ -74,7 +74,7 @@ getAssets = ->
]
when 'win32'
assets = [{assetName: 'atom-windows.zip', sourcePath: appName}]
for squirrelAsset in ['AtomSetup.exe', 'RELEASES', "atom-#{version}-full.nupkg", "atom-#{version}-delta.nupkg"]
for squirrelAsset in ['AtomSetup.exe', 'AtomSetup.msi', 'RELEASES', "atom-#{version}-full.nupkg", "atom-#{version}-delta.nupkg"]
cp path.join(buildDir, 'installer', squirrelAsset), path.join(buildDir, squirrelAsset)
assets.push({assetName: squirrelAsset, sourcePath: assetName})
assets

View File

@@ -22,6 +22,7 @@
"clear-cut": "^2.0.1",
"coffee-script": "1.8.0",
"color": "^0.7.3",
"devtron": "1.1.0",
"event-kit": "^1.5.0",
"find-parent-dir": "^0.3.0",
"first-mate": "^5.1.1",
@@ -41,7 +42,7 @@
"nslog": "^3",
"ohnogit": "0.0.11",
"oniguruma": "^5",
"pathwatcher": "~6.2",
"pathwatcher": "~6.5",
"property-accessors": "^1.1.3",
"random-words": "0.0.1",
"resolve": "^1.1.6",
@@ -54,7 +55,7 @@
"service-hub": "^0.7.0",
"source-map-support": "^0.3.2",
"temp": "0.8.1",
"text-buffer": "9.0.0",
"text-buffer": "9.1.0",
"typescript-simple": "1.0.0",
"underscore-plus": "^1.6.6",
"yargs": "^3.23.0"
@@ -78,18 +79,18 @@
"autocomplete-css": "0.11.1",
"autocomplete-html": "0.7.2",
"autocomplete-plus": "2.31.0",
"autocomplete-snippets": "1.10.0",
"autocomplete-snippets": "1.11.0",
"autoflow": "0.27.0",
"autosave": "0.23.1",
"background-tips": "0.26.0",
"bookmarks": "0.41.0",
"bracket-matcher": "0.82.0",
"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",
"fuzzy-finder": "1.0.5",
"fuzzy-finder": "1.2.0",
"git-diff": "1.0.1",
"find-and-replace": "0.198.0",
"go-to-line": "0.30.0",
@@ -101,23 +102,23 @@
"link": "0.31.1",
"markdown-preview": "0.158.0",
"metrics": "0.53.1",
"notifications": "0.63.2",
"notifications": "0.64.0",
"open-on-github": "1.1.0",
"package-generator": "1.0.0",
"settings-view": "0.236.0",
"settings-view": "0.237.0",
"snippets": "1.0.2",
"spell-check": "0.67.1",
"status-bar": "1.2.6",
"styleguide": "0.45.2",
"symbols-view": "0.113.0",
"tabs": "0.93.2",
"tabs": "0.95.0",
"timecop": "0.33.1",
"tree-view": "0.206.2",
"tree-view": "0.207.0",
"update-package-dependencies": "0.10.0",
"welcome": "0.34.0",
"whitespace": "0.32.2",
"wrap-guide": "0.38.1",
"language-c": "0.51.4",
"language-c": "0.51.5",
"language-clojure": "0.20.0",
"language-coffee-script": "0.47.0",
"language-csharp": "0.12.1",
@@ -134,20 +135,20 @@
"language-make": "0.22.0",
"language-mustache": "0.13.0",
"language-objective-c": "0.15.1",
"language-perl": "0.34.0",
"language-perl": "0.35.0",
"language-php": "0.37.0",
"language-property-list": "0.8.0",
"language-python": "0.43.1",
"language-python": "0.43.2",
"language-ruby": "0.68.5",
"language-ruby-on-rails": "0.25.0",
"language-sass": "0.49.0",
"language-shellscript": "0.22.0",
"language-sass": "0.51.1",
"language-shellscript": "0.22.2",
"language-source": "0.9.0",
"language-sql": "0.21.0",
"language-text": "0.7.1",
"language-todo": "0.27.0",
"language-toml": "0.18.0",
"language-xml": "0.34.5",
"language-xml": "0.34.6",
"language-yaml": "0.26.0"
},
"private": true,

View File

@@ -1,5 +1,5 @@
#!/bin/sh
pushd $(dirname "$0") > /dev/null
pushd "$(dirname "$0")" > /dev/null
ATOMCMD=""$(pwd -W)"/atom.cmd"
popd > /dev/null
cmd.exe //c "$ATOMCMD" "$@"

57
spec/spawner-spec.coffee Normal file
View File

@@ -0,0 +1,57 @@
ChildProcess = require 'child_process'
Spawner = require '../src/browser/spawner'
describe "Spawner", ->
beforeEach ->
# Prevent any commands from actually running and affecting the host
originalSpawn = ChildProcess.spawn
harmlessSpawn =
# Just spawn something that won't actually modify the host
if process.platform is 'win32'
originalSpawn('dir')
else
originalSpawn('ls')
spyOn(ChildProcess, 'spawn').andCallFake (command, args, callback) ->
harmlessSpawn
it "invokes passed callback", ->
someCallback = jasmine.createSpy('someCallback')
Spawner.spawn('some-command', 'some-args', someCallback)
waitsFor ->
someCallback.callCount is 1
it "spawns passed command with arguments", ->
actualCommand = null
actualArgs = null
# Redefine fake invocation, so to remember passed arguments
jasmine.unspy(ChildProcess, 'spawn')
spyOn(ChildProcess, 'spawn').andCallFake (command, args) ->
actualCommand = command
actualArgs = args
harmlessSpawn
expectedCommand = 'some-command'
expectedArgs = 'some-args'
someCallback = jasmine.createSpy('someCallback')
Spawner.spawn(expectedCommand, expectedArgs, someCallback)
expect(actualCommand).toBe expectedCommand
expect(actualArgs).toBe expectedArgs
it "ignores errors by spawned process", ->
# Redefine fake invocation, so to cause an error
jasmine.unspy(ChildProcess, 'spawn')
spyOn(ChildProcess, 'spawn').andCallFake -> throw new Error("EBUSY")
someCallback = jasmine.createSpy('someCallback')
expect(Spawner.spawn('some-command', 'some-args', someCallback)).toBe undefined
waitsFor ->
someCallback.callCount is 1

View File

@@ -1,39 +1,37 @@
ChildProcess = require 'child_process'
{EventEmitter} = require 'events'
fs = require 'fs-plus'
path = require 'path'
temp = require 'temp'
SquirrelUpdate = require '../src/browser/squirrel-update'
Spawner = require '../src/browser/spawner'
WinPowerShell = require '../src/browser/win-powershell'
WinRegistry = require '../src/browser/win-registry'
# Run passed callback as Spawner.spawn() would do
invokeCallback = (callback) ->
error = null
stdout = ''
callback?(error, stdout)
describe "Windows Squirrel Update", ->
tempHomeDirectory = null
originalSpawn = ChildProcess.spawn
harmlessSpawn = ->
# Just spawn something that won't actually modify the host
if process.platform is 'win32'
originalSpawn('dir')
else
originalSpawn('ls')
beforeEach ->
# Prevent the actual home directory from being manipulated
tempHomeDirectory = temp.mkdirSync('atom-temp-home-')
spyOn(fs, 'getHomeDirectory').andReturn(tempHomeDirectory)
# Prevent any commands from actually running and affecting the host
spyOn(ChildProcess, 'spawn').andCallFake (command, args) ->
harmlessSpawn()
# Prevent any spawned command from actually running and affecting the host
spyOn(Spawner, 'spawn').andCallFake (command, args, callback) ->
# do nothing on command, just run passed callback
invokeCallback callback
it "ignores errors spawning Squirrel", ->
jasmine.unspy(ChildProcess, 'spawn')
spyOn(ChildProcess, 'spawn').andCallFake -> throw new Error("EBUSY")
app = quit: jasmine.createSpy('quit')
expect(SquirrelUpdate.handleStartupEvent(app, '--squirrel-install')).toBe true
waitsFor ->
app.quit.callCount is 1
# Prevent any actual change to Windows registry
for own method of WinRegistry
# all WinRegistry APIs share the same signature
spyOn(WinRegistry, method).andCallFake (callback) ->
# do nothing on registry, just run passed callback
invokeCallback callback
it "quits the app on all squirrel events", ->
app = quit: jasmine.createSpy('quit')
@@ -69,51 +67,52 @@ describe "Windows Squirrel Update", ->
describe "Desktop shortcut", ->
desktopShortcutPath = '/non/existing/path'
beforeEach ->
desktopShortcutPath = path.join(tempHomeDirectory, 'Desktop', 'Atom.lnk')
jasmine.unspy(ChildProcess, 'spawn')
spyOn(ChildProcess, 'spawn').andCallFake (command, args) ->
jasmine.unspy(Spawner, 'spawn')
spyOn(Spawner, 'spawn').andCallFake (command, args, callback) ->
if path.basename(command) is 'Update.exe' and args?[0] is '--createShortcut'
fs.writeFileSync(path.join(tempHomeDirectory, 'Desktop', 'Atom.lnk'), '')
harmlessSpawn()
fs.writeFileSync(desktopShortcutPath, '')
else
throw new Error("API not mocked")
# simply ignore other commands
invokeCallback callback
it "does not exist before install", ->
expect(fs.existsSync(desktopShortcutPath)).toBe false
describe "on install", ->
beforeEach ->
app = quit: jasmine.createSpy('quit')
SquirrelUpdate.handleStartupEvent(app, '--squirrel-install')
waitsFor ->
app.quit.callCount is 1
it "creates desktop shortcut", ->
expect(fs.existsSync(desktopShortcutPath)).toBe true
describe "when shortcut is deleted and then app is updated", ->
beforeEach ->
fs.removeSync(desktopShortcutPath)
expect(fs.existsSync(desktopShortcutPath)).toBe false
app = quit: jasmine.createSpy('quit')
SquirrelUpdate.handleStartupEvent(app, '--squirrel-updated')
waitsFor ->
app.quit.callCount is 1
it "does not recreate shortcut", ->
expect(fs.existsSync(desktopShortcutPath)).toBe false
describe "when shortcut is kept and app is updated", ->
beforeEach ->
app = quit: jasmine.createSpy('quit')
SquirrelUpdate.handleStartupEvent(app, '--squirrel-updated')
waitsFor ->
app.quit.callCount is 1
it "still has desktop shortcut", ->
expect(fs.existsSync(desktopShortcutPath)).toBe true
@@ -125,7 +124,7 @@ describe "Windows Squirrel Update", ->
SquirrelUpdate.restartAtom(app)
expect(app.quit.callCount).toBe 1
expect(ChildProcess.spawn.callCount).toBe 0
expect(Spawner.spawn.callCount).toBe 0
app.emit('will-quit')
expect(ChildProcess.spawn.callCount).toBe 1
expect(path.basename(ChildProcess.spawn.argsForCall[0][0])).toBe 'atom.cmd'
expect(Spawner.spawn.callCount).toBe 1
expect(path.basename(Spawner.spawn.argsForCall[0][0])).toBe 'atom.cmd'

View File

@@ -2805,16 +2805,20 @@ describe "TextEditorPresenter", ->
editor.setSoftWrapped(true)
editor.setDefaultCharWidth(1)
editor.setEditorWidthInChars(51)
presenter = buildPresenter(explicitHeight: 25, scrollTop: 30, lineHeight: 10, tileSize: 2)
presenter = buildPresenter(explicitHeight: 25, scrollTop: 30, lineHeight: 10, tileSize: 3)
presenter.setScreenRowsToMeasure([9, 11])
expect(lineNumberStateForScreenRow(presenter, 1)).toBeUndefined()
expectValues lineNumberStateForScreenRow(presenter, 2), {screenRow: 2, bufferRow: 2, softWrapped: false}
expect(lineNumberStateForScreenRow(presenter, 2)).toBeUndefined()
expectValues lineNumberStateForScreenRow(presenter, 3), {screenRow: 3, bufferRow: 3, softWrapped: false}
expectValues lineNumberStateForScreenRow(presenter, 4), {screenRow: 4, bufferRow: 3, softWrapped: true}
expectValues lineNumberStateForScreenRow(presenter, 5), {screenRow: 5, bufferRow: 4, softWrapped: false}
expectValues lineNumberStateForScreenRow(presenter, 6), {screenRow: 6, bufferRow: 7, softWrapped: false}
expectValues lineNumberStateForScreenRow(presenter, 7), {screenRow: 7, bufferRow: 8, softWrapped: false}
expect(lineNumberStateForScreenRow(presenter, 8)).toBeUndefined()
expectValues lineNumberStateForScreenRow(presenter, 8), {screenRow: 8, bufferRow: 8, softWrapped: true}
expect(lineNumberStateForScreenRow(presenter, 9)).toBeUndefined()
expect(lineNumberStateForScreenRow(presenter, 10)).toBeUndefined()
expect(lineNumberStateForScreenRow(presenter, 11)).toBeUndefined()
expect(lineNumberStateForScreenRow(presenter, 12)).toBeUndefined()
it "updates when the editor's content changes", ->
editor.foldBufferRow(4)
@@ -3069,15 +3073,28 @@ describe "TextEditorPresenter", ->
expect(lineNumberStateForScreenRow(presenter, 0).decorationClasses).toContain 'a'
expect(lineNumberStateForScreenRow(presenter, 1).decorationClasses).toContain 'a'
it "applies the 'folded' decoration only to the initial screen row of a soft-wrapped buffer row", ->
editor.setSoftWrapped(true)
editor.setDefaultCharWidth(1)
editor.setEditorWidthInChars(15)
editor.foldBufferRange([[0, 20], [0, 22]])
presenter = buildPresenter(explicitHeight: 35, scrollTop: 0, tileSize: 2)
describe "when a fold spans a single soft-wrapped buffer row", ->
it "applies the 'folded' decoration only to its initial screen row", ->
editor.setSoftWrapped(true)
editor.setDefaultCharWidth(1)
editor.setEditorWidthInChars(20)
editor.foldBufferRange([[0, 20], [0, 22]])
editor.foldBufferRange([[0, 10], [0, 14]])
presenter = buildPresenter(explicitHeight: 35, scrollTop: 0, tileSize: 2)
expect(lineNumberStateForScreenRow(presenter, 0).decorationClasses).toContain 'folded'
expect(lineNumberStateForScreenRow(presenter, 1).decorationClasses).toBeNull()
expect(lineNumberStateForScreenRow(presenter, 0).decorationClasses).toContain('folded')
expect(lineNumberStateForScreenRow(presenter, 1).decorationClasses).toBeNull()
describe "when a fold is at the end of a soft-wrapped buffer row", ->
it "applies the 'folded' decoration only to its initial screen row", ->
editor.setSoftWrapped(true)
editor.setDefaultCharWidth(1)
editor.setEditorWidthInChars(25)
editor.foldBufferRow(1)
presenter = buildPresenter(explicitHeight: 35, scrollTop: 0, tileSize: 2)
expect(lineNumberStateForScreenRow(presenter, 2).decorationClasses).toContain('folded')
expect(lineNumberStateForScreenRow(presenter, 3).decorationClasses).toBeNull()
describe ".foldable", ->
it "marks line numbers at the start of a foldable region as foldable", ->

View File

@@ -5268,6 +5268,30 @@ describe "TextEditor", ->
coffeeEditor.insertText("\n")
expect(coffeeEditor.lineTextForBufferRow(2)).toBe ""
describe "editor.atomicSoftTabs", ->
it "skips tab-length runs of leading whitespace when moving the cursor", ->
atom.config.set('editor.tabLength', 4)
atom.config.set('editor.atomicSoftTabs', true)
editor.setCursorScreenPosition([2, 3])
expect(editor.getCursorScreenPosition()).toEqual [2, 4]
atom.config.set('editor.atomicSoftTabs', false)
editor.setCursorScreenPosition([2, 3])
expect(editor.getCursorScreenPosition()).toEqual [2, 3]
atom.config.set('editor.atomicSoftTabs', true)
editor.setCursorScreenPosition([2, 3])
expect(editor.getCursorScreenPosition()).toEqual [2, 4]
atom.config.set('editor.atomicSoftTabs', false, scopeSelector: '.source.foo')
editor.setCursorScreenPosition([2, 3])
expect(editor.getCursorScreenPosition()).toEqual [2, 4]
atom.config.set('editor.atomicSoftTabs', false, scopeSelector: '.source.js')
editor.setCursorScreenPosition([2, 3])
expect(editor.getCursorScreenPosition()).toEqual [2, 3]
describe ".destroy()", ->
it "destroys marker layers associated with the text editor", ->
selectionsMarkerLayerId = editor.selectionsMarkerLayer.id

View File

@@ -790,6 +790,7 @@ class AtomEnvironment extends Model
# Returns a {Promise} that resolves when the DevTools have been opened or
# closed.
toggleDevTools: ->
require("devtron").install()
@applicationDelegate.toggleWindowDevTools()
# Extended: Execute code in dev tools.

View File

@@ -0,0 +1,36 @@
ChildProcess = require 'child_process'
# Spawn a command and invoke the callback when it completes with an error
# and the output from standard out.
#
# * `command` The underlying OS command {String} to execute.
# * `args` (optional) The {Array} with arguments to be passed to command.
# * `callback` (optional) The {Function} to call after the command has run. It will be invoked with arguments:
# * `error` (optional) An {Error} object returned by the command, `null` if no error was thrown.
# * `code` Error code returned by the command.
# * `stdout` The {String} output text generated by the command.
# * `stdout` The {String} output text generated by the command.
#
# Returns `undefined`.
exports.spawn = (command, args, callback) ->
stdout = ''
try
spawnedProcess = ChildProcess.spawn(command, args)
catch error
# Spawn can throw an error
process.nextTick -> callback?(error, stdout)
return
spawnedProcess.stdout.on 'data', (data) -> stdout += data
error = null
spawnedProcess.on 'error', (processError) -> error ?= processError
spawnedProcess.on 'close', (code, signal) ->
error ?= new Error("Command failed: #{signal ? code}") if code isnt 0
error?.code ?= code
error?.stdout ?= stdout
callback?(error, stdout)
# This is necessary if using Powershell 2 on Windows 7 to get the events to raise
# http://stackoverflow.com/questions/9155289/calling-powershell-from-nodejs
spawnedProcess.stdin.end()

View File

@@ -1,6 +1,8 @@
ChildProcess = require 'child_process'
fs = require 'fs-plus'
path = require 'path'
Spawner = require './spawner'
WinRegistry = require './win-registry'
WinPowerShell = require './win-powershell'
appFolder = path.resolve(process.execPath, '..')
rootAtomFolder = path.resolve(appFolder, '..')
@@ -10,118 +12,18 @@ exeName = path.basename(process.execPath)
if process.env.SystemRoot
system32Path = path.join(process.env.SystemRoot, 'System32')
regPath = path.join(system32Path, 'reg.exe')
powershellPath = path.join(system32Path, 'WindowsPowerShell', 'v1.0', 'powershell.exe')
setxPath = path.join(system32Path, 'setx.exe')
else
regPath = 'reg.exe'
powershellPath = 'powershell.exe'
setxPath = 'setx.exe'
# Registry keys used for context menu
fileKeyPath = 'HKCU\\Software\\Classes\\*\\shell\\Atom'
directoryKeyPath = 'HKCU\\Software\\Classes\\directory\\shell\\Atom'
backgroundKeyPath = 'HKCU\\Software\\Classes\\directory\\background\\shell\\Atom'
applicationsKeyPath = 'HKCU\\Software\\Classes\\Applications\\atom.exe'
environmentKeyPath = 'HKCU\\Environment'
# Spawn a command and invoke the callback when it completes with an error
# and the output from standard out.
spawn = (command, args, callback) ->
stdout = ''
try
spawnedProcess = ChildProcess.spawn(command, args)
catch error
# Spawn can throw an error
process.nextTick -> callback?(error, stdout)
return
spawnedProcess.stdout.on 'data', (data) -> stdout += data
error = null
spawnedProcess.on 'error', (processError) -> error ?= processError
spawnedProcess.on 'close', (code, signal) ->
error ?= new Error("Command failed: #{signal ? code}") if code isnt 0
error?.code ?= code
error?.stdout ?= stdout
callback?(error, stdout)
# This is necessary if using Powershell 2 on Windows 7 to get the events to raise
# http://stackoverflow.com/questions/9155289/calling-powershell-from-nodejs
spawnedProcess.stdin.end()
# Spawn reg.exe and callback when it completes
spawnReg = (args, callback) ->
spawn(regPath, args, callback)
# Spawn powershell.exe and callback when it completes
spawnPowershell = (args, callback) ->
# set encoding and execute the command, capture the output, and return it via .NET's console in order to have consistent UTF-8 encoding
# http://stackoverflow.com/questions/22349139/utf-8-output-from-powershell
# to address https://github.com/atom/atom/issues/5063
args[0] = """
[Console]::OutputEncoding=[System.Text.Encoding]::UTF8
$output=#{args[0]}
[Console]::WriteLine($output)
"""
args.unshift('-command')
args.unshift('RemoteSigned')
args.unshift('-ExecutionPolicy')
args.unshift('-noprofile')
spawn(powershellPath, args, callback)
# Spawn setx.exe and callback when it completes
spawnSetx = (args, callback) ->
spawn(setxPath, args, callback)
Spawner.spawn(setxPath, args, callback)
# Spawn the Update.exe with the given arguments and invoke the callback when
# the command completes.
spawnUpdate = (args, callback) ->
spawn(updateDotExe, args, callback)
# Install the Open with Atom explorer context menu items via the registry.
installContextMenu = (callback) ->
addToRegistry = (args, callback) ->
args.unshift('add')
args.push('/f')
spawnReg(args, callback)
installFileHandler = (callback) ->
args = ["#{applicationsKeyPath}\\shell\\open\\command", '/ve', '/d', "\"#{process.execPath}\" \"%1\""]
addToRegistry(args, callback)
installMenu = (keyPath, arg, callback) ->
args = [keyPath, '/ve', '/d', 'Open with Atom']
addToRegistry args, ->
args = [keyPath, '/v', 'Icon', '/d', "\"#{process.execPath}\""]
addToRegistry args, ->
args = ["#{keyPath}\\command", '/ve', '/d', "\"#{process.execPath}\" \"#{arg}\""]
addToRegistry(args, callback)
installMenu fileKeyPath, '%1', ->
installMenu directoryKeyPath, '%1', ->
installMenu backgroundKeyPath, '%V', ->
installFileHandler(callback)
# Get the user's PATH environment variable registry value.
getPath = (callback) ->
spawnPowershell ['[environment]::GetEnvironmentVariable(\'Path\',\'User\')'], (error, stdout) ->
if error?
return callback(error)
pathOutput = stdout.replace(/^\s+|\s+$/g, '')
callback(null, pathOutput)
# Uninstall the Open with Atom explorer context menu items via the registry.
uninstallContextMenu = (callback) ->
deleteFromRegistry = (keyPath, callback) ->
spawnReg(['delete', keyPath, '/f'], callback)
deleteFromRegistry fileKeyPath, ->
deleteFromRegistry directoryKeyPath, ->
deleteFromRegistry backgroundKeyPath, ->
deleteFromRegistry(applicationsKeyPath, callback)
Spawner.spawn(updateDotExe, args, callback)
# Add atom and apm to the PATH
#
@@ -160,7 +62,7 @@ addCommandsToPath = (callback) ->
installCommands (error) ->
return callback(error) if error?
getPath (error, pathEnv) ->
WinPowerShell.getPath (error, pathEnv) ->
return callback(error) if error?
pathSegments = pathEnv.split(/;+/).filter (pathSegment) -> pathSegment
@@ -171,7 +73,7 @@ addCommandsToPath = (callback) ->
# Remove atom and apm from the PATH
removeCommandsFromPath = (callback) ->
getPath (error, pathEnv) ->
WinPowerShell.getPath (error, pathEnv) ->
return callback(error) if error?
pathSegments = pathEnv.split(/;+/).filter (pathSegment) ->
@@ -220,7 +122,7 @@ exports.existsSync = ->
exports.restartAtom = (app) ->
if projectPath = global.atomApplication?.lastFocusedWindow?.projectPath
args = [projectPath]
app.once 'will-quit', -> spawn(path.join(binFolder, 'atom.cmd'), args)
app.once 'will-quit', -> Spawner.spawn(path.join(binFolder, 'atom.cmd'), args)
app.quit()
# Handle squirrel events denoted by --squirrel-* command line arguments.
@@ -228,19 +130,19 @@ exports.handleStartupEvent = (app, squirrelCommand) ->
switch squirrelCommand
when '--squirrel-install'
createShortcuts ->
installContextMenu ->
WinRegistry.installContextMenu ->
addCommandsToPath ->
app.quit()
true
when '--squirrel-updated'
updateShortcuts ->
installContextMenu ->
WinRegistry.installContextMenu ->
addCommandsToPath ->
app.quit()
true
when '--squirrel-uninstall'
removeShortcuts ->
uninstallContextMenu ->
WinRegistry.uninstallContextMenu ->
removeCommandsFromPath ->
app.quit()
true

View File

@@ -0,0 +1,39 @@
path = require 'path'
Spawner = require './spawner'
if process.env.SystemRoot
system32Path = path.join(process.env.SystemRoot, 'System32')
powershellPath = path.join(system32Path, 'WindowsPowerShell', 'v1.0', 'powershell.exe')
else
powershellPath = 'powershell.exe'
# Spawn powershell.exe and callback when it completes
spawnPowershell = (args, callback) ->
# Set encoding and execute the command, capture the output, and return it
# via .NET's console in order to have consistent UTF-8 encoding.
# See http://stackoverflow.com/questions/22349139/utf-8-output-from-powershell
# to address https://github.com/atom/atom/issues/5063
args[0] = """
[Console]::OutputEncoding=[System.Text.Encoding]::UTF8
$output=#{args[0]}
[Console]::WriteLine($output)
"""
args.unshift('-command')
args.unshift('RemoteSigned')
args.unshift('-ExecutionPolicy')
args.unshift('-noprofile')
Spawner.spawn(powershellPath, args, callback)
# Get the user's PATH environment variable registry value.
#
# * `callback` The {Function} to call after registry operation is done.
# It will be invoked with the same arguments provided by {Spawner.spawn}.
#
# Returns the user's path {String}.
exports.getPath = (callback) ->
spawnPowershell ['[environment]::GetEnvironmentVariable(\'Path\',\'User\')'], (error, stdout) ->
if error?
return callback(error)
pathOutput = stdout.replace(/^\s+|\s+$/g, '')
callback(null, pathOutput)

View File

@@ -0,0 +1,62 @@
path = require 'path'
Spawner = require './spawner'
if process.env.SystemRoot
system32Path = path.join(process.env.SystemRoot, 'System32')
regPath = path.join(system32Path, 'reg.exe')
else
regPath = 'reg.exe'
# Registry keys used for context menu
fileKeyPath = 'HKCU\\Software\\Classes\\*\\shell\\Atom'
directoryKeyPath = 'HKCU\\Software\\Classes\\directory\\shell\\Atom'
backgroundKeyPath = 'HKCU\\Software\\Classes\\directory\\background\\shell\\Atom'
applicationsKeyPath = 'HKCU\\Software\\Classes\\Applications\\atom.exe'
# Spawn reg.exe and callback when it completes
spawnReg = (args, callback) ->
Spawner.spawn(regPath, args, callback)
# Install the Open with Atom explorer context menu items via the registry.
#
# * `callback` The {Function} to call after registry operation is done.
# It will be invoked with the same arguments provided by {Spawner.spawn}.
#
# Returns `undefined`.
exports.installContextMenu = (callback) ->
addToRegistry = (args, callback) ->
args.unshift('add')
args.push('/f')
spawnReg(args, callback)
installFileHandler = (callback) ->
args = ["#{applicationsKeyPath}\\shell\\open\\command", '/ve', '/d', "\"#{process.execPath}\" \"%1\""]
addToRegistry(args, callback)
installMenu = (keyPath, arg, callback) ->
args = [keyPath, '/ve', '/d', 'Open with Atom']
addToRegistry args, ->
args = [keyPath, '/v', 'Icon', '/d', "\"#{process.execPath}\""]
addToRegistry args, ->
args = ["#{keyPath}\\command", '/ve', '/d', "\"#{process.execPath}\" \"#{arg}\""]
addToRegistry(args, callback)
installMenu fileKeyPath, '%1', ->
installMenu directoryKeyPath, '%1', ->
installMenu backgroundKeyPath, '%V', ->
installFileHandler(callback)
# Uninstall the Open with Atom explorer context menu items via the registry.
#
# * `callback` The {Function} to call after registry operation is done.
# It will be invoked with the same arguments provided by {Spawner.spawn}.
#
# Returns `undefined`.
exports.uninstallContextMenu = (callback) ->
deleteFromRegistry = (keyPath, callback) ->
spawnReg(['delete', keyPath, '/f'], callback)
deleteFromRegistry fileKeyPath, ->
deleteFromRegistry directoryKeyPath, ->
deleteFromRegistry backgroundKeyPath, ->
deleteFromRegistry(applicationsKeyPath, callback)

View File

@@ -491,7 +491,7 @@ class TextEditorComponent
screenPosition = Point.fromObject(screenPosition)
screenPosition = @editor.clipScreenPosition(screenPosition) if clip
unless @presenter.isRowVisible(screenPosition.row)
unless @presenter.isRowRendered(screenPosition.row)
@presenter.setScreenRowsToMeasure([screenPosition.row])
unless @linesComponent.lineNodeForScreenRow(screenPosition.row)?
@@ -503,7 +503,7 @@ class TextEditorComponent
screenPositionForPixelPosition: (pixelPosition) ->
row = @linesYardstick.measuredRowForPixelPosition(pixelPosition)
if row? and not @presenter.isRowVisible(row)
if row? and not @presenter.isRowRendered(row)
@presenter.setScreenRowsToMeasure([row])
@updateSyncPreMeasurement()
@@ -513,9 +513,9 @@ class TextEditorComponent
pixelRectForScreenRange: (screenRange) ->
rowsToMeasure = []
unless @presenter.isRowVisible(screenRange.start.row)
unless @presenter.isRowRendered(screenRange.start.row)
rowsToMeasure.push(screenRange.start.row)
unless @presenter.isRowVisible(screenRange.end.row)
unless @presenter.isRowRendered(screenRange.end.row)
rowsToMeasure.push(screenRange.end.row)
if rowsToMeasure.length > 0

View File

@@ -601,42 +601,19 @@ class TextEditorPresenter
tileState.lineNumbers ?= {}
visibleLineNumberIds = {}
startRow = screenRows[screenRows.length - 1]
endRow = Math.min(screenRows[0] + 1, @model.getScreenLineCount())
for screenRow in screenRows when @isRowRendered(screenRow)
lineId = @linesByScreenRow.get(screenRow).id
{bufferRow, softWrappedAtStart: softWrapped} = @displayLayer.softWrapDescriptorForScreenRow(screenRow)
foldable = not softWrapped and @model.isFoldableAtBufferRow(bufferRow)
decorationClasses = @lineNumberDecorationClassesForRow(screenRow)
blockDecorationsBeforeCurrentScreenRowHeight = @lineTopIndex.pixelPositionAfterBlocksForRow(screenRow) - @lineTopIndex.pixelPositionBeforeBlocksForRow(screenRow)
blockDecorationsHeight = blockDecorationsBeforeCurrentScreenRowHeight
if screenRow % @tileSize isnt 0
blockDecorationsAfterPreviousScreenRowHeight = @lineTopIndex.pixelPositionBeforeBlocksForRow(screenRow) - @lineHeight - @lineTopIndex.pixelPositionAfterBlocksForRow(screenRow - 1)
blockDecorationsHeight += blockDecorationsAfterPreviousScreenRowHeight
if startRow > 0
rowBeforeStartRow = startRow - 1
lastBufferRow = @model.bufferRowForScreenRow(rowBeforeStartRow)
else
lastBufferRow = null
if endRow > startRow
bufferRows = @model.bufferRowsForScreenRows(startRow, endRow - 1)
previousBufferRow = -1
foldable = false
for bufferRow, i in bufferRows
# don't compute foldability more than once per buffer row
if previousBufferRow isnt bufferRow
foldable = @model.isFoldableAtBufferRow(bufferRow)
previousBufferRow = bufferRow
if bufferRow is lastBufferRow
softWrapped = true
else
lastBufferRow = bufferRow
softWrapped = false
screenRow = startRow + i
lineId = @linesByScreenRow.get(screenRow).id
decorationClasses = @lineNumberDecorationClassesForRow(screenRow)
blockDecorationsBeforeCurrentScreenRowHeight = @lineTopIndex.pixelPositionAfterBlocksForRow(screenRow) - @lineTopIndex.pixelPositionBeforeBlocksForRow(screenRow)
blockDecorationsHeight = blockDecorationsBeforeCurrentScreenRowHeight
if screenRow % @tileSize isnt 0
blockDecorationsAfterPreviousScreenRowHeight = @lineTopIndex.pixelPositionBeforeBlocksForRow(screenRow) - @lineHeight - @lineTopIndex.pixelPositionAfterBlocksForRow(screenRow - 1)
blockDecorationsHeight += blockDecorationsAfterPreviousScreenRowHeight
tileState.lineNumbers[lineId] = {screenRow, bufferRow, softWrapped, decorationClasses, foldable, blockDecorationsHeight}
visibleLineNumberIds[lineId] = true
tileState.lineNumbers[lineId] = {screenRow, bufferRow, softWrapped, decorationClasses, foldable, blockDecorationsHeight}
visibleLineNumberIds[lineId] = true
for id of tileState.lineNumbers
delete tileState.lineNumbers[id] unless visibleLineNumberIds[id]
@@ -1153,13 +1130,11 @@ class TextEditorPresenter
if rangeIsReversed
headScreenPosition = screenRange.start
headBufferPosition = bufferRange.start
else
headScreenPosition = screenRange.end
headBufferPosition = bufferRange.end
if properties.class is 'folded' and Decoration.isType(properties, 'line-number')
screenRow = @model.screenRowForBufferRow(headBufferPosition.row)
screenRow = @model.screenRowForBufferRow(bufferRange.start.row)
@lineNumberDecorationsByScreenRow[screenRow] ?= {}
@lineNumberDecorationsByScreenRow[screenRow][decorationId] = properties
else
@@ -1550,8 +1525,8 @@ class TextEditorPresenter
getVisibleRowRange: ->
[@startRow, @endRow]
isRowVisible: (row) ->
@startRow <= row < @endRow
isRowRendered: (row) ->
@getStartTileRow() <= row < @constrainRow(@getEndTileRow() + @tileSize)
isOpenTagCode: (tagCode) ->
@displayLayer.isOpenTagCode(tagCode)

View File

@@ -231,6 +231,7 @@ class TextEditor extends Model
@scopedConfigSubscriptions = subscriptions = new CompositeDisposable
scopeDescriptor = @getRootScopeDescriptor()
subscriptions.add @config.onDidChange 'editor.atomicSoftTabs', scope: scopeDescriptor, @resetDisplayLayer.bind(this)
subscriptions.add @config.onDidChange 'editor.tabLength', scope: scopeDescriptor, @resetDisplayLayer.bind(this)
subscriptions.add @config.onDidChange 'editor.invisibles', scope: scopeDescriptor, @resetDisplayLayer.bind(this)
subscriptions.add @config.onDidChange 'editor.showInvisibles', scope: scopeDescriptor, @resetDisplayLayer.bind(this)
@@ -1702,8 +1703,6 @@ class TextEditor extends Model
# operations, but uses more time and memory. (default: false)
# * `reversed` (optional) {Boolean} Creates the marker in a reversed
# orientation. (default: false)
# * `persistent` (optional) {Boolean} Whether to include this marker when
# serializing the buffer. (default: true)
# * `invalidate` (optional) {String} Determines the rules by which changes
# to the buffer *invalidate* the marker. (default: 'overlap') It can be
# any of the following strategies, in order of fragility:
@@ -1737,8 +1736,6 @@ class TextEditor extends Model
# operations, but uses more time and memory. (default: false)
# * `reversed` (optional) {Boolean} Creates the marker in a reversed
# orientation. (default: false)
# * `persistent` (optional) {Boolean} Whether to include this marker when
# serializing the buffer. (default: true)
# * `invalidate` (optional) {String} Determines the rules by which changes
# to the buffer *invalidate* the marker. (default: 'overlap') It can be
# any of the following strategies, in order of fragility:
@@ -3011,7 +3008,7 @@ class TextEditor extends Model
maintainClipboard = false
for selection in @getSelectionsOrderedByBufferPosition()
if not selection.isEmpty()
selection.copy(maintainClipboard, true)
selection.copy(maintainClipboard, false)
maintainClipboard = true
return