define(function(require, exports, module) { "use strict"; var oop = require("ace/lib/oop"); var lang = require("ace/lib/lang"); var event = require("ace/lib/event"); var Range = require("ace/range").Range; var dom = require("ace/lib/dom"); var config = require("ace/config"); var LineWidgets = require("ace/line_widgets").LineWidgets; var css = require("text!./unified.css"); dom.importCssString(css, "unidiff.css"); var diff_match_patch = require("./diff_match_patch").diff_match_patch; var Editor = require("ace/editor").Editor; var Renderer = require("ace/virtual_renderer").VirtualRenderer; var UndoManager = require("ace/undomanager").UndoManager; var EditSession = require("ace/edit_session").EditSession; var Mode = require("ace/mode/text").Mode; var mode = new Mode(); var HEADER_ROWS = 3; function createEditor(el) { if (el instanceof Editor) return el; var editor = new Editor(new Renderer(el), null); editor.session.setUndoManager(new UndoManager()); return editor; } function DiffView(element, options) { this.renderedHeaders = []; this.renderHeaders = this.renderHeaders.bind(this); this.handleWidgetMouseDown = this.handleWidgetMouseDown.bind(this); // this.onInput = this.onInput.bind(this); this.options = {}; var editor = createEditor(element); this.container = editor.container; this.editor = editor; this.attachToEditor(editor); oop.mixin(this.options, { showDiffs: true, maxDiffs: 5000 }, options); config.resetOptions(this); config._signal("diffView", this); } (function() { /*** theme/session ***/ this.setValueFromPatch = function(v) { var editor = this.editor; var lines = editor.session.doc.$split(v); var states = []; var result = []; var rowInsert = 0; var rowRemove = 0; var file; var insertedTotal = 0; var removedTotal = 0; var inserted = 0; var removed = 0; for (var i = 0; i < lines.length; i++) { var line = lines[i]; if (line[0] == "d" && line.slice(0, 5) == "diff ") { if (file) { file.inserted = inserted; file.removed = removed; } insertedTotal += inserted; removedTotal += removed; inserted = removed = 0; file = { type: "file" }; var none = { type: "none" }; var path = line.split(" b/").pop(); result.push("", "", "", path); states.push(none, none, none, file); while (i + 1 < lines.length && lines[i + 1][0] != "@") i++; } else if (line[0] == "@") { var m = line.match(/^@@ -(\d+)(,\d+) \+(\d+)(,\d+) @@/); if (m) { rowRemove = parseInt(m[1], 10); rowInsert = parseInt(m[3], 10); result.push(line); states.push({ type: "header" }); } } else if (line[0] == " ") { result.push(line.substr(1)); states.push({ type: "context", row2: rowInsert, row1: rowRemove }); rowInsert++; rowRemove++; } else if (line[0] == "+") { result.push(line.substr(1)); states.push({ type: "insert", row2: rowInsert, row1: "" }); rowInsert++; inserted++; } else if (line[0] == "-") { result.push(line.substr(1)); states.push({ type: "remove", row2: "", row1: rowRemove }); rowRemove++; removed++; } } result.push("", ""); states.push({ type: "none" }, { type: "file" }); v = result.join("\n"); editor.setValue(v, -1); editor.session.bgTokenizer.diffStates = states; editor.session.bgTokenizer.stop(); editor.setReadOnly(true); editor.session.bgTokenizer.getTokens = function(row) { var line = this.doc.getLine(row); var type = this.diffStates[row].type; return [{ value: line, type: "uniDiff_" + type }]; }; if (!editor.session.meta) editor.session.meta = {}; editor.session.meta.deletedLines = removedTotal; editor.session.meta.addedLines = insertedTotal; }; this.foldingRules = { getFoldWidget: function(session, foldStyle, row) { var state = session.bgTokenizer.diffStates[row]; if (state && state.type == "file" || state.type == "header") return "start"; return ""; }, getFoldWidgetRange: function(session, foldStyle, row) { var states = session.bgTokenizer.diffStates; var state = states[row]; var type = state.type; if (!state || (type != "header" && type != "file")) return null; var line = session.getLine(row); var start = { row: row, column: line.length }; for (var l = states.length; ++row < l;) { state = states[row]; if (state.type == type || state.type == "file" || state.type == "none") break; } if (row == start.row + 1) return; return new Range(start.row, start.column, row - 1, session.getLine(row - 1).length); } }; this.attachToEditor = function(editor) { mode.foldingRules = this.foldingRules; editor.session.setMode(mode); editor.session.removeMarker(editor.session.mi); editor.session.mi = editor.session.addDynamicMarker(new DiffHighlight); editor.renderer.on("afterRender", this.renderHeaders); editor.session.diffView = this; editor.renderer.$gutterLayer.$cells = []; editor.renderer.$gutterLayer.element.innerHTML = ""; editor.renderer.$gutterLayer.gutterWidth = NaN; editor.renderer.$gutterLayer.$padding = null; editor.renderer.$gutterLayer.update = this.updateGutter; editor.diffView = this; }; this.updateGutter = function(config) { var session = this.session; var firstRow = config.firstRow; var lastRow = Math.min(config.lastRow + config.gutterOffset, // needed to compensate for hor scollbar session.getLength() - 1); var fold = session.getNextFoldLine(firstRow); var foldStart = fold ? fold.start.row : Infinity; var foldWidgets = this.$showFoldWidgets && session.foldWidgets; var diffStates = session.bgTokenizer.diffStates; if (!diffStates) return; var cell = null; var index = -1; var row = firstRow; while (true) { if (row > foldStart) { row = fold.end.row + 1; fold = session.getNextFoldLine(row, fold); foldStart = fold ? fold.start.row : Infinity; } if (row > lastRow) { while (this.$cells.length > index + 1) { cell = this.$cells.pop(); this.element.removeChild(cell.element); } break; } cell = this.$cells[++index]; if (!cell) { cell = { element: null, textNode: null, foldWidget: null }; cell.element = dom.createElement("div"); cell.textNode = document.createTextNode(''); cell.element1 = dom.createElement("span"); cell.element2 = dom.createElement("span"); cell.element1.className = "unidiff-cell first"; cell.element2.className = "unidiff-cell"; cell.element.appendChild(cell.element1); cell.element.appendChild(cell.element2); this.element.appendChild(cell.element); this.$cells[index] = cell; } var line = session.getLine(row); var state = diffStates[row]; var className = "unidiff_gutter-cell unidiff " + state.type; if (cell.element.className != className) cell.element.className = className; var height = session.getRowLength(row) * config.lineHeight + "px"; if (height != cell.element.style.height) cell.element.style.height = height; if (foldWidgets) { var c = foldWidgets[row]; // check if cached value is invalidated and we need to recompute if (c == null) c = foldWidgets[row] = session.getFoldWidget(row); } if (c) { if (!cell.foldWidget) { cell.foldWidget = dom.createElement("span"); cell.element.appendChild(cell.foldWidget); } var className = "ace_fold-widget ace_" + c; if (c == "start" && row == foldStart && row < fold.end.row) className += " ace_closed"; else className += " ace_open"; if (cell.foldWidget.className != className) cell.foldWidget.className = className; var height = config.lineHeight + "px"; if (cell.foldWidget.style.height != height) cell.foldWidget.style.height = height; } else { if (cell.foldWidget) { cell.element.removeChild(cell.foldWidget); cell.foldWidget = null; } } if (line[0] == "@") { cell.element1.innerHTML = cell.element2.innerHTML = "\xb7\xb7\xb7"; } else { cell.element1.textContent = state.row1 || "\x1b"; cell.element2.textContent = state.row2 || "\x1b"; } row++; } this.element.style.height = config.minHeight + "px"; var gutterWidth = 2 * 6 * config.characterWidth; var padding = this.$padding || this.$computePadding(); gutterWidth += padding.left + padding.right; if (gutterWidth !== this.gutterWidth && !isNaN(gutterWidth)) { this.gutterWidth = gutterWidth; this.element.style.width = Math.ceil(this.gutterWidth) + "px"; this._emit("changeGutterWidth", gutterWidth); } }; this.handleWidgetMouseDown = function(e) { e.stopPropagation(); var w = e.currentTarget.w; var editor = this.editor; if (e.target.classList.contains("ace_fold-widget")) { editor.session.onFoldWidgetClick(w.row, { domEvent: e }); e.preventDefault(); } }; this.createWidget = function(row, renderedHeaders) { var w = { row: row, el: document.createElement("div"), rowCount: 0, pixelHeight: 0, foldClosed: false, coverGutter: 1, fixedWidth: true }; w.el.onmousedown = this.handleWidgetMouseDown; renderedHeaders.push(w); w.el.w = w; return w; }; this.renderWidget = function(w, i, state) { if (state === w.state) return; w.state = state; var editor = this.editor; var session = editor.session; var line = session.getLine(i); var lineHeight = editor.renderer.layerConfig.lineHeight; w.el.style.borderTopWidth = i > HEADER_ROWS ? "" : "0"; w.el.className = "unidiff_fileHeader ace_lineWidgetContainer"; if (!line) { w.el.style.height = lineHeight * 100 + "px"; w.el.innerHTML = ""; w.foldArrow = null; return; } w.el.style.height = lineHeight * HEADER_ROWS + "px"; w.el.innerHTML = '