diff --git a/src/app/git.coffee b/src/app/git.coffee index b01cf826b..ee9c953ac 100644 --- a/src/app/git.coffee +++ b/src/app/git.coffee @@ -123,7 +123,7 @@ class Git @statusTask = null @statusTask.start() - getDirectoryStatus: (directoryPath) -> + getDirectoryStatus: (directoryPath) -> directoryPath = "#{directoryPath}/" directoryStatus = 0 for path, status of @statuses @@ -133,5 +133,8 @@ class Git getAheadBehindCounts: -> @getRepo().getAheadBehindCount() + getLineDiffs: (path, text) -> + @getRepo().getLineDiffs(@relativize(path), text) + _.extend Git.prototype, Subscriber _.extend Git.prototype, EventEmitter diff --git a/src/app/gutter.coffee b/src/app/gutter.coffee index da97e6e89..76bf22cad 100644 --- a/src/app/gutter.coffee +++ b/src/app/gutter.coffee @@ -76,7 +76,7 @@ class Gutter extends View rowValue = (row + 1).toString() classes = ['line-number'] classes.push('fold') if editor.isFoldedAtBufferRow(row) - @div class: classes.join(' '), => + @div lineNumber: row, class: classes.join(' '), => rowValuePadding = _.multiplyString(' ', maxDigits - rowValue.length) @raw("#{rowValuePadding}#{rowValue}") diff --git a/src/packages/git-diff/lib/git-diff-view.coffee b/src/packages/git-diff/lib/git-diff-view.coffee new file mode 100644 index 000000000..f49df2e42 --- /dev/null +++ b/src/packages/git-diff/lib/git-diff-view.coffee @@ -0,0 +1,62 @@ +_ = require 'underscore' + +module.exports = +class GitDiffView + diffs: null + editor: null + + constructor: (@editor) -> + @gutter = @editor.gutter + @diffs = {} + + @editor.on 'editor:path-changed', => @subscribeToBuffer() + @editor.on 'editor:display-updated', => @renderDiffs() + git.on 'statuses-changed', => + @diffs = {} + @scheduleDiffs() + git.on 'status-changed', (path) => + delete @diffs[path] + @scheduleDiffs() if path is @editor.getPath() + + @subscribeToBuffer() + + subscribeToBuffer: -> + if @buffer? + @removeDiffs() + delete @diffs[@buffer.getPath()] if @buffer.destroyed + @buffer.off '.git-diff' + @buffer = null + + if @buffer = @editor.getBuffer() + @scheduleDiffs() unless @diffs[@buffer.getPath()]? + @buffer.on 'contents-modified.git-diff', => + @generateDiffs() + @renderDiffs() + + scheduleDiffs: -> + _.nextTick => + @generateDiffs() + @renderDiffs() + + generateDiffs: -> + if path = @buffer.getPath() + @diffs[path] = git?.getLineDiffs(path, @buffer.getText()) + + removeDiffs: -> + @gutter.find('.line-number').removeClass('git-line-added git-line-modified git-line-removed') + + renderDiffs: -> + return unless @gutter.isVisible() + + @removeDiffs() + + hunks = @diffs[@editor.getPath()] ? [] + for hunk in hunks + if hunk.oldLines is 0 and hunk.newLines > 0 + for row in [hunk.newStart...hunk.newStart + hunk.newLines] + @gutter.find(".line-number[lineNumber=#{row - 1}]").addClass('git-line-added') + else if hunk.newLines is 0 and hunk.oldLines > 0 + @gutter.find(".line-number[lineNumber=#{hunk.newStart - 1}]").addClass('git-line-removed') + else + for row in [hunk.newStart...hunk.newStart + hunk.newLines] + @gutter.find(".line-number[lineNumber=#{row - 1}]").addClass('git-line-modified') diff --git a/src/packages/git-diff/lib/git-diff.coffee b/src/packages/git-diff/lib/git-diff.coffee new file mode 100644 index 000000000..127eb4b81 --- /dev/null +++ b/src/packages/git-diff/lib/git-diff.coffee @@ -0,0 +1,12 @@ +GitDiffView = require './git-diff-view' + +module.exports = + configDefaults: + enabled: false + + activate: -> + return unless git? + return unless config.get('git-diff.enabled') + + rootView.eachEditor (editor) => + new GitDiffView(editor) if git? and editor.getPane()? diff --git a/src/packages/git-diff/package.cson b/src/packages/git-diff/package.cson new file mode 100644 index 000000000..8fe8b9080 --- /dev/null +++ b/src/packages/git-diff/package.cson @@ -0,0 +1 @@ +'main': 'lib/git-diff' diff --git a/src/packages/git-diff/spec/git-diff-spec.coffee b/src/packages/git-diff/spec/git-diff-spec.coffee new file mode 100644 index 000000000..a800ac37a --- /dev/null +++ b/src/packages/git-diff/spec/git-diff-spec.coffee @@ -0,0 +1,63 @@ +RootView = require 'root-view' +_ = require 'underscore' + +describe "GitDiff package", -> + editor = null + + beforeEach -> + config.set('git-diff.enabled', true) + window.rootView = new RootView + rootView.attachToDom() + rootView.open('sample.js') + atom.activatePackage('git-diff') + editor = rootView.getActiveView() + + describe "when the editor has modified lines", -> + it "highlights the modified lines", -> + expect(editor.find('.git-line-modified').length).toBe 0 + editor.insertText('a') + advanceClock(editor.getBuffer().stoppedChangingDelay) + expect(editor.find('.git-line-modified').length).toBe 1 + expect(editor.find('.git-line-modified').attr('lineNumber')).toBe '0' + + describe "when the editor has added lines", -> + it "highlights the added lines", -> + expect(editor.find('.git-line-added').length).toBe 0 + editor.moveCursorToEndOfLine() + editor.insertNewline() + editor.insertText('a') + advanceClock(editor.getBuffer().stoppedChangingDelay) + expect(editor.find('.git-line-added').length).toBe 1 + expect(editor.find('.git-line-added').attr('lineNumber')).toBe '1' + + describe "when the editor has removed lines", -> + it "highlights the line preceeding the deleted lines", -> + expect(editor.find('.git-line-added').length).toBe 0 + editor.setCursorBufferPosition([5]) + editor.deleteLine() + advanceClock(editor.getBuffer().stoppedChangingDelay) + expect(editor.find('.git-line-removed').length).toBe 1 + expect(editor.find('.git-line-removed').attr('lineNumber')).toBe '4' + + describe "when a modified line is restored to the HEAD version contents", -> + it "removes the diff highlight", -> + expect(editor.find('.git-line-modified').length).toBe 0 + editor.insertText('a') + advanceClock(editor.getBuffer().stoppedChangingDelay) + expect(editor.find('.git-line-modified').length).toBe 1 + editor.backspace() + advanceClock(editor.getBuffer().stoppedChangingDelay) + expect(editor.find('.git-line-modified').length).toBe 0 + + describe "when a modified file is opened", -> + it "highlights the changed lines", -> + path = project.resolve('sample.txt') + buffer = project.buildBuffer(path) + buffer.setText("Some different text.") + rootView.open('sample.txt') + nextTick = false + _.nextTick -> nextTick = true + waitsFor -> nextTick + runs -> + expect(editor.find('.git-line-modified').length).toBe 1 + expect(editor.find('.git-line-modified').attr('lineNumber')).toBe '0' diff --git a/src/packages/git-diff/stylesheets/git-diff.less b/src/packages/git-diff/stylesheets/git-diff.less new file mode 100644 index 000000000..a04bed75d --- /dev/null +++ b/src/packages/git-diff/stylesheets/git-diff.less @@ -0,0 +1,11 @@ +.git-line-modified { + color: #f78a46; +} + +.git-line-added { + color: #5293d8; +} + +.git-line-removed { + color: #c41e3a; +}