mirror of
https://github.com/atom/atom.git
synced 2026-02-12 15:45:23 -05:00
Merge branch 'master' into config
This commit is contained in:
@@ -63,3 +63,7 @@ class Git
|
||||
|
||||
checkoutHead: (path) ->
|
||||
@repo.checkoutHead(@relativize(path))
|
||||
|
||||
getDiffStats: (path) ->
|
||||
stats = @repo.getDiffStats(@relativize(path))
|
||||
stats or {'added': 0, 'deleted': 0}
|
||||
|
||||
@@ -52,20 +52,24 @@ class RootView extends View
|
||||
panesViewState: @panes.children().view()?.serialize()
|
||||
packageStates: @serializePackages()
|
||||
|
||||
handleEvents: ->
|
||||
@on 'toggle-dev-tools', => atom.toggleDevTools()
|
||||
@on 'focus', (e) =>
|
||||
if @getActiveEditor()
|
||||
@getActiveEditor().focus()
|
||||
handleFocus: (e) ->
|
||||
if @getActiveEditor()
|
||||
@getActiveEditor().focus()
|
||||
false
|
||||
else
|
||||
@setTitle(null)
|
||||
focusableChild = this.find("[tabindex=-1]:visible:first")
|
||||
if focusableChild.length
|
||||
focusableChild.focus()
|
||||
false
|
||||
else
|
||||
@setTitle(null)
|
||||
focusableChild = this.find("[tabindex=-1]:visible:first")
|
||||
if focusableChild.length
|
||||
focusableChild.focus()
|
||||
false
|
||||
else
|
||||
true
|
||||
true
|
||||
|
||||
handleEvents: ->
|
||||
@on 'toggle-dev-tools', => atom.toggleDevTools()
|
||||
@on 'focus', (e) => @handleFocus(e)
|
||||
$(window).on 'focus', (e) =>
|
||||
@handleFocus(e) if document.activeElement is document.body
|
||||
|
||||
@on 'active-editor-path-change', (e, path) =>
|
||||
@project.setPath(path) unless @project.getRootDirectory()
|
||||
|
||||
@@ -166,3 +166,12 @@ describe "StatusBar", ->
|
||||
fs.write(path, originalPathText)
|
||||
$(window).trigger 'focus'
|
||||
expect(statusBar.gitStatusIcon).not.toHaveClass('modified-status-icon')
|
||||
|
||||
it "displays the diff stat for modified files", ->
|
||||
fs.write(path, "i've changed for the worse")
|
||||
rootView.open(path)
|
||||
expect(statusBar.gitStatusIcon).toHaveText('+1,-1')
|
||||
|
||||
it "displays the diff stat for new files", ->
|
||||
rootView.open(newPath)
|
||||
expect(statusBar.gitStatusIcon).toHaveText('+1')
|
||||
|
||||
@@ -79,8 +79,18 @@ class StatusBar extends View
|
||||
@gitStatusIcon.removeClass().addClass('git-status octicons')
|
||||
if @buffer.getGit()?.isPathModified(path)
|
||||
@gitStatusIcon.addClass('modified-status-icon')
|
||||
else if @buffer.getGit()?.isPathNew(path)
|
||||
stats = @buffer.getGit().getDiffStats(path)
|
||||
if stats.added and stats.deleted
|
||||
@gitStatusIcon.text("+#{stats.added},-#{stats.deleted}")
|
||||
else if stats.added
|
||||
@gitStatusIcon.text("+#{stats.added}")
|
||||
else if stats.deleted
|
||||
@gitStatusIcon.text("-#{stats.deleted}")
|
||||
else
|
||||
@gitStatusIcon.text('')
|
||||
else if @buffer.getGit()?.isPathNew(path)
|
||||
@gitStatusIcon.addClass('new-status-icon')
|
||||
@gitStatusIcon.text("+#{@buffer.getLineCount()}")
|
||||
|
||||
updatePathText: ->
|
||||
if path = @editor.getPath()
|
||||
|
||||
@@ -11,6 +11,25 @@ class Tab extends View
|
||||
@updateFileName()
|
||||
@editSession.on 'buffer-path-change.tab', =>
|
||||
@updateFileName()
|
||||
@subscribeToBuffer()
|
||||
|
||||
updateTab: ->
|
||||
@updateBufferHasModifiedText(@buffer.isModified())
|
||||
|
||||
subscribeToBuffer: ->
|
||||
@buffer = @editSession.buffer
|
||||
@subscribe @buffer, 'contents-modified.tabs', (e) => @updateBufferHasModifiedText(e.differsFromDisk)
|
||||
@subscribe @buffer, 'after-save.tabs', => @updateTab()
|
||||
@subscribe @buffer, 'git-status-change.tabs', => @updateTab()
|
||||
@updateTab()
|
||||
|
||||
updateBufferHasModifiedText: (differsFromDisk) ->
|
||||
if differsFromDisk
|
||||
@toggleClass('file-modified') unless @isModified
|
||||
@isModified = true
|
||||
else
|
||||
@removeClass('file-modified') if @isModified
|
||||
@isModified = false
|
||||
|
||||
updateFileName: ->
|
||||
@fileName.text(@editSession.buffer.getBaseName() ? 'untitled')
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
.tabs {
|
||||
background: #333333;
|
||||
border-bottom: 4px solid #424242;
|
||||
font: caption !important;
|
||||
font: caption;
|
||||
}
|
||||
|
||||
.tab {
|
||||
cursor: default;
|
||||
padding: 2px 21px 2px 9px;
|
||||
background-image: -webkit-linear-gradient(#444, #3d3d3d);
|
||||
color: #a5aaaa;
|
||||
display: table-cell;
|
||||
position: relative;
|
||||
width:175px;
|
||||
@@ -21,6 +20,43 @@
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
.tab,
|
||||
.tab .close-icon {
|
||||
color: #aaa;
|
||||
}
|
||||
|
||||
.tab.file-modified .close-icon {
|
||||
border-color: #aaa;
|
||||
}
|
||||
|
||||
.tab.active,
|
||||
.tab.active:hover,
|
||||
.tab.active .close-icon {
|
||||
color: #e6e6e6;
|
||||
}
|
||||
|
||||
.tab.file-modified.active .close-icon {
|
||||
border-color: #e6e6e6;
|
||||
}
|
||||
|
||||
.tab:hover .close-icon {
|
||||
color: #c8c8c8;
|
||||
border-color: #c8c8c8;
|
||||
}
|
||||
|
||||
.tab.file-modified .close-icon {
|
||||
border: 3px solid #777;
|
||||
top: 6px;
|
||||
border-radius: 10px;
|
||||
width: 5px;
|
||||
height: 5px;
|
||||
right: 5px;
|
||||
}
|
||||
|
||||
.tab.file-modified .close-icon:before {
|
||||
content: "";
|
||||
}
|
||||
|
||||
.tab:first-child {
|
||||
box-shadow: inset 0 0 5px #383838, 0 1px 0 #585858, inset -1px 0 0 #4a4a4a;
|
||||
}
|
||||
@@ -32,7 +68,6 @@
|
||||
|
||||
.tab.active,
|
||||
.tab.active:hover {
|
||||
color: #dae6e6;
|
||||
border-top: 1px solid #4a4a4a;
|
||||
box-shadow: inset -1px 0 0 #595959, inset 1px 0 0 #595959;
|
||||
border-bottom: 0 none;
|
||||
@@ -66,12 +101,12 @@
|
||||
}
|
||||
|
||||
.tab:hover {
|
||||
color: #c8c8c5;
|
||||
color: #c8c8c8;
|
||||
background-image: -webkit-linear-gradient(#474747, #444444);
|
||||
}
|
||||
|
||||
.tab .file-name {
|
||||
font-size: 11px !important;
|
||||
font-size: 11px;
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
@@ -90,7 +125,6 @@
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
display: block;
|
||||
color: #777;
|
||||
cursor: pointer;
|
||||
position: absolute;
|
||||
right: 4px;
|
||||
|
||||
@@ -194,6 +194,19 @@ describe "TreeView", ->
|
||||
expect(treeView).not.toMatchSelector(':focus')
|
||||
expect(rootView.getActiveEditor().isFocused).toBeTruthy()
|
||||
|
||||
describe "when core:close is triggered on the tree view", ->
|
||||
it "detaches the TreeView, focuses the RootView and does not bubble the core:close event", ->
|
||||
treeView.attach()
|
||||
treeView.focus()
|
||||
rootViewCloseHandler = jasmine.createSpy('rootViewCloseHandler')
|
||||
rootView.on 'core:close', rootViewCloseHandler
|
||||
spyOn(rootView, 'focus')
|
||||
|
||||
treeView.trigger('core:close')
|
||||
expect(rootView.focus).toHaveBeenCalled()
|
||||
expect(rootViewCloseHandler).not.toHaveBeenCalled()
|
||||
expect(treeView.hasParent()).toBeFalsy()
|
||||
|
||||
describe "when a directory's disclosure arrow is clicked", ->
|
||||
it "expands / collapses the associated directory", ->
|
||||
subdir = treeView.root.find('.entries > li:contains(dir1)').view()
|
||||
@@ -859,3 +872,31 @@ describe "TreeView", ->
|
||||
config.set("core.hideGitIgnoredFiles", false)
|
||||
|
||||
expect(treeView.find('.file:contains(tree-view.js)').length).toBe 1
|
||||
|
||||
describe "Git status decorations", ->
|
||||
[ignoreFile, modifiedFile, originalFileContent] = []
|
||||
|
||||
beforeEach ->
|
||||
config.set "core.hideGitIgnoredFiles", false
|
||||
ignoreFile = fs.join(require.resolve('fixtures/tree-view'), '.gitignore')
|
||||
fs.write(ignoreFile, 'tree-view.js')
|
||||
modifiedFile = fs.join(require.resolve('fixtures/tree-view'), 'tree-view.txt')
|
||||
originalFileContent = fs.read(modifiedFile)
|
||||
fs.write modifiedFile, 'ch ch changes'
|
||||
treeView.updateRoot()
|
||||
|
||||
afterEach ->
|
||||
fs.remove(ignoreFile) if fs.exists(ignoreFile)
|
||||
fs.write modifiedFile, originalFileContent
|
||||
|
||||
describe "when a file is modified", ->
|
||||
it "adds a custom style", ->
|
||||
expect(treeView.find('.file:contains(tree-view.txt)')).toHaveClass 'modified'
|
||||
|
||||
describe "when a file is new", ->
|
||||
it "adds a custom style", ->
|
||||
expect(treeView.find('.file:contains(.gitignore)')).toHaveClass 'new'
|
||||
|
||||
describe "when a file is ignored", ->
|
||||
it "adds a custom style", ->
|
||||
expect(treeView.find('.file:contains(tree-view.js)')).toHaveClass 'ignored'
|
||||
|
||||
@@ -25,7 +25,13 @@ class FileView extends View
|
||||
else
|
||||
@fileName.addClass('text-name')
|
||||
|
||||
@addClass('ignored') if new Git(path).isPathIgnored(path)
|
||||
git = new Git(path)
|
||||
if git.isPathIgnored(path)
|
||||
@addClass('ignored')
|
||||
else if git.isPathModified(path)
|
||||
@addClass('modified')
|
||||
else if git.isPathNew(path)
|
||||
@addClass('new')
|
||||
|
||||
getPath: ->
|
||||
@file.path
|
||||
|
||||
@@ -48,7 +48,7 @@ class TreeView extends ScrollView
|
||||
@on 'click', '.entry', (e) => @entryClicked(e)
|
||||
@command 'core:move-up', => @moveUp()
|
||||
@command 'core:move-down', => @moveDown()
|
||||
@command 'core:close', => false
|
||||
@command 'core:close', => @detach(); false
|
||||
@command 'tree-view:expand-directory', => @expandDirectory()
|
||||
@command 'tree-view:collapse-directory', => @collapseDirectory()
|
||||
@command 'tree-view:open-selected-entry', => @openSelectedEntry(true)
|
||||
|
||||
@@ -38,39 +38,23 @@ describe "WrapGuide", ->
|
||||
expect(wrapGuide.position().left).toBeGreaterThan(initial)
|
||||
expect(wrapGuide).toBeVisible()
|
||||
|
||||
describe "overriding getGuideColumn", ->
|
||||
it "invokes the callback with the editor path", ->
|
||||
editorPath = null
|
||||
wrapGuide.getGuideColumn = (path) ->
|
||||
editorPath = path
|
||||
80
|
||||
describe "using a custom config column", ->
|
||||
it "places the wrap guide at the custom column", ->
|
||||
config.set('wrapGuide.columns', [{pattern: '\.js$', column: 20}])
|
||||
wrapGuide.updateGuide()
|
||||
expect(editorPath).toBe(require.resolve('fixtures/sample.js'))
|
||||
|
||||
it "invokes the callback with a default value", ->
|
||||
column = null
|
||||
wrapGuide.getGuideColumn = (path, defaultColumn) ->
|
||||
editorPath = path
|
||||
column = defaultColumn
|
||||
defaultColumn
|
||||
width = editor.charWidth * 20
|
||||
expect(width).toBeGreaterThan(0)
|
||||
expect(wrapGuide.position().left).toBe(width)
|
||||
|
||||
it "uses the default column when no custom column matches the path", ->
|
||||
config.set('wrapGuide.columns', [{pattern: '\.jsp$', column: '100'}])
|
||||
wrapGuide.updateGuide()
|
||||
expect(column).toBeGreaterThan(0)
|
||||
width = editor.charWidth * wrapGuide.defaultColumn
|
||||
expect(width).toBeGreaterThan(0)
|
||||
expect(wrapGuide.position().left).toBe(width)
|
||||
|
||||
# this is disabled because we no longer support passing config to an extension
|
||||
# at load time. we need to convert it to use the global config vars.
|
||||
xit "uses the function from the config data", ->
|
||||
rootView.find('.wrap-guide').remove()
|
||||
config =
|
||||
getGuideColumn: ->
|
||||
1
|
||||
atom.loadPackage('wrap-guide', config)
|
||||
wrapGuide = rootView.find('.wrap-guide').view()
|
||||
expect(wrapGuide.getGuideColumn).toBe(config.getGuideColumn)
|
||||
|
||||
it "hides the guide when the column is less than 1", ->
|
||||
wrapGuide.getGuideColumn = (path) ->
|
||||
-1
|
||||
it "hides the guide when the config column is less than 1", ->
|
||||
config.set('wrapGuide.columns', [{pattern: 'sample\.js$', column: -1}])
|
||||
wrapGuide.updateGuide()
|
||||
expect(wrapGuide).toBeHidden()
|
||||
|
||||
|
||||
@@ -1,21 +1,22 @@
|
||||
{View} = require 'space-pen'
|
||||
$ = require 'jquery'
|
||||
_ = require 'underscore'
|
||||
|
||||
module.exports =
|
||||
class WrapGuide extends View
|
||||
@activate: (rootView, state, config) ->
|
||||
@activate: (rootView, state) ->
|
||||
requireStylesheet 'wrap-guide.css'
|
||||
|
||||
for editor in rootView.getEditors()
|
||||
if rootView.parents('html').length
|
||||
@appendToEditorPane(rootView, editor, config)
|
||||
@appendToEditorPane(rootView, editor)
|
||||
|
||||
rootView.on 'editor-open', (e, editor) =>
|
||||
@appendToEditorPane(rootView, editor, config)
|
||||
@appendToEditorPane(rootView, editor)
|
||||
|
||||
@appendToEditorPane: (rootView, editor, config) ->
|
||||
if underlayer = editor.pane()?.find('.underlayer')
|
||||
underlayer.append(new WrapGuide(rootView, editor, config))
|
||||
underlayer.append(new WrapGuide(rootView, editor))
|
||||
|
||||
@content: ->
|
||||
@div class: 'wrap-guide'
|
||||
@@ -23,17 +24,22 @@ class WrapGuide extends View
|
||||
getGuideColumn: null
|
||||
defaultColumn: 80
|
||||
|
||||
initialize: (@rootView, @editor, options = {}) =>
|
||||
if typeof options.getGuideColumn is 'function'
|
||||
@getGuideColumn = options.getGuideColumn
|
||||
else
|
||||
@getGuideColumn = (path, defaultColumn) -> defaultColumn
|
||||
|
||||
initialize: (@rootView, @editor) =>
|
||||
@observeConfig 'editor.fontSize', => @updateGuide()
|
||||
@subscribe @editor, 'editor-path-change', => @updateGuide()
|
||||
@subscribe @editor, 'editor:min-width-changed', => @updateGuide()
|
||||
@subscribe $(window), 'resize', => @updateGuide()
|
||||
|
||||
getGuideColumn: (path) ->
|
||||
customColumns = config.get('wrapGuide.columns')
|
||||
return @defaultColumn unless _.isArray(customColumns)
|
||||
for customColumn in customColumns
|
||||
continue unless _.isObject(customColumn)
|
||||
regex = customColumn['pattern']
|
||||
continue unless regex
|
||||
return parseInt(customColumn['column']) if new RegExp(regex).test(path)
|
||||
@defaultColumn
|
||||
|
||||
updateGuide: ->
|
||||
column = @getGuideColumn(@editor.getPath(), @defaultColumn)
|
||||
if column > 0
|
||||
|
||||
Reference in New Issue
Block a user