c9-core/plugins/c9.ide.scm/scm.commit.js

1707 wiersze
64 KiB
JavaScript

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 "<span class='status-icon " + icon + "'>"
+ (node.type || "") + "</span>";
},
getCaptionHTML: function(node) {
if (node.path) {
var path = node.labelPath || node.path;
return basename(path)
+ "<span class='extrainfo'> - "
+ dirname(path) + "</span>"
+ (node.parent == staged
? "<span class='min'>-</span>"
: (node.parent == conflicts
? "<span class='plus'>+</span>"
: "<span class='revert'>-</span>"
+ "<span class='plus'>+</span>"));
}
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 "<span class='sync-icon " + icon + "'></span>";
// },
// getCaptionHTML: function(node) {
// if (node.label)
// return escapeHTML(node.label);
// if (node.fileName) {
// var fileName = node.fileName;
// return escapeHTML(basename(fileName))
// + "<span class='extrainfo'> - "
// + escapeHTML(dirname(fileName)) + "</span>";
// }
// },
// 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.",
// '<pre class="sfdc-alert-output">' + trimLongStatus(escapeHTML(err.message)) + "</pre>",
// 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:<br>"
// + '<pre class="sfdc-alert-output">' + trimLongStatus(escapeHTML(status || "") || "") + '</pre>',
// 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?",
// '<pre class="sfdc-alert-output">' + trimLongStatus(escapeHTML(unresolved.join("\n"))) + '</pre>',
// 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.",
// '<pre class="sfdc-alert-output">' + trimLongStatus(escapeHTML(conflicts)) + '</pre>',
// 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.",
// '<pre class="sfdc-alert-output">' + trimLongStatus(escapeHTML(err.message)) + '</pre>',
// 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:",
// '<pre class="sfdc-alert-output">' + trimLongStatus(escapeHTML(conflicts.join("\n"))) + '</pre>'
// );
// }
// 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.",
// '<pre class="sfdc-alert-output">' + trimLongStatus(escapeHTML(conflictsToManuallyResolve.join("\n"))) + '</pre>'
// + '<p>Please use the Sync button dropdown and pick <b>Mark Conflicts Resolved</b> 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 <b>Salesforce > Mark Conflicts Resolved</b> after fixing the error below.",
// '<pre class="sfdc-alert-output">' + trimLongStatus(escapeHTML(err.message)) + '</pre>',
// 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
});
}
});