Skip to content

Commit 4b81f22

Browse files
committed
Instant blame display, remove key throttling
This make the blame window appear instantly when the key is pressed. It feels nicer like this because there's instant feedback that you hit the correct key. Same goes for closing the window. Throttling/debouncing is completely removed. Test Plan: Open a new Atom window and press `ctrl-b`. Now the window opens and closes instantly. The data loads in the background if necessary. I also held down `ctrl-b` with a really small key-repeat delay and it seems to work pretty robustly.
1 parent 455f731 commit 4b81f22

File tree

4 files changed

+114
-87
lines changed

4 files changed

+114
-87
lines changed
Lines changed: 21 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -1,79 +1,38 @@
1-
const _ = require('underscore');
21
const $ = require('atom').$;
32
const React = require('atom').React;
43
const BlameListView = require('../views/blame-list-view');
5-
const errorController = require('../controllers/errorController');
64

7-
const TOGGLE_DEBOUNCE_TIME = 600;
85

96
/**
10-
* Getter for the currently focused editor.
7+
* Display or hide a BlameListView for the active editor.
118
*
12-
* @return {JQuery} - The currently focused editor element.
13-
*/
14-
function getFocusedEditorView () {
15-
var activePane = atom.workspaceView.getActivePaneView();
16-
return activePane.find('.editor.is-focused').view();
17-
}
18-
19-
/**
20-
* If active editor is not currently blaming funs blame command for given
21-
* file and blamer and inserts a BlameView for the file. If blame data is already
22-
* shown it removes that element.
9+
* If the active editor does not have an existing BlameListView, one will be
10+
* mounted.
2311
*
24-
* @param {String} filePath - path to the file to blame
25-
* @param {Blamer} projectBlamer - a fully initialized Blamer for the current project
12+
* @param {Blamer} projectBlamer - a Blamer for the current project
2613
*/
27-
function toggleBlame(filePath, projectBlamer) {
28-
var focusedEditor = getFocusedEditorView();
29-
if (focusedEditor.blaming) {
30-
// we're already blaming this container, so unmount
31-
focusedEditor.blaming = false;
32-
var mountPoint = focusedEditor.find('.git-blame-mount');
33-
React.unmountComponentAtNode(mountPoint);
34-
mountPoint.remove();
14+
function toggleBlame(projectBlamer) {
15+
var editorView = atom.workspaceView.getActiveView();
16+
var editor = editorView.getEditor();
17+
18+
if (!editorView.blameView) {
19+
// insert the BlameListView after the gutter div
20+
var mountPoint = $('<div>', {'class': 'git-blame-mount'});
21+
editorView.find('.gutter').after(mountPoint);
22+
23+
editorView.blameView = React.renderComponent(new BlameListView({
24+
projectBlamer: projectBlamer,
25+
filePath: editor.getPath(),
26+
lineCount: editor.getLineCount(),
27+
scrollbar: editorView.find('.vertical-scrollbar')
28+
}), mountPoint[0]);
3529
} else {
36-
// blame the given file + show view on success
37-
projectBlamer.blame(filePath, function(err, blameData) {
38-
if (err) {
39-
errorController.handleError(err);
40-
} else {
41-
insertBlameView(blameData, focusedEditor);
42-
}
43-
});
30+
editorView.blameView.toggle();
4431
}
4532
}
4633

47-
/**
48-
* Debounced version of toggleBlame that will only allow toggleBlame function
49-
* to be executed 700ms after the last execution. Executes immediately the first
50-
* time its called.
51-
*/
52-
var debouncedToggleBlame = _.debounce(toggleBlame, TOGGLE_DEBOUNCE_TIME, true);
53-
54-
/**
55-
* Inserts a BlameView rendered from input blameData into its proper
56-
* spot within the focusedEditor.
57-
*
58-
* @param {Array|Object} blameData - array of data for a blame of each line output
59-
* of blameFormatter
60-
* @param {JQuery} focusedEditor - the currently focused editor element in which
61-
* the BlameView should be inserted
62-
*/
63-
function insertBlameView(blameData, focusedEditor) {
64-
// insert the BlameListView after the gutter div
65-
var mountPoint = $('<div>', {'class': 'git-blame-mount'});
66-
focusedEditor.find('.gutter').after(mountPoint);
67-
68-
React.renderComponent(new BlameListView({
69-
annotations: blameData,
70-
scrollbar: focusedEditor.find('.vertical-scrollbar')
71-
}), mountPoint[0]);
72-
73-
focusedEditor.blaming = true;
74-
}
7534

7635
// EXPORTS
7736
module.exports = {
78-
toggleBlame: debouncedToggleBlame
37+
toggleBlame: toggleBlame
7938
};

lib/git-blame.js

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,7 @@ function activate() {
1010
initializeContext();
1111

1212
// git-blame:blame
13-
atom.workspaceView.command('git-blame:toggle', function() {
14-
return toggleBlame();
15-
});
16-
17-
return;
13+
atom.workspaceView.command('git-blame:toggle', toggleBlame);
1814
}
1915

2016
function initializeContext() {
@@ -35,11 +31,7 @@ function toggleBlame() {
3531
if (!projectBlamer) {
3632
return;
3733
}
38-
39-
var editor = atom.workspace.activePaneItem;
40-
var filePath = editor.getPath();
41-
42-
BlameViewController.toggleBlame(filePath, projectBlamer);
34+
BlameViewController.toggleBlame(projectBlamer);
4335
}
4436

4537
// EXPORTS

lib/views/blame-line-view.coffee

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,32 @@
11
{React, Reactionary} = require 'atom'
2+
RP = React.PropTypes
23
{div, span, a} = Reactionary
34
RemoteRevision = require '../util/RemoteRevision'
45

56
HASH_LENGTH = 7 # github uses this length
7+
BLANK_HASH = '-'.repeat(HASH_LENGTH)
8+
9+
10+
renderLoading = ->
11+
div className: 'blame-line loading',
12+
span className: 'hash', BLANK_HASH
13+
span className: 'date', '1337-01-01'
14+
span className: 'committer', 'Loading'
15+
616

7-
module.exports =
817
BlameLineComponent = React.createClass
18+
propTypes:
19+
date: RP.string.isRequired
20+
hash: RP.string.isRequired
21+
url: RP.string.isRequired
22+
committer: RP.string.isRequired
23+
backgroundClass: RP.string
24+
noCommit: RP.bool
25+
926
render: ->
1027
if @props.noCommit
1128
div className: 'blame-line no-commit text-subtle',
12-
span className: 'hash', '-'.repeat(HASH_LENGTH)
29+
span className: 'hash', BLANK_HASH
1330
span className: 'date', @props.date
1431
span className: 'committer', 'Nobody'
1532
else
@@ -23,3 +40,5 @@ BlameLineComponent = React.createClass
2340

2441
shouldComponentUpdate: ->
2542
false
43+
44+
module.exports = {BlameLineComponent, renderLoading}

lib/views/blame-list-view.coffee

Lines changed: 70 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,21 @@
11
{React, Reactionary, $} = require 'atom'
22
{div, span, a} = Reactionary
3-
_ = require('underscore')
4-
BlameLineComponent = require './blame-line-view'
3+
RP = React.PropTypes
4+
_ = require 'underscore'
5+
{BlameLineComponent, renderLoading} = require './blame-line-view'
56

67

78
BlameListLinesComponent = React.createClass
9+
propTypes:
10+
annotations: RP.arrayOf(RP.object)
11+
loading: RP.bool.isRequired
12+
filePath: RP.string.isRequired
13+
lineCount: RP.number.isRequired
14+
15+
renderLoading: ->
16+
lines = [0...@props.lineCount].map renderLoading
17+
div null, lines
18+
819
# makes background color alternate by commit
920
_addAlternatingBackgroundColor: (lines) ->
1021
bgClass = null
@@ -22,42 +33,66 @@ BlameListLinesComponent = React.createClass
2233
lastHash = line.hash
2334
lines
2435

25-
render: ->
36+
renderLoaded: ->
2637
# clone so it can be modified
2738
lines = _.clone @props.annotations
2839

2940
# add url to open diff
3041
filePath = atom.workspace.activePaneItem.getPath()
3142
remoteUrl = atom.project.getRepo()?.getOriginUrl(filePath)
3243
l['url'] = remoteUrl for l in lines
33-
3444
@_addAlternatingBackgroundColor lines
35-
3645
div null, lines.map BlameLineComponent
3746

38-
shouldComponentUpdate: ->
39-
false
47+
render: ->
48+
if @props.loading
49+
@renderLoading()
50+
else
51+
@renderLoaded()
52+
53+
shouldComponentUpdate: ({loading}) ->
54+
loading isnt @props.loading
4055

4156

4257
BlameListView = React.createClass
58+
propTypes:
59+
projectBlamer: RP.object.isRequired
60+
filePath: RP.string.isRequired
61+
lineCount: RP.number.isRequired
62+
scrollbar: RP.object.isRequired
63+
4364
getInitialState: ->
4465
{
4566
# TODO: get this from the parent component somehow?
4667
scrollTop: @props.scrollbar.scrollTop()
4768
# TODO: be intelligent about persisting this so it doesn't reset
4869
width: 210
70+
loading: true
71+
visible: true
4972
}
5073

5174
render: ->
52-
div
53-
className: 'git-blame'
54-
style: width: @state.width,
55-
div className: 'git-blame-resize-handle', onMouseDown: @resizeStarted
56-
div className: 'git-blame-scroller',
75+
display = if @state.visible then 'inline-block' else 'none'
76+
77+
body = if @state.error
78+
div "Sorry, an error occurred." # TODO: make this better
79+
else
80+
div
81+
className: 'git-blame-scroller'
5782
div
5883
className: 'blame-lines'
5984
style: WebkitTransform: @getTransform()
60-
BlameListLinesComponent annotations: @props.annotations
85+
BlameListLinesComponent
86+
annotations: @state.annotations
87+
loading: @state.loading
88+
filePath: @props.filePath
89+
lineCount: @props.lineCount
90+
91+
div
92+
className: 'git-blame'
93+
style: width: @state.width, display: display
94+
div className: 'git-blame-resize-handle', onMouseDown: @resizeStarted
95+
body
6196

6297
getTransform: ->
6398
{scrollTop} = @state
@@ -69,6 +104,28 @@ BlameListView = React.createClass
69104
else
70105
"translate(0px, #{-scrollTop}px)"
71106

107+
componentWillMount: ->
108+
# kick off async request for blame data
109+
@loadBlame true
110+
111+
loadBlame: (force) ->
112+
return if @state.loading and not force
113+
114+
@setState loading: true
115+
@props.projectBlamer.blame @props.filePath, (err, data) =>
116+
if err
117+
@setState
118+
loading: false
119+
error: true
120+
else
121+
@setState
122+
loading: false
123+
error: false
124+
annotations: data
125+
126+
toggle: ->
127+
@setState visible: !@state.visible
128+
72129
componentDidMount: ->
73130
# Bind to scroll event on vertical-scrollbar to sync up scroll position of
74131
# blame gutter.

0 commit comments

Comments
 (0)