c9-core/plugins/c9.ide.language.core/keyhandler.js

201 wiersze
7.0 KiB
JavaScript

/**
* Cloud9 Language Foundation
*
* @copyright 2013, Ajax.org B.V.
*/
define(function(require, exports, module) {
main.consumes = [
"Plugin", "tabManager", "ace", "language",
"language.complete", "language.tooltip"
];
main.provides = ["language.keyhandler"];
return main;
function main(options, imports, register) {
var Plugin = imports.Plugin;
var language = imports.language;
var complete = imports["language.complete"];
var tooltip = imports["language.tooltip"];
var complete_util = require("plugins/c9.ide.language/complete_util");
var DEFAULT_ID_REGEX = complete_util.DEFAULT_ID_REGEX;
var ace;
/***** Initialization *****/
var plugin = new Plugin("Ajax.org", main.consumes);
//var emit = plugin.getEmitter();
var loaded = false;
function load() {
if (loaded) return false;
loaded = true;
language.on("attachToEditor", function addBinding(ace) {
var kb = ace.keyBinding;
var defaultCommandHandler = kb.onCommandKey.bind(kb);
kb.onCommandKey = composeHandlers(onCommandKey, defaultCommandHandler, ace);
ace.commands.on("afterExec", onAfterExec, true);
});
complete.on("replaceText", function(e) {
onTextInput(e.newText, false, true);
});
}
/***** Methods *****/
function onAfterExec(e) {
if (e.command.name === "insertstring") {
ace = e.editor;
onTextInput(e.args);
} else if (e.command.name === "backspace") {
ace = e.editor;
if (language.isContinuousCompletionEnabled())
onBackspace(e);
}
}
function composeHandlers(mainHandler, fallbackHandler, myAce) {
return function onKeyPress() {
ace = myAce;
var result = mainHandler.apply(null, arguments);
if (!result)
fallbackHandler.apply(null, arguments);
};
}
function onTextInput(text, pasted, completed) {
inputTriggerTooltip(text, pasted);
if (completed)
return false;
if (complete.isPopupVisible())
return false;
if (language.isContinuousCompletionEnabled())
typeAlongCompleteTextInput(text, pasted);
else
inputTriggerComplete(text, pasted);
return false;
}
function onCommandKey(e) {
if (e.keyCode == 27) // Esc
tooltip.hide();
}
function onBackspace(e) {
if (complete.isPopupVisible())
return false;
var pos = ace.getCursorPosition();
var line = ace.session.doc.getLine(pos.row);
if (inCommentToken(pos))
return false;
if (!complete_util.precededByIdentifier(line, pos.column, null, ace) && !inTextToken(pos))
return false;
if (complete.getCompletionRegex(null, ace))
complete.deferredInvoke(false, ace, true);
}
function inputTriggerComplete(text, pasted) {
var pos = ace.getCursorPosition();
var completionRegex = complete.getCompletionRegex(null, ace);
var idRegex = complete.getIdentifierRegex(null, ace);
if (!pasted && completionRegex && text.match(completionRegex) && !inCommentToken(pos))
handleChar(text, idRegex, completionRegex);
}
function inputTriggerTooltip(text, pasted) {
var tooltipRegex = tooltip.getTooltipRegex(null, ace);
if (!pasted && tooltipRegex && text.match(tooltipRegex))
language.onCursorChange(null, null, true);
}
function typeAlongCompleteTextInput(text, pasted) {
var completionRegex = complete.getCompletionRegex(null, ace);
var idRegex = complete.getIdentifierRegex(null, ace);
if (pasted)
return false;
handleChar(text, idRegex, completionRegex);
}
function inTextToken(pos) {
var token = ace.getSession().getTokenAt(pos.row, pos.column - 1);
return token && token.type && token.type === "text";
}
function inCommentToken(pos) {
var token = ace.getSession().getTokenAt(pos.row, pos.column - 1);
return token && token.type && token.type.indexOf("comment") === 0;
}
function handleChar(ch, idRegex, completionRegex) {
var pos = ace.getCursorPosition();
if (inCommentToken(pos))
return;
var line = ace.getSession().getDocument().getLine(pos.row);
var matchIdRegex = ch.match(idRegex || DEFAULT_ID_REGEX);
if (matchIdRegex || complete.matchCompletionRegex(completionRegex, line, pos)) {
if (!complete_util.precededByIdentifier(line, pos.column, ch, ace))
return false;
complete.deferredInvoke(true, ace);
}
else if (ch === '"' || ch === "'") {
// TODO: move this special handing into infer_completer's getCompletionRegex
if (complete_util.isRequireJSCall(line, pos.column, "", ace, true))
complete.deferredInvoke(true, ace);
}
else {
// No useful character was pressed, but maybe we can
// predict what the user wants to complete next?
setTimeout(complete.invoke.bind(complete, { predictOnly: true }));
}
}
function setSkipInput(input) {
// TODO: skip characters in input
}
/***** Lifecycle *****/
plugin.on("load", function() {
load();
});
plugin.on("enable", function() {
});
plugin.on("disable", function() {
});
plugin.on("unload", function() {
loaded = false;
});
/***** Register and define API *****/
/**
*/
plugin.freezePublicAPI({
/**
*
*/
composeHandlers: composeHandlers,
/**
* Set text to skip when typed in.
* Used when automatically inserting text, but tolerating
* users also typing it, e.g. when inserting a closing }.
*
* @ignore not implemented
*
* @param {String} input
*/
setSkipInput: setSkipInput
});
register(null, {
"language.keyhandler": plugin
});
}
});