mirror of
https://github.com/atom/atom.git
synced 2026-04-06 03:02:13 -04:00
Merge remote-tracking branch 'refs/remotes/origin/master' into wl-electron-37
This commit is contained in:
@@ -6,6 +6,6 @@
|
||||
"url": "https://github.com/atom/atom.git"
|
||||
},
|
||||
"dependencies": {
|
||||
"atom-package-manager": "1.9.2"
|
||||
"atom-package-manager": "1.9.3"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,7 +57,7 @@ module.exports = (grunt) ->
|
||||
homeDir = process.env.USERPROFILE
|
||||
contentsDir = shellAppDir
|
||||
appDir = path.join(shellAppDir, 'resources', 'app')
|
||||
installDir ?= path.join(process.env.ProgramFiles, appName)
|
||||
installDir ?= path.join(process.env.LOCALAPPDATA, appName, 'app-dev')
|
||||
killCommand = 'taskkill /F /IM atom.exe'
|
||||
else if process.platform is 'darwin'
|
||||
homeDir = process.env.HOME
|
||||
@@ -298,6 +298,7 @@ module.exports = (grunt) ->
|
||||
unless process.platform is 'linux' or grunt.option('no-install')
|
||||
defaultTasks.push 'install'
|
||||
grunt.registerTask('default', defaultTasks)
|
||||
grunt.registerTask('build-and-sign', ['download-electron', 'download-electron-chromedriver', 'build', 'set-version', 'generate-asar', 'codesign:app', 'install'])
|
||||
|
||||
getDefaultChannelAndReleaseBranch = (version) ->
|
||||
if version.match(/dev/) or isBuildingPR()
|
||||
|
||||
@@ -54,9 +54,9 @@ module.exports = (grunt) ->
|
||||
# so that it doesn't becomes larger than it needs to be.
|
||||
ignoredPaths = [
|
||||
path.join('git-utils', 'deps')
|
||||
path.join('nodegit', 'vendor')
|
||||
path.join('nodegit', 'node_modules', 'node-pre-gyp')
|
||||
path.join('nodegit', 'node_modules', '.bin')
|
||||
path.join('ohnogit', 'node_modules', 'nodegit', 'vendor')
|
||||
path.join('ohnogit', 'node_modules', 'nodegit', 'node_modules', 'node-pre-gyp')
|
||||
path.join('ohnogit', 'node_modules', 'nodegit', 'node_modules', '.bin')
|
||||
path.join('oniguruma', 'deps')
|
||||
path.join('less', 'dist')
|
||||
path.join('bootstrap', 'docs')
|
||||
@@ -122,9 +122,9 @@ module.exports = (grunt) ->
|
||||
# Ignore *.cc and *.h files from native modules
|
||||
ignoredPaths.push "#{_.escapeRegExp(path.join('ctags', 'src') + path.sep)}.*\\.(cc|h)*"
|
||||
ignoredPaths.push "#{_.escapeRegExp(path.join('git-utils', 'src') + path.sep)}.*\\.(cc|h)*"
|
||||
ignoredPaths.push "#{_.escapeRegExp(path.join('nodegit', 'src') + path.sep)}.*\\.(cc|h)?"
|
||||
ignoredPaths.push "#{_.escapeRegExp(path.join('nodegit', 'generate') + path.sep)}.*\\.(cc|h)?"
|
||||
ignoredPaths.push "#{_.escapeRegExp(path.join('nodegit', 'include') + path.sep)}.*\\.(cc|h)?"
|
||||
ignoredPaths.push "#{_.escapeRegExp(path.join('ohnogit', 'node_modules', 'nodegit', 'src') + path.sep)}.*\\.(cc|h)?"
|
||||
ignoredPaths.push "#{_.escapeRegExp(path.join('ohnogit', 'node_modules', 'nodegit', 'generate') + path.sep)}.*\\.(cc|h)?"
|
||||
ignoredPaths.push "#{_.escapeRegExp(path.join('ohnogit', 'node_modules', 'nodegit', 'include') + path.sep)}.*\\.(cc|h)?"
|
||||
ignoredPaths.push "#{_.escapeRegExp(path.join('keytar', 'src') + path.sep)}.*\\.(cc|h)*"
|
||||
ignoredPaths.push "#{_.escapeRegExp(path.join('nslog', 'src') + path.sep)}.*\\.(cc|h)*"
|
||||
ignoredPaths.push "#{_.escapeRegExp(path.join('oniguruma', 'src') + path.sep)}.*\\.(cc|h)*"
|
||||
|
||||
@@ -16,10 +16,22 @@ module.exports = (grunt) ->
|
||||
{description} = grunt.config.get('atom.metadata')
|
||||
|
||||
if process.platform is 'win32'
|
||||
runas ?= require 'runas'
|
||||
copyFolder = path.resolve 'script', 'copy-folder.cmd'
|
||||
if runas('cmd', ['/c', copyFolder, shellAppDir, installDir], admin: true) isnt 0
|
||||
grunt.log.error("Failed to copy #{shellAppDir} to #{installDir}")
|
||||
done = @async()
|
||||
fs.access(installDir, fs.W_OK, (err) ->
|
||||
adminRequired = true if err
|
||||
if adminRequired
|
||||
grunt.log.ok("User does not have write access to #{installDir}, elevating to admin")
|
||||
runas ?= require 'runas'
|
||||
copyFolder = path.resolve 'script', 'copy-folder.cmd'
|
||||
|
||||
if runas('cmd', ['/c', copyFolder, shellAppDir, installDir], admin: adminRequired) isnt 0
|
||||
grunt.log.error("Failed to copy #{shellAppDir} to #{installDir}")
|
||||
else
|
||||
grunt.log.ok("Installed into #{installDir}")
|
||||
|
||||
done()
|
||||
)
|
||||
|
||||
else if process.platform is 'darwin'
|
||||
rm installDir
|
||||
mkdir path.dirname(installDir)
|
||||
|
||||
@@ -34,7 +34,7 @@ git clone https://github.com/atom/atom/
|
||||
cd atom
|
||||
script/build
|
||||
```
|
||||
This will create the Atom application in the `out\Atom` folder as well as copy it to a folder named `Atom` within `Program Files`.
|
||||
This will create the Atom application in the `out\Atom` folder as well as copy it to a subfolder of your user profile (e.g. `c:\Users\Bob`) called `AppData\Local\atom\app-dev`.
|
||||
|
||||
### `script/build` Options
|
||||
* `--install-dir` - Creates the final built application in this directory. Example (trailing slash is optional):
|
||||
|
||||
@@ -18,15 +18,15 @@
|
||||
# 'ctrl-p': 'core:move-down'
|
||||
#
|
||||
# You can find more information about keymaps in these guides:
|
||||
# * https://atom.io/docs/latest/using-atom-basic-customization#customizing-key-bindings
|
||||
# * https://atom.io/docs/latest/behind-atom-keymaps-in-depth
|
||||
# * http://flight-manual.atom.io/using-atom/sections/basic-customization/#_customizing_keybindings
|
||||
# * http://flight-manual.atom.io/behind-atom/sections/keymaps-in-depth/
|
||||
#
|
||||
# If you're having trouble with your keybindings not working, try the
|
||||
# Keybinding Resolver: `Cmd+.` on OS X and `Ctrl+.` on other platforms. See the
|
||||
# Debugging Guide for more information:
|
||||
# * https://atom.io/docs/latest/hacking-atom-debugging#check-the-keybindings
|
||||
# * http://flight-manual.atom.io/hacking-atom/sections/debugging/#check-the-keybindings
|
||||
#
|
||||
# This file uses CoffeeScript Object Notation (CSON).
|
||||
# If you are unfamiliar with CSON, you can read more about it in the
|
||||
# Atom Flight Manual:
|
||||
# https://atom.io/docs/latest/using-atom-basic-customization#cson
|
||||
# http://flight-manual.atom.io/using-atom/sections/basic-customization/#_cson
|
||||
|
||||
@@ -18,4 +18,4 @@
|
||||
# This file uses CoffeeScript Object Notation (CSON).
|
||||
# If you are unfamiliar with CSON, you can read more about it in the
|
||||
# Atom Flight Manual:
|
||||
# https://atom.io/docs/latest/using-atom-basic-customization#cson
|
||||
# http://flight-manual.atom.io/using-atom/sections/basic-customization/#_cson
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
'ctrl-n': 'application:new-file'
|
||||
'ctrl-s': 'core:save'
|
||||
'ctrl-S': 'core:save-as'
|
||||
'ctrl-f4': 'core:close'
|
||||
'ctrl-w': 'core:close'
|
||||
'ctrl-z': 'core:undo'
|
||||
'ctrl-y': 'core:redo'
|
||||
|
||||
@@ -228,10 +228,10 @@
|
||||
{label: 'Delete', command: 'core:delete'}
|
||||
{label: 'Select All', command: 'core:select-all'}
|
||||
{type: 'separator'}
|
||||
{label: 'Split Up', command: 'pane:split-up'}
|
||||
{label: 'Split Down', command: 'pane:split-down'}
|
||||
{label: 'Split Left', command: 'pane:split-left'}
|
||||
{label: 'Split Right', command: 'pane:split-right'}
|
||||
{label: 'Split Up', command: 'pane:split-up-and-copy-active-item'}
|
||||
{label: 'Split Down', command: 'pane:split-down-and-copy-active-item'}
|
||||
{label: 'Split Left', command: 'pane:split-left-and-copy-active-item'}
|
||||
{label: 'Split Right', command: 'pane:split-right-and-copy-active-item'}
|
||||
{label: 'Close Pane', command: 'pane:close'}
|
||||
{type: 'separator'}
|
||||
]
|
||||
|
||||
@@ -204,10 +204,10 @@
|
||||
{label: 'Delete', command: 'core:delete'}
|
||||
{label: 'Select All', command: 'core:select-all'}
|
||||
{type: 'separator'}
|
||||
{label: 'Split Up', command: 'pane:split-up'}
|
||||
{label: 'Split Down', command: 'pane:split-down'}
|
||||
{label: 'Split Left', command: 'pane:split-left'}
|
||||
{label: 'Split Right', command: 'pane:split-right'}
|
||||
{label: 'Split Up', command: 'pane:split-up-and-copy-active-item'}
|
||||
{label: 'Split Down', command: 'pane:split-down-and-copy-active-item'}
|
||||
{label: 'Split Left', command: 'pane:split-left-and-copy-active-item'}
|
||||
{label: 'Split Right', command: 'pane:split-right-and-copy-active-item'}
|
||||
{label: 'Close Pane', command: 'pane:close'}
|
||||
{type: 'separator'}
|
||||
]
|
||||
|
||||
@@ -207,10 +207,10 @@
|
||||
{label: 'Delete', command: 'core:delete'}
|
||||
{label: 'Select All', command: 'core:select-all'}
|
||||
{type: 'separator'}
|
||||
{label: 'Split Up', command: 'pane:split-up'}
|
||||
{label: 'Split Down', command: 'pane:split-down'}
|
||||
{label: 'Split Left', command: 'pane:split-left'}
|
||||
{label: 'Split Right', command: 'pane:split-right'}
|
||||
{label: 'Split Up', command: 'pane:split-up-and-copy-active-item'}
|
||||
{label: 'Split Down', command: 'pane:split-down-and-copy-active-item'}
|
||||
{label: 'Split Left', command: 'pane:split-left-and-copy-active-item'}
|
||||
{label: 'Split Right', command: 'pane:split-right-and-copy-active-item'}
|
||||
{label: 'Close Pane', command: 'pane:close'}
|
||||
{type: 'separator'}
|
||||
]
|
||||
|
||||
40
package.json
40
package.json
@@ -37,9 +37,9 @@
|
||||
"less-cache": "0.23",
|
||||
"line-top-index": "0.2.0",
|
||||
"marked": "^0.3.4",
|
||||
"nodegit": "0.12.2",
|
||||
"normalize-package-data": "^2.0.0",
|
||||
"nslog": "^3",
|
||||
"ohnogit": "0.0.11",
|
||||
"oniguruma": "^5",
|
||||
"pathwatcher": "~6.2",
|
||||
"property-accessors": "^1.1.3",
|
||||
@@ -54,7 +54,7 @@
|
||||
"service-hub": "^0.7.0",
|
||||
"source-map-support": "^0.3.2",
|
||||
"temp": "0.8.1",
|
||||
"text-buffer": "8.4.6",
|
||||
"text-buffer": "8.5.0",
|
||||
"typescript-simple": "1.0.0",
|
||||
"underscore-plus": "^1.6.6",
|
||||
"yargs": "^3.23.0"
|
||||
@@ -72,12 +72,12 @@
|
||||
"one-light-syntax": "1.2.0",
|
||||
"solarized-dark-syntax": "1.0.2",
|
||||
"solarized-light-syntax": "1.0.2",
|
||||
"about": "1.5.0",
|
||||
"about": "1.5.2",
|
||||
"archive-view": "0.61.1",
|
||||
"autocomplete-atom-api": "0.10.0",
|
||||
"autocomplete-css": "0.11.1",
|
||||
"autocomplete-html": "0.7.2",
|
||||
"autocomplete-plus": "2.29.2",
|
||||
"autocomplete-plus": "2.30.0",
|
||||
"autocomplete-snippets": "1.10.0",
|
||||
"autoflow": "0.27.0",
|
||||
"autosave": "0.23.1",
|
||||
@@ -89,7 +89,7 @@
|
||||
"dev-live-reload": "0.47.0",
|
||||
"encoding-selector": "0.21.0",
|
||||
"exception-reporting": "0.38.1",
|
||||
"fuzzy-finder": "1.0.4",
|
||||
"fuzzy-finder": "1.0.5",
|
||||
"git-diff": "1.0.1",
|
||||
"find-and-replace": "0.198.0",
|
||||
"go-to-line": "0.30.0",
|
||||
@@ -101,37 +101,37 @@
|
||||
"link": "0.31.1",
|
||||
"markdown-preview": "0.158.0",
|
||||
"metrics": "0.53.1",
|
||||
"notifications": "0.63.1",
|
||||
"notifications": "0.63.2",
|
||||
"open-on-github": "1.1.0",
|
||||
"package-generator": "1.0.0",
|
||||
"settings-view": "0.235.1",
|
||||
"snippets": "1.0.2",
|
||||
"spell-check": "0.67.1",
|
||||
"status-bar": "1.2.3",
|
||||
"status-bar": "1.2.6",
|
||||
"styleguide": "0.45.2",
|
||||
"symbols-view": "0.112.0",
|
||||
"tabs": "0.92.1",
|
||||
"tabs": "0.93.1",
|
||||
"timecop": "0.33.1",
|
||||
"tree-view": "0.206.0",
|
||||
"tree-view": "0.206.2",
|
||||
"update-package-dependencies": "0.10.0",
|
||||
"welcome": "0.34.0",
|
||||
"whitespace": "0.32.2",
|
||||
"wrap-guide": "0.38.1",
|
||||
"language-c": "0.51.3",
|
||||
"language-c": "0.51.4",
|
||||
"language-clojure": "0.20.0",
|
||||
"language-coffee-script": "0.46.1",
|
||||
"language-coffee-script": "0.47.0",
|
||||
"language-csharp": "0.12.1",
|
||||
"language-css": "0.36.1",
|
||||
"language-gfm": "0.85.0",
|
||||
"language-git": "0.12.1",
|
||||
"language-gfm": "0.86.0",
|
||||
"language-git": "0.13.0",
|
||||
"language-go": "0.42.0",
|
||||
"language-html": "0.44.1",
|
||||
"language-hyperlink": "0.16.0",
|
||||
"language-java": "0.17.0",
|
||||
"language-java": "0.18.0",
|
||||
"language-javascript": "0.110.0",
|
||||
"language-json": "0.18.0",
|
||||
"language-less": "0.29.3",
|
||||
"language-make": "0.21.1",
|
||||
"language-make": "0.22.0",
|
||||
"language-mustache": "0.13.0",
|
||||
"language-objective-c": "0.15.1",
|
||||
"language-perl": "0.34.0",
|
||||
@@ -140,15 +140,15 @@
|
||||
"language-python": "0.43.1",
|
||||
"language-ruby": "0.68.5",
|
||||
"language-ruby-on-rails": "0.25.0",
|
||||
"language-sass": "0.46.0",
|
||||
"language-shellscript": "0.21.1",
|
||||
"language-sass": "0.49.0",
|
||||
"language-shellscript": "0.22.0",
|
||||
"language-source": "0.9.0",
|
||||
"language-sql": "0.20.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.4",
|
||||
"language-yaml": "0.25.2"
|
||||
"language-xml": "0.34.5",
|
||||
"language-yaml": "0.26.0"
|
||||
},
|
||||
"private": true,
|
||||
"scripts": {
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
SET EXPECT_OUTPUT=
|
||||
SET WAIT=
|
||||
SET PSARGS=%*
|
||||
|
||||
FOR %%a IN (%*) DO (
|
||||
IF /I "%%a"=="-f" SET EXPECT_OUTPUT=YES
|
||||
@@ -25,7 +26,8 @@ FOR %%a IN (%*) DO (
|
||||
IF "%EXPECT_OUTPUT%"=="YES" (
|
||||
SET ELECTRON_ENABLE_LOGGING=YES
|
||||
IF "%WAIT%"=="YES" (
|
||||
powershell -noexit "%~dp0\..\..\atom.exe" --pid=$pid %* ; wait-event
|
||||
powershell -noexit "Start-Process -FilePath \"%~dp0\..\..\atom.exe\" -ArgumentList \"--pid=$pid $env:PSARGS\" ; wait-event"
|
||||
exit 0
|
||||
) ELSE (
|
||||
"%~dp0\..\..\atom.exe" %*
|
||||
)
|
||||
|
||||
@@ -1,2 +1,5 @@
|
||||
#!/bin/sh
|
||||
$(dirname "$0")/atom.cmd "$@"
|
||||
pushd $(dirname "$0") > /dev/null
|
||||
ATOMCMD=""$(pwd -W)"/atom.cmd"
|
||||
popd > /dev/null
|
||||
cmd.exe //c "$ATOMCMD" "$@"
|
||||
|
||||
@@ -28,8 +28,10 @@ describe "AtomEnvironment", ->
|
||||
atom.setSize(originalSize.width, originalSize.height)
|
||||
|
||||
it 'sets the size of the window, and can retrieve the size just set', ->
|
||||
atom.setSize(100, 400)
|
||||
expect(atom.getSize()).toEqual width: 100, height: 400
|
||||
newWidth = originalSize.width + 12
|
||||
newHeight = originalSize.height + 23
|
||||
atom.setSize(newWidth, newHeight)
|
||||
expect(atom.getSize()).toEqual width: newWidth, height: newHeight
|
||||
|
||||
describe ".isReleasedVersion()", ->
|
||||
it "returns false if the version is a SHA and true otherwise", ->
|
||||
|
||||
@@ -5,6 +5,12 @@
|
||||
logallrefupdates = true
|
||||
ignorecase = true
|
||||
precomposeunicode = true
|
||||
[branch "master"]
|
||||
remote = origin
|
||||
merge = refs/heads/master
|
||||
[remote "origin"]
|
||||
url = git@github.com:atom/some-repo-i-guess.git
|
||||
fetch = +refs/heads/*:refs/remotes/origin/*
|
||||
[submodule "jstips"]
|
||||
url = https://github.com/loverajoel/jstips
|
||||
[submodule "You-Dont-Need-jQuery"]
|
||||
|
||||
1
spec/fixtures/git/repo-with-submodules/git.git/refs/remotes/origin/master
vendored
Normal file
1
spec/fixtures/git/repo-with-submodules/git.git/refs/remotes/origin/master
vendored
Normal file
@@ -0,0 +1 @@
|
||||
d2b0ad9cbc6f6c4372e8956e5cc5af771b2342e5
|
||||
@@ -3,7 +3,6 @@
|
||||
import fs from 'fs-plus'
|
||||
import path from 'path'
|
||||
import temp from 'temp'
|
||||
import Git from 'nodegit'
|
||||
|
||||
import {it, beforeEach, afterEach} from './async-spec-helpers'
|
||||
|
||||
@@ -47,7 +46,7 @@ describe('GitRepositoryAsync', () => {
|
||||
|
||||
let threw = false
|
||||
try {
|
||||
await repo.repoPromise
|
||||
await repo.getRepo()
|
||||
} catch (e) {
|
||||
threw = true
|
||||
}
|
||||
@@ -56,6 +55,14 @@ describe('GitRepositoryAsync', () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe('openedPath', () => {
|
||||
it('is the path passed to .open', () => {
|
||||
const workingDirPath = copyRepository()
|
||||
repo = GitRepositoryAsync.open(workingDirPath)
|
||||
expect(repo.openedPath).toBe(workingDirPath)
|
||||
})
|
||||
})
|
||||
|
||||
describe('.getRepo()', () => {
|
||||
beforeEach(() => {
|
||||
const workingDirectory = copySubmoduleRepository()
|
||||
@@ -64,19 +71,19 @@ describe('GitRepositoryAsync', () => {
|
||||
})
|
||||
|
||||
it('returns the repository when not given a path', async () => {
|
||||
const nodeGitRepo1 = await repo.repoPromise
|
||||
const nodeGitRepo1 = await repo.getRepo()
|
||||
const nodeGitRepo2 = await repo.getRepo()
|
||||
expect(nodeGitRepo1.workdir()).toBe(nodeGitRepo2.workdir())
|
||||
})
|
||||
|
||||
it('returns the repository when given a non-submodule path', async () => {
|
||||
const nodeGitRepo1 = await repo.repoPromise
|
||||
const nodeGitRepo1 = await repo.getRepo()
|
||||
const nodeGitRepo2 = await repo.getRepo('README')
|
||||
expect(nodeGitRepo1.workdir()).toBe(nodeGitRepo2.workdir())
|
||||
})
|
||||
|
||||
it('returns the submodule repository when given a submodule path', async () => {
|
||||
const nodeGitRepo1 = await repo.repoPromise
|
||||
const nodeGitRepo1 = await repo.getRepo()
|
||||
const nodeGitRepo2 = await repo.getRepo('jstips')
|
||||
expect(nodeGitRepo1.workdir()).not.toBe(nodeGitRepo2.workdir())
|
||||
|
||||
@@ -103,7 +110,7 @@ describe('GitRepositoryAsync', () => {
|
||||
it('returns the repository path for a repository path', async () => {
|
||||
repo = openFixture('master.git')
|
||||
const repoPath = await repo.getPath()
|
||||
expect(repoPath).toBe(path.join(__dirname, 'fixtures', 'git', 'master.git'))
|
||||
expect(repoPath).toEqualPath(path.join(__dirname, 'fixtures', 'git', 'master.git'))
|
||||
})
|
||||
})
|
||||
|
||||
@@ -303,7 +310,7 @@ describe('GitRepositoryAsync', () => {
|
||||
await repo.getPathStatus(filePath)
|
||||
|
||||
expect(statusHandler.callCount).toBe(1)
|
||||
const status = Git.Status.STATUS.WT_MODIFIED
|
||||
const status = GitRepositoryAsync.Git.Status.STATUS.WT_MODIFIED
|
||||
expect(statusHandler.argsForCall[0][0]).toEqual({path: filePath, pathStatus: status})
|
||||
fs.writeFileSync(filePath, 'abc')
|
||||
|
||||
@@ -878,4 +885,34 @@ describe('GitRepositoryAsync', () => {
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('.getOriginURL()', () => {
|
||||
beforeEach(() => {
|
||||
const workingDirectory = copyRepository('repo-with-submodules')
|
||||
repo = GitRepositoryAsync.open(workingDirectory)
|
||||
})
|
||||
|
||||
it('returns the origin URL', async () => {
|
||||
const url = await repo.getOriginURL()
|
||||
expect(url).toBe('git@github.com:atom/some-repo-i-guess.git')
|
||||
})
|
||||
})
|
||||
|
||||
describe('.getUpstreamBranch()', () => {
|
||||
it('returns null when there is no upstream branch', async () => {
|
||||
const workingDirectory = copyRepository()
|
||||
repo = GitRepositoryAsync.open(workingDirectory)
|
||||
|
||||
const upstream = await repo.getUpstreamBranch()
|
||||
expect(upstream).toBe(null)
|
||||
})
|
||||
|
||||
it('returns the upstream branch', async () => {
|
||||
const workingDirectory = copyRepository('repo-with-submodules')
|
||||
repo = GitRepositoryAsync.open(workingDirectory)
|
||||
|
||||
const upstream = await repo.getUpstreamBranch()
|
||||
expect(upstream).toBe('refs/remotes/origin/master')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -33,7 +33,7 @@ describe "GitRepository", ->
|
||||
waitsForPromise ->
|
||||
repo.async.getPath().then(onSuccess)
|
||||
runs ->
|
||||
expect(onSuccess.mostRecentCall.args[0]).toBe(repoPath)
|
||||
expect(onSuccess.mostRecentCall.args[0]).toEqualPath(repoPath)
|
||||
|
||||
describe "new GitRepository(path)", ->
|
||||
it "throws an exception when no repository is found", ->
|
||||
@@ -289,6 +289,16 @@ describe "GitRepository", ->
|
||||
expect(repo.isStatusModified(status)).toBe true
|
||||
expect(repo.isStatusNew(status)).toBe false
|
||||
|
||||
it 'caches statuses that were looked up synchronously', ->
|
||||
originalContent = 'undefined'
|
||||
fs.writeFileSync(modifiedPath, 'making this path modified')
|
||||
repo.getPathStatus('file.txt')
|
||||
|
||||
fs.writeFileSync(modifiedPath, originalContent)
|
||||
waitsForPromise -> repo.refreshStatus()
|
||||
runs ->
|
||||
expect(repo.isStatusModified(repo.getCachedPathStatus(modifiedPath))).toBeFalsy()
|
||||
|
||||
describe "buffer events", ->
|
||||
[editor] = []
|
||||
|
||||
|
||||
@@ -526,7 +526,7 @@ describe "Project", ->
|
||||
expect(atom.project.getDirectories()[1].contains(inputPath)).toBe true
|
||||
expect(atom.project.relativizePath(inputPath)).toEqual [
|
||||
atom.project.getPaths()[1],
|
||||
'somewhere/something.txt'
|
||||
path.join('somewhere', 'something.txt')
|
||||
]
|
||||
|
||||
describe ".contains(path)", ->
|
||||
|
||||
@@ -1,66 +0,0 @@
|
||||
/** @babel */
|
||||
|
||||
import ResourcePool from '../src/resource-pool'
|
||||
|
||||
import {it} from './async-spec-helpers'
|
||||
|
||||
describe('ResourcePool', () => {
|
||||
let queue
|
||||
|
||||
beforeEach(() => {
|
||||
queue = new ResourcePool([{}])
|
||||
})
|
||||
|
||||
describe('.enqueue', () => {
|
||||
it('calls the enqueued function', async () => {
|
||||
let called = false
|
||||
await queue.enqueue(() => {
|
||||
called = true
|
||||
return Promise.resolve()
|
||||
})
|
||||
expect(called).toBe(true)
|
||||
})
|
||||
|
||||
it('forwards values from the inner promise', async () => {
|
||||
const result = await queue.enqueue(() => Promise.resolve(42))
|
||||
expect(result).toBe(42)
|
||||
})
|
||||
|
||||
it('forwards errors from the inner promise', async () => {
|
||||
let threw = false
|
||||
try {
|
||||
await queue.enqueue(() => Promise.reject(new Error('down with the sickness')))
|
||||
} catch (e) {
|
||||
threw = true
|
||||
}
|
||||
expect(threw).toBe(true)
|
||||
})
|
||||
|
||||
it('continues to dequeue work after a promise has been rejected', async () => {
|
||||
try {
|
||||
await queue.enqueue(() => Promise.reject(new Error('down with the sickness')))
|
||||
} catch (e) {}
|
||||
|
||||
const result = await queue.enqueue(() => Promise.resolve(42))
|
||||
expect(result).toBe(42)
|
||||
})
|
||||
|
||||
it('queues up work', async () => {
|
||||
let resolve = null
|
||||
queue.enqueue(() => {
|
||||
return new Promise((resolve_, reject) => {
|
||||
resolve = resolve_
|
||||
})
|
||||
})
|
||||
|
||||
expect(queue.getQueueDepth()).toBe(0)
|
||||
|
||||
queue.enqueue(() => new Promise((resolve, reject) => {}))
|
||||
|
||||
expect(queue.getQueueDepth()).toBe(1)
|
||||
resolve()
|
||||
|
||||
waitsFor(() => queue.getQueueDepth() === 0)
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -91,3 +91,13 @@ describe "Selection", ->
|
||||
expect(buffer.lineForRow(0)).toBe " "
|
||||
expect(buffer.lineForRow(1)).toBe " "
|
||||
expect(buffer.lineForRow(2)).toBe ""
|
||||
|
||||
it "auto-indents if only a newline is inserted", ->
|
||||
selection.setBufferRange [[2, 0], [3, 0]]
|
||||
selection.insertText("\n", autoIndent: true)
|
||||
expect(buffer.lineForRow(2)).toBe " "
|
||||
|
||||
it "auto-indents if only a carriage return + newline is inserted", ->
|
||||
selection.setBufferRange [[2, 0], [3, 0]]
|
||||
selection.insertText("\r\n", autoIndent: true)
|
||||
expect(buffer.lineForRow(2)).toBe " "
|
||||
|
||||
@@ -172,8 +172,8 @@ jasmine.useRealClock = ->
|
||||
addCustomMatchers = (spec) ->
|
||||
spec.addMatchers
|
||||
toBeInstanceOf: (expected) ->
|
||||
notText = if @isNot then " not" else ""
|
||||
this.message = => "Expected #{jasmine.pp(@actual)} to#{notText} be instance of #{expected.name} class"
|
||||
beOrNotBe = if @isNot then "not be" else "be"
|
||||
this.message = => "Expected #{jasmine.pp(@actual)} to #{beOrNotBe} instance of #{expected.name} class"
|
||||
@actual instanceof expected
|
||||
|
||||
toHaveLength: (expected) ->
|
||||
@@ -181,32 +181,38 @@ addCustomMatchers = (spec) ->
|
||||
this.message = => "Expected object #{@actual} has no length method"
|
||||
false
|
||||
else
|
||||
notText = if @isNot then " not" else ""
|
||||
this.message = => "Expected object with length #{@actual.length} to#{notText} have length #{expected}"
|
||||
haveOrNotHave = if @isNot then "not have" else "have"
|
||||
this.message = => "Expected object with length #{@actual.length} to #{haveOrNotHave} length #{expected}"
|
||||
@actual.length is expected
|
||||
|
||||
toExistOnDisk: (expected) ->
|
||||
notText = this.isNot and " not" or ""
|
||||
@message = -> return "Expected path '" + @actual + "'" + notText + " to exist."
|
||||
toOrNotTo = this.isNot and "not to" or "to"
|
||||
@message = -> return "Expected path '#{@actual}' #{toOrNotTo} exist."
|
||||
fs.existsSync(@actual)
|
||||
|
||||
toHaveFocus: ->
|
||||
notText = this.isNot and " not" or ""
|
||||
toOrNotTo = this.isNot and "not to" or "to"
|
||||
if not document.hasFocus()
|
||||
console.error "Specs will fail because the Dev Tools have focus. To fix this close the Dev Tools or click the spec runner."
|
||||
|
||||
@message = -> return "Expected element '" + @actual + "' or its descendants" + notText + " to have focus."
|
||||
@message = -> return "Expected element '#{@actual}' or its descendants #{toOrNotTo} have focus."
|
||||
element = @actual
|
||||
element = element.get(0) if element.jquery
|
||||
element is document.activeElement or element.contains(document.activeElement)
|
||||
|
||||
toShow: ->
|
||||
notText = if @isNot then " not" else ""
|
||||
toOrNotTo = this.isNot and "not to" or "to"
|
||||
element = @actual
|
||||
element = element.get(0) if element.jquery
|
||||
@message = -> return "Expected element '#{element}' or its descendants#{notText} to show."
|
||||
@message = -> return "Expected element '#{element}' or its descendants #{toOrNotTo} show."
|
||||
element.style.display in ['block', 'inline-block', 'static', 'fixed']
|
||||
|
||||
toEqualPath: (expected) ->
|
||||
actualPath = path.normalize(@actual)
|
||||
expectedPath = path.normalize(expected)
|
||||
@message = -> return "Expected path '#{actualPath}' to be equal to '#{expectedPath}'."
|
||||
actualPath is expectedPath
|
||||
|
||||
window.waitsForPromise = (args...) ->
|
||||
label = null
|
||||
if args.length > 1
|
||||
|
||||
@@ -4022,15 +4022,33 @@ describe('TextEditorComponent', function () {
|
||||
})
|
||||
})
|
||||
|
||||
describe('when changing the font', async function () {
|
||||
it('measures the default char, the korean char, the double width char and the half width char widths', async function () {
|
||||
expect(editor.getDefaultCharWidth()).toBeCloseTo(12, 0)
|
||||
describe('when decreasing the fontSize', async function () {
|
||||
it('decreases the widths of the korean char, the double width char and the half width char', async function () {
|
||||
originalDefaultCharWidth = editor.getDefaultCharWidth()
|
||||
koreanDefaultCharWidth = editor.getKoreanCharWidth()
|
||||
doubleWidthDefaultCharWidth = editor.getDoubleWidthCharWidth()
|
||||
halfWidthDefaultCharWidth = editor.getHalfWidthCharWidth()
|
||||
component.setFontSize(10)
|
||||
await nextViewUpdatePromise()
|
||||
expect(editor.getDefaultCharWidth()).toBeCloseTo(6, 0)
|
||||
expect(editor.getKoreanCharWidth()).toBeCloseTo(9, 0)
|
||||
expect(editor.getDoubleWidthCharWidth()).toBe(10)
|
||||
expect(editor.getHalfWidthCharWidth()).toBe(5)
|
||||
expect(editor.getDefaultCharWidth()).toBeLessThan(originalDefaultCharWidth)
|
||||
expect(editor.getKoreanCharWidth()).toBeLessThan(koreanDefaultCharWidth)
|
||||
expect(editor.getDoubleWidthCharWidth()).toBeLessThan(doubleWidthDefaultCharWidth)
|
||||
expect(editor.getHalfWidthCharWidth()).toBeLessThan(halfWidthDefaultCharWidth)
|
||||
})
|
||||
})
|
||||
|
||||
describe('when increasing the fontSize', function() {
|
||||
it('increases the widths of the korean char, the double width char and the half width char', async function () {
|
||||
originalDefaultCharWidth = editor.getDefaultCharWidth()
|
||||
koreanDefaultCharWidth = editor.getKoreanCharWidth()
|
||||
doubleWidthDefaultCharWidth = editor.getDoubleWidthCharWidth()
|
||||
halfWidthDefaultCharWidth = editor.getHalfWidthCharWidth()
|
||||
component.setFontSize(25)
|
||||
await nextViewUpdatePromise()
|
||||
expect(editor.getDefaultCharWidth()).toBeGreaterThan(originalDefaultCharWidth)
|
||||
expect(editor.getKoreanCharWidth()).toBeGreaterThan(koreanDefaultCharWidth)
|
||||
expect(editor.getDoubleWidthCharWidth()).toBeGreaterThan(doubleWidthDefaultCharWidth)
|
||||
expect(editor.getHalfWidthCharWidth()).toBeGreaterThan(halfWidthDefaultCharWidth)
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@@ -182,17 +182,19 @@ describe "TextEditor", ->
|
||||
expect(editor1.getLongTitle()).toBe "readme \u2014 sample-theme-1"
|
||||
expect(editor2.getLongTitle()).toBe "readme \u2014 sample-theme-2"
|
||||
|
||||
it "returns '<filename> — <parent-directories>' when opened files have identical file and dir names", ->
|
||||
it "returns '<filename> — <parent-directories>' when opened files have identical file names in subdirectories", ->
|
||||
editor1 = null
|
||||
editor2 = null
|
||||
path1 = path.join('sample-theme-1', 'src', 'js')
|
||||
path2 = path.join('sample-theme-2', 'src', 'js')
|
||||
waitsForPromise ->
|
||||
atom.workspace.open(path.join('sample-theme-1', 'src', 'js', 'main.js')).then (o) ->
|
||||
atom.workspace.open(path.join(path1, 'main.js')).then (o) ->
|
||||
editor1 = o
|
||||
atom.workspace.open(path.join('sample-theme-2', 'src', 'js', 'main.js')).then (o) ->
|
||||
atom.workspace.open(path.join(path2, 'main.js')).then (o) ->
|
||||
editor2 = o
|
||||
runs ->
|
||||
expect(editor1.getLongTitle()).toBe "main.js \u2014 sample-theme-1/src/js"
|
||||
expect(editor2.getLongTitle()).toBe "main.js \u2014 sample-theme-2/src/js"
|
||||
expect(editor1.getLongTitle()).toBe "main.js \u2014 #{path1}"
|
||||
expect(editor2.getLongTitle()).toBe "main.js \u2014 #{path2}"
|
||||
|
||||
it "returns '<filename> — <parent-directories>' when opened files have identical file and same parent dir name", ->
|
||||
editor1 = null
|
||||
@@ -204,7 +206,7 @@ describe "TextEditor", ->
|
||||
editor2 = o
|
||||
runs ->
|
||||
expect(editor1.getLongTitle()).toBe "main.js \u2014 js"
|
||||
expect(editor2.getLongTitle()).toBe "main.js \u2014 js/plugin"
|
||||
expect(editor2.getLongTitle()).toBe "main.js \u2014 " + path.join('js', 'plugin')
|
||||
|
||||
it "notifies ::onDidChangeTitle observers when the underlying buffer path changes", ->
|
||||
observed = []
|
||||
@@ -1187,14 +1189,10 @@ describe "TextEditor", ->
|
||||
cursor2 = editor.addCursorAtBufferPosition([1, 4])
|
||||
expect(cursor2.marker).toBe cursor1.marker
|
||||
|
||||
describe '.logCursorScope()', ->
|
||||
beforeEach ->
|
||||
spyOn(atom.notifications, 'addInfo')
|
||||
|
||||
it 'opens a notification', ->
|
||||
editor.logCursorScope()
|
||||
|
||||
expect(atom.notifications.addInfo).toHaveBeenCalled()
|
||||
describe '.getCursorScope()', ->
|
||||
it 'returns the current scope', ->
|
||||
descriptor = editor.getCursorScope()
|
||||
expect(descriptor.scopes).toContain('source.js')
|
||||
|
||||
describe "selection", ->
|
||||
selection = null
|
||||
@@ -5729,28 +5727,6 @@ describe "TextEditor", ->
|
||||
expect(handler).toHaveBeenCalledWith 'OK'
|
||||
expect(editor.getPlaceholderText()).toBe 'OK'
|
||||
|
||||
describe ".checkoutHeadRevision()", ->
|
||||
it "reverts to the version of its file checked into the project repository", ->
|
||||
atom.config.set("editor.confirmCheckoutHeadRevision", false)
|
||||
|
||||
editor.setCursorBufferPosition([0, 0])
|
||||
editor.insertText("---\n")
|
||||
expect(editor.lineTextForBufferRow(0)).toBe "---"
|
||||
|
||||
waitsForPromise ->
|
||||
editor.checkoutHeadRevision()
|
||||
|
||||
runs ->
|
||||
expect(editor.lineTextForBufferRow(0)).toBe "var quicksort = function () {"
|
||||
|
||||
describe "when there's no repository for the editor's file", ->
|
||||
it "doesn't do anything", ->
|
||||
editor = atom.workspace.buildTextEditor()
|
||||
editor.setText("stuff")
|
||||
editor.checkoutHeadRevision()
|
||||
|
||||
waitsForPromise -> editor.checkoutHeadRevision()
|
||||
|
||||
describe 'gutters', ->
|
||||
describe 'the TextEditor constructor', ->
|
||||
it 'creates a line-number gutter', ->
|
||||
|
||||
@@ -175,7 +175,7 @@ describe "atom.themes", ->
|
||||
expect(styleElementAddedHandler).toHaveBeenCalled()
|
||||
|
||||
element = document.querySelector('head style[source-path*="css.css"]')
|
||||
expect(element.getAttribute('source-path')).toBe atom.themes.stringToId(cssPath)
|
||||
expect(element.getAttribute('source-path')).toEqualPath atom.themes.stringToId(cssPath)
|
||||
expect(element.textContent).toBe fs.readFileSync(cssPath, 'utf8')
|
||||
|
||||
# doesn't append twice
|
||||
@@ -194,7 +194,7 @@ describe "atom.themes", ->
|
||||
expect(document.querySelectorAll('head style').length).toBe lengthBefore + 1
|
||||
|
||||
element = document.querySelector('head style[source-path*="sample.less"]')
|
||||
expect(element.getAttribute('source-path')).toBe atom.themes.stringToId(lessPath)
|
||||
expect(element.getAttribute('source-path')).toEqualPath atom.themes.stringToId(lessPath)
|
||||
expect(element.textContent).toBe """
|
||||
#header {
|
||||
color: #4d926f;
|
||||
@@ -213,9 +213,9 @@ describe "atom.themes", ->
|
||||
|
||||
it "supports requiring css and less stylesheets without an explicit extension", ->
|
||||
atom.themes.requireStylesheet path.join(__dirname, 'fixtures', 'css')
|
||||
expect(document.querySelector('head style[source-path*="css.css"]').getAttribute('source-path')).toBe atom.themes.stringToId(atom.project.getDirectories()[0]?.resolve('css.css'))
|
||||
expect(document.querySelector('head style[source-path*="css.css"]').getAttribute('source-path')).toEqualPath atom.themes.stringToId(atom.project.getDirectories()[0]?.resolve('css.css'))
|
||||
atom.themes.requireStylesheet path.join(__dirname, 'fixtures', 'sample')
|
||||
expect(document.querySelector('head style[source-path*="sample.less"]').getAttribute('source-path')).toBe atom.themes.stringToId(atom.project.getDirectories()[0]?.resolve('sample.less'))
|
||||
expect(document.querySelector('head style[source-path*="sample.less"]').getAttribute('source-path')).toEqualPath atom.themes.stringToId(atom.project.getDirectories()[0]?.resolve('sample.less'))
|
||||
|
||||
document.querySelector('head style[source-path*="css.css"]').remove()
|
||||
document.querySelector('head style[source-path*="sample.less"]').remove()
|
||||
|
||||
@@ -445,13 +445,12 @@ describe "TokenizedBuffer", ->
|
||||
expect(screenLine0.text).toBe "# Econ 101#{tabAsSpaces}"
|
||||
{tokens} = screenLine0
|
||||
|
||||
expect(tokens.length).toBe 4
|
||||
expect(tokens.length).toBe 3
|
||||
expect(tokens[0].value).toBe "#"
|
||||
expect(tokens[1].value).toBe " Econ 101"
|
||||
expect(tokens[2].value).toBe tabAsSpaces
|
||||
expect(tokens[2].scopes).toEqual tokens[1].scopes
|
||||
expect(tokens[2].isAtomic).toBeTruthy()
|
||||
expect(tokens[3].value).toBe ""
|
||||
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(2).text).toBe "#{tabAsSpaces} buy()#{tabAsSpaces}while supply > demand"
|
||||
|
||||
|
||||
@@ -80,7 +80,8 @@ describe "Workspace", ->
|
||||
expect(untitledEditor.getText()).toBe("An untitled editor.")
|
||||
|
||||
expect(atom.workspace.getActiveTextEditor().getPath()).toBe editor3.getPath()
|
||||
expect(document.title).toMatch ///^#{path.basename(editor3.getLongTitle())}\ \u2014\ #{atom.project.getPaths()[0]}///
|
||||
pathEscaped = escapeStringRegex(atom.project.getPaths()[0])
|
||||
expect(document.title).toMatch ///^#{path.basename(editor3.getLongTitle())}\ \u2014\ #{pathEscaped}///
|
||||
|
||||
describe "where there are no open panes or editors", ->
|
||||
it "constructs the view with no open editors", ->
|
||||
@@ -833,25 +834,29 @@ describe "Workspace", ->
|
||||
describe "when there is an active pane item", ->
|
||||
it "sets the title to the pane item's title plus the project path", ->
|
||||
item = atom.workspace.getActivePaneItem()
|
||||
expect(document.title).toMatch ///^#{item.getTitle()}\ \u2014\ #{atom.project.getPaths()[0]}///
|
||||
pathEscaped = escapeStringRegex(atom.project.getPaths()[0])
|
||||
expect(document.title).toMatch ///^#{item.getTitle()}\ \u2014\ #{pathEscaped}///
|
||||
|
||||
describe "when the title of the active pane item changes", ->
|
||||
it "updates the window title based on the item's new title", ->
|
||||
editor = atom.workspace.getActivePaneItem()
|
||||
editor.buffer.setPath(path.join(temp.dir, 'hi'))
|
||||
expect(document.title).toMatch ///^#{editor.getTitle()}\ \u2014\ #{atom.project.getPaths()[0]}///
|
||||
pathEscaped = escapeStringRegex(atom.project.getPaths()[0])
|
||||
expect(document.title).toMatch ///^#{editor.getTitle()}\ \u2014\ #{pathEscaped}///
|
||||
|
||||
describe "when the active pane's item changes", ->
|
||||
it "updates the title to the new item's title plus the project path", ->
|
||||
atom.workspace.getActivePane().activateNextItem()
|
||||
item = atom.workspace.getActivePaneItem()
|
||||
expect(document.title).toMatch ///^#{item.getTitle()}\ \u2014\ #{atom.project.getPaths()[0]}///
|
||||
pathEscaped = escapeStringRegex(atom.project.getPaths()[0])
|
||||
expect(document.title).toMatch ///^#{item.getTitle()}\ \u2014\ #{pathEscaped}///
|
||||
|
||||
describe "when the last pane item is removed", ->
|
||||
it "updates the title to contain the project's path", ->
|
||||
atom.workspace.getActivePane().destroy()
|
||||
expect(atom.workspace.getActivePaneItem()).toBeUndefined()
|
||||
expect(document.title).toMatch ///^#{atom.project.getPaths()[0]}///
|
||||
pathEscaped = escapeStringRegex(atom.project.getPaths()[0])
|
||||
expect(document.title).toMatch ///^#{pathEscaped}///
|
||||
|
||||
describe "when an inactive pane's item changes", ->
|
||||
it "does not update the title", ->
|
||||
@@ -875,7 +880,8 @@ describe "Workspace", ->
|
||||
})
|
||||
workspace2.deserialize(atom.workspace.serialize(), atom.deserializers)
|
||||
item = workspace2.getActivePaneItem()
|
||||
expect(document.title).toMatch ///^#{item.getLongTitle()}\ \u2014\ #{atom.project.getPaths()[0]}///
|
||||
pathEscaped = escapeStringRegex(atom.project.getPaths()[0])
|
||||
expect(document.title).toMatch ///^#{item.getLongTitle()}\ \u2014\ #{pathEscaped}///
|
||||
workspace2.destroy()
|
||||
|
||||
describe "document edited status", ->
|
||||
@@ -1610,3 +1616,48 @@ describe "Workspace", ->
|
||||
|
||||
runs ->
|
||||
expect(pane.getPendingItem()).toBeFalsy()
|
||||
|
||||
describe "grammar activation", ->
|
||||
beforeEach ->
|
||||
waitsForPromise ->
|
||||
atom.packages.activatePackage('language-javascript')
|
||||
|
||||
it "notifies the workspace of which grammar is used", ->
|
||||
editor = null
|
||||
|
||||
grammarUsed = jasmine.createSpy()
|
||||
atom.workspace.handleGrammarUsed = grammarUsed
|
||||
|
||||
waitsForPromise -> atom.workspace.open('sample-with-comments.js').then (o) -> editor = o
|
||||
waitsFor -> grammarUsed.callCount is 1
|
||||
runs ->
|
||||
expect(grammarUsed.argsForCall[0][0].name).toBe 'JavaScript'
|
||||
|
||||
describe ".checkoutHeadRevision()", ->
|
||||
editor = null
|
||||
beforeEach ->
|
||||
atom.config.set("editor.confirmCheckoutHeadRevision", false)
|
||||
|
||||
waitsForPromise -> atom.workspace.open('sample-with-comments.js').then (o) -> editor = o
|
||||
|
||||
it "reverts to the version of its file checked into the project repository", ->
|
||||
editor.setCursorBufferPosition([0, 0])
|
||||
editor.insertText("---\n")
|
||||
expect(editor.lineTextForBufferRow(0)).toBe "---"
|
||||
|
||||
waitsForPromise ->
|
||||
atom.workspace.checkoutHeadRevision(editor)
|
||||
|
||||
runs ->
|
||||
expect(editor.lineTextForBufferRow(0)).toBe ""
|
||||
|
||||
describe "when there's no repository for the editor's file", ->
|
||||
it "doesn't do anything", ->
|
||||
editor = atom.workspace.buildTextEditor()
|
||||
editor.setText("stuff")
|
||||
atom.workspace.checkoutHeadRevision(editor)
|
||||
|
||||
waitsForPromise -> atom.workspace.checkoutHeadRevision(editor)
|
||||
|
||||
escapeStringRegex = (str) ->
|
||||
str.replace(/[|\\{}()[\]^$+*?.]/g, '\\$&')
|
||||
|
||||
@@ -255,7 +255,7 @@ class AtomEnvironment extends Model
|
||||
@deserializers.add(TextBuffer)
|
||||
|
||||
registerDefaultCommands: ->
|
||||
registerDefaultCommands({commandRegistry: @commands, @config, @commandInstaller})
|
||||
registerDefaultCommands({commandRegistry: @commands, @config, @commandInstaller, notificationManager: @notifications, @project, @clipboard})
|
||||
|
||||
registerDefaultViewProviders: ->
|
||||
@views.addViewProvider Workspace, (model, env) ->
|
||||
|
||||
@@ -138,7 +138,11 @@ class AtomApplication
|
||||
return unless @socketPath?
|
||||
@deleteSocketFile()
|
||||
server = net.createServer (connection) =>
|
||||
connection.on 'data', (data) =>
|
||||
data = ''
|
||||
connection.on 'data', (chunk) ->
|
||||
data = data + chunk
|
||||
|
||||
connection.on 'end', =>
|
||||
options = JSON.parse(data)
|
||||
@openWithOptions(options)
|
||||
|
||||
@@ -170,9 +174,6 @@ class AtomApplication
|
||||
@on 'application:quit', -> app.quit()
|
||||
@on 'application:new-window', -> @openPath(getLoadSettings())
|
||||
@on 'application:new-file', -> (@focusedWindow() ? this).openPath()
|
||||
@on 'application:open', -> @promptForPathToOpen('all', getLoadSettings())
|
||||
@on 'application:open-file', -> @promptForPathToOpen('file', getLoadSettings())
|
||||
@on 'application:open-folder', -> @promptForPathToOpen('folder', getLoadSettings())
|
||||
@on 'application:open-dev', -> @promptForPathToOpen('all', devMode: true)
|
||||
@on 'application:open-safe', -> @promptForPathToOpen('all', safeMode: true)
|
||||
@on 'application:inspect', ({x, y, atomWindow}) ->
|
||||
@@ -255,6 +256,14 @@ class AtomApplication
|
||||
ipcMain.on 'command', (event, command) =>
|
||||
@emit(command)
|
||||
|
||||
ipcMain.on 'open-command', (event, command, args...) =>
|
||||
defaultPath = args[0] if args.length > 0
|
||||
switch command
|
||||
when 'application:open' then @promptForPathToOpen('all', getLoadSettings(), defaultPath)
|
||||
when 'application:open-file' then @promptForPathToOpen('file', getLoadSettings(), defaultPath)
|
||||
when 'application:open-folder' then @promptForPathToOpen('folder', getLoadSettings(), defaultPath)
|
||||
else console.log "Invalid open-command received: " + command
|
||||
|
||||
ipcMain.on 'window-command', (event, command, args...) ->
|
||||
win = BrowserWindow.fromWebContents(event.sender)
|
||||
win.emit(command, args...)
|
||||
@@ -653,11 +662,13 @@ class AtomApplication
|
||||
# :safeMode - A Boolean which controls whether any newly opened windows
|
||||
# should be in safe mode or not.
|
||||
# :window - An {AtomWindow} to use for opening a selected file path.
|
||||
promptForPathToOpen: (type, {devMode, safeMode, window}) ->
|
||||
@promptForPath type, (pathsToOpen) =>
|
||||
@openPaths({pathsToOpen, devMode, safeMode, window})
|
||||
# :path - An optional String which controls the default path to which the
|
||||
# file dialog opens.
|
||||
promptForPathToOpen: (type, {devMode, safeMode, window}, path=null) ->
|
||||
@promptForPath type, ((pathsToOpen) =>
|
||||
@openPaths({pathsToOpen, devMode, safeMode, window})), path
|
||||
|
||||
promptForPath: (type, callback) ->
|
||||
promptForPath: (type, callback, path) ->
|
||||
properties =
|
||||
switch type
|
||||
when 'file' then ['openFile']
|
||||
@@ -680,8 +691,8 @@ class AtomApplication
|
||||
when 'folder' then 'Open Folder'
|
||||
else 'Open'
|
||||
|
||||
if process.platform is 'linux'
|
||||
if projectPath = @lastFocusedWindow?.projectPath
|
||||
openOptions.defaultPath = projectPath
|
||||
# File dialog defaults to project directory of currently active editor
|
||||
if path?
|
||||
openOptions.defaultPath = path
|
||||
|
||||
dialog.showOpenDialog(parentWindow, openOptions, callback)
|
||||
|
||||
@@ -35,7 +35,6 @@ class DisplayBuffer extends Model
|
||||
state.config = atomEnvironment.config
|
||||
state.assert = atomEnvironment.assert
|
||||
state.grammarRegistry = atomEnvironment.grammars
|
||||
state.packageManager = atomEnvironment.packages
|
||||
new this(state)
|
||||
|
||||
constructor: (params={}) ->
|
||||
@@ -43,7 +42,7 @@ class DisplayBuffer extends Model
|
||||
|
||||
{
|
||||
tabLength, @editorWidthInChars, @tokenizedBuffer, @foldsMarkerLayer, buffer,
|
||||
ignoreInvisibles, @largeFileMode, @config, @assert, @grammarRegistry, @packageManager
|
||||
ignoreInvisibles, @largeFileMode, @config, @assert, @grammarRegistry
|
||||
} = params
|
||||
|
||||
@emitter = new Emitter
|
||||
@@ -51,7 +50,7 @@ class DisplayBuffer extends Model
|
||||
|
||||
@tokenizedBuffer ?= new TokenizedBuffer({
|
||||
tabLength, buffer, ignoreInvisibles, @largeFileMode, @config,
|
||||
@grammarRegistry, @packageManager, @assert
|
||||
@grammarRegistry, @assert
|
||||
})
|
||||
@buffer = @tokenizedBuffer.buffer
|
||||
@charWidthsByScope = {}
|
||||
@@ -122,7 +121,7 @@ class DisplayBuffer extends Model
|
||||
foldsMarkerLayer = @foldsMarkerLayer.copy()
|
||||
new DisplayBuffer({
|
||||
@buffer, tabLength: @getTabLength(), @largeFileMode, @config, @assert,
|
||||
@grammarRegistry, @packageManager, foldsMarkerLayer
|
||||
@grammarRegistry, foldsMarkerLayer
|
||||
})
|
||||
|
||||
updateAllScreenLines: ->
|
||||
|
||||
@@ -58,7 +58,7 @@ function getFromShell () {
|
||||
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')) {
|
||||
if (shell.endsWith('csh') || shell.endsWith('tcsh') || shell.endsWith('fish')) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
@@ -67,9 +67,20 @@ function needsPatching (options = { platform: process.platform, env: process.env
|
||||
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) {
|
||||
delete to[key]
|
||||
}
|
||||
|
||||
Object.assign(to, from)
|
||||
}
|
||||
|
||||
function normalize (options = {}) {
|
||||
if (options && options.env) {
|
||||
process.env = options.env
|
||||
clone(process.env, options.env)
|
||||
}
|
||||
|
||||
if (!options.env) {
|
||||
@@ -85,8 +96,8 @@ function normalize (options = {}) {
|
||||
// in #4126. Retain the original in case someone needs it.
|
||||
let shellEnv = getFromShell()
|
||||
if (shellEnv && shellEnv.PATH) {
|
||||
process._originalEnv = process.env
|
||||
process.env = shellEnv
|
||||
process._originalEnv = Object.assign({}, process.env)
|
||||
clone(process.env, shellEnv)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -96,7 +107,7 @@ function replace (env) {
|
||||
return
|
||||
}
|
||||
|
||||
process.env = env
|
||||
clone(process.env, env)
|
||||
}
|
||||
|
||||
export default { getFromShell, needsPatching, normalize, replace }
|
||||
|
||||
@@ -1,20 +1,7 @@
|
||||
'use babel'
|
||||
|
||||
import fs from 'fs-plus'
|
||||
import path from 'path'
|
||||
import Git from 'nodegit'
|
||||
import ResourcePool from './resource-pool'
|
||||
import {Emitter, CompositeDisposable, Disposable} from 'event-kit'
|
||||
|
||||
const modifiedStatusFlags = Git.Status.STATUS.WT_MODIFIED | Git.Status.STATUS.INDEX_MODIFIED | Git.Status.STATUS.WT_DELETED | Git.Status.STATUS.INDEX_DELETED | Git.Status.STATUS.WT_TYPECHANGE | Git.Status.STATUS.INDEX_TYPECHANGE
|
||||
const newStatusFlags = Git.Status.STATUS.WT_NEW | Git.Status.STATUS.INDEX_NEW
|
||||
const deletedStatusFlags = Git.Status.STATUS.WT_DELETED | Git.Status.STATUS.INDEX_DELETED
|
||||
const indexStatusFlags = Git.Status.STATUS.INDEX_NEW | Git.Status.STATUS.INDEX_MODIFIED | Git.Status.STATUS.INDEX_DELETED | Git.Status.STATUS.INDEX_RENAMED | Git.Status.STATUS.INDEX_TYPECHANGE
|
||||
const ignoredStatusFlags = 1 << 14 // TODO: compose this from libgit2 constants
|
||||
const submoduleMode = 57344 // TODO: compose this from libgit2 constants
|
||||
|
||||
// Just using this for _.isEqual and _.object, we should impl our own here
|
||||
import _ from 'underscore-plus'
|
||||
import {Repository} from 'ohnogit'
|
||||
import {CompositeDisposable, Disposable} from 'event-kit'
|
||||
|
||||
// For the most part, this class behaves the same as `GitRepository`, with a few
|
||||
// notable differences:
|
||||
@@ -29,39 +16,19 @@ export default class GitRepositoryAsync {
|
||||
}
|
||||
|
||||
static get Git () {
|
||||
return Git
|
||||
return Repository.Git
|
||||
}
|
||||
|
||||
// The name of the error thrown when an action is attempted on a destroyed
|
||||
// repository.
|
||||
static get DestroyedErrorName () {
|
||||
return 'GitRepositoryAsync.destroyed'
|
||||
return Repository.DestroyedErrorName
|
||||
}
|
||||
|
||||
constructor (_path, options = {}) {
|
||||
// We'll serialize our access manually.
|
||||
Git.setThreadSafetyStatus(Git.THREAD_SAFETY.DISABLED)
|
||||
this.repo = Repository.open(_path, options)
|
||||
|
||||
this.emitter = new Emitter()
|
||||
this.subscriptions = new CompositeDisposable()
|
||||
this.pathStatusCache = {}
|
||||
this.path = null
|
||||
|
||||
// NB: These needs to happen before the following .openRepository call.
|
||||
this.openedPath = _path
|
||||
this._openExactPath = options.openExactPath || false
|
||||
|
||||
this.repoPromise = this.openRepository()
|
||||
// NB: We don't currently _use_ the pooled object. But by giving it one
|
||||
// thing, we're really just serializing all the work. Down the road, we
|
||||
// could open multiple connections to the repository.
|
||||
this.repoPool = new ResourcePool([this.repoPromise])
|
||||
|
||||
this.isCaseInsensitive = fs.isCaseInsensitive()
|
||||
this.upstream = {}
|
||||
this.submodules = {}
|
||||
|
||||
this._refreshingPromise = Promise.resolve()
|
||||
|
||||
let {refreshOnWindowFocus = true} = options
|
||||
if (refreshOnWindowFocus) {
|
||||
@@ -78,23 +45,26 @@ export default class GitRepositoryAsync {
|
||||
}
|
||||
}
|
||||
|
||||
// This exists to provide backwards compatibility.
|
||||
get _refreshingPromise () {
|
||||
return this.repo._refreshingPromise
|
||||
}
|
||||
|
||||
get openedPath () {
|
||||
return this.repo.openedPath
|
||||
}
|
||||
|
||||
// Public: Destroy this {GitRepositoryAsync} object.
|
||||
//
|
||||
// This destroys any tasks and subscriptions and releases the underlying
|
||||
// libgit2 repository handle. This method is idempotent.
|
||||
destroy () {
|
||||
if (this.emitter) {
|
||||
this.emitter.emit('did-destroy')
|
||||
this.emitter.dispose()
|
||||
this.emitter = null
|
||||
}
|
||||
this.repo.destroy()
|
||||
|
||||
if (this.subscriptions) {
|
||||
this.subscriptions.dispose()
|
||||
this.subscriptions = null
|
||||
}
|
||||
|
||||
this.repoPromise = null
|
||||
}
|
||||
|
||||
// Event subscription
|
||||
@@ -107,7 +77,7 @@ export default class GitRepositoryAsync {
|
||||
//
|
||||
// Returns a {Disposable} on which `.dispose()` can be called to unsubscribe.
|
||||
onDidDestroy (callback) {
|
||||
return this.emitter.on('did-destroy', callback)
|
||||
return this.repo.onDidDestroy(callback)
|
||||
}
|
||||
|
||||
// Public: Invoke the given callback when a specific file's status has
|
||||
@@ -122,7 +92,7 @@ export default class GitRepositoryAsync {
|
||||
//
|
||||
// Returns a {Disposable} on which `.dispose()` can be called to unsubscribe.
|
||||
onDidChangeStatus (callback) {
|
||||
return this.emitter.on('did-change-status', callback)
|
||||
return this.repo.onDidChangeStatus(callback)
|
||||
}
|
||||
|
||||
// Public: Invoke the given callback when a multiple files' statuses have
|
||||
@@ -134,7 +104,7 @@ export default class GitRepositoryAsync {
|
||||
//
|
||||
// Returns a {Disposable} on which `.dispose()` can be called to unsubscribe.
|
||||
onDidChangeStatuses (callback) {
|
||||
return this.emitter.on('did-change-statuses', callback)
|
||||
return this.repo.onDidChangeStatuses(callback)
|
||||
}
|
||||
|
||||
// Repository details
|
||||
@@ -151,25 +121,13 @@ export default class GitRepositoryAsync {
|
||||
// Public: Returns a {Promise} which resolves to the {String} path of the
|
||||
// repository.
|
||||
getPath () {
|
||||
return this.getRepo().then(repo => {
|
||||
if (!this.path) {
|
||||
this.path = repo.path().replace(/\/$/, '')
|
||||
}
|
||||
|
||||
return this.path
|
||||
})
|
||||
return this.repo.getPath()
|
||||
}
|
||||
|
||||
// Public: Returns a {Promise} which resolves to the {String} working
|
||||
// directory path of the repository.
|
||||
getWorkingDirectory (_path) {
|
||||
return this.getRepo(_path).then(repo => {
|
||||
if (!repo.cachedWorkdir) {
|
||||
repo.cachedWorkdir = repo.workdir()
|
||||
}
|
||||
|
||||
return repo.cachedWorkdir
|
||||
})
|
||||
return this.repo.getWorkingDirectory()
|
||||
}
|
||||
|
||||
// Public: Returns a {Promise} that resolves to true if at the root, false if
|
||||
@@ -191,8 +149,7 @@ export default class GitRepositoryAsync {
|
||||
//
|
||||
// Returns a {Promise} which resolves to the relative {String} path.
|
||||
relativizeToWorkingDirectory (_path) {
|
||||
return this.getWorkingDirectory()
|
||||
.then(wd => this.relativize(_path, wd))
|
||||
return this.repo.relativizeToWorkingDirectory(_path)
|
||||
}
|
||||
|
||||
// Public: Makes a path relative to the repository's working directory.
|
||||
@@ -202,78 +159,13 @@ export default class GitRepositoryAsync {
|
||||
//
|
||||
// Returns the relative {String} path.
|
||||
relativize (_path, workingDirectory) {
|
||||
// The original implementation also handled null workingDirectory as it
|
||||
// pulled it from a sync function that could return null. We require it
|
||||
// to be passed here.
|
||||
let openedWorkingDirectory
|
||||
if (!_path || !workingDirectory) {
|
||||
return _path
|
||||
}
|
||||
|
||||
// If the opened directory and the workdir differ, this is a symlinked repo
|
||||
// root, so we have to do all the checks below twice--once against the realpath
|
||||
// and one against the opened path
|
||||
const opened = this.openedPath.replace(/\/\.git$/, '')
|
||||
if (path.relative(opened, workingDirectory) !== '') {
|
||||
openedWorkingDirectory = opened
|
||||
}
|
||||
|
||||
if (process.platform === 'win32') {
|
||||
_path = _path.replace(/\\/g, '/')
|
||||
} else {
|
||||
if (_path[0] !== '/') {
|
||||
return _path
|
||||
}
|
||||
}
|
||||
|
||||
workingDirectory = workingDirectory.replace(/\/$/, '')
|
||||
|
||||
// Depending on where the paths come from, they may have a '/private/'
|
||||
// prefix. Standardize by stripping that out.
|
||||
_path = _path.replace(/^\/private\//i, '/')
|
||||
workingDirectory = workingDirectory.replace(/^\/private\//i, '/')
|
||||
|
||||
const originalPath = _path
|
||||
const originalWorkingDirectory = workingDirectory
|
||||
if (this.isCaseInsensitive) {
|
||||
_path = _path.toLowerCase()
|
||||
workingDirectory = workingDirectory.toLowerCase()
|
||||
}
|
||||
|
||||
if (_path.indexOf(workingDirectory) === 0) {
|
||||
return originalPath.substring(originalWorkingDirectory.length + 1)
|
||||
} else if (_path === workingDirectory) {
|
||||
return ''
|
||||
}
|
||||
|
||||
if (openedWorkingDirectory) {
|
||||
openedWorkingDirectory = openedWorkingDirectory.replace(/\/$/, '')
|
||||
openedWorkingDirectory = openedWorkingDirectory.replace(/^\/private\//i, '/')
|
||||
|
||||
const originalOpenedWorkingDirectory = openedWorkingDirectory
|
||||
if (this.isCaseInsensitive) {
|
||||
openedWorkingDirectory = openedWorkingDirectory.toLowerCase()
|
||||
}
|
||||
|
||||
if (_path.indexOf(openedWorkingDirectory) === 0) {
|
||||
return originalPath.substring(originalOpenedWorkingDirectory.length + 1)
|
||||
} else if (_path === openedWorkingDirectory) {
|
||||
return ''
|
||||
}
|
||||
}
|
||||
|
||||
return _path
|
||||
return this.repo.relativize(_path, workingDirectory)
|
||||
}
|
||||
|
||||
// Public: Returns a {Promise} which resolves to whether the given branch
|
||||
// exists.
|
||||
hasBranch (branch) {
|
||||
return this.repoPool.enqueue(() => {
|
||||
return this.getRepo()
|
||||
.then(repo => repo.getBranch(branch))
|
||||
.then(branch => branch != null)
|
||||
.catch(_ => false)
|
||||
})
|
||||
return this.repo.hasBranch(branch)
|
||||
}
|
||||
|
||||
// Public: Retrieves a shortened version of the HEAD reference value.
|
||||
@@ -287,11 +179,7 @@ export default class GitRepositoryAsync {
|
||||
//
|
||||
// Returns a {Promise} which resolves to a {String}.
|
||||
getShortHead (_path) {
|
||||
return this.repoPool.enqueue(() => {
|
||||
return this.getRepo(_path)
|
||||
.then(repo => repo.getCurrentBranch())
|
||||
.then(branch => branch.shorthand())
|
||||
})
|
||||
return this.repo.getShortHead(_path)
|
||||
}
|
||||
|
||||
// Public: Is the given path a submodule in the repository?
|
||||
@@ -301,19 +189,7 @@ export default class GitRepositoryAsync {
|
||||
// Returns a {Promise} that resolves true if the given path is a submodule in
|
||||
// the repository.
|
||||
isSubmodule (_path) {
|
||||
return this.relativizeToWorkingDirectory(_path)
|
||||
.then(relativePath => {
|
||||
return this.repoPool.enqueue(() => {
|
||||
return this.getRepo()
|
||||
.then(repo => repo.index())
|
||||
.then(index => {
|
||||
const entry = index.getByPath(relativePath)
|
||||
if (!entry) return false
|
||||
|
||||
return entry.mode === submoduleMode
|
||||
})
|
||||
})
|
||||
})
|
||||
return this.repo.isSubmodule(_path)
|
||||
}
|
||||
|
||||
// Public: Returns the number of commits behind the current branch is from the
|
||||
@@ -327,18 +203,7 @@ export default class GitRepositoryAsync {
|
||||
// * `ahead` The {Number} of commits ahead.
|
||||
// * `behind` The {Number} of commits behind.
|
||||
getAheadBehindCount (reference, _path) {
|
||||
return this.repoPool.enqueue(() => {
|
||||
return this.getRepo(_path)
|
||||
.then(repo => Promise.all([repo, repo.getBranch(reference)]))
|
||||
.then(([repo, local]) => {
|
||||
const upstream = Git.Branch.upstream(local)
|
||||
return Promise.all([repo, local, upstream])
|
||||
})
|
||||
.then(([repo, local, upstream]) => {
|
||||
return Git.Graph.aheadBehind(repo, local.target(), upstream.target())
|
||||
})
|
||||
.catch(_ => ({ahead: 0, behind: 0}))
|
||||
})
|
||||
return this.repo.getAheadBehindCount(reference, _path)
|
||||
}
|
||||
|
||||
// Public: Get the cached ahead/behind commit counts for the current branch's
|
||||
@@ -351,15 +216,7 @@ export default class GitRepositoryAsync {
|
||||
// * `ahead` The {Number} of commits ahead.
|
||||
// * `behind` The {Number} of commits behind.
|
||||
getCachedUpstreamAheadBehindCount (_path) {
|
||||
return this.relativizeToWorkingDirectory(_path)
|
||||
.then(relativePath => this._submoduleForPath(_path))
|
||||
.then(submodule => {
|
||||
if (submodule) {
|
||||
return submodule.getCachedUpstreamAheadBehindCount(_path)
|
||||
} else {
|
||||
return this.upstream
|
||||
}
|
||||
})
|
||||
return this.repo.getCachedUpstreamAheadBehindCount(_path)
|
||||
}
|
||||
|
||||
// Public: Returns the git configuration value specified by the key.
|
||||
@@ -370,12 +227,7 @@ export default class GitRepositoryAsync {
|
||||
// Returns a {Promise} which resolves to the {String} git configuration value
|
||||
// specified by the key.
|
||||
getConfigValue (key, _path) {
|
||||
return this.repoPool.enqueue(() => {
|
||||
return this.getRepo(_path)
|
||||
.then(repo => repo.configSnapshot())
|
||||
.then(config => config.getStringBuf(key))
|
||||
.catch(_ => null)
|
||||
})
|
||||
return this.repo.getConfigValue(key, _path)
|
||||
}
|
||||
|
||||
// Public: Get the URL for the 'origin' remote.
|
||||
@@ -386,7 +238,7 @@ export default class GitRepositoryAsync {
|
||||
// Returns a {Promise} which resolves to the {String} origin url of the
|
||||
// repository.
|
||||
getOriginURL (_path) {
|
||||
return this.getConfigValue('remote.origin.url', _path)
|
||||
return this.repo.getOriginURL(_path)
|
||||
}
|
||||
|
||||
// Public: Returns the upstream branch for the current HEAD, or null if there
|
||||
@@ -398,11 +250,7 @@ export default class GitRepositoryAsync {
|
||||
// Returns a {Promise} which resolves to a {String} branch name such as
|
||||
// `refs/remotes/origin/master`.
|
||||
getUpstreamBranch (_path) {
|
||||
return this.repoPool.enqueue(() => {
|
||||
return this.getRepo(_path)
|
||||
.then(repo => repo.getCurrentBranch())
|
||||
.then(branch => Git.Branch.upstream(branch))
|
||||
})
|
||||
return this.repo.getUpstreamBranch(_path)
|
||||
}
|
||||
|
||||
// Public: Gets all the local and remote references.
|
||||
@@ -415,25 +263,7 @@ export default class GitRepositoryAsync {
|
||||
// * `remotes` An {Array} of remote reference names.
|
||||
// * `tags` An {Array} of tag reference names.
|
||||
getReferences (_path) {
|
||||
return this.repoPool.enqueue(() => {
|
||||
return this.getRepo(_path)
|
||||
.then(repo => repo.getReferences(Git.Reference.TYPE.LISTALL))
|
||||
.then(refs => {
|
||||
const heads = []
|
||||
const remotes = []
|
||||
const tags = []
|
||||
for (const ref of refs) {
|
||||
if (ref.isTag()) {
|
||||
tags.push(ref.name())
|
||||
} else if (ref.isRemote()) {
|
||||
remotes.push(ref.name())
|
||||
} else if (ref.isBranch()) {
|
||||
heads.push(ref.name())
|
||||
}
|
||||
}
|
||||
return {heads, remotes, tags}
|
||||
})
|
||||
})
|
||||
return this.repo.getReferences(_path)
|
||||
}
|
||||
|
||||
// Public: Get the SHA for the given reference.
|
||||
@@ -445,11 +275,7 @@ export default class GitRepositoryAsync {
|
||||
// Returns a {Promise} which resolves to the current {String} SHA for the
|
||||
// given reference.
|
||||
getReferenceTarget (reference, _path) {
|
||||
return this.repoPool.enqueue(() => {
|
||||
return this.getRepo(_path)
|
||||
.then(repo => Git.Reference.nameToId(repo, reference))
|
||||
.then(oid => oid.tostrS())
|
||||
})
|
||||
return this.repo.getReferenceTarget(reference, _path)
|
||||
}
|
||||
|
||||
// Reading Status
|
||||
@@ -462,9 +288,7 @@ export default class GitRepositoryAsync {
|
||||
// Returns a {Promise} which resolves to a {Boolean} that's true if the `path`
|
||||
// is modified.
|
||||
isPathModified (_path) {
|
||||
return this.relativizeToWorkingDirectory(_path)
|
||||
.then(relativePath => this._getStatus([relativePath]))
|
||||
.then(statuses => statuses.some(status => status.isModified()))
|
||||
return this.repo.isPathModified(_path)
|
||||
}
|
||||
|
||||
// Public: Resolves true if the given path is new.
|
||||
@@ -474,9 +298,7 @@ export default class GitRepositoryAsync {
|
||||
// Returns a {Promise} which resolves to a {Boolean} that's true if the `path`
|
||||
// is new.
|
||||
isPathNew (_path) {
|
||||
return this.relativizeToWorkingDirectory(_path)
|
||||
.then(relativePath => this._getStatus([relativePath]))
|
||||
.then(statuses => statuses.some(status => status.isNew()))
|
||||
return this.repo.isPathNew(_path)
|
||||
}
|
||||
|
||||
// Public: Is the given path ignored?
|
||||
@@ -486,17 +308,7 @@ export default class GitRepositoryAsync {
|
||||
// Returns a {Promise} which resolves to a {Boolean} that's true if the `path`
|
||||
// is ignored.
|
||||
isPathIgnored (_path) {
|
||||
return this.getWorkingDirectory()
|
||||
.then(wd => {
|
||||
return this.repoPool.enqueue(() => {
|
||||
return this.getRepo()
|
||||
.then(repo => {
|
||||
const relativePath = this.relativize(_path, wd)
|
||||
return Git.Ignore.pathIsIgnored(repo, relativePath)
|
||||
})
|
||||
.then(ignored => Boolean(ignored))
|
||||
})
|
||||
})
|
||||
return this.repo.isPathIgnored(_path)
|
||||
}
|
||||
|
||||
// Get the status of a directory in the repository's working directory.
|
||||
@@ -507,18 +319,7 @@ export default class GitRepositoryAsync {
|
||||
// value can be passed to {::isStatusModified} or {::isStatusNew} to get more
|
||||
// information.
|
||||
getDirectoryStatus (directoryPath) {
|
||||
return this.relativizeToWorkingDirectory(directoryPath)
|
||||
.then(relativePath => {
|
||||
const pathspec = relativePath + '/**'
|
||||
return this._getStatus([pathspec])
|
||||
})
|
||||
.then(statuses => {
|
||||
return Promise.all(statuses.map(s => s.statusBit())).then(bits => {
|
||||
return bits
|
||||
.filter(b => b > 0)
|
||||
.reduce((status, bit) => status | bit, 0)
|
||||
})
|
||||
})
|
||||
return this.repo.getDirectoryStatus(directoryPath)
|
||||
}
|
||||
|
||||
// Refresh the status bit for the given path.
|
||||
@@ -531,27 +332,7 @@ export default class GitRepositoryAsync {
|
||||
// Returns a {Promise} which resolves to a {Number} which is the refreshed
|
||||
// status bit for the path.
|
||||
refreshStatusForPath (_path) {
|
||||
let relativePath
|
||||
return this.getWorkingDirectory()
|
||||
.then(wd => {
|
||||
relativePath = this.relativize(_path, wd)
|
||||
return this._getStatus([relativePath])
|
||||
})
|
||||
.then(statuses => {
|
||||
const cachedStatus = this.pathStatusCache[relativePath] || 0
|
||||
const status = statuses[0] ? statuses[0].statusBit() : Git.Status.STATUS.CURRENT
|
||||
if (status !== cachedStatus) {
|
||||
if (status === Git.Status.STATUS.CURRENT) {
|
||||
delete this.pathStatusCache[relativePath]
|
||||
} else {
|
||||
this.pathStatusCache[relativePath] = status
|
||||
}
|
||||
|
||||
this.emitter.emit('did-change-status', {path: _path, pathStatus: status})
|
||||
}
|
||||
|
||||
return status
|
||||
})
|
||||
return this.repo.refreshStatusForPath(_path)
|
||||
}
|
||||
|
||||
// Returns a Promise that resolves to the status bit of a given path if it has
|
||||
@@ -567,8 +348,7 @@ export default class GitRepositoryAsync {
|
||||
// Returns a {Promise} which resolves to a status {Number} or null if the
|
||||
// path is not in the cache.
|
||||
getCachedPathStatus (_path) {
|
||||
return this.relativizeToWorkingDirectory(_path)
|
||||
.then(relativePath => this.pathStatusCache[relativePath])
|
||||
return this.repo.getCachedPathStatus(_path)
|
||||
}
|
||||
|
||||
// Public: Get the cached statuses for the repository.
|
||||
@@ -576,7 +356,7 @@ export default class GitRepositoryAsync {
|
||||
// Returns an {Object} of {Number} statuses, keyed by {String} working
|
||||
// directory-relative file names.
|
||||
getCachedPathStatuses () {
|
||||
return this.pathStatusCache
|
||||
return this.repo.pathStatusCache
|
||||
}
|
||||
|
||||
// Public: Returns true if the given status indicates modification.
|
||||
@@ -585,7 +365,7 @@ export default class GitRepositoryAsync {
|
||||
//
|
||||
// Returns a {Boolean} that's true if the `statusBit` indicates modification.
|
||||
isStatusModified (statusBit) {
|
||||
return (statusBit & modifiedStatusFlags) > 0
|
||||
return this.repo.isStatusModified(statusBit)
|
||||
}
|
||||
|
||||
// Public: Returns true if the given status indicates a new path.
|
||||
@@ -594,7 +374,7 @@ export default class GitRepositoryAsync {
|
||||
//
|
||||
// Returns a {Boolean} that's true if the `statusBit` indicates a new path.
|
||||
isStatusNew (statusBit) {
|
||||
return (statusBit & newStatusFlags) > 0
|
||||
return this.repo.isStatusNew(statusBit)
|
||||
}
|
||||
|
||||
// Public: Returns true if the given status indicates the path is staged.
|
||||
@@ -604,7 +384,7 @@ export default class GitRepositoryAsync {
|
||||
// Returns a {Boolean} that's true if the `statusBit` indicates the path is
|
||||
// staged.
|
||||
isStatusStaged (statusBit) {
|
||||
return (statusBit & indexStatusFlags) > 0
|
||||
return this.repo.isStatusStaged(statusBit)
|
||||
}
|
||||
|
||||
// Public: Returns true if the given status indicates the path is ignored.
|
||||
@@ -614,7 +394,7 @@ export default class GitRepositoryAsync {
|
||||
// Returns a {Boolean} that's true if the `statusBit` indicates the path is
|
||||
// ignored.
|
||||
isStatusIgnored (statusBit) {
|
||||
return (statusBit & ignoredStatusFlags) > 0
|
||||
return this.repo.isStatusIgnored(statusBit)
|
||||
}
|
||||
|
||||
// Public: Returns true if the given status indicates the path is deleted.
|
||||
@@ -624,7 +404,7 @@ export default class GitRepositoryAsync {
|
||||
// Returns a {Boolean} that's true if the `statusBit` indicates the path is
|
||||
// deleted.
|
||||
isStatusDeleted (statusBit) {
|
||||
return (statusBit & deletedStatusFlags) > 0
|
||||
return this.repo.isStatusDeleted(statusBit)
|
||||
}
|
||||
|
||||
// Retrieving Diffs
|
||||
@@ -640,40 +420,7 @@ export default class GitRepositoryAsync {
|
||||
// * `added` The {Number} of added lines.
|
||||
// * `deleted` The {Number} of deleted lines.
|
||||
getDiffStats (_path) {
|
||||
return this.getWorkingDirectory(_path)
|
||||
.then(wd => {
|
||||
return this.repoPool.enqueue(() => {
|
||||
return this.getRepo(_path)
|
||||
.then(repo => Promise.all([repo, repo.getHeadCommit()]))
|
||||
.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, wd)
|
||||
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))
|
||||
.then(lines => {
|
||||
const stats = {added: 0, deleted: 0}
|
||||
for (const line of lines) {
|
||||
const origin = line.origin()
|
||||
if (origin === Git.Diff.LINE.ADDITION) {
|
||||
stats.added++
|
||||
} else if (origin === Git.Diff.LINE.DELETION) {
|
||||
stats.deleted++
|
||||
}
|
||||
}
|
||||
return stats
|
||||
})
|
||||
})
|
||||
})
|
||||
return this.repo.getDiffStats(_path)
|
||||
}
|
||||
|
||||
// Public: Retrieves the line diffs comparing the `HEAD` version of the given
|
||||
@@ -688,30 +435,7 @@ export default class GitRepositoryAsync {
|
||||
// * `oldLines` The {Number} of lines in the old hunk.
|
||||
// * `newLines` The {Number} of lines in the new hunk
|
||||
getLineDiffs (_path, text) {
|
||||
return this.getWorkingDirectory(_path)
|
||||
.then(wd => {
|
||||
let relativePath = null
|
||||
return this.repoPool.enqueue(() => {
|
||||
return this.getRepo(_path)
|
||||
.then(repo => {
|
||||
relativePath = this.relativize(_path, wd)
|
||||
return repo.getHeadCommit()
|
||||
})
|
||||
.then(commit => commit.getEntry(relativePath))
|
||||
.then(entry => entry.getBlob())
|
||||
.then(blob => {
|
||||
const options = new Git.DiffOptions()
|
||||
options.contextLines = 0
|
||||
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 this._diffBlobToBuffer(blob, text, options)
|
||||
})
|
||||
})
|
||||
})
|
||||
return this.repo.getLineDiffs(_path, text)
|
||||
}
|
||||
|
||||
// Checking Out
|
||||
@@ -732,19 +456,7 @@ export default class GitRepositoryAsync {
|
||||
// Returns a {Promise} that resolves or rejects depending on whether the
|
||||
// method was successful.
|
||||
checkoutHead (_path) {
|
||||
return this.getWorkingDirectory(_path)
|
||||
.then(wd => {
|
||||
return this.repoPool.enqueue(() => {
|
||||
return this.getRepo(_path)
|
||||
.then(repo => {
|
||||
const checkoutOptions = new Git.CheckoutOptions()
|
||||
checkoutOptions.paths = [this.relativize(_path, wd)]
|
||||
checkoutOptions.checkoutStrategy = Git.Checkout.STRATEGY.FORCE | Git.Checkout.STRATEGY.DISABLE_PATHSPEC_MATCH
|
||||
return Git.Checkout.head(repo, checkoutOptions)
|
||||
})
|
||||
})
|
||||
})
|
||||
.then(() => this.refreshStatusForPath(_path))
|
||||
return this.repo.checkoutHead(_path)
|
||||
}
|
||||
|
||||
// Public: Checks out a branch in your repository.
|
||||
@@ -755,19 +467,7 @@ export default class GitRepositoryAsync {
|
||||
//
|
||||
// Returns a {Promise} that resolves if the method was successful.
|
||||
checkoutReference (reference, create) {
|
||||
return this.repoPool.enqueue(() => {
|
||||
return this.getRepo()
|
||||
.then(repo => repo.checkoutBranch(reference))
|
||||
})
|
||||
.catch(error => {
|
||||
if (create) {
|
||||
return this._createBranch(reference)
|
||||
.then(_ => this.checkoutReference(reference, false))
|
||||
} else {
|
||||
throw error
|
||||
}
|
||||
})
|
||||
.then(_ => null)
|
||||
return this.repo.checkoutReference(reference, create)
|
||||
}
|
||||
|
||||
// Private
|
||||
@@ -786,107 +486,10 @@ export default class GitRepositoryAsync {
|
||||
return this.checkoutHead(filePath)
|
||||
}
|
||||
|
||||
// Create a new branch with the given name.
|
||||
// Refreshes the git status.
|
||||
//
|
||||
// * `name` The {String} name of the new branch.
|
||||
//
|
||||
// Returns a {Promise} which resolves to a {NodeGit.Ref} reference to the
|
||||
// created branch.
|
||||
_createBranch (name) {
|
||||
return this.repoPool.enqueue(() => {
|
||||
return this.getRepo()
|
||||
.then(repo => Promise.all([repo, repo.getHeadCommit()]))
|
||||
.then(([repo, commit]) => repo.createBranch(name, commit))
|
||||
})
|
||||
}
|
||||
|
||||
// Get all the hunks in the diff.
|
||||
//
|
||||
// * `diff` The {NodeGit.Diff} whose hunks should be retrieved.
|
||||
//
|
||||
// Returns a {Promise} which resolves to an {Array} of {NodeGit.Hunk}.
|
||||
_getDiffHunks (diff) {
|
||||
return diff.patches()
|
||||
.then(patches => Promise.all(patches.map(p => p.hunks()))) // patches :: Array<Patch>
|
||||
.then(hunks => _.flatten(hunks)) // hunks :: Array<Array<Hunk>>
|
||||
}
|
||||
|
||||
// Get all the lines contained in the diff.
|
||||
//
|
||||
// * `diff` The {NodeGit.Diff} use lines should be retrieved.
|
||||
//
|
||||
// Returns a {Promise} which resolves to an {Array} of {NodeGit.Line}.
|
||||
_getDiffLines (diff) {
|
||||
return this._getDiffHunks(diff)
|
||||
.then(hunks => Promise.all(hunks.map(h => h.lines())))
|
||||
.then(lines => _.flatten(lines)) // lines :: Array<Array<Line>>
|
||||
}
|
||||
|
||||
// Diff the given blob and buffer with the provided options.
|
||||
//
|
||||
// * `blob` The {NodeGit.Blob}
|
||||
// * `buffer` The {String} buffer.
|
||||
// * `options` The {NodeGit.DiffOptions}
|
||||
//
|
||||
// Returns a {Promise} which resolves to an {Array} of {Object}s which have
|
||||
// the following keys:
|
||||
// * `oldStart` The {Number} of the old starting line.
|
||||
// * `newStart` The {Number} of the new starting line.
|
||||
// * `oldLines` The {Number} of old lines.
|
||||
// * `newLines` The {Number} of new lines.
|
||||
_diffBlobToBuffer (blob, buffer, options) {
|
||||
const hunks = []
|
||||
const hunkCallback = (delta, hunk, payload) => {
|
||||
hunks.push({
|
||||
oldStart: hunk.oldStart(),
|
||||
newStart: hunk.newStart(),
|
||||
oldLines: hunk.oldLines(),
|
||||
newLines: hunk.newLines()
|
||||
})
|
||||
}
|
||||
|
||||
return Git.Diff.blobToBuffer(blob, null, buffer, null, options, null, null, hunkCallback, null)
|
||||
.then(_ => hunks)
|
||||
}
|
||||
|
||||
// Get the current branch and update this.branch.
|
||||
//
|
||||
// Returns a {Promise} which resolves to a {boolean} indicating whether the
|
||||
// branch name changed.
|
||||
_refreshBranch () {
|
||||
return this.repoPool.enqueue(() => {
|
||||
return this.getRepo()
|
||||
.then(repo => repo.getCurrentBranch())
|
||||
.then(ref => ref.name())
|
||||
.then(branchName => {
|
||||
const changed = branchName !== this.branch
|
||||
this.branch = branchName
|
||||
return changed
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// Refresh the cached ahead/behind count with the given branch.
|
||||
//
|
||||
// * `branchName` The {String} name of the branch whose ahead/behind should be
|
||||
// used for the refresh.
|
||||
//
|
||||
// Returns a {Promise} which will resolve to a {boolean} indicating whether
|
||||
// the ahead/behind count changed.
|
||||
_refreshAheadBehindCount (branchName) {
|
||||
return this.getAheadBehindCount(branchName)
|
||||
.then(counts => {
|
||||
const changed = !_.isEqual(counts, this.upstream)
|
||||
this.upstream = counts
|
||||
return changed
|
||||
})
|
||||
}
|
||||
|
||||
// Get the status for this repository.
|
||||
//
|
||||
// Returns a {Promise} that will resolve to an object of {String} paths to the
|
||||
// {Number} status.
|
||||
_getRepositoryStatus () {
|
||||
// Returns a {Promise} which will resolve to {null} when refresh is complete.
|
||||
refreshStatus () {
|
||||
let projectPathsPromises = [Promise.resolve('')]
|
||||
if (this.project) {
|
||||
projectPathsPromises = this.project.getPaths()
|
||||
@@ -895,163 +498,7 @@ export default class GitRepositoryAsync {
|
||||
|
||||
return Promise.all(projectPathsPromises)
|
||||
.then(paths => paths.map(p => p.length > 0 ? p + '/**' : '*'))
|
||||
.then(projectPaths => {
|
||||
return this._getStatus(projectPaths.length > 0 ? projectPaths : null)
|
||||
})
|
||||
.then(statuses => {
|
||||
const statusPairs = statuses.map(status => [status.path(), status.statusBit()])
|
||||
return _.object(statusPairs)
|
||||
})
|
||||
}
|
||||
|
||||
// Get the status for the given submodule.
|
||||
//
|
||||
// * `submodule` The {GitRepositoryAsync} for the submodule.
|
||||
//
|
||||
// Returns a {Promise} which resolves to an {Object}, keyed by {String}
|
||||
// repo-relative {Number} statuses.
|
||||
async _getSubmoduleStatus (submodule) {
|
||||
// At this point, we've called submodule._refreshSubmodules(), which would
|
||||
// have refreshed the status on *its* submodules, etc. So we know that its
|
||||
// cached path statuses are up-to-date.
|
||||
//
|
||||
// Now we just need to hoist those statuses into our repository by changing
|
||||
// their paths to be relative to us.
|
||||
|
||||
const statuses = submodule.getCachedPathStatuses()
|
||||
const repoRelativeStatuses = {}
|
||||
const submoduleRepo = await submodule.getRepo()
|
||||
const submoduleWorkDir = submoduleRepo.workdir()
|
||||
for (const relativePath in statuses) {
|
||||
const statusBit = statuses[relativePath]
|
||||
const absolutePath = path.join(submoduleWorkDir, relativePath)
|
||||
const repoRelativePath = await this.relativizeToWorkingDirectory(absolutePath)
|
||||
repoRelativeStatuses[repoRelativePath] = statusBit
|
||||
}
|
||||
|
||||
return repoRelativeStatuses
|
||||
}
|
||||
|
||||
// Refresh the list of submodules in the repository.
|
||||
//
|
||||
// Returns a {Promise} which resolves to an {Object} keyed by {String}
|
||||
// submodule names with {GitRepositoryAsync} values.
|
||||
async _refreshSubmodules () {
|
||||
const repo = await this.getRepo()
|
||||
const wd = await this.getWorkingDirectory()
|
||||
const submoduleNames = await repo.getSubmoduleNames()
|
||||
for (const name of submoduleNames) {
|
||||
const alreadyExists = Boolean(this.submodules[name])
|
||||
if (alreadyExists) continue
|
||||
|
||||
const submodule = await Git.Submodule.lookup(repo, name)
|
||||
const absolutePath = path.join(wd, submodule.path())
|
||||
const submoduleRepo = GitRepositoryAsync.open(absolutePath, {openExactPath: true, refreshOnWindowFocus: false})
|
||||
this.submodules[name] = submoduleRepo
|
||||
}
|
||||
|
||||
for (const name in this.submodules) {
|
||||
const repo = this.submodules[name]
|
||||
const gone = submoduleNames.indexOf(name) < 0
|
||||
if (gone) {
|
||||
repo.destroy()
|
||||
delete this.submodules[name]
|
||||
} else {
|
||||
try {
|
||||
await repo.refreshStatus()
|
||||
} catch (e) {
|
||||
// libgit2 will sometimes report submodules that aren't actually valid
|
||||
// (https://github.com/libgit2/libgit2/issues/3580). So check the
|
||||
// validity of the submodules by removing any that fail.
|
||||
repo.destroy()
|
||||
delete this.submodules[name]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return _.values(this.submodules)
|
||||
}
|
||||
|
||||
// Get the status for the submodules in the repository.
|
||||
//
|
||||
// Returns a {Promise} that will resolve to an object of {String} paths to the
|
||||
// {Number} status.
|
||||
_getSubmoduleStatuses () {
|
||||
return this._refreshSubmodules()
|
||||
.then(repos => {
|
||||
return Promise.all(repos.map(repo => this._getSubmoduleStatus(repo)))
|
||||
})
|
||||
.then(statuses => _.extend({}, ...statuses))
|
||||
}
|
||||
|
||||
// Refresh the cached status.
|
||||
//
|
||||
// Returns a {Promise} which will resolve to a {boolean} indicating whether
|
||||
// any statuses changed.
|
||||
_refreshStatus () {
|
||||
return Promise.all([this._getRepositoryStatus(), this._getSubmoduleStatuses()])
|
||||
.then(([repositoryStatus, submoduleStatus]) => {
|
||||
const statusesByPath = _.extend({}, repositoryStatus, submoduleStatus)
|
||||
const changed = !_.isEqual(this.pathStatusCache, statusesByPath)
|
||||
this.pathStatusCache = statusesByPath
|
||||
return changed
|
||||
})
|
||||
}
|
||||
|
||||
// Refreshes the git status.
|
||||
//
|
||||
// Returns a {Promise} which will resolve to {null} when refresh is complete.
|
||||
refreshStatus () {
|
||||
const status = this._refreshStatus()
|
||||
const branch = this._refreshBranch()
|
||||
const aheadBehind = branch.then(() => this._refreshAheadBehindCount(this.branch))
|
||||
|
||||
this._refreshingPromise = this._refreshingPromise.then(_ => {
|
||||
return Promise.all([status, branch, aheadBehind])
|
||||
.then(([statusChanged, branchChanged, aheadBehindChanged]) => {
|
||||
if (this.emitter && (statusChanged || branchChanged || aheadBehindChanged)) {
|
||||
this.emitter.emit('did-change-statuses')
|
||||
}
|
||||
|
||||
return null
|
||||
})
|
||||
// Because all these refresh steps happen asynchronously, it's entirely
|
||||
// possible the repository was destroyed while we were working. In which
|
||||
// case we should just swallow the error.
|
||||
.catch(e => {
|
||||
if (this._isDestroyed()) {
|
||||
return null
|
||||
} else {
|
||||
return Promise.reject(e)
|
||||
}
|
||||
})
|
||||
.catch(e => {
|
||||
console.error('Error refreshing repository status:')
|
||||
console.error(e)
|
||||
return Promise.reject(e)
|
||||
})
|
||||
})
|
||||
return this._refreshingPromise
|
||||
}
|
||||
|
||||
// Get the submodule for the given path.
|
||||
//
|
||||
// Returns a {Promise} which resolves to the {GitRepositoryAsync} submodule or
|
||||
// null if it isn't a submodule path.
|
||||
async _submoduleForPath (_path) {
|
||||
let relativePath = await this.relativizeToWorkingDirectory(_path)
|
||||
for (const submodulePath in this.submodules) {
|
||||
const submoduleRepo = this.submodules[submodulePath]
|
||||
if (relativePath === submodulePath) {
|
||||
return submoduleRepo
|
||||
} else if (relativePath.indexOf(`${submodulePath}/`) === 0) {
|
||||
relativePath = relativePath.substring(submodulePath.length + 1)
|
||||
const innerSubmodule = await submoduleRepo._submoduleForPath(relativePath)
|
||||
return innerSubmodule || submoduleRepo
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
.then(pathspecs => this.repo.refreshStatus(pathspecs))
|
||||
}
|
||||
|
||||
// Get the NodeGit repository for the given path.
|
||||
@@ -1062,16 +509,7 @@ export default class GitRepositoryAsync {
|
||||
//
|
||||
// Returns a {Promise} which resolves to the {NodeGit.Repository}.
|
||||
getRepo (_path) {
|
||||
if (this._isDestroyed()) {
|
||||
const error = new Error('Repository has been destroyed')
|
||||
error.name = GitRepositoryAsync.DestroyedErrorName
|
||||
return Promise.reject(error)
|
||||
}
|
||||
|
||||
if (!_path) return this.repoPromise
|
||||
|
||||
return this._submoduleForPath(_path)
|
||||
.then(submodule => submodule ? submodule.getRepo() : this.repoPromise)
|
||||
return this.repo.getRepo(_path)
|
||||
}
|
||||
|
||||
// Open a new instance of the underlying {NodeGit.Repository}.
|
||||
@@ -1081,11 +519,7 @@ export default class GitRepositoryAsync {
|
||||
//
|
||||
// Returns the new {NodeGit.Repository}.
|
||||
openRepository () {
|
||||
if (this._openExactPath) {
|
||||
return Git.Repository.open(this.openedPath)
|
||||
} else {
|
||||
return Git.Repository.openExt(this.openedPath, 0, '')
|
||||
}
|
||||
return this.repo.openRepository()
|
||||
}
|
||||
|
||||
// Section: Private
|
||||
@@ -1095,7 +529,7 @@ export default class GitRepositoryAsync {
|
||||
//
|
||||
// Returns a {Boolean}.
|
||||
_isDestroyed () {
|
||||
return this.repoPromise == null
|
||||
return this.repo._isDestroyed()
|
||||
}
|
||||
|
||||
// Subscribe to events on the given buffer.
|
||||
@@ -1121,28 +555,4 @@ export default class GitRepositoryAsync {
|
||||
|
||||
this.subscriptions.add(bufferSubscriptions)
|
||||
}
|
||||
|
||||
// Get the status for the given paths.
|
||||
//
|
||||
// * `paths` The {String} paths whose status is wanted. If undefined, get the
|
||||
// status for the whole repository.
|
||||
//
|
||||
// Returns a {Promise} which resolves to an {Array} of {NodeGit.StatusFile}
|
||||
// statuses for the paths.
|
||||
_getStatus (paths) {
|
||||
return this.repoPool.enqueue(() => {
|
||||
return this.getRepo()
|
||||
.then(repo => {
|
||||
const opts = {
|
||||
flags: Git.Status.OPT.INCLUDE_UNTRACKED | Git.Status.OPT.RECURSE_UNTRACKED_DIRS
|
||||
}
|
||||
|
||||
if (paths) {
|
||||
opts.pathspec = paths
|
||||
}
|
||||
|
||||
return repo.getStatusExt(opts)
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -166,12 +166,7 @@ class GitRepository
|
||||
#
|
||||
# Returns a {Disposable} on which `.dispose()` can be called to unsubscribe.
|
||||
onDidChangeStatuses: (callback) ->
|
||||
@async.onDidChangeStatuses ->
|
||||
# Defer the callback to the next tick so that we've reset
|
||||
# `@statusesByPath` by the time it's called. Otherwise reads from within
|
||||
# the callback could be inconsistent.
|
||||
# See https://github.com/atom/atom/issues/11396
|
||||
process.nextTick callback
|
||||
@emitter.on 'did-change-statuses', callback
|
||||
|
||||
###
|
||||
Section: Repository Details
|
||||
@@ -496,9 +491,27 @@ class GitRepository
|
||||
#
|
||||
# Returns a promise that resolves when the repository has been refreshed.
|
||||
refreshStatus: ->
|
||||
statusesChanged = false
|
||||
|
||||
# Listen for `did-change-statuses` so we know if something changed. But we
|
||||
# need to wait to propagate it until after we've set the branch and cleared
|
||||
# the `statusesByPath` cache. So just set a flag, and we'll emit the event
|
||||
# after refresh is done.
|
||||
subscription = @async.onDidChangeStatuses ->
|
||||
subscription?.dispose()
|
||||
subscription = null
|
||||
|
||||
statusesChanged = true
|
||||
|
||||
asyncRefresh = @async.refreshStatus().then =>
|
||||
@statusesByPath = {}
|
||||
subscription?.dispose()
|
||||
subscription = null
|
||||
|
||||
@branch = @async?.branch
|
||||
@statusesByPath = {}
|
||||
|
||||
if statusesChanged
|
||||
@emitter.emit 'did-change-statuses'
|
||||
|
||||
syncRefresh = new Promise (resolve, reject) =>
|
||||
@handlerPath ?= require.resolve('./repository-status-handler')
|
||||
|
||||
@@ -391,6 +391,9 @@ class Project extends Model
|
||||
|
||||
subscribeToBuffer: (buffer) ->
|
||||
buffer.onDidDestroy => @removeBuffer(buffer)
|
||||
buffer.onDidChangePath =>
|
||||
unless @getPaths().length > 0
|
||||
@setPaths([path.dirname(buffer.getPath())])
|
||||
buffer.onWillThrowWatchError ({error, handle}) =>
|
||||
handle()
|
||||
@notificationManager.addWarning """
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{ipcRenderer} = require 'electron'
|
||||
|
||||
module.exports = ({commandRegistry, commandInstaller, config}) ->
|
||||
module.exports = ({commandRegistry, commandInstaller, config, notificationManager, project, clipboard}) ->
|
||||
commandRegistry.add 'atom-workspace',
|
||||
'pane:show-next-recently-used-item': -> @getModel().getActivePane().activateNextRecentlyUsedItem()
|
||||
'pane:show-previous-recently-used-item': -> @getModel().getActivePane().activatePreviousRecentlyUsedItem()
|
||||
@@ -31,9 +31,15 @@ module.exports = ({commandRegistry, commandInstaller, config}) ->
|
||||
'application:unhide-all-applications': -> ipcRenderer.send('command', 'application:unhide-all-applications')
|
||||
'application:new-window': -> ipcRenderer.send('command', 'application:new-window')
|
||||
'application:new-file': -> ipcRenderer.send('command', 'application:new-file')
|
||||
'application:open': -> ipcRenderer.send('command', 'application:open')
|
||||
'application:open-file': -> ipcRenderer.send('command', 'application:open-file')
|
||||
'application:open-folder': -> ipcRenderer.send('command', 'application:open-folder')
|
||||
'application:open': ->
|
||||
defaultPath = atom.workspace.getActiveTextEditor()?.getPath() ? atom.project.getPaths()?[0]
|
||||
ipcRenderer.send('open-command', 'application:open', defaultPath)
|
||||
'application:open-file': ->
|
||||
defaultPath = atom.workspace.getActiveTextEditor()?.getPath() ? atom.project.getPaths()?[0]
|
||||
ipcRenderer.send('open-command', 'application:open-file', defaultPath)
|
||||
'application:open-folder': ->
|
||||
defaultPath = atom.workspace.getActiveTextEditor()?.getPath() ? atom.project.getPaths()?[0]
|
||||
ipcRenderer.send('open-command', 'application:open-folder', defaultPath)
|
||||
'application:open-dev': -> ipcRenderer.send('command', 'application:open-dev')
|
||||
'application:open-safe': -> ipcRenderer.send('command', 'application:open-safe')
|
||||
'application:add-project-folder': -> atom.addProjectFolder()
|
||||
@@ -187,9 +193,9 @@ module.exports = ({commandRegistry, commandInstaller, config}) ->
|
||||
'editor:fold-at-indent-level-7': -> @foldAllAtIndentLevel(6)
|
||||
'editor:fold-at-indent-level-8': -> @foldAllAtIndentLevel(7)
|
||||
'editor:fold-at-indent-level-9': -> @foldAllAtIndentLevel(8)
|
||||
'editor:log-cursor-scope': -> @logCursorScope()
|
||||
'editor:copy-path': -> @copyPathToClipboard(false)
|
||||
'editor:copy-project-path': -> @copyPathToClipboard(true)
|
||||
'editor:log-cursor-scope': -> showCursorScope(@getCursorScope(), notificationManager)
|
||||
'editor:copy-path': -> copyPathToClipboard(this, project, clipboard, false)
|
||||
'editor:copy-project-path': -> copyPathToClipboard(this, project, clipboard, true)
|
||||
'editor:toggle-indent-guide': -> config.set('editor.showIndentGuide', not config.get('editor.showIndentGuide'))
|
||||
'editor:toggle-line-numbers': -> config.set('editor.showLineNumbers', not config.get('editor.showLineNumbers'))
|
||||
'editor:scroll-to-cursor': -> @scrollToCursorPosition()
|
||||
@@ -204,7 +210,7 @@ module.exports = ({commandRegistry, commandInstaller, config}) ->
|
||||
'editor:newline-below': -> @insertNewlineBelow()
|
||||
'editor:newline-above': -> @insertNewlineAbove()
|
||||
'editor:toggle-line-comments': -> @toggleLineCommentsInSelection()
|
||||
'editor:checkout-head-revision': -> @checkoutHeadRevision()
|
||||
'editor:checkout-head-revision': -> atom.workspace.checkoutHeadRevision(this)
|
||||
'editor:move-line-up': -> @moveLineUp()
|
||||
'editor:move-line-down': -> @moveLineDown()
|
||||
'editor:move-selection-left': -> @moveSelectionLeft()
|
||||
@@ -232,3 +238,15 @@ stopEventPropagationAndGroupUndo = (config, commandListeners) ->
|
||||
model.transact config.get('editor.undoGroupingInterval'), ->
|
||||
commandListener.call(model, event)
|
||||
newCommandListeners
|
||||
|
||||
showCursorScope = (descriptor, notificationManager) ->
|
||||
list = descriptor.scopes.toString().split(',')
|
||||
list = list.map (item) -> "* #{item}"
|
||||
content = "Scopes at Cursor\n#{list.join('\n')}"
|
||||
|
||||
notificationManager.addInfo(content, dismissable: true)
|
||||
|
||||
copyPathToClipboard = (editor, project, clipboard, relative) ->
|
||||
if filePath = editor.getPath()
|
||||
filePath = project.relativize(filePath) if relative
|
||||
clipboard.write(filePath)
|
||||
|
||||
@@ -1,57 +0,0 @@
|
||||
/** @babel */
|
||||
|
||||
// Manages a pool of some resource.
|
||||
export default class ResourcePool {
|
||||
constructor (pool) {
|
||||
this.pool = pool
|
||||
|
||||
this.queue = []
|
||||
}
|
||||
|
||||
// Enqueue the given function. The function will be given an object from the
|
||||
// pool. The function must return a {Promise}.
|
||||
enqueue (fn) {
|
||||
let resolve = null
|
||||
let reject = null
|
||||
const wrapperPromise = new Promise((resolve_, reject_) => {
|
||||
resolve = resolve_
|
||||
reject = reject_
|
||||
})
|
||||
|
||||
this.queue.push(this.wrapFunction(fn, resolve, reject))
|
||||
|
||||
this.dequeueIfAble()
|
||||
|
||||
return wrapperPromise
|
||||
}
|
||||
|
||||
wrapFunction (fn, resolve, reject) {
|
||||
return (resource) => {
|
||||
const promise = fn(resource)
|
||||
promise
|
||||
.then(result => {
|
||||
resolve(result)
|
||||
this.taskDidComplete(resource)
|
||||
}, error => {
|
||||
reject(error)
|
||||
this.taskDidComplete(resource)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
taskDidComplete (resource) {
|
||||
this.pool.push(resource)
|
||||
|
||||
this.dequeueIfAble()
|
||||
}
|
||||
|
||||
dequeueIfAble () {
|
||||
if (!this.pool.length || !this.queue.length) return
|
||||
|
||||
const fn = this.queue.shift()
|
||||
const resource = this.pool.shift()
|
||||
fn(resource)
|
||||
}
|
||||
|
||||
getQueueDepth () { return this.queue.length }
|
||||
}
|
||||
@@ -378,7 +378,8 @@ class Selection extends Model
|
||||
indentAdjustment = @editor.indentLevelForLine(precedingText) - options.indentBasis
|
||||
@adjustIndent(remainingLines, indentAdjustment)
|
||||
|
||||
if options.autoIndent and NonWhitespaceRegExp.test(text) and not NonWhitespaceRegExp.test(precedingText) and remainingLines.length > 0
|
||||
textIsAutoIndentable = text is '\n' or text is '\r\n' or NonWhitespaceRegExp.test(text)
|
||||
if options.autoIndent and textIsAutoIndentable and not NonWhitespaceRegExp.test(precedingText) and remainingLines.length > 0
|
||||
autoIndentFirstLine = true
|
||||
firstLine = precedingText + firstInsertedLine
|
||||
desiredIndentLevel = @editor.languageMode.suggestedIndentForLineAtBufferRow(oldBufferRange.start.row, firstLine)
|
||||
|
||||
@@ -459,19 +459,21 @@ class TextEditorPresenter
|
||||
|
||||
pixelPosition = @pixelPositionForScreenPosition(screenPosition)
|
||||
|
||||
top = pixelPosition.top + @lineHeight
|
||||
left = pixelPosition.left + @gutterWidth
|
||||
# Fixed positioning.
|
||||
top = @boundingClientRect.top + pixelPosition.top + @lineHeight
|
||||
left = @boundingClientRect.left + pixelPosition.left + @gutterWidth
|
||||
|
||||
if overlayDimensions = @overlayDimensions[decoration.id]
|
||||
{itemWidth, itemHeight, contentMargin} = overlayDimensions
|
||||
|
||||
rightDiff = left + @boundingClientRect.left + itemWidth + contentMargin - @windowWidth
|
||||
rightDiff = left + itemWidth + contentMargin - @windowWidth
|
||||
left -= rightDiff if rightDiff > 0
|
||||
|
||||
leftDiff = left + @boundingClientRect.left + contentMargin
|
||||
leftDiff = left + contentMargin
|
||||
left -= leftDiff if leftDiff < 0
|
||||
|
||||
if top + @boundingClientRect.top + itemHeight > @windowHeight and top - (itemHeight + @lineHeight) >= 0
|
||||
if top + itemHeight > @windowHeight and
|
||||
top - (itemHeight + @lineHeight) >= 0
|
||||
top -= itemHeight + @lineHeight
|
||||
|
||||
pixelPosition.top = top
|
||||
|
||||
@@ -9,7 +9,6 @@ Cursor = require './cursor'
|
||||
Model = require './model'
|
||||
Selection = require './selection'
|
||||
TextMateScopeSelector = require('first-mate').ScopeSelector
|
||||
{Directory} = require "pathwatcher"
|
||||
GutterContainer = require './gutter-container'
|
||||
TextEditorElement = require './text-editor-element'
|
||||
|
||||
@@ -79,14 +78,9 @@ class TextEditor extends Model
|
||||
state.displayBuffer = displayBuffer
|
||||
state.selectionsMarkerLayer = displayBuffer.getMarkerLayer(state.selectionsMarkerLayerId)
|
||||
state.config = atomEnvironment.config
|
||||
state.notificationManager = atomEnvironment.notifications
|
||||
state.packageManager = atomEnvironment.packages
|
||||
state.clipboard = atomEnvironment.clipboard
|
||||
state.viewRegistry = atomEnvironment.views
|
||||
state.grammarRegistry = atomEnvironment.grammars
|
||||
state.project = atomEnvironment.project
|
||||
state.assert = atomEnvironment.assert.bind(atomEnvironment)
|
||||
state.applicationDelegate = atomEnvironment.applicationDelegate
|
||||
editor = new this(state)
|
||||
if state.registered
|
||||
disposable = atomEnvironment.textEditors.add(editor)
|
||||
@@ -99,20 +93,15 @@ class TextEditor extends Model
|
||||
{
|
||||
@softTabs, @firstVisibleScreenRow, @firstVisibleScreenColumn, initialLine, initialColumn, tabLength,
|
||||
softWrapped, @displayBuffer, @selectionsMarkerLayer, buffer, suppressCursorCreation,
|
||||
@mini, @placeholderText, lineNumberGutterVisible, largeFileMode, @config,
|
||||
@notificationManager, @packageManager, @clipboard, @viewRegistry, @grammarRegistry,
|
||||
@project, @assert, @applicationDelegate, grammar, showInvisibles, @autoHeight, @scrollPastEnd
|
||||
@mini, @placeholderText, lineNumberGutterVisible, largeFileMode, @config, @clipboard, @grammarRegistry,
|
||||
@assert, grammar, showInvisibles, @autoHeight, @scrollPastEnd
|
||||
} = params
|
||||
|
||||
throw new Error("Must pass a config parameter when constructing TextEditors") unless @config?
|
||||
throw new Error("Must pass a notificationManager parameter when constructing TextEditors") unless @notificationManager?
|
||||
throw new Error("Must pass a packageManager parameter when constructing TextEditors") unless @packageManager?
|
||||
throw new Error("Must pass a clipboard parameter when constructing TextEditors") unless @clipboard?
|
||||
throw new Error("Must pass a viewRegistry parameter when constructing TextEditors") unless @viewRegistry?
|
||||
throw new Error("Must pass a grammarRegistry parameter when constructing TextEditors") unless @grammarRegistry?
|
||||
throw new Error("Must pass a project parameter when constructing TextEditors") unless @project?
|
||||
throw new Error("Must pass an assert parameter when constructing TextEditors") unless @assert?
|
||||
|
||||
@assert ?= (condition) -> condition
|
||||
@firstVisibleScreenRow ?= 0
|
||||
@firstVisibleScreenColumn ?= 0
|
||||
@emitter = new Emitter
|
||||
@@ -129,7 +118,7 @@ class TextEditor extends Model
|
||||
buffer ?= new TextBuffer
|
||||
@displayBuffer ?= new DisplayBuffer({
|
||||
buffer, tabLength, softWrapped, ignoreInvisibles: @mini or not showInvisibles, largeFileMode,
|
||||
@config, @assert, @grammarRegistry, @packageManager
|
||||
@config, @assert, @grammarRegistry
|
||||
})
|
||||
@buffer = @displayBuffer.buffer
|
||||
@selectionsMarkerLayer ?= @addMarkerLayer(maintainHistory: true)
|
||||
@@ -173,8 +162,6 @@ class TextEditor extends Model
|
||||
subscribeToBuffer: ->
|
||||
@buffer.retain()
|
||||
@disposables.add @buffer.onDidChangePath =>
|
||||
unless @project.getPaths().length > 0
|
||||
@project.setPaths([path.dirname(@getPath())])
|
||||
@emitter.emit 'did-change-title', @getTitle()
|
||||
@emitter.emit 'did-change-path', @getPath()
|
||||
@disposables.add @buffer.onDidChangeEncoding =>
|
||||
@@ -487,12 +474,12 @@ class TextEditor extends Model
|
||||
onDidChangeScrollTop: (callback) ->
|
||||
Grim.deprecate("This is now a view method. Call TextEditorElement::onDidChangeScrollTop instead.")
|
||||
|
||||
@viewRegistry.getView(this).onDidChangeScrollTop(callback)
|
||||
@getElement().onDidChangeScrollTop(callback)
|
||||
|
||||
onDidChangeScrollLeft: (callback) ->
|
||||
Grim.deprecate("This is now a view method. Call TextEditorElement::onDidChangeScrollLeft instead.")
|
||||
|
||||
@viewRegistry.getView(this).onDidChangeScrollLeft(callback)
|
||||
@getElement().onDidChangeScrollLeft(callback)
|
||||
|
||||
onDidRequestAutoscroll: (callback) ->
|
||||
@displayBuffer.onDidRequestAutoscroll(callback)
|
||||
@@ -520,9 +507,9 @@ class TextEditor extends Model
|
||||
softTabs = @getSoftTabs()
|
||||
newEditor = new TextEditor({
|
||||
@buffer, displayBuffer, selectionsMarkerLayer, @tabLength, softTabs,
|
||||
suppressCursorCreation: true, @config, @notificationManager, @packageManager,
|
||||
suppressCursorCreation: true, @config,
|
||||
@firstVisibleScreenRow, @firstVisibleScreenColumn,
|
||||
@clipboard, @viewRegistry, @grammarRegistry, @project, @assert, @applicationDelegate
|
||||
@clipboard, @grammarRegistry, @assert
|
||||
})
|
||||
newEditor
|
||||
|
||||
@@ -682,12 +669,6 @@ class TextEditor extends Model
|
||||
# Essential: Returns {Boolean} `true` if this editor has no content.
|
||||
isEmpty: -> @buffer.isEmpty()
|
||||
|
||||
# Copies the current file path to the native clipboard.
|
||||
copyPathToClipboard: (relative = false) ->
|
||||
if filePath = @getPath()
|
||||
filePath = atom.project.relativize(filePath) if relative
|
||||
@clipboard.write(filePath)
|
||||
|
||||
###
|
||||
Section: File Operations
|
||||
###
|
||||
@@ -716,25 +697,6 @@ class TextEditor extends Model
|
||||
# via {Pane::saveItemAs}.
|
||||
getSaveDialogOptions: -> {}
|
||||
|
||||
checkoutHeadRevision: ->
|
||||
if @getPath()
|
||||
checkoutHead = =>
|
||||
@project.repositoryForDirectory(new Directory(@getDirectoryPath()))
|
||||
.then (repository) =>
|
||||
repository?.async.checkoutHeadForEditor(this)
|
||||
|
||||
if @config.get('editor.confirmCheckoutHeadRevision')
|
||||
@applicationDelegate.confirm
|
||||
message: 'Confirm Checkout HEAD Revision'
|
||||
detailedMessage: "Are you sure you want to discard all changes to \"#{@getFileName()}\" since the last Git commit?"
|
||||
buttons:
|
||||
OK: checkoutHead
|
||||
Cancel: null
|
||||
else
|
||||
checkoutHead()
|
||||
else
|
||||
Promise.resolve(false)
|
||||
|
||||
###
|
||||
Section: Reading Text
|
||||
###
|
||||
@@ -2827,13 +2789,9 @@ class TextEditor extends Model
|
||||
@commentScopeSelector ?= new TextMateScopeSelector('comment.*')
|
||||
@commentScopeSelector.matches(@scopeDescriptorForBufferPosition([bufferRow, match.index]).scopes)
|
||||
|
||||
logCursorScope: ->
|
||||
scopeDescriptor = @getLastCursor().getScopeDescriptor()
|
||||
list = scopeDescriptor.scopes.toString().split(',')
|
||||
list = list.map (item) -> "* #{item}"
|
||||
content = "Scopes at Cursor\n#{list.join('\n')}"
|
||||
|
||||
@notificationManager.addInfo(content, dismissable: true)
|
||||
# Get the scope descriptor at the cursor.
|
||||
getCursorScope: ->
|
||||
@getLastCursor().getScopeDescriptor()
|
||||
|
||||
# {Delegates to: DisplayBuffer.tokenForBufferPosition}
|
||||
tokenForBufferPosition: (bufferPosition) -> @displayBuffer.tokenForBufferPosition(bufferPosition)
|
||||
@@ -3138,24 +3096,24 @@ class TextEditor extends Model
|
||||
scrollToTop: ->
|
||||
Grim.deprecate("This is now a view method. Call TextEditorElement::scrollToTop instead.")
|
||||
|
||||
@viewRegistry.getView(this).scrollToTop()
|
||||
@getElement().scrollToTop()
|
||||
|
||||
scrollToBottom: ->
|
||||
Grim.deprecate("This is now a view method. Call TextEditorElement::scrollToTop instead.")
|
||||
|
||||
@viewRegistry.getView(this).scrollToBottom()
|
||||
@getElement().scrollToBottom()
|
||||
|
||||
scrollToScreenRange: (screenRange, options) -> @displayBuffer.scrollToScreenRange(screenRange, options)
|
||||
|
||||
getHorizontalScrollbarHeight: ->
|
||||
Grim.deprecate("This is now a view method. Call TextEditorElement::getHorizontalScrollbarHeight instead.")
|
||||
|
||||
@viewRegistry.getView(this).getHorizontalScrollbarHeight()
|
||||
@getElement().getHorizontalScrollbarHeight()
|
||||
|
||||
getVerticalScrollbarWidth: ->
|
||||
Grim.deprecate("This is now a view method. Call TextEditorElement::getVerticalScrollbarWidth instead.")
|
||||
|
||||
@viewRegistry.getView(this).getVerticalScrollbarWidth()
|
||||
@getElement().getVerticalScrollbarWidth()
|
||||
|
||||
pageUp: ->
|
||||
@moveUp(@getRowsPerPage())
|
||||
@@ -3222,11 +3180,11 @@ class TextEditor extends Model
|
||||
|
||||
pixelPositionForBufferPosition: (bufferPosition) ->
|
||||
Grim.deprecate("This method is deprecated on the model layer. Use `TextEditorElement::pixelPositionForBufferPosition` instead")
|
||||
@viewRegistry.getView(this).pixelPositionForBufferPosition(bufferPosition)
|
||||
@getElement().pixelPositionForBufferPosition(bufferPosition)
|
||||
|
||||
pixelPositionForScreenPosition: (screenPosition) ->
|
||||
Grim.deprecate("This method is deprecated on the model layer. Use `TextEditorElement::pixelPositionForScreenPosition` instead")
|
||||
@viewRegistry.getView(this).pixelPositionForScreenPosition(screenPosition)
|
||||
@getElement().pixelPositionForScreenPosition(screenPosition)
|
||||
|
||||
getSelectionMarkerAttributes: ->
|
||||
{type: 'selection', invalidate: 'never'}
|
||||
@@ -3255,7 +3213,7 @@ class TextEditor extends Model
|
||||
@displayBuffer.setHeight(height)
|
||||
else
|
||||
Grim.deprecate("This is now a view method. Call TextEditorElement::setHeight instead.")
|
||||
@viewRegistry.getView(this).setHeight(height)
|
||||
@getElement().setHeight(height)
|
||||
|
||||
getHeight: ->
|
||||
Grim.deprecate("This is now a view method. Call TextEditorElement::getHeight instead.")
|
||||
@@ -3268,7 +3226,7 @@ class TextEditor extends Model
|
||||
@displayBuffer.setWidth(width)
|
||||
else
|
||||
Grim.deprecate("This is now a view method. Call TextEditorElement::setWidth instead.")
|
||||
@viewRegistry.getView(this).setWidth(width)
|
||||
@getElement().setWidth(width)
|
||||
|
||||
getWidth: ->
|
||||
Grim.deprecate("This is now a view method. Call TextEditorElement::getWidth instead.")
|
||||
@@ -3312,77 +3270,77 @@ class TextEditor extends Model
|
||||
getScrollTop: ->
|
||||
Grim.deprecate("This is now a view method. Call TextEditorElement::getScrollTop instead.")
|
||||
|
||||
@viewRegistry.getView(this).getScrollTop()
|
||||
@getElement().getScrollTop()
|
||||
|
||||
setScrollTop: (scrollTop) ->
|
||||
Grim.deprecate("This is now a view method. Call TextEditorElement::setScrollTop instead.")
|
||||
|
||||
@viewRegistry.getView(this).setScrollTop(scrollTop)
|
||||
@getElement().setScrollTop(scrollTop)
|
||||
|
||||
getScrollBottom: ->
|
||||
Grim.deprecate("This is now a view method. Call TextEditorElement::getScrollBottom instead.")
|
||||
|
||||
@viewRegistry.getView(this).getScrollBottom()
|
||||
@getElement().getScrollBottom()
|
||||
|
||||
setScrollBottom: (scrollBottom) ->
|
||||
Grim.deprecate("This is now a view method. Call TextEditorElement::setScrollBottom instead.")
|
||||
|
||||
@viewRegistry.getView(this).setScrollBottom(scrollBottom)
|
||||
@getElement().setScrollBottom(scrollBottom)
|
||||
|
||||
getScrollLeft: ->
|
||||
Grim.deprecate("This is now a view method. Call TextEditorElement::getScrollLeft instead.")
|
||||
|
||||
@viewRegistry.getView(this).getScrollLeft()
|
||||
@getElement().getScrollLeft()
|
||||
|
||||
setScrollLeft: (scrollLeft) ->
|
||||
Grim.deprecate("This is now a view method. Call TextEditorElement::setScrollLeft instead.")
|
||||
|
||||
@viewRegistry.getView(this).setScrollLeft(scrollLeft)
|
||||
@getElement().setScrollLeft(scrollLeft)
|
||||
|
||||
getScrollRight: ->
|
||||
Grim.deprecate("This is now a view method. Call TextEditorElement::getScrollRight instead.")
|
||||
|
||||
@viewRegistry.getView(this).getScrollRight()
|
||||
@getElement().getScrollRight()
|
||||
|
||||
setScrollRight: (scrollRight) ->
|
||||
Grim.deprecate("This is now a view method. Call TextEditorElement::setScrollRight instead.")
|
||||
|
||||
@viewRegistry.getView(this).setScrollRight(scrollRight)
|
||||
@getElement().setScrollRight(scrollRight)
|
||||
|
||||
getScrollHeight: ->
|
||||
Grim.deprecate("This is now a view method. Call TextEditorElement::getScrollHeight instead.")
|
||||
|
||||
@viewRegistry.getView(this).getScrollHeight()
|
||||
@getElement().getScrollHeight()
|
||||
|
||||
getScrollWidth: ->
|
||||
Grim.deprecate("This is now a view method. Call TextEditorElement::getScrollWidth instead.")
|
||||
|
||||
@viewRegistry.getView(this).getScrollWidth()
|
||||
@getElement().getScrollWidth()
|
||||
|
||||
getMaxScrollTop: ->
|
||||
Grim.deprecate("This is now a view method. Call TextEditorElement::getMaxScrollTop instead.")
|
||||
|
||||
@viewRegistry.getView(this).getMaxScrollTop()
|
||||
@getElement().getMaxScrollTop()
|
||||
|
||||
intersectsVisibleRowRange: (startRow, endRow) ->
|
||||
Grim.deprecate("This is now a view method. Call TextEditorElement::intersectsVisibleRowRange instead.")
|
||||
|
||||
@viewRegistry.getView(this).intersectsVisibleRowRange(startRow, endRow)
|
||||
@getElement().intersectsVisibleRowRange(startRow, endRow)
|
||||
|
||||
selectionIntersectsVisibleRowRange: (selection) ->
|
||||
Grim.deprecate("This is now a view method. Call TextEditorElement::selectionIntersectsVisibleRowRange instead.")
|
||||
|
||||
@viewRegistry.getView(this).selectionIntersectsVisibleRowRange(selection)
|
||||
@getElement().selectionIntersectsVisibleRowRange(selection)
|
||||
|
||||
screenPositionForPixelPosition: (pixelPosition) ->
|
||||
Grim.deprecate("This is now a view method. Call TextEditorElement::screenPositionForPixelPosition instead.")
|
||||
|
||||
@viewRegistry.getView(this).screenPositionForPixelPosition(pixelPosition)
|
||||
@getElement().screenPositionForPixelPosition(pixelPosition)
|
||||
|
||||
pixelRectForScreenRange: (screenRange) ->
|
||||
Grim.deprecate("This is now a view method. Call TextEditorElement::pixelRectForScreenRange instead.")
|
||||
|
||||
@viewRegistry.getView(this).pixelRectForScreenRange(screenRange)
|
||||
@getElement().pixelRectForScreenRange(screenRange)
|
||||
|
||||
###
|
||||
Section: Utility
|
||||
|
||||
@@ -29,14 +29,13 @@ class TokenizedBuffer extends Model
|
||||
state.buffer = atomEnvironment.project.bufferForPathSync(state.bufferPath)
|
||||
state.config = atomEnvironment.config
|
||||
state.grammarRegistry = atomEnvironment.grammars
|
||||
state.packageManager = atomEnvironment.packages
|
||||
state.assert = atomEnvironment.assert
|
||||
new this(state)
|
||||
|
||||
constructor: (params) ->
|
||||
{
|
||||
@buffer, @tabLength, @ignoreInvisibles, @largeFileMode, @config,
|
||||
@grammarRegistry, @packageManager, @assert, grammarScopeName
|
||||
@grammarRegistry, @assert, grammarScopeName
|
||||
} = params
|
||||
|
||||
@emitter = new Emitter
|
||||
@@ -126,7 +125,7 @@ class TokenizedBuffer extends Model
|
||||
@disposables.add(@configSubscriptions)
|
||||
|
||||
@retokenizeLines()
|
||||
@packageManager.triggerActivationHook("#{grammar.packageName}:grammar-used")
|
||||
|
||||
@emitter.emit 'did-change-grammar', grammar
|
||||
|
||||
getGrammarSelectionContent: ->
|
||||
|
||||
@@ -4,6 +4,7 @@ path = require 'path'
|
||||
{join} = path
|
||||
{Emitter, Disposable, CompositeDisposable} = require 'event-kit'
|
||||
fs = require 'fs-plus'
|
||||
{Directory} = require 'pathwatcher'
|
||||
DefaultDirectorySearcher = require './default-directory-searcher'
|
||||
Model = require './model'
|
||||
TextEditor = require './text-editor'
|
||||
@@ -550,9 +551,17 @@ class Workspace extends Model
|
||||
@project.bufferForPath(filePath, options).then (buffer) =>
|
||||
editor = @buildTextEditor(_.extend({buffer, largeFileMode}, options))
|
||||
disposable = atom.textEditors.add(editor)
|
||||
editor.onDidDestroy -> disposable.dispose()
|
||||
grammarSubscription = editor.observeGrammar(@handleGrammarUsed.bind(this))
|
||||
editor.onDidDestroy ->
|
||||
grammarSubscription.dispose()
|
||||
disposable.dispose()
|
||||
editor
|
||||
|
||||
handleGrammarUsed: (grammar) ->
|
||||
return unless grammar?
|
||||
|
||||
@packageManager.triggerActivationHook("#{grammar.packageName}:grammar-used")
|
||||
|
||||
# Public: Returns a {Boolean} that is `true` if `object` is a `TextEditor`.
|
||||
#
|
||||
# * `object` An {Object} you want to perform the check against.
|
||||
@@ -564,8 +573,7 @@ class Workspace extends Model
|
||||
# Returns a {TextEditor}.
|
||||
buildTextEditor: (params) ->
|
||||
params = _.extend({
|
||||
@config, @notificationManager, @packageManager, @clipboard, @viewRegistry,
|
||||
@grammarRegistry, @project, @assert, @applicationDelegate
|
||||
@config, @clipboard, @grammarRegistry, @assert
|
||||
}, params)
|
||||
new TextEditor(params)
|
||||
|
||||
@@ -1079,3 +1087,22 @@ class Workspace extends Model
|
||||
|
||||
inProcessFinished = true
|
||||
checkFinished()
|
||||
|
||||
checkoutHeadRevision: (editor) ->
|
||||
if editor.getPath()
|
||||
checkoutHead = =>
|
||||
@project.repositoryForDirectory(new Directory(editor.getDirectoryPath()))
|
||||
.then (repository) =>
|
||||
repository?.async.checkoutHeadForEditor(editor)
|
||||
|
||||
if @config.get('editor.confirmCheckoutHeadRevision')
|
||||
@applicationDelegate.confirm
|
||||
message: 'Confirm Checkout HEAD Revision'
|
||||
detailedMessage: "Are you sure you want to discard all changes to \"#{editor.getFileName()}\" since the last Git commit?"
|
||||
buttons:
|
||||
OK: checkoutHead
|
||||
Cancel: null
|
||||
else
|
||||
checkoutHead()
|
||||
else
|
||||
Promise.resolve(false)
|
||||
|
||||
@@ -15,7 +15,7 @@ atom-text-editor[mini] {
|
||||
}
|
||||
|
||||
atom-overlay {
|
||||
position: absolute;
|
||||
position: fixed;
|
||||
display: block;
|
||||
z-index: 4;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user