Merge pull request #240 from dmathieu/multiple_lines
Highlight multiple lines
This commit is contained in:
commit
6ec13ceee1
assets
scripts
app
lib/travis
spec/unit
styles
|
@ -2,7 +2,6 @@ Travis.BuildController = Ember.Controller.extend
|
||||||
needs: ['repo']
|
needs: ['repo']
|
||||||
repoBinding: 'controllers.repo.repo'
|
repoBinding: 'controllers.repo.repo'
|
||||||
commitBinding: 'build.commit'
|
commitBinding: 'build.commit'
|
||||||
lineNumberBinding: 'controllers.repo.lineNumber'
|
|
||||||
currentUserBinding: 'controllers.repo.currentUser'
|
currentUserBinding: 'controllers.repo.currentUser'
|
||||||
tabBinding: 'controllers.repo.tab'
|
tabBinding: 'controllers.repo.tab'
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,6 @@ Travis.JobController = Em.Controller.extend
|
||||||
jobBinding: 'controllers.repo.job'
|
jobBinding: 'controllers.repo.job'
|
||||||
repoBinding: 'controllers.repo.repo'
|
repoBinding: 'controllers.repo.repo'
|
||||||
commitBinding: 'job.commit'
|
commitBinding: 'job.commit'
|
||||||
lineNumberBinding: 'controllers.repo.lineNumber'
|
|
||||||
currentUserBinding: 'controllers.repo.currentUser'
|
currentUserBinding: 'controllers.repo.currentUser'
|
||||||
tabBinding: 'controllers.repo.tab'
|
tabBinding: 'controllers.repo.tab'
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
require 'travis/location'
|
require 'travis/location'
|
||||||
require 'travis/line_number_parser'
|
|
||||||
|
|
||||||
Ember.Router.reopen
|
Ember.Router.reopen
|
||||||
location: (if testMode? then Ember.NoneLocation.create() else Travis.Location.create())
|
location: (if testMode? then Ember.NoneLocation.create() else Travis.Location.create())
|
||||||
|
@ -79,12 +78,6 @@ Ember.Route.reopen
|
||||||
Travis.storeAfterSignInPath(path)
|
Travis.storeAfterSignInPath(path)
|
||||||
@transitionTo('auth')
|
@transitionTo('auth')
|
||||||
|
|
||||||
Travis.Router.reopen
|
|
||||||
transitionTo: ->
|
|
||||||
this.container.lookup('controller:repo').set('lineNumber', null)
|
|
||||||
|
|
||||||
@_super.apply this, arguments
|
|
||||||
|
|
||||||
Travis.Router.map ->
|
Travis.Router.map ->
|
||||||
@resource 'index', path: '/', ->
|
@resource 'index', path: '/', ->
|
||||||
@route 'current', path: '/'
|
@route 'current', path: '/'
|
||||||
|
@ -108,12 +101,6 @@ Travis.Router.map ->
|
||||||
@route 'index', path: '/'
|
@route 'index', path: '/'
|
||||||
@route 'profile', path: '/profile'
|
@route 'profile', path: '/profile'
|
||||||
|
|
||||||
Travis.ApplicationRoute = Ember.Route.extend Travis.LineNumberParser,
|
|
||||||
setupController: ->
|
|
||||||
@_super.apply this, arguments
|
|
||||||
|
|
||||||
this.controllerFor('repo').set('lineNumber', @fetchLineNumber())
|
|
||||||
|
|
||||||
Travis.SetupLastBuild = Ember.Mixin.create
|
Travis.SetupLastBuild = Ember.Mixin.create
|
||||||
setupController: ->
|
setupController: ->
|
||||||
@repoDidLoad()
|
@repoDidLoad()
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
require 'log'
|
require 'log'
|
||||||
|
require 'travis/lines_selector'
|
||||||
|
|
||||||
Log.DEBUG = false
|
Log.DEBUG = false
|
||||||
Log.LIMIT = 10000
|
Log.LIMIT = 10000
|
||||||
|
@ -26,12 +27,12 @@ Travis.reopen
|
||||||
console.log 'log view: did insert' if Log.DEBUG
|
console.log 'log view: did insert' if Log.DEBUG
|
||||||
@_super.apply this, arguments
|
@_super.apply this, arguments
|
||||||
@createEngine()
|
@createEngine()
|
||||||
@lineNumberDidChange()
|
|
||||||
|
|
||||||
willDestroyElement: ->
|
willDestroyElement: ->
|
||||||
console.log 'log view: will destroy' if Log.DEBUG
|
console.log 'log view: will destroy' if Log.DEBUG
|
||||||
parts = @get('log.parts')
|
parts = @get('log.parts')
|
||||||
parts.removeArrayObserver(@, didChange: 'partsDidChange', willChange: 'noop')
|
parts.removeArrayObserver(@, didChange: 'partsDidChange', willChange: 'noop')
|
||||||
|
@lineSelector?.willDestroy()
|
||||||
|
|
||||||
versionDidChange: (->
|
versionDidChange: (->
|
||||||
@rerender() if @get('state') == 'inDOM'
|
@rerender() if @get('state') == 'inDOM'
|
||||||
|
@ -46,8 +47,8 @@ Travis.reopen
|
||||||
console.log 'log view: create engine' if Log.DEBUG
|
console.log 'log view: create engine' if Log.DEBUG
|
||||||
@scroll = new Log.Scroll
|
@scroll = new Log.Scroll
|
||||||
@engine = Log.create(limit: Log.LIMIT, listeners: [@scroll])
|
@engine = Log.create(limit: Log.LIMIT, listeners: [@scroll])
|
||||||
|
@lineSelector = new Travis.LinesSelector(@$().find('#log'), @scroll, window.location)
|
||||||
@observeParts()
|
@observeParts()
|
||||||
@numberLineOnHover()
|
|
||||||
|
|
||||||
observeParts: ->
|
observeParts: ->
|
||||||
parts = @get('log.parts')
|
parts = @get('log.parts')
|
||||||
|
@ -63,10 +64,6 @@ Travis.reopen
|
||||||
@engine.set(part.number, part.content)
|
@engine.set(part.number, part.content)
|
||||||
@propertyDidChange('limited')
|
@propertyDidChange('limited')
|
||||||
|
|
||||||
lineNumberDidChange: (->
|
|
||||||
@scroll.set(number) if !@get('isDestroyed') && number = @get('controller.lineNumber')
|
|
||||||
).observes('controller.lineNumber')
|
|
||||||
|
|
||||||
limited: (->
|
limited: (->
|
||||||
@engine?.limit?.limited
|
@engine?.limit?.limited
|
||||||
).property()
|
).property()
|
||||||
|
@ -79,24 +76,11 @@ Travis.reopen
|
||||||
Travis.tailing.toggle()
|
Travis.tailing.toggle()
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
|
|
||||||
numberLineOnHover: ->
|
|
||||||
$('#log').on 'mouseenter', 'a', ->
|
|
||||||
$(@).attr('href', '#L' + ($("#log p:visible").index(@parentNode) + 1))
|
|
||||||
|
|
||||||
click: (event) ->
|
click: (event) ->
|
||||||
if (href = $(event.target).attr('href')) && matches = href?.match(/#L(\d+)$/)
|
target = $(event.target)
|
||||||
@lineNumberClicked(matches[1])
|
if target.prop('tagName') == 'P'
|
||||||
event.stopPropagation()
|
|
||||||
false
|
|
||||||
else
|
|
||||||
target = $(event.target)
|
|
||||||
target.closest('.fold').toggleClass('open')
|
target.closest('.fold').toggleClass('open')
|
||||||
|
|
||||||
lineNumberClicked: (number) ->
|
|
||||||
path = "#{window.location.pathname}#L#{number}"
|
|
||||||
window.history.pushState({ path: path }, null, path);
|
|
||||||
@set('controller.lineNumber', number)
|
|
||||||
|
|
||||||
actions:
|
actions:
|
||||||
toTop: () ->
|
toTop: () ->
|
||||||
$(window).scrollTop(0)
|
$(window).scrollTop(0)
|
||||||
|
@ -105,25 +89,14 @@ Travis.reopen
|
||||||
|
|
||||||
Log.Scroll = ->
|
Log.Scroll = ->
|
||||||
Log.Scroll.prototype = $.extend new Log.Listener,
|
Log.Scroll.prototype = $.extend new Log.Listener,
|
||||||
set: (number) ->
|
|
||||||
return unless number
|
|
||||||
@number = number
|
|
||||||
@tryScroll()
|
|
||||||
|
|
||||||
insert: (log, data, pos) ->
|
insert: (log, data, pos) ->
|
||||||
@tryScroll() if @number
|
@tryScroll() if @numbers
|
||||||
true
|
true
|
||||||
|
|
||||||
tryScroll: ->
|
tryScroll: ->
|
||||||
if element = $("#log p:visible")[@number - 1]
|
if element = $("#log p:visible.highlight:first")
|
||||||
$('#main').scrollTop(0)
|
$('#main').scrollTop(0)
|
||||||
$('html, body').scrollTop($(element).offset()?.top) # weird, html works in chrome, body in firefox
|
$('html, body').scrollTop(element.offset()?.top) # weird, html works in chrome, body in firefox
|
||||||
@highlight(element)
|
|
||||||
@number = undefined
|
|
||||||
|
|
||||||
highlight: (element) ->
|
|
||||||
$('#log p.highlight').removeClass('highlight')
|
|
||||||
$(element).addClass('highlight')
|
|
||||||
|
|
||||||
# Log.Logger = ->
|
# Log.Logger = ->
|
||||||
# Log.Logger.prototype = $.extend new Log.Listener,
|
# Log.Logger.prototype = $.extend new Log.Listener,
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
Travis.LineNumberParser = Ember.Mixin.create
|
|
||||||
fetchLineNumber: ->
|
|
||||||
match[1] if match = document.location.hash.match(/#L(\d+)$/)
|
|
55
assets/scripts/lib/travis/lines_selector.coffee
Normal file
55
assets/scripts/lib/travis/lines_selector.coffee
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
class Travis.LinesSelector
|
||||||
|
element: null
|
||||||
|
scroll: null
|
||||||
|
location: null
|
||||||
|
last_selected_line: null
|
||||||
|
|
||||||
|
constructor: (@element, @scroll, @location) ->
|
||||||
|
Ember.run.scheduleOnce 'afterRender', this, ->
|
||||||
|
@last_selected_line = @getSelectedLines()?.first
|
||||||
|
@highlightLines()
|
||||||
|
|
||||||
|
@element.on 'click', 'a', (event) =>
|
||||||
|
element = $(event.target).parent('p')
|
||||||
|
@loadLineNumbers(element, event.shiftKey)
|
||||||
|
|
||||||
|
event.preventDefault()
|
||||||
|
false
|
||||||
|
|
||||||
|
willDestroy: ->
|
||||||
|
@location.hash = ''
|
||||||
|
|
||||||
|
loadLineNumbers: (element, multiple) ->
|
||||||
|
@setHashValueWithLine(element, multiple)
|
||||||
|
@highlightLines()
|
||||||
|
|
||||||
|
highlightLines: ->
|
||||||
|
@removeAllHighlights()
|
||||||
|
|
||||||
|
if lines = @getSelectedLines()
|
||||||
|
@element.find('p:visible').slice(lines.first - 1, lines.last).addClass('highlight')
|
||||||
|
@scroll.tryScroll()
|
||||||
|
|
||||||
|
setHashValueWithLine: (line, multiple) ->
|
||||||
|
line_number = @getLineNumberFromElement(line)
|
||||||
|
|
||||||
|
if multiple && @last_selected_line?
|
||||||
|
lines = [line_number, @last_selected_line].sort (a,b) -> a - b
|
||||||
|
hash = "#L#{lines[0]}-L#{lines[1]}"
|
||||||
|
else
|
||||||
|
hash = "#L#{line_number}"
|
||||||
|
|
||||||
|
@last_selected_line = line_number
|
||||||
|
@location.hash = hash
|
||||||
|
|
||||||
|
getLineNumberFromElement: (element) ->
|
||||||
|
@element.find('p:visible').index(element) + 1
|
||||||
|
|
||||||
|
removeAllHighlights: ->
|
||||||
|
@element.find('p.highlight').removeClass('highlight')
|
||||||
|
|
||||||
|
getSelectedLines: ->
|
||||||
|
if match = @location.hash.match(/#L(\d+)(-L(\d+))?$/)
|
||||||
|
first = match[1]
|
||||||
|
last = match[3] || match[1]
|
||||||
|
{first: first, last: last}
|
92
assets/scripts/spec/unit/line_selector_spec.coffee
Normal file
92
assets/scripts/spec/unit/line_selector_spec.coffee
Normal file
|
@ -0,0 +1,92 @@
|
||||||
|
fakeLocation = {}
|
||||||
|
fakeScroll =
|
||||||
|
tryScroll: sinon.spy()
|
||||||
|
|
||||||
|
element = jQuery('<div id="fakeLog">
|
||||||
|
<p><a></a>first line</p>
|
||||||
|
<p><a></a>second line</p>
|
||||||
|
<p><a></a>third line</p>
|
||||||
|
</div>')
|
||||||
|
|
||||||
|
module "Travis.LinesSelector",
|
||||||
|
setup: ->
|
||||||
|
fakeLocation.hash = ''
|
||||||
|
jQuery('body').append(element)
|
||||||
|
|
||||||
|
teardown: ->
|
||||||
|
element.remove()
|
||||||
|
|
||||||
|
test "defaults to no line selected", ->
|
||||||
|
Ember.run ->
|
||||||
|
new Travis.LinesSelector(element, fakeScroll, fakeLocation)
|
||||||
|
|
||||||
|
wait().then ->
|
||||||
|
equal($('#fakeLog p.highlight').length, 0)
|
||||||
|
|
||||||
|
test "defaults to a single line selected", ->
|
||||||
|
fakeLocation.hash = '#L2'
|
||||||
|
Ember.run ->
|
||||||
|
new Travis.LinesSelector(element, fakeScroll, fakeLocation)
|
||||||
|
|
||||||
|
wait().then ->
|
||||||
|
equal($('#fakeLog p.highlight').length, 1)
|
||||||
|
equal($('#fakeLog p:nth-child(2)').hasClass('highlight'), true)
|
||||||
|
|
||||||
|
test "defaults to multiple lines selected", ->
|
||||||
|
fakeLocation.hash = '#L2-L3'
|
||||||
|
Ember.run ->
|
||||||
|
new Travis.LinesSelector(element, fakeScroll, fakeLocation)
|
||||||
|
|
||||||
|
wait().then ->
|
||||||
|
equal($('#fakeLog p.highlight').length, 2)
|
||||||
|
equal($('#fakeLog p:nth-child(2)').hasClass('highlight'), true)
|
||||||
|
equal($('#fakeLog p:nth-child(3)').hasClass('highlight'), true)
|
||||||
|
|
||||||
|
test "selects a single line", ->
|
||||||
|
Ember.run ->
|
||||||
|
new Travis.LinesSelector(element, fakeScroll, fakeLocation)
|
||||||
|
|
||||||
|
wait().then ->
|
||||||
|
equal($('#fakeLog p.highlight').length, 0)
|
||||||
|
$('#fakeLog p:first a').click()
|
||||||
|
equal($('#fakeLog p.highlight').length, 1)
|
||||||
|
equal($('#fakeLog p:nth-child(1)').hasClass('highlight'), true)
|
||||||
|
equal('#L1', fakeLocation.hash)
|
||||||
|
|
||||||
|
test "selects multiple lines", ->
|
||||||
|
fakeLocation.hash = '#L2'
|
||||||
|
Ember.run ->
|
||||||
|
new Travis.LinesSelector(element, fakeScroll, fakeLocation)
|
||||||
|
|
||||||
|
wait().then ->
|
||||||
|
equal($('#fakeLog p.highlight').length, 1)
|
||||||
|
|
||||||
|
event = jQuery.Event('click')
|
||||||
|
event.shiftKey = true
|
||||||
|
$('#fakeLog p:first a').trigger(event)
|
||||||
|
|
||||||
|
equal($('#fakeLog p.highlight').length, 2)
|
||||||
|
equal($('#fakeLog p:nth-child(1)').hasClass('highlight'), true)
|
||||||
|
equal($('#fakeLog p:nth-child(2)').hasClass('highlight'), true)
|
||||||
|
equal('#L1-L2', fakeLocation.hash)
|
||||||
|
|
||||||
|
test "uses the last selected line as second selection line", ->
|
||||||
|
selector = null
|
||||||
|
Ember.run ->
|
||||||
|
selector = new Travis.LinesSelector(element, fakeScroll, fakeLocation)
|
||||||
|
|
||||||
|
wait().then ->
|
||||||
|
$('#fakeLog p:last a').click()
|
||||||
|
equal($('#fakeLog p.highlight').length, 1)
|
||||||
|
equal(3, selector.last_selected_line)
|
||||||
|
|
||||||
|
event = jQuery.Event('click')
|
||||||
|
event.shiftKey = true
|
||||||
|
$('#fakeLog p:first a').trigger(event)
|
||||||
|
|
||||||
|
equal($('#fakeLog p.highlight').length, 3)
|
||||||
|
equal($('#fakeLog p:nth-child(1)').hasClass('highlight'), true)
|
||||||
|
equal($('#fakeLog p:nth-child(2)').hasClass('highlight'), true)
|
||||||
|
equal($('#fakeLog p:nth-child(3)').hasClass('highlight'), true)
|
||||||
|
equal('#L1-L3', fakeLocation.hash)
|
||||||
|
equal(1, selector.last_selected_line)
|
|
@ -23,6 +23,7 @@ $yellow-light-2: #fffcf4
|
||||||
$gray-dark-1: #333
|
$gray-dark-1: #333
|
||||||
$gray-dark-2: #444
|
$gray-dark-2: #444
|
||||||
$gray-dark-3: #666
|
$gray-dark-3: #666
|
||||||
|
$gray-dark-4: #777
|
||||||
$gray-medium-1: #999
|
$gray-medium-1: #999
|
||||||
$gray-medium-2: #aaa
|
$gray-medium-2: #aaa
|
||||||
$gray-medium-3: #c4cbcc
|
$gray-medium-3: #c4cbcc
|
||||||
|
@ -65,6 +66,7 @@ $color-bg-log: #222222
|
||||||
$color-bg-log-fold: $gray-dark-1
|
$color-bg-log-fold: $gray-dark-1
|
||||||
$color-bg-log-hover: $gray-dark-2
|
$color-bg-log-hover: $gray-dark-2
|
||||||
$color-bg-log-highlight: $gray-dark-3
|
$color-bg-log-highlight: $gray-dark-3
|
||||||
|
$color-bg-log-fold-highlight: $gray-dark-4
|
||||||
$color-bg-slider: $slate-blue-3
|
$color-bg-slider: $slate-blue-3
|
||||||
$color-bg-left: $gray-light-4
|
$color-bg-left: $gray-light-4
|
||||||
$color-bg-list-odd: $white
|
$color-bg-list-odd: $white
|
||||||
|
|
|
@ -57,6 +57,8 @@ pre#log
|
||||||
// &.active
|
// &.active
|
||||||
p:first-of-type
|
p:first-of-type
|
||||||
background: $color-bg-log-fold inline-image('ui/log.fold.open.2.png') no-repeat 8px 3px
|
background: $color-bg-log-fold inline-image('ui/log.fold.open.2.png') no-repeat 8px 3px
|
||||||
|
&.highlight
|
||||||
|
background-color: $color-bg-log-fold-highlight
|
||||||
|
|
||||||
&:not(.open) p:first-of-type
|
&:not(.open) p:first-of-type
|
||||||
visibility: visible
|
visibility: visible
|
||||||
|
|
Loading…
Reference in New Issue
Block a user