
Till now, log viewer was rendered in handlebars, which was the simplest solution, but it had a major drawback - every append to log caused it to rerender which was not efficient and memory consuming. The new approach is to make Travis.Log interpret the log and send lines with instructions to the view, so for example if view should add a line, it gets something like: { number: 1, content: '$ bundle install' } Such approach is required to handle cases where data coming from pusher is not actually a new line. For example output containing dots from tests needs to be appended: $ rake .... Such output could be sent to client in 2 chunks: "$ rake\n.." and "..". In such situation we would need to send 3 instructions: { number: 1, content: '$ rake' } { number: 2, content: '..' } { number: 2, content: '..', append: true } The third instruction can come much later, because tests can take a while to run, so we can't assume that each line will come in one piece. The other scenario is \r, for example when showing progress: \rDownloading: 10% \rDownloading: 50% \rDownloading: 100% Such input should be changed into such instructions: { number: 1, content: 'Downloading: 10%' } { number: 1, content: 'Downloading: 50%', replace: true } { number: 1, content: 'Downloading: 100%', replace: true } Travis.Log also supports folds, for example on bundle install, the code was rewritten to make folds management simpler.
133 lines
3.4 KiB
CoffeeScript
133 lines
3.4 KiB
CoffeeScript
# TODO: revisit those patterns
|
||
FOLDS = [
|
||
Em.Object.create(name: 'schema', startPattern: /^\$ (?:bundle exec )?rake( db:create)? db:schema:load/, endPattern: /^\$/)
|
||
Em.Object.create(name: 'migrate', startPattern: /^\$ (?:bundle exec )?rake( db:create)? db:migrate/, endPattern: /^\$/)
|
||
Em.Object.create(name: 'bundle', startPattern: /^\$ bundle install/, endPattern: /^\$/)
|
||
]
|
||
|
||
@Travis.Log = Em.Object.extend
|
||
init: ->
|
||
@set 'folds', []
|
||
@set 'line', 1
|
||
|
||
for fold in FOLDS
|
||
@addFold fold
|
||
|
||
append: (lines) ->
|
||
log = @join lines
|
||
log = @escape log
|
||
log = @deansi log
|
||
lines = @split log
|
||
|
||
target = @get 'target'
|
||
index = 0
|
||
currentFold = @currentFold
|
||
|
||
@set 'lineNumber', 1 unless @get 'lineNumber'
|
||
|
||
result = []
|
||
|
||
for line in lines
|
||
if line == '\r'
|
||
@set 'replace', true
|
||
else if line == '\n'
|
||
@set 'newline', true
|
||
index += 1
|
||
else
|
||
if currentFold && ( @isFoldEnding(currentFold, line) )
|
||
# end of the fold, send fold to target
|
||
if result.length > 0
|
||
result.slice(-1)[0].foldEnd = true
|
||
target.appendLog result
|
||
|
||
@currentFold = currentFold = null
|
||
@set 'foldContinuation', false
|
||
result = []
|
||
|
||
if !currentFold && ( currentFold = @foldByStart(line) )
|
||
# beginning new fold, send current lines to target
|
||
if result.length > 0
|
||
target.appendLog result
|
||
|
||
result = []
|
||
start = index
|
||
|
||
payload = { content: line }
|
||
|
||
if currentFold
|
||
payload.fold = currentFold.get('name')
|
||
|
||
if @get 'foldContinuation'
|
||
payload.foldContinuation = true
|
||
|
||
payload.number = @get('lineNumber') + index
|
||
|
||
if @get 'replace'
|
||
@set 'replace', false
|
||
payload.replace = true
|
||
else if @get 'newline'
|
||
@set 'newline', false
|
||
else if payload.number != 1
|
||
payload.append = true
|
||
|
||
result.pushObject payload
|
||
|
||
if currentFold
|
||
@set 'foldContinuation', true
|
||
|
||
if result.length > 0
|
||
if currentFold
|
||
@currentFold = currentFold
|
||
|
||
target.appendLog result
|
||
|
||
nextLineNumber = @get('lineNumber') + index
|
||
@set 'lineNumber', nextLineNumber
|
||
|
||
join: (lines) ->
|
||
if typeof lines == 'string'
|
||
lines
|
||
else
|
||
lines.toArray().join ''
|
||
|
||
split: (log) ->
|
||
log = log.replace /\r\n/g, '\n'
|
||
lines = log.split(/(\n)/)
|
||
|
||
result = []
|
||
for line in lines
|
||
result.pushObjects line.split(/(\r)/)
|
||
|
||
result
|
||
|
||
escape: (log) ->
|
||
Handlebars.Utils.escapeExpression log
|
||
|
||
deansi: (log) ->
|
||
log = log.replace(/\r\r/g, '\r')
|
||
.replace(/\033\[K\r/g, '\r')
|
||
.replace(/\[2K/g, '')
|
||
.replace(/\033\(B/g, '')
|
||
.replace(/\033\[\d+G/, '')
|
||
|
||
ansi = ansiparse(log)
|
||
|
||
text = ''
|
||
ansi.forEach (part) ->
|
||
classes = []
|
||
part.foreground and classes.push(part.foreground)
|
||
part.background and classes.push('bg-' + part.background)
|
||
part.bold and classes.push('bold')
|
||
part.italic and classes.push('italic')
|
||
text += (if classes.length then ('<span class=\'' + classes.join(' ') + '\'>' + part.text + '</span>') else part.text)
|
||
text.replace /\033/g, ''
|
||
|
||
addFold: (fold) ->
|
||
@get('folds').pushObject fold
|
||
|
||
foldByStart: (line) ->
|
||
@get('folds').find (fold) -> line.match(fold.get('startPattern'))
|
||
|
||
isFoldEnding: (fold, line) ->
|
||
line.match(fold.get('endPattern'))
|