class OutputLines constructor: (@screen, @render) -> @renderingCache = {} append: (lines) -> @screen.append(@renderLines(@filter(lines))) setFilter: -> true filter: (lines) -> lines highlight: (line) -> line renderLines: (lines) -> for line in lines @highlight(@renderingCache[line] ||= @render(line)) class @ClusterizeOutputLines extends OutputLines constructor: (@screen, @render) -> super @raw = [] @query = '' @highlightRegexp = null @stripCache = {} append: (lines) -> @raw = @raw.concat(lines) super setFilter: (query) -> if @query = query @screen.options.no_data_text = 'No matches' else @screen.options.no_data_text = 'Loading...' @highlightRegexp = @buildHighlightRegexp(@query) @reset() reset: -> @screen.update(@renderLines(@filter(@raw))) strip: (line) -> @stripCache[line] ||= AnsiStream.strip(line) filter: (lines) -> return lines unless @query line for line in lines when @strip(line).includes(@query) buildHighlightRegexp: (query) -> pattern = query.replace(/[|\\{}()[\]^$+*?.]/g, '\\$&').replace(/(\s+)/g, '(<[^>]+>)*$1(<[^>]+>)*') new RegExp("(#{pattern})", 'g') highlight: (renderedLine) -> return renderedLine unless @query renderedLine.replace(@highlightRegexp, '$1').replace(/([^<>]*)((<[^>]+>)+)([^<>]*<\/mark>)/, '$1$2$4'); class @TTY FORMATTERS = [] STICKY_SCROLL_TOLERENCE = 200 @appendFormatter: (formatter) -> FORMATTERS.push(formatter) @prependFormatter: (formatter) -> FORMATTERS.unshift(formatter) constructor: ($body) -> @outputLines = [] @$code = $body.find('code') @$container = @$code.closest('.task-output-container') if @$container.hasClass('clusterize-scroll') scroller = new Clusterize( no_data_text: 'Loading...' tag: 'div' contentElem: @$code[0] scrollElem: @$container[0] ) @output = new ClusterizeOutputLines(scroller, (line) => @createLine(@formatChunks(line))) else @output = new OutputLines(@$code, (line) => @createLine(@formatChunks(line))) filterOutput: (query) => @output.setFilter(query) formatChunks: (chunk) -> for formatter in FORMATTERS chunk = formatter(chunk) || chunk chunk appendChunk: (chunk) => lines = chunk.rawLines() return unless lines.length @preserveScroll => @output.append(lines) createLine: (fragment) -> div = document.createElement('div') div.appendChild(fragment) div.className = 'output-line' div.outerHTML isScrolledToBottom: -> (@getMaxScroll() - @$container.scrollTop()) < 1 scrollToBottom: -> @$container.scrollTop(@getMaxScroll()) getMaxScroll: -> @$code.parent().outerHeight(true) - @$container.outerHeight(true) preserveScroll: (callback) -> wasScrolledToBottom = @isScrolledToBottom() callback() @scrollToBottom() if wasScrolledToBottom