kopia lustrzana https://github.com/c9/core
523 wiersze
19 KiB
JavaScript
523 wiersze
19 KiB
JavaScript
define(function(require, exports, module) {
|
|
main.consumes = [
|
|
"Plugin", "ui", "tabManager", "ace", "language",
|
|
"menus", "commands", "c9", "tabManager",
|
|
"settings", "language.jumptodef"
|
|
];
|
|
main.provides = ["language.quickfix"];
|
|
return main;
|
|
|
|
function main(options, imports, register) {
|
|
var Plugin = imports.Plugin;
|
|
var tabs = imports.tabManager;
|
|
var language = imports.language;
|
|
var commands = imports.commands;
|
|
var jumptodef = imports["language.jumptodef"];
|
|
var plugin = new Plugin("Ajax.org", main.consumes);
|
|
|
|
var CRASHED_JOB_TIMEOUT = 30000;
|
|
|
|
var worker;
|
|
|
|
var loaded;
|
|
function load() {
|
|
if (loaded) return;
|
|
loaded = true;
|
|
|
|
commands.on("update", function() {
|
|
var key = commands.getPrettyHotkey("quickfix");
|
|
|
|
language.getWorker(function(err, result) {
|
|
if (err) return console.error(err);
|
|
worker = result;
|
|
|
|
worker.emit("quickfix_key", { data: key });
|
|
});
|
|
});
|
|
|
|
commands.addCommand({
|
|
name: "quickfix",
|
|
hint: "quickfix",
|
|
bindKey: { mac: "Command-F3", win: "Ctrl-F3" },
|
|
exec: function(editor) {
|
|
invoke();
|
|
}
|
|
}, plugin);
|
|
|
|
language.getWorker(function(err, result) {
|
|
if (err) console.error(err);
|
|
worker = result;
|
|
|
|
worker.on("quickfixes_result", onResult);
|
|
});
|
|
|
|
/*
|
|
menus.addItemByPath("Tools/Quickfix", new apf.item({
|
|
caption: "Quickfix",
|
|
command: "quickfix"
|
|
}), 20001, plugin)
|
|
*/
|
|
}
|
|
|
|
function invoke() {
|
|
if (!tabs.focussedTab || !tabs.focussedTab.editor || !tabs.focussedTab.editor.ace)
|
|
return;
|
|
|
|
var tab = tabs.focussedTab;
|
|
var ace = tab.editor.ace;
|
|
var sel = ace.getSelection();
|
|
var pos = sel.getCursor();
|
|
|
|
activateSpinner(tabs.focussedTab);
|
|
|
|
worker.emit("quickfixes", {
|
|
data: pos
|
|
});
|
|
}
|
|
|
|
function onResult(e) {
|
|
var tab = tabs.findTab(e.data.path);
|
|
if (!tab || tabs.focussedTab !== tab)
|
|
return;
|
|
|
|
clearSpinners(tab);
|
|
var results = e.data.results;
|
|
|
|
if (!results.length)
|
|
return;
|
|
|
|
// HACK: don't show UI for now, assume there's only 1 result
|
|
if (results[0].deltas.length > 1 && results[0].delta.some(function(d) { return d.path; }))
|
|
throw new Error("Multiple deltas with paths not allowed");
|
|
|
|
applyQuickfix(e.data.path, results[0]);
|
|
}
|
|
|
|
function activateSpinner(tab) {
|
|
tab.classList.add("loading");
|
|
clearTimeout(tab.$quickfixReset);
|
|
tab.$quickfixReset = setTimeout(function() {
|
|
clearSpinners(tab);
|
|
}, CRASHED_JOB_TIMEOUT);
|
|
}
|
|
|
|
function clearSpinners(tab) {
|
|
clearTimeout(tab.$quickfixReset);
|
|
tab.classList.remove("loading");
|
|
}
|
|
|
|
function applyQuickfix(sourcePath, fix) {
|
|
var tab = fix.deltas[0].path
|
|
? tabs.findTab(fix.deltas[0].path)
|
|
: tabs.focussedTab;
|
|
if (tab !== tabs.focussedTab) {
|
|
var sourcePos = tabs.focussedTab.editor.ace.getCursorPosition();
|
|
return jumptodef.jumpToPos(fix.deltas[0].path, fix.pos, sourcePath, sourcePos, function(err) {
|
|
if (err) return console.error(err);
|
|
applyQuickfix(sourcePath, fix);
|
|
});
|
|
}
|
|
|
|
var ace = tab.editor.ace;
|
|
var doc = ace.getSession().getDocument();
|
|
|
|
doc.applyDeltas(fix.deltas);
|
|
|
|
if (fix.pos) {
|
|
var pos = fix.pos;
|
|
var selection = ace.getSelection();
|
|
selection.clearSelection();
|
|
selection.moveCursorTo(pos.row, pos.column, false);
|
|
}
|
|
}
|
|
|
|
/*
|
|
var CLASS_SELECTED = "cc_complete_option selected";
|
|
var CLASS_UNSELECTED = "cc_complete_option";
|
|
var SHOW_DOC_DELAY = 1500;
|
|
var SHOW_DOC_DELAY_MOUSE_OVER = 100;
|
|
var HIDE_DOC_DELAY = 1000;
|
|
var MENU_WIDTH = 400;
|
|
var MENU_SHOWN_ITEMS = 9;
|
|
var EXTRA_LINE_HEIGHT = 3;
|
|
var QFBOX_MINTIME = 500;
|
|
|
|
var ignoreMouseOnce = false;
|
|
var isDocShown;
|
|
var isDrawDocInvokeScheduled = false;
|
|
var quickfixElement;
|
|
var editor;
|
|
var selectedIdx;
|
|
var scrollIdx;
|
|
var quickfixEls;
|
|
var docElement;
|
|
var cursorConfig;
|
|
var lineHeight;
|
|
var quickFixes;
|
|
var popupTime;
|
|
var oldCommandKey;
|
|
var oldOnTextInput;
|
|
|
|
function isPopupVisible() {
|
|
return barQuickfixCont.$ext.style.display !== "none";
|
|
}
|
|
|
|
var drawDocInvoke = lang.deferredCall(function() {
|
|
if (isPopupVisible() && (quickFixes[selectedIdx].preview
|
|
|| quickFixes[selectedIdx].previewHtml)) {
|
|
isDocShown = true;
|
|
txtQuickfixDoc.parentNode.show();
|
|
}
|
|
isDrawDocInvokeScheduled = false;
|
|
});
|
|
|
|
var undrawDocInvoke = lang.deferredCall(function() {
|
|
if (!isPopupVisible()) {
|
|
isDocShown = false;
|
|
txtQuickfixDoc.parentNode.hide();
|
|
}
|
|
});
|
|
|
|
function initEditor(editor) {
|
|
|
|
editor.on("guttermousedown", editor.$markerListener = function(e) {
|
|
editor = editor;
|
|
if (!e.getButton())
|
|
return;
|
|
apf.addListener(mnuCtxEditor, "prop.visible", hideContext);
|
|
function hideContext(ev) {
|
|
// only fire when visibility is set to true
|
|
if (ev.value) {
|
|
apf.removeListener(mnuCtxEditor, "prop.visible", hideContext);
|
|
mnuCtxEditor.hide();
|
|
}
|
|
}
|
|
var gutterRegion = editor.renderer.$gutterLayer.getRegion(e);
|
|
if (gutterRegion != "markers")
|
|
return;
|
|
|
|
var row = e.getDocumentPosition().row;
|
|
showQuickfixBox(row, 0);
|
|
|
|
});
|
|
}
|
|
|
|
function getAnnos(row) {
|
|
var editor = editors.currentEditor.amlEditor.$editor;
|
|
var res = [];
|
|
|
|
editor.getSession().languageAnnos.forEach(function(anno, idx) {
|
|
if (anno.row == row) {
|
|
res.push(anno);
|
|
|
|
// Select the annotation in the editor
|
|
anno.select = function() {
|
|
if (!(anno.pos.sl && anno.pos.sc && anno.pos.el && anno.pos.ec)) {
|
|
return;
|
|
}
|
|
var startPos = { row: anno.pos.sl, column: anno.pos.sc };
|
|
var endPos = { row: anno.pos.el, column: anno.pos.ec };
|
|
if (startPos.row < endPos.row || startPos.column < endPos.column) {
|
|
editor.getSelection().setSelectionRange(
|
|
{start: startPos, end: endPos});
|
|
}
|
|
};
|
|
|
|
// Returns the screen coordinates of the start of the annotation
|
|
anno.getScreenCoordinates = function() {
|
|
return editor.renderer.textToScreenCoordinates(anno.pos.sl,
|
|
anno.pos.sc);
|
|
};
|
|
}
|
|
});
|
|
|
|
res.sort(function(a,b) { return a.pos.sc - b.pos.sc; });
|
|
|
|
return res;
|
|
}
|
|
|
|
function showQuickfixBox(row, column) {
|
|
// Get the annotation on this line that is containing or left of the
|
|
// position (row,column)
|
|
var annos = getAnnos(row);
|
|
if (!annos.length) {
|
|
return;
|
|
}
|
|
for (var i = 0; i < annos.length - 1; i++) {
|
|
if (annos[i+1].pos.sc > column) { break; }
|
|
}
|
|
var anno = annos[i];
|
|
if (!anno.resolutions.length) {
|
|
// TODO If some other annotation on this line has resolutions,
|
|
// quickfix that one instead
|
|
return;
|
|
}
|
|
|
|
editor = editors.currentEditor;
|
|
var ace = editor.amlEditor.$editor;
|
|
selectedIdx = 0;
|
|
scrollIdx = 0;
|
|
quickfixEls = [];
|
|
// annos = annos;
|
|
quickFixes = [];
|
|
quickfixElement = txtQuickfix.$ext;
|
|
docElement = txtQuickfixDoc.$ext;
|
|
cursorConfig = ace.renderer.$cursorLayer.config;
|
|
lineHeight = cursorConfig.lineHeight + EXTRA_LINE_HEIGHT;
|
|
var style = getComputedStyle(editor.amlEditor.$ext);
|
|
quickfixElement.style.fontSize = style.fontSize;
|
|
|
|
barQuickfixCont.setAttribute('visible', true);
|
|
|
|
|
|
// Monkey patch
|
|
if (!oldCommandKey) {
|
|
oldCommandKey = ace.keyBinding.onCommandKey;
|
|
ace.keyBinding.onCommandKey = onKeyPress.bind(this);
|
|
oldOnTextInput = ace.keyBinding.onTextInput;
|
|
ace.keyBinding.onTextInput = onTextInput.bind(this);
|
|
}
|
|
|
|
// Collect all quickfixes for the given annotation
|
|
quickFixes = anno.resolutions;
|
|
|
|
// Select it in the editor
|
|
anno.select();
|
|
|
|
populateQuickfixBox(quickFixes);
|
|
|
|
apf.popup.setContent("quickfixBox", barQuickfixCont.$ext);
|
|
var boxLength = quickFixes.length || 1;
|
|
var quickfixBoxHeight = 11 + Math.min(10 * lineHeight, boxLength * (lineHeight));
|
|
|
|
var innerBoxLength = quickFixes.length || 1;
|
|
var innerQuickfixBoxHeight = Math.min(10 * lineHeight, innerBoxLength * (lineHeight));
|
|
txtQuickfixHolder.$ext.style.height = innerQuickfixBoxHeight + "px";
|
|
|
|
ignoreMouseOnce = !isPopupVisible();
|
|
|
|
var pos = anno.getScreenCoordinates();
|
|
apf.popup.show("quickfixBox", {
|
|
x: pos.pageX,
|
|
y: pos.pageY + cursorConfig.lineHeight,
|
|
height: quickfixBoxHeight,
|
|
width: MENU_WIDTH,
|
|
animate: false,
|
|
callback: function() {
|
|
barQuickfixCont.setHeight(quickfixBoxHeight);
|
|
barQuickfixCont.$ext.style.height = quickfixBoxHeight + "px";
|
|
sbQuickfix.$resize();
|
|
// HACK: Need to set with non-falsy value first
|
|
quickfixElement.scrollTop = 1;
|
|
quickfixElement.scrollTop = 0;
|
|
}
|
|
});
|
|
|
|
popupTime = new Date().getTime();
|
|
document.addEventListener("click", closeQuickfixBox, false);
|
|
ace.container.addEventListener("DOMMouseScroll", closeQuickfixBox, false);
|
|
ace.container.addEventListener("mousewheel", closeQuickfixBox, false);
|
|
}
|
|
|
|
function closeQuickfixBox(event) {
|
|
var qfBoxTime = new Date().getTime() - popupTime;
|
|
if (!forceClose && qfBoxTime < QFBOX_MINTIME) {
|
|
return;
|
|
}
|
|
|
|
forceClose = false;
|
|
|
|
barQuickfixCont.$ext.style.display = "none";
|
|
if (!editors.currentEditor.amlEditor) // no editor, try again later
|
|
return;
|
|
var ace = editors.currentEditor.amlEditor.$editor;
|
|
|
|
// TODO these calls don't work.
|
|
document.removeEventListener("click", closeQuickfixBox, false);
|
|
ace.container.removeEventListener("DOMMouseScroll", closeQuickfixBox, false);
|
|
ace.container.removeEventListener("mousewheel", closeQuickfixBox, false);
|
|
|
|
if (oldCommandKey) {
|
|
ace.keyBinding.onCommandKey = oldCommandKey;
|
|
ace.keyBinding.onTextInput = oldOnTextInput;
|
|
}
|
|
oldCommandKey = oldOnTextInput = null;
|
|
undrawDocInvoke.schedule(HIDE_DOC_DELAY);
|
|
}
|
|
|
|
function populateQuickfixBox(quickFixes) {
|
|
|
|
quickfixElement.innerHTML = "";
|
|
var cursorConfig = code.amlEditor.$editor.renderer.$cursorLayer.config;
|
|
|
|
// For each quickfix, create a list entry
|
|
quickFixes.forEach(function(qfix, qfidx) {
|
|
|
|
var annoEl = dom.createElement("div");
|
|
annoEl.className = qfidx === selectedIdx ? CLASS_SELECTED : CLASS_UNSELECTED;
|
|
var html = "";
|
|
|
|
if (qfix.image)
|
|
html = "<img src='" + ide.staticPrefix + qfix.image + "'/>";
|
|
|
|
html += '<span class="main">' + (qfix.messageHtml || escapeHtml(qfix.message)) + '</span>';
|
|
|
|
annoEl.innerHTML = html;
|
|
|
|
annoEl.addEventListener("mouseover", function() {
|
|
if (ignoreMouseOnce) {
|
|
ignoreMouseOnce = false;
|
|
return;
|
|
}
|
|
quickfixEls[selectedIdx].className = CLASS_UNSELECTED;
|
|
selectedIdx = qfidx;
|
|
quickfixEls[selectedIdx].className = CLASS_SELECTED;
|
|
updateDoc();
|
|
if (!isDrawDocInvokeScheduled)
|
|
drawDocInvoke.schedule(SHOW_DOC_DELAY_MOUSE_OVER);
|
|
});
|
|
|
|
|
|
annoEl.addEventListener("click", function() {
|
|
forceClose = true;
|
|
applyQuickfix(qfix);
|
|
});
|
|
|
|
|
|
annoEl.style.height = cursorConfig.lineHeight + EXTRA_LINE_HEIGHT + "px";
|
|
annoEl.style.width = (MENU_WIDTH - 10) + "px";
|
|
quickfixElement.appendChild(annoEl);
|
|
quickfixEls.push(annoEl);
|
|
});
|
|
|
|
updateDoc(true);
|
|
|
|
}
|
|
|
|
function updateDoc(delayPopup) {
|
|
docElement.innerHTML = '<span class="code_complete_doc_body">';
|
|
var selected = quickFixes[selectedIdx];
|
|
|
|
if (selected && (selected.preview || selected.previewHtml)) {
|
|
if (isDocShown) {
|
|
txtQuickfixDoc.parentNode.show();
|
|
}
|
|
else {
|
|
txtQuickfixDoc.parentNode.hide();
|
|
if (!isDrawDocInvokeScheduled || delayPopup)
|
|
drawDocInvoke.schedule(SHOW_DOC_DELAY);
|
|
}
|
|
docElement.innerHTML +=
|
|
selected.previewHtml
|
|
|| escapeHtml(selected.preview).replace(/\n/g, '<br/>');
|
|
docElement.innerHTML += '</span>';
|
|
}
|
|
else {
|
|
txtQuickfixDoc.parentNode.hide();
|
|
}
|
|
|
|
docElement.innerHTML += '</span>';
|
|
}
|
|
|
|
function onTextInput(text, pasted) {
|
|
closeQuickfixBox();
|
|
}
|
|
|
|
function onKeyPress(e, hashKey, keyCode) {
|
|
|
|
if (e.metaKey || e.ctrlKey || e.altKey) {
|
|
closeQuickfixBox();
|
|
return;
|
|
}
|
|
|
|
var keyBinding = editors.currentEditor.amlEditor.$editor.keyBinding;
|
|
|
|
switch (keyCode) {
|
|
case 0: break;
|
|
case 32: // Space
|
|
closeQuickfixBox();
|
|
break;
|
|
case 27: // Esc
|
|
closeQuickfixBox();
|
|
e.preventDefault();
|
|
break;
|
|
case 8: // Backspace
|
|
closeQuickfixBox();
|
|
e.preventDefault();
|
|
break;
|
|
case 37:
|
|
case 39:
|
|
oldCommandKey.apply(keyBinding, arguments);
|
|
closeQuickfixBox();
|
|
e.preventDefault();
|
|
break;
|
|
case 13: // Enter
|
|
case 9: // Tab
|
|
applyQuickfix(quickFixes[selectedIdx]);
|
|
forceClose = true;
|
|
closeQuickfixBox();
|
|
e.stopPropagation();
|
|
e.preventDefault();
|
|
break;
|
|
case 40: // Down
|
|
if (quickfixEls.length === 1) {
|
|
closeQuickfixBox();
|
|
break;
|
|
}
|
|
e.stopPropagation();
|
|
e.preventDefault();
|
|
quickfixEls[selectedIdx].className = CLASS_UNSELECTED;
|
|
if (selectedIdx < quickFixes.length-1)
|
|
selectedIdx++;
|
|
quickfixEls[selectedIdx].className = CLASS_SELECTED;
|
|
if (selectedIdx - scrollIdx > MENU_SHOWN_ITEMS) {
|
|
scrollIdx++;
|
|
quickfixEls[scrollIdx].scrollIntoView(true);
|
|
}
|
|
updateDoc();
|
|
break;
|
|
case 38: // Up
|
|
if (quickfixEls.length === 1) {
|
|
closeQuickfixBox();
|
|
break;
|
|
}
|
|
e.stopPropagation();
|
|
e.preventDefault();
|
|
if (selectedIdx <= 0)
|
|
return;
|
|
quickfixEls[selectedIdx].className = CLASS_UNSELECTED;
|
|
selectedIdx--;
|
|
quickfixEls[selectedIdx].className = CLASS_SELECTED;
|
|
if (selectedIdx < scrollIdx) {
|
|
scrollIdx--;
|
|
quickfixEls[scrollIdx].scrollIntoView(true);
|
|
}
|
|
updateDoc();
|
|
break;
|
|
}
|
|
}
|
|
|
|
function invoke(forceBox) {
|
|
var editor = editors.currentEditor.amlEditor.$editor;
|
|
if (editor.inMultiSelectMode) {
|
|
closeQuickfixBox();
|
|
return;
|
|
}
|
|
forceBox = forceBox;
|
|
|
|
var pos = editor.getCursorPosition();
|
|
|
|
showQuickfixBox(pos.row, pos.column);
|
|
}
|
|
*/
|
|
|
|
plugin.on("load", load);
|
|
|
|
register(null, {
|
|
"language.quickfix": plugin.freezePublicAPI({
|
|
|
|
})
|
|
});
|
|
}
|
|
|
|
}); |