define(function(require, exports, module) { main.consumes = [ "Panel", "ui", "settings", "collab.workspace", "dialog.confirm", "dialog.info", "dialog.confirm", "dialog.error", "fs", "dialog.alert", "Menu", "MenuItem", "Divider", "layout", "Tree", "tabManager", "dialog.question", "dialog.filechange", "tree", "save", "commands", "c9", "scm", "console", "preferences.experimental", "watcher", "dialog.question", "panels" ]; main.provides = ["scm.commit"]; return main; /* TODO: - Test amend - Test nothing to do - Add setting to not depend on the status listener - Cmd-Enter to commit Add: - Pull / Push - Undo Last Commit - Unstage All - Clean All - On Hover: - Unstage (-) - Stage (+) - Clean (undo) BUG: - Why does tree not update after commit (have to wait a while) */ function main(options, imports, register) { var Panel = imports.Panel; var ui = imports.ui; var fs = imports.fs; var c9 = imports.c9; var settings = imports.settings; var panels = imports.panels; var commands = imports.commands; var collabWorkspace = imports["collab.workspace"]; var showInfo = imports["dialog.info"].show; var showError = imports["dialog.error"].show; var confirm = imports["dialog.confirm"].show; var alert = imports["dialog.alert"].show; var question = imports["dialog.question"]; var showFileChange = imports["dialog.filechange"].show; var Menu = imports.Menu; var MenuItem = imports.MenuItem; var Divider = imports.Divider; var layout = imports.layout; var Tree = imports.Tree; var scmProvider = imports.scm; var save = imports.save; var watcher = imports.watcher; var experimental = imports["preferences.experimental"]; var cnsl = imports.console; var tabManager = imports.tabManager; var async = require("async"); var basename = require("path").basename; var dirname = require("path").dirname; var escapeHTML = require("ace/lib/lang").escapeHTML; /***** Initialization *****/ var ENABLED = experimental.addExperiment("git", !c9.hosted, "Panels/Source Control Management"); if (!ENABLED) return register(null, { "scm.commit": {}}); var plugin = new Panel("Ajax.org", main.consumes, { index: options.index || 400, caption: "Commit", minWidth: 150, where: options.where || "left", autohide: false }); var emit = plugin.getEmitter(); var CAPTION = { "commit": "Commit", "sync": "Sync", "conflict": "Resolve Conflicts", "rebase": "????" }; var btnScmClassName = "splitbutton btn-scm-sync"; var btnMode = "sync"; var btnScm, title, tree, status, scm; var arrayCache = []; var reloading, lastReloadT = 0; var body, commitBox, ammendCb, commitBtn, onclick; var container, lastCommitMessage, winCommit; var isCommitting; function load() { ui.insertCss(require("text!./style.css"), null, plugin); plugin.setCommand({ name: "showcommit", group: "scm", bindKey: { mac: "Cmd-Shift-C", win: "Ctrl-Shift-C" } // exec: function(editor, args){ // if (args.message) commit(args.message, args.amend); // else plugin.show(); // } }, plugin); settings.on("read", function(e) { settings.setDefaults("state/scm", [ ["auto", false] ]); }, plugin); // settings.on("state/scm", function(){ // clearInterval(syncInterval); // if (settings.getBool("state/scm/@auto")) { // syncInterval = setInterval(syncNow, // settings.getNumber("state/scm/@interval") * 1000); // syncNow(); // } // }, plugin); // prefs.add({ // "Salesforce" : { // position: 3000, // "Synchronization" : { // position: 200, // "Automatically Synchronize When Detecting a Change" : { // type: "checkbox", // position: 100, // setting: "state/scm/@auto" // }, // "Synchronization Interval (in seconds)" : { // type: "spinner", // position: 200, // min: 1, // max: 60 * 60, // setting: "state/scm/@interval" // } // } // } // }, plugin); // commands.addCommand({ // name: "sync-salesforce", // bindKey: { mac: "Command-Ctrl-S", win: "Ctrl-Alt-S" }, // exec: function(editor, args) { // syncNow(); // } // }, plugin); scmProvider.on("scm", function(implementation) { scm = implementation; if (scm) { scm.on("status", function(e) { updateStatus(e.status); }, plugin); scm.on("log.dirty", function() { if (!plugin.active) return; updateLastCommit(); }, plugin); var timer; scm.on("status.dirty", function() { clearTimeout(timer); timer = setTimeout(reload, 500); }); } if (plugin.active) { if (reload()) updateLastCommit(); } }); watcher.on("change.all", function(e) { if (plugin.active && !isChanged(e.path) && Date.now() - lastReloadT > 2000) reload(); }); watcher.on("directory.all", function(e) { if (plugin.active && !isChanged(e.path) && Date.now() - lastReloadT > 2000) reload(); }); panels.on("showPanelScm.commit", function(e) { plugin.autohide = !e.button; }, plugin); panels.on("hidePanelScm.commit", function(e) { plugin.autohide = true; }, plugin); plugin.autohide = !panels.isActive("scm.commit"); plugin.on("show", function(e) { plugin.autohide = !e.button; // TODO is this needed? // if (e.button === "restoreSettings") // return; if (reload()) updateLastCommit(); commitBox.focus(); }); } function draw(opts) { winCommit = opts.aml; var vbox = winCommit.appendChild(new ui.vbox({ anchors: "0 0 0 0" })); // Toolbar var toolbar = vbox.appendChild(new ui.hbox({ id: "toolbar", class: "toolbar-top", align: "center", edge: "0 2 0 0", // padding: 3 // class: "fakehbox aligncenter debugger_buttons basic", // style: "white-space:nowrap !important;" style: "border-top:0" })); plugin.addElement(toolbar); ui.insertByIndex(toolbar, new ui.filler(), 150, plugin); ammendCb = ui.insertByIndex(toolbar, new ui.checkbox({ label: "amend", skin: "checkbox_black", margin: "-2px 0 0 0", onafterchange: function() { commitBox.ace.setValue(ammendCb.checked ? lastCommitMessage || "" : ""); } }), 200, plugin); vbox.appendChild(new ui.bar({ class: "form-bar", childNodes: [ commitBox = new apf.codebox({ "initial-message": "Message (Press " + (apf.isMac ? "Cmd-Enter" : "Ctrl-Enter") + " to commit)" }) ] })); container = vbox.appendChild(new ui.bar({ style: "padding: 10px 0 10px 0;overflow:auto;flex:1" })); commitBox.ace.setOption("minLines", 1); commitBox.ace.setOption("wrap", true); commitBox.ace.commands.addCommand({ bindKey: "Ctrl-Enter|Cmd-Enter", name: "commit", exec: function(editor) { commit(); } }); function onAllBlur(e) { if (!winCommit.visible || !plugin.autohide || isCommitting) return; var to = e.toElement; if (!to || apf.isChildOf(winCommit, to, true)) { return; } // TODO add better support for overlay panels setTimeout(function() { plugin.hide(); }, 10); } apf.addEventListener("movefocus", onAllBlur); /**** Main UI ****/ mnuCommit = new Menu({ items: [ new MenuItem({ caption: "Refresh", onclick: function() { reload(); } }), new Divider(), new MenuItem({ caption: "Push", onclick: function() { push(); } }), new MenuItem({ caption: "Pull", onclick: function() { pull(); } }), new Divider(), new MenuItem({ caption: "Reset Local Changes...", onclick: function() { resetHard(); } }), new MenuItem({ caption: "Stash Changes", onclick: function() { scm.stash(function() {}); } }, plugin), new MenuItem({ caption: "Apply Stashed Changes", onclick: function() { scm.stashPop(function() {}); } }, plugin), new Divider(), new MenuItem({ caption: "Rebase Against Master", onclick: function() { }, isAvailable: function() { // return collabWorkspace.isAdmin && sync.conflicts; } }), new MenuItem({ caption: "Merge Master Into This Branch", onclick: function() { mergeMaster(); }, isAvailable: function() { // return collabWorkspace.isAdmin && sync.conflicts; } }), new MenuItem({ caption: "Mark Conflicts As Resolved", onclick: function() { markResolved(); }, isAvailable: function() { // return collabWorkspace.isAdmin && sync.conflicts; } }), new Divider(), new MenuItem({ caption: "Show Log...", onclick: function() { openLog(); } }), new Divider(), new MenuItem({ caption: "Automatically Sync After Commit", type: "check", checked: "state/scm/@auto" }), ] }, plugin); btnScm = ui.insertByIndex(toolbar, new ui.splitbutton({ icon: "syncing.gif", class: "btn-scm-sync", skinset: "default", skin: "c9-menu-btn", submenu: mnuCommit.aml, onclick: function() { // if (btnMode == "commit") // dialogCommit.show(); // else if (btnMode == "sync") sync(); else if (btnMode == "conflict") { markResolved(); } else if (btnMode == "rebase") { } } }), 100, plugin); btnScm.$ext.className = btnScmClassName; // sync.on("conflicts", function(e){ // updateStatusTree(); // if (e.conflicts) // reportConflicts(e.conflicts); // }, plugin); // sync.on("errors", function(e){ // updateStatusTree(); // }, plugin); // sync.on("afterPush", function(e){ // updateStatusTree(false); // updateStatusMessage(); // }, plugin); // sync.on("afterFetch", function(e){ // updateStatusTree(false); // updateStatusMessage(); // }, plugin); // function setTheme(e) { // var isDark = e.theme.indexOf("dark") > -1; // mnuCommit.aml.setAttribute("class", isDark ? "dark" : ""); // if (detailsView) // detailsView.className = "c9menu synctooltip " + (isDark ? "dark" : ""); // if (isDark) { // btnScmClassName += " dark"; // btnScm.$ext.className += " dark"; // } // else { // btnScmClassName = btnScmClassName.replace(/ dark/g, ""); // btnScm.$ext.className = btnScm.$ext.className.replace(/ dark/g, ""); // } // } // layout.on("themeChange", setTheme, plugin); // setTheme({ theme: settings.get("user/general/@skin") }); // updateStatusTree(); var mnuContext = new Menu({ items: [ new MenuItem({ caption: "Stage", onclick: function() { tree.selectedNodes.forEach(stageFile); }, isAvailable: function() { var node = tree.selectedNode; return node && node.parent != staged; } }), new MenuItem({ caption: "Unstage", onclick: function() { var nodes = tree.selectedNodes; removeFromStaging(nodes); }, isAvailable: function() { var node = tree.selectedNode; return node && node.parent == staged; } }), new MenuItem({ caption: "Revert", onclick: function() { tree.selectedNodes.forEach(revertFile); }, isAvailable: function() { var node = tree.selectedNode; return node && (node.parent == staged || node.parent == changed); } }), new Divider(), new MenuItem({ caption: "Stage All", onclick: function() { scm.addAll(); }, isAvailable: function() { return changed.children.length; } }), new MenuItem({ caption: "Unstage All", onclick: function() { scm.unstageAll(); }, isAvailable: function() { return staged.children.length; } }), new Divider(), new MenuItem({ caption: "Show Changes", hotkey: "Enter", onclick: function() { showDiff(tree.selectedNode); }, isAvailable: function() { var node = tree.selectedNode; return node && (node.parent != ignored && node.parent != untracked); } }), new MenuItem({ caption: "Open File", hotkey: "Shift-Enter", onclick: function() { openSelectedFiles(); }, isAvailable: function() { return tree.selectedNode; } }) ]}, plugin); container.setAttribute("contextmenu", mnuContext.aml); tree = new Tree({ container: container.$int, scrollMargin: [0, 0], theme: "filetree scmtree", enableDragdrop: true, emptyMessage: "No changes", getIconHTML: function(node) { var icon = node.isFolder ? "folder" : "status-icon-" + node.type; if (node.parent == conflicts) icon = "status-icon-conflict"; if (node.status === "loading") icon = "loading"; if (tree.model.twoWay && !node.isFolder) icon += " clickable"; return " - " + dirname(path) + "" + (node.parent == staged ? "-" : (node.parent == conflicts ? "+" : "-" + "+")); } return escapeHTML(node.label || node.name); }, getClassName: function(node) { return (node.className || "") + (node.status == "loading" ? " loading" : "") + (node.type && ~node.type.indexOf("D") ? " deleted" : ""); }, getRowIndent: function(node) { return 0; //node.$depth ? node.$depth - 2 : 0; }, isLoading: function() {} }, plugin); tree.renderer.scrollBarV.$minWidth = 10; tree.commands.bindKey("Space", function(e) { if (tabManager.previewTab) tabManager.preview({ cancel: true }); else showDiff(tree.selectedNode, true); }); var openSelection = function(e) { if (showDiff(tree.selectedNode) === false) openSelectedFiles(); }; tree.commands.bindKey("Enter", openSelection); tree.on("afterChoose", openSelection); tree.commands.bindKey("Shift-Enter", function(e) { openSelectedFiles(); }); layout.on("eachTheme", function(e) { var height = parseInt(ui.getStyleRule(".filetree .tree-row", "height"), 10) || 22; tree.rowHeightInner = height; tree.rowHeight = height + 1; if (e.changed) tree.resize(); }, plugin); tree.on("userSelect", function(e) { if (tabManager.previewTab) showDiff(tree.selectedNode, true); }); // TODO: Immediate feedback tree.on("drop", function(e) { if (e.target && e.selectedNodes) { var nodes = e.selectedNodes; if (e.target == staged) addToStaging(nodes); else if (e.target == changed || e.target == untracked) removeFromStaging(nodes); } }); tree.on("click", function(e) { var classList = e.domEvent.target.classList; if (classList.contains("plus")) { stageFile(e.getNode()); } else if (classList.contains("min")) { removeFromStaging([e.getNode()]); } else if (classList.contains("revert")) { revertFile(e.getNode()); } }); tree.setRoot(arrayCache); // tree.on("focus", function(){ // test.focussedPanel = plugin; // }); // settings.on("read", function(){ // test.settingsMenu.append(new MenuItem({ // caption: "Collapse Passed and Skipped Groups", // checked: "user/test/@collapsegroups", // type: "check", // position: 300 // })); // }, plugin); // settings.on("user/test/@collapsegroups", function(value){ // if (plugin.visible) { // skipNode.isOpen = !value; // passNode.isOpen = !value; // tree.refresh(); // } // }, plugin); // scm.on("reload", function(options){ // reload(options || { hash: 0, force: true }, function(e, status) { // }); // }, plugin); // scm.on("resize", function(){ // tree && tree.resize(); // }); // scmlog.on("select", function(options){ // if (options) reload(options, function(){}); // }, plugin); // Context Menu // menuContext = new Menu({ items: [ // new MenuItem({ match: "file", class: "strong", caption: "Open Diff", onclick: openSelection }, plugin), // new MenuItem({ match: "file", caption: "Open", onclick: openSelectedFiles }, plugin), // new MenuItem({ match: "file", caption: "Reveal in File Tree", onclick: reveal }, plugin), // ]}); // opts.aml.setAttribute("contextmenu", menuContext.aml); // logTree = new Tree({ // container: parentHtml, // scrollMargin: [0, 0], // theme: "filetree synctree", // isLoading: function() {}, // getIconHTML: function(node) { // var icon; // if (node.parent == conflicts) // icon = "conflict"; // else if (node.parent == errors) // icon = "error"; // else return ""; // return ""; // }, // getCaptionHTML: function(node) { // if (node.label) // return escapeHTML(node.label); // if (node.fileName) { // var fileName = node.fileName; // return escapeHTML(basename(fileName)) // + " - " // + escapeHTML(dirname(fileName)) + ""; // } // }, // getRowIndent: function(node) { // return 0; // }, // },plugin); // // TODO generalize this // logTree.renderer.scrollBarV.$minWidth = 10; // logTree.commands.bindKey("Space", function(e) { // if (tabManager.previewTab) // tabManager.preview({ cancel: true }); // else // openSelectedFiles({ preview: true }); // }); // logTree.commands.bindKey("Enter", function(e) { // openSelectedFiles(); // }); // layout.on("eachTheme", function(e) { // var height = parseInt(ui.getStyleRule(".filetree .tree-row", "height"), 10) || 22; // logTree.rowHeightInner = height; // logTree.rowHeight = height + 1; // if (e.changed) // logTree.resize(); // }, plugin); // logTree.on("afterChoose", function(e) { // openSelectedFiles(); // }); // logTree.on("userSelect", function(e) { // if (tabManager.previewTab) // openSelectedFiles({ preview: true }); // showDetails(logTree.selectedNode); // }); tree.minLines = 2; tree.maxLines = Math.floor((window.innerHeight - 100) / tree.rowHeight); tree.emptyMessage = "Loading..."; // sync.on("log", function(e){ // updateStatusMessage(); // }, plugin); } /***** Helper Methods *****/ var changed = { label: "unstaged", className: "heading", children: [], isOpen: true, isFolder: true, noSelect: true, $sorted: true }; var staged = { label: "staged", className: "heading", children: [], isOpen: true, isFolder: true, noSelect: true, $sorted: true }; var ignored = { label: "ignored", className: "heading", children: [], isOpen: true, isFolder: true, map: {}, noSelect: true, $sorted: true }; var untracked = { label: "untracked", className: "heading", children: [], isOpen: true, isFolder: true, map: {}, noSelect: true, $sorted: true }; var conflicts = { label: "conflicts", className: "heading", children: [], isOpen: true, isFolder: true, noSelect: true, $sorted: true }; var queue; function updateStatus(status) { if (!tree) { if (!queue) plugin.once("draw", function() { updateStatus(queue); }); queue = status; return; } if (!status) { tree.setRoot(null); tree.emptyMessage = "No changes"; updateButton("sync"); return; } changed.children = status.changed || []; staged.children = status.staged || []; ignored.children = status.ignored || []; conflicts.children = status.conflicts || []; untracked.children = status.untracked || []; var root = { children: [staged, changed, untracked], $sorted: true, isFolder: true }; if (ignored.children.length) root.children.push(ignored); if (conflicts.children.length) root.children.unshift(conflicts); tree.setRoot(root); tree.meta.options = options; // if (dialogCommit.button) // dialogCommit.button.setCaption(staged.children.length // ? "Commit" // : "Add All and Commit"); updateButton(conflicts.children.length ? "conflict" : ((status.changed || 0).length || (status.staged || 0).length ? "sync" : "sync")); } function updateButton(type) { btnMode = type; if (!isSyncing) removeLoading(); } // TODO update UI somehow // - maybe big 3 dots from earlier version of salesforce sync button // + a small dropdown below the button stating what the new hash is var isSyncing; function sync() { if (isSyncing) return; if (settings.getBool("state/scm/@auto") || settings.getBool("user/scm/@dontask")) return _sync(); question.show("Synchronize Changes With Origin", "Are you sure you want to sync?", "Syncing will fetch and merge all remote changes of this branch " + "to your working copy and push all your changes to the remote " + "origin. Essentially this will execute a pull and then a push.", function() { // Yes if (question.dontAsk) settings.set("user/scm/@dontask", true); _sync(); }, function() { // No // Do Nothing }, { showDontAsk: true }); function _sync() { isSyncing = true; setLoading(); function done(err) { isSyncing = false; removeLoading(); if (err && (err.code == scm.errors.NOPUSHDESTINATION || err.code == scm.errors.NOREMOTEREPO)) { alert("Unable To Sync", "No origin specified.", "Please add a remote (origin) via the branches " + "panel in order to enable synchronization."); } if (err) return; // TODO } scm.pull(function(err) { if (err) return done(err); scm.push(function(err) { done(err); }); }); } } function push() { if (isSyncing) return; isSyncing = true; setLoading(); scm.push(function(err) { removeLoading(); isSyncing = false; if (err && err.code == scm.errors.NOPUSHDESTINATION) { alert("Unable To Push", "No origin specified.", "Please add a remote (origin) via the branches " + "panel in order to enable push."); } }); } function pull() { if (isSyncing) return; isSyncing = true; setLoading(); scm.pull(function(err) { removeLoading(); isSyncing = false; if (err && err.code == scm.errors.NOREMOTEREPO) { alert("Unable To Push", "No origin specified.", "Please add a remote (origin) via the branches " + "panel in order to enable push."); } }); } function mergeMaster() { if (isSyncing) return; isSyncing = true; setLoading(); scm.pull({ branch: "origin master" }, function(err) { removeLoading(); isSyncing = false; if (err && err.code == scm.errors.NOREMOTEREPO) { alert("Unable To Push", "No origin specified.", "Please add a remote (origin) via the branches " + "panel in order to enable push."); } }); } function resetHard() { setLoading(); scm.resetHard(function(err) { removeLoading(); if (err) return; // TODO }); } function markResolved() { if (isSyncing || !conflicts.children.length) return; confirm("Resolve Conflicts", "Would you like to resolve all conflicts?", "Click OK to resolve all conflicts", function() { isSyncing = true; setLoading(); async.each(conflicts.children, function(n, next) { scm.addFileToStaging(n.path, next); }, function(err) { removeLoading(); if (err) return; // TODO }); }); } function commit(message, amend, callback, force) { if (!message || typeof message == "function") { callback = message; message = commitBox.getValue(); amend = ammendCb.checked; } if (typeof amend == "function") callback = amend, amend = false; if (!callback) callback = function() {}; if (!message) return callback(new Error("Nothing To Do")); if (conflicts.children.length) { alert("Unresolved Conflicts", "There are unresolved conflicts.", "Please resolve the conflicts before committing."); return callback(new Error("Unresolved Conflicts")); } if (!staged.children.length && !changed.children.length) { alert("Nothing to do", "There is nothing to commit", "Please make some changes to commit and try again."); return callback(new Error("Nothing to do")); } isCommitting = true; ammendCb.disable(); commitBox.disable(); function done(err) { ammendCb.enable(); commitBox.enable(); isCommitting = false; if (err) return console.error(err); ammendCb.uncheck(); commitBox.setValue(""); callback && callback(); } if (!staged.children.length && !force) { scm.addFileToStaging(changed.children.map(function(n) { return n.path; }), function(err) { if (err) return done(err); commit(message, amend, callback, true); }); return; } scm.commit({ message: message, amend: amend }, function(err) { if (err) return done(err); if (settings.getBool("state/scm/@auto")) sync(); if (plugin.autohide) plugin.hide(); done(); }); } function openLog(callback) { var tabs = tabManager.getTabs(); var tab; if (tabs.some(function(t) { return (tab = t).editorType == "scmlog"; })) return tabManager.focusTab(tab); cnsl.show(); tabManager.open({ editorType: "scmlog", focus: true, pane: cnsl.getPanes()[0] }, function(err, tab) { callback && callback(err, tab); }); } function stageFile(node) { if (node.parent === conflicts && node.type.indexOf("D") === -1) { fs.readFile(node.path, function(err, data) { if (err || data.indexOf("<<<<<<<") === -1) return addToStaging([node]); confirm("Conflict Not Resolved", "The merge conflict is not yet resolved", "The file '" + node.path + "' still has an " + "unresolved merge conflict. Click OK to mark " + "the conflict as resolved.", function() { addToStaging([node]); }); }); return; } addToStaging([node]); } function addToStaging(nodes) { scm.addFileToStaging(nodes.map(function(n) { return n.path; }).filter(Boolean)); } function removeFromStaging(nodes) { scm.unstage(nodes.map(function(n) { return n.path; }).filter(Boolean)); } function revertFile(node) { var path = node.path; confirm("Revert File", "Are you sure you want to revert all changes in '" + path + "'", "Click OK to revert all changes or click Cancel to cancel this action.", function() { scm.revert(path, function(err) { if (err) { return alert("Could Not Revert Changes", "Received Error While Reverting Changes", err.message || err); } }); }, function() {}); } function reload(e) { if (!scm) { updateStatus(null); tree.emptyMessage = "No repository detected"; return false; } if (reloading) return; reloading = true; tree.emptyMessage = "Loading..."; setLoading(); scm.getStatus({ hash: 0, force: true, untracked: "all" }, function(err) { reloading = false; lastReloadT = Date.now(); removeLoading(); if (err && err.code == scm.errors.NOTAREPO) updateStatus(null); }); return true; } function isChanged(path) { if (changed.children.some(function(n) { if (n.path == path) return; })) return true; if (untracked.children.some(function(n) { if (n.path == path) return; })) return true; if (conflicts.children.some(function(n) { if (n.path == path) return; })) return true; if (ignored.children.some(function(n) { if (n.path == path) return; })) return true; return false; } function updateLastCommit() { scm.getLastLogMessage(function(err, message) { lastCommitMessage = err ? "" : message; }); } function setLoading() { setSyncStatus("syncing" + (conflicts.children.length ? " conflict" : "")); } function removeLoading() { setSyncStatus((conflicts.children.length ? "conflict" : "")); } function showDiff(node, preview) { if (node.parent == ignored || node.parent == untracked) return false; scmProvider.openDiff({ path: node.path, preview: preview }); } // function trimLongStatus(output) { // var lines = output.split("\n"); // if (lines.length < 20) // return output; // return lines.slice(0, 19).join("\n") // + "\n[...truncated, use s9 status in your terminal to see the full output...]"; // } // function setConflicts() { // if (!logTree) return sync.conflicts ? true : false; // conflicts.children.length = 0; // if (sync.conflicts) { // sync.conflicts.forEach(function(x) { // conflicts.children.push(x); // }); // return true; // } // return false; // } // function setErrors() { // if (!logTree) return sync.errors ? true : false; // errors.children.length = 0; // if (sync.errors) { // if (sync.errors.details) // sync.errors.details.forEach(function(e, i) { // errors.children.push(e); // }); // return true; // } // return false; // } // function updateTree() { // if (!logTree) return; // logTree.model.setRoot([ // conflicts, // errors // ].filter(function(x) { // return x.children.length; // })); // var bar = logTree.container.parentNode.host; // if (logTree.model.root.children.length) { // bar.show(); // bar.previousSibling.show(); // } // else { // bar.hide(); // bar.previousSibling.hide(); // } // // html.nextSibling.style.display = // // html.style.display = logTree.model.root.children.length // // ? "block" : "none"; // } // var detailsView; // function showDetails(item) { // if (!item || !item.message) // return detailsView && detailsView.remove(); // var i = logTree.model.getIndexForNode(item); // var domNode = logTree.renderer.$cellLayer.getDomNodeAtIndex(i); // if (!domNode) return; // var popup = mnuCommit.aml.$ext; // if (!detailsView) { // detailsView = document.createElement("div"); // detailsView.className = "c9menu synctooltip " // + (~settings.get("user/general/@skin").indexOf("dark") ? "dark" : ""); // detailsView.style.right; // detailsView.style.zIndex = mnuCommit.aml.$ext.style.zIndex; // } // if (!detailsView.parentNode) // document.body.appendChild(detailsView); // detailsView.style.display = "block"; // detailsView.textContent = item.message; // var rectRow = domNode.getBoundingClientRect(); // var rectPopup = popup.getBoundingClientRect(); // // detailsView.style.bottom = popup.style.bottom; // detailsView.style.maxWidth = "320px"; // detailsView.style.top = (rectRow.top // - ((detailsView.offsetHeight - rectRow.height) / 2)) + "px"; // if (window.innerWidth - rectPopup.right < 320) { // detailsView.style.right = (window.innerWidth - rectPopup.left + 10) + "px"; // detailsView.style.left = ""; // } else { // detailsView.style.left = (rectPopup.right + 10) + "px"; // detailsView.style.right = ""; // } // } // function updateStatusTree(isSyncing){ // var state = []; // if (setErrors()) // state.push("error"); // if (setConflicts()) // state.push("conflict"); // if (isSyncing === undefined ? sync.getState() === sync.STATE_SYNCING : isSyncing) // state.push("syncing"); // setSyncStatus(state.join(" ")); // if (logTree) { // updateTree(); // logTree.resize(true); // var toWidth = logTree.renderer.$cellLayer.element.scrollWidth + 2; // mnuCommit.minWidth = toWidth > 100 ? toWidth : ""; // } // } function openSelectedFiles(opts) { // if (!c9.has(c9.STORAGE)) // return; var focus = true; var sel = tree.selectedNodes; var main = tree.selectedNode; sel.forEach(function(node) { if (!node || node.isFolder) return; var pane = tabManager.focussedTab && tabManager.focussedTab.pane; if (tabManager.getPanes(tabManager.container).indexOf(pane) == -1) pane = null; var options = { path: "/" + node.path, pane: pane, noanim: sel.length > 1, active: node === main, focus: node === main && focus }; tabManager.open(options, function() {}); }); } // /***** Methods *****/ // function init(version){ // sync.init(version, function(err, forceSync) { // if (err) { // showInitError(err); // return; // } // fs.on("afterWriteFile", onFsChange, plugin); // fs.on("afterUnlink", onFsChange, plugin); // fs.on("afterRmfile", onFsChange, plugin); // fs.on("afterRmdir", onFsChange, plugin); // if (settings.getBool("state/scm/@auto")) // syncInterval = setInterval(syncNow, // settings.getNumber("state/scm/@interval") * 1000); // save.on("afterSave", onFsChange, plugin); // save.CAPTION_SAVED = ""; // function onFsChange(e) { // if (!settings.getBool("state/scm/@auto")) // return; // var ext = extname(e.path); // if (metadata.extensions[ext && ext.substr(1)] // TODO: move this check to metadata.js // || metadata.folders[e.path]) { // syncNow(SYNC_TIMEOUT); // } // } // // Draw the toolbar button // draw(); // // Sync // if (forceSync || settings.getBool("state/scm/@auto")) // syncNow(); // }); // } // function showInitError(err){ // showAlert( // "Salesforce", // "Your workspace could not be configured to synchronize with Salesforce.", // '
"; }, getCaptionHTML: function(node) { if (node.path) { var path = node.labelPath || node.path; return basename(path) + "' + trimLongStatus(escapeHTML(err.message)) + "", // function() { // }, // { isHTML: true } // ); // } // var syncTimeout; // function syncNow(timeout, callback){ // if (!collabWorkspace.isAdmin || sync.isSyncing) { // // Make sure button reflects that we are fetching // if (sync.isSyncing === 2) updateStatusTree(true); // // Schedule new sync // plugin.once("afterSync", syncNow.bind(this, arguments)); // return false; // } // if (typeof timeout == "function") // callback = timeout, timeout = null; // clearTimeout(syncTimeout); // if (timeout) { // syncTimeout = setTimeout(syncNow.bind(this, callback), timeout); // return true; // } // updateStatusTree(true); // sync.sync(function(err){ // if (err && err.code !== sync.ERROR_NO_CHANGES) { // if (err.code === sync.ERROR_INVALID_MANIFEST) { // var list = []; // err.message.replace(RE_INVALID_MANIFEST, function(m, type, name){ // list.push({ type: type, name: name }); // }); // if (list.length) // handleInvalidManifest(list); // else if (RE_INVALID_VERSION.test(err.message)) // handleInvalidVersion(); // } // else if (err.code === sync.ERROR_MERGE_CONFLICT) { // // Nothing special needed // } // else if (err.code === sync.ERROR_NETWORK) { // // It already retried 3 times. Ignore // } // else if (err.code === sync.ERROR_RESOLVE_FIRST) { // // Do Nothing // } // else if (err.salesforce) { // showSFErrorAlert(err); // } // else { // // TODO: perhaps display the error message in the menu status // var errors = sync.errors || {}; // errors.message = err.message; // sync.setSyncState("errors", sync.errors); // } // } // else if (sync.errors) { // sync.setSyncState("errors", null); // } // updateStatusMessage(); // callback && callback(err); // emit("afterSync"); // }, timeout); // return true; // } // function abortSync(callback){ // sync.abort(function(err){ // updateStatusTree(sync.isSyncing); // callback && callback(err); // }); // } // function resetHard(callback) { // sync.getOurStatus(function(err, status){ // if (err) console.error(err); // showConfirm( // "Reset Local Changes", // "Are you sure you would like to reset your local changes?", // "This will remove all local changes not uploaded to Salesforce yet:
' + trimLongStatus(escapeHTML(status || "") || "") + '', // function() { // sync.resetHard(function(err){ // if (err) // return showAlert("Reset Local Changes", "Error resetting local changes", err.message); // showAlert("Reset Local Changes", "Successfully reset your local changes"); // syncNow(); // }); // }, // function() { // }, // { isHTML: true } // ); // }); // } // function markConflictsResolved() { // async.waterfall([ // function(next) { // sync.getUnresolvedConflicts(next); // }, // function(unresolved, next) { // if (unresolved.length) { // return showQuestion( // "Mark Conflicts Resolved", // "The following files still appear to have unresolved conflicts. Would you like to abort and review these files first?", // '
' + trimLongStatus(escapeHTML(unresolved.join("\n"))) + '', // function() { // unresolved.forEach(function(file) { // tabManager.openFile("/" + file, true); // }); // done(); // }, // function(){ // next(); // }, // { isHTML: true } // ); // } // next(); // }, // function(next) { // sync.getConflicts(next); // }, // function(conflicts, next) { // if (!conflicts.length) { // showAlert("Mark Conflicts Resolved", // "No conflicts found.", // "Congrats! Your workspace currently has no merge conflicts with Salesforce."); // // Let's be paranoid and mark everything as resolved anyway // return sync.resolveConflicts(function(err){ // if (err) return done(err); // syncNow(); // done(); // }); // } // return showConfirm( // "Mark Conflicts Resolved", // "This will mark conflicts in the following files as resolved.", // '
' + trimLongStatus(escapeHTML(conflicts)) + '', // function confirm() { // next(); // }, // function(){ // done(); // }, // { isHTML: true } // ); // }, // function(next) { // sync.resolveConflicts(function(err){ // if (err) return next(err); // conflictsToManuallyResolve = []; // showAlert("Mark Conflicts Resolved", "", "Conflicts marked resolved."); // syncNow(); // }); // } // ], done); // function done(err) { // if (err) { // return showAlert( // "Mark Conflicts Resolved", // "Could not resolve conflicts because of an internal error. Please try again.", // '
' + trimLongStatus(escapeHTML(err.message)) + '', // function() {}, // { isHTML: true } // ); // } // } // } // function reportConflicts(conflicts) { // updateStatusTree(); // if (conflictsToManuallyResolve && conflictsToManuallyResolve.length) // return; // if (resolvingConflicts) // return; // resolvingConflicts = true; // conflicts = conflicts.map(function (conflict) { return conflict.fileName; }); // async.mapSeries(conflicts, showMergeConflictDialog, function(err, result) { // resolvingConflicts = false; // if (err) { // return showAlert( // "Error resolving conflicts", // "An error occured while resolving sync conflicts. Please manually check the following files:", // '
' + trimLongStatus(escapeHTML(conflicts.join("\n"))) + '' // ); // } // conflictsToManuallyResolve = result // .map(function(r) { return r && r.resolveManually; }) // .filter(function (t) { return !!t; }); // if (conflictsToManuallyResolve.length) { // showAlert( // "Manually Resolving Conflicts", // "You've chosen to manually resolve the conflict(s) below.", // '
' + trimLongStatus(escapeHTML(conflictsToManuallyResolve.join("\n"))) + '' // + '
Please use the Sync button dropdown and pick Mark Conflicts Resolved once you have resolved the conflict(s).', // function() { // settings.setJson("state/scm/@conflicts", conflictsToManuallyResolve); // conflictsToManuallyResolve.forEach(function (f) { // tabManager.openFile("/" + f, true); // }); // }, // { isHTML: true } // ); // return; // } // sync.resolveConflicts(function(err) { // if (err) { // return showAlert( // "Mark Conflicts Resolved", // "Could not finish resolving conflicts because of an internal error. Please use Salesforce > Mark Conflicts Resolved after fixing the error below.", // '
' + trimLongStatus(escapeHTML(err.message)) + '', // function() {}, // { isHTML: true } // ); // } // showAlert("Mark Conflicts Resolved", "", "All conflicts resolved"); // }); // }); // } // function showMergeConflictDialog(file, callback) { // showFileChange( // "Unresolved Sync Conflict", // "The file " + file + " was changed remotely.", // "Another user has changed this file since it was last synchronized. What would you like to do?", // function useOurs() { // sync.resolveConflict(file, true, function(err) { // if (err) return errorResolvingSyncConflict(err, file, callback); // callback(); // }); // }, // function useTheirs() { // sync.resolveConflict(file, false, function(err) { // if (err) return errorResolvingSyncConflict(err, file, callback); // callback(); // }); // }, // function resolveManually() { // callback(null, {resolveManually: file}); // }, // { all: false, merge: { caption: "Merge Manually" } } // ); // } // function errorResolvingSyncConflict(err, file, callback) { // showConfirm( // "Error resolving conflict", // "An error occured resolving sync conflicts in the file: " + file + ". The error is below. Would you like to try again?", // err && err.message, // function tryAgain () { // return showMergeConflictDialog(file, callback); // }, // function cancel() { // return callback(new Error("Failed to resolve sync conflict")); // } // ); // } function setSyncStatus(type) { if (!btnScm) return; var cls; if (!type) cls = btnScmClassName; else cls = btnScmClassName + " " + type; btnScm.$ext.className = cls + (isSyncing ? " syncing" : ""); } /***** Lifecycle *****/ plugin.on("load", function() { load(); }); plugin.on("draw", function(options) { draw(options); }); plugin.on("resize", function(options) { tree && tree.resize(); }); plugin.on("show", function(options) { tree && setTimeout(tree.resize); }); plugin.on("unload", function() { clearTimeout(syncTimeout); isSyncing = null; isCommitting = false; syncTimeout = null; drawn = false; logTree = null; title = null; btnScm = null; conflicts = null; conflictsToManuallyResolve = null; btnScmClassName = "splitbutton btn-sync"; resolvingConflicts = false; errors = null; detailsView = null; mnuCommit = null; clearInterval(syncInterval); }); /***** Register and define API *****/ /** */ plugin.freezePublicAPI({ get tree() { return tree; }, /** * */ // syncNow: syncNow }); register(null, { "scm.commit": plugin }); } });