define(function(require, exports, module) { main.consumes = [ "editors", "Editor", "ui", "scm", "layout", "settings", "threewaymerge", "menus", "Menu", "MenuItem", "Divider", "ace" ]; main.provides = ["diff.split"]; return main; function main(options, imports, register) { var settings = imports.settings; var editors = imports.editors; var Editor = imports.Editor; var scm = imports.scm; var layout = imports.layout; var MenuItem = imports.MenuItem; var Divider = imports.Divider; var merge = imports.threewaymerge; var Menu = imports.Menu; var ace = imports.ace; var ui = imports.ui; var dirname = require("path").dirname; var basename = require("path").basename; var DiffView = require("./diff/twoway").DiffView; /***** Initialization *****/ var extensions = []; // :( var BGCOLOR = { "flat-light": "#F1F1F1", "flat-dark": "#3D3D3D", "light": "#D3D3D3", "light-gray": "#D3D3D3", "dark": "#3D3D3D", "dark-gray": "#3D3D3D" }; var menuAce; var menuGutter; var handle = editors.register("diff.split", "Compare", DiffViewer, extensions); function createMenu() { menuAce = new Menu({ id: "menu", items: [ new MenuItem({ position: 10, command: "cut", caption: "Cut" }, handle), new MenuItem({ position: 20, command: "copy", caption: "Copy" }, handle), new MenuItem({ position: 30, command: "paste", caption: "Paste" }, handle), new Divider({ position: 40 }, handle), new MenuItem({ position: 50, command: "selectall", caption: "Select All" }, handle), new Divider({ position: 60 }, handle) ] }, handle); menuGutter = new Menu({ id: "menu-gutter", items: [ ] }, handle); } function DiffViewer() { // TODO it is too difficult to hook into initialization flow of ace plugin // so we have to copy paste bunch of code here :( // var Baseclass = editors.findEditor("ace"); // var plugin = new Baseclass(true, []); var plugin = new Editor(true, []); var emit = plugin.getEmitter(); var currentSession; var diffview; var lastAce; var lblLeft, lblRight, btnNext, btnPrev, btnFold, container; var toolbar; plugin.on("draw", function(e) { var tab = e.tab; lblLeft = new ui.label({ flex: 1 }); lblRight = new ui.label({ flex: 1, class: "right" }); btnNext = new ui.button({ caption: ">", height: 24, skin: "c9-toolbarbutton-glossy", onclick: function() { diffview.gotoNext(1); } }); btnPrev = new ui.button({ caption: "<", height: 24, skin: "c9-toolbarbutton-glossy", onclick: function() { diffview.gotoNext(-1); } }); btnFold = new ui.button({ caption: "Fold", height: 24, skin: "c9-toolbarbutton-glossy", onclick: function() { if (diffview.orig.session.$foldData.length) diffview.orig.session.unfold(); else diffview.foldUnchanged(); } }); container = new ui.bar({ flex: 1, class: "ace_diff-container" }); tab.appendChild(new ui.vsplitbox({ anchors: "0 0 0 0", childNodes: [ toolbar = new ui.hbox({ class: "difftoolbar", height: 36, align: "center", edge: "0 5 0 3", padding: 3, childNodes: [ lblLeft, new ui.hbox({ padding: 3, edge: 3, margin: "0 7 0 7", align: "center", class: "buttons", childNodes: [ btnPrev, btnFold, btnNext ] }), lblRight ] }), container ] })); diffview = new DiffView(container.$ext, {}); // temporary workaround for apf focus bugs // only blur is needed sinse the rest is handled by tabManager // todo remove this when there is proper focus manager tab.$blur = function(e) { var ace = plugin.ace; // can be null when called for destroyed tab if (!ace || !e || !e.toElement || e.toElement.tagName == "menu") return; if (!ace.isFocused()) ace.renderer.visualizeBlur(); else ace.textInput.blur(); }; function focusApf() { var page = apf.findHost(diffview.container.parentElement.parentElement); if (apf.activeElement != page) page.focus(); } function updateLastAce(e, ace) { lastAce = ace; } diffview.edit.on("focus", focusApf); diffview.orig.on("focus", focusApf); diffview.edit.keyBinding.setDefaultHandler(null); diffview.orig.keyBinding.setDefaultHandler(null); diffview.edit.on("focus", updateLastAce); diffview.orig.on("focus", updateLastAce); lastAce = diffview.edit; // createProgressIndicator(e.htmlNode); tab.on("contextmenu", function(e) { if (!menuAce) createMenu(); var target = e.htmlEvent.target; var gutter = plugin.diffview.gutterEl; // Set Gutter Context Menu if (ui.isChildOf(gutter, target, true)) { menuGutter.show(e.x, e.y); } // Set main Ace Context Menu else { menuAce.show(e.x, e.y); } return false; }); }); /***** Method *****/ function getLabelValue(path) { var hash; if (path.indexOf(":") > -1) { hash = path.split(":"); path = hash[1], hash = hash[0]; } var dirpath = dirname(path); return (hash ? "" + hash + "" : "") + basename(dirpath) + "/" + basename(path) + " - " + dirname(dirpath) + ""; } function loadSession(session) { if (session.diffSession) { diffview.setSession(session.diffSession); return; } diffview.setSession(session.diffSession = { orig: diffview.createSession(), edit: diffview.createSession(), chunks: [] }); var diff = session.diff || {}; if (typeof diff.patch == "string") { diffview.setValueFromFullPatch(diff.patch); } else { diffview.orig.session.setValue(diff.orig || ""); diffview.edit.session.setValue(diff.edit || ""); } diffview.orig.setReadOnly(true); diffview.edit.setReadOnly(true); var syntax = ace.getSyntaxForPath(session.newPath); if (syntax && syntax.indexOf("/") == -1) syntax = "ace/mode/" + syntax; if (syntax) { diffview.orig.session.setMode(syntax); diffview.edit.session.setMode(syntax); } diffview.orig.renderer.once("afterRender", function() { if (diffview.session == session.diffSession) { if (!diffview.chunks.length) diffview.computeDiff(); diffview.foldUnchanged(); diffview.gotoNext(1); } }); } /***** Lifecycle *****/ plugin.on("load", function() { }); plugin.on("documentLoad", function(e) { var doc = e.doc; var session = e.doc.getSession(); if (e.state.oldPath) session.oldPath = e.state.oldPath; if (e.state.newPath) session.newPath = e.state.newPath; doc.title = "Compare Files..."; diffview.c9session = session; diffview.orig.session.c9session = session; diffview.edit.session.c9session = session; function setTheme(e) { var tab = doc.tab; if (e.theme && BGCOLOR[e.theme]) { var isDark = e.theme == "dark"; toolbar.$ext.style.backgroundColor = tab.backgroundColor = BGCOLOR[e.theme]; if (isDark) tab.classList.add("dark"); else tab.classList.remove("dark"); } diffview.setTheme(settings.get("user/ace/@theme")); } layout.on("themeChange", setTheme, doc); settings.on("user/ace/@theme", setTheme, doc); setTheme({ theme: settings.get("user/general/@skin") }); }); plugin.on("documentActivate", function(e) { var session = currentSession = e.doc.getSession(); if (!session.newPath) return; var newFilename = (session.newPath + "").split("/").pop(); e.doc.title = "Compare " + newFilename; lblLeft.setAttribute("caption", getLabelValue(session.oldPath)); lblRight.setAttribute("caption", getLabelValue(session.newPath)); if (session.diff) return loadSession(session); var newPath = session.newPath .replace(/MODIFIED:/, "") .replace(/STAGED:/, ":"); var oldPath = session.oldPath .replace(/PREVIOUS:/, ":"); session.request = scm.loadDiff({ oldPath: oldPath, newPath: newPath }, function(err, diff) { if (err) diff; if (session.request == diff.request) { session.diff = diff; loadSession(session); } }); }); plugin.on("documentUnload", function(e) { // var session = e.doc.getSession(); }); plugin.on("getState", function(e) { var session = e.doc.getSession(); e.state.oldPath = session.oldPath; e.state.newPath = session.newPath; }); plugin.on("setState", function(e) { var session = e.doc.getSession(); session.oldPath = e.state.oldPath; session.newPath = e.state.newPath; }); plugin.on("clear", function() { }); plugin.on("focus", function() { }); plugin.on("enable", function() { }); plugin.on("disable", function() { }); plugin.on("unload", function() { }); plugin.on("resize", function(e) { diffview && diffview.resize(e); }); /***** Register and define API *****/ /** * Read Only Image Editor **/ plugin.freezePublicAPI({ get diffview() { return diffview; }, get ace () { return lastAce; } }); plugin.load(null, "ace.repl"); return plugin; } register(null, { "diff.split": handle }); } });