Caches the tokens for the given editor/line if needed
function _manageCache(cm, line) {
if (!cache || !cache.tokens || cache.line !== line || cache.cm !== cm) {
// Cache is no longer matching -> Update
var tokens = cm.getLineTokens(line, false);
// Add empty beginning-of-line token for backwards compatibility
tokens.unshift(cm.getTokenAt({line: line, ch: 0}, false));
cache = {
cm: cm,
line: line,
timeStamp: Date.now(),
tokens: tokens
};
cm.off("changes", _clearCache);
cm.on("changes", _clearCache);
}
return cache.tokens;
}
Creates a context object for the given editor and position, suitable for passing to the move functions.
function getInitialContext(cm, pos) {
return {
"editor": cm,
"pos": pos,
"token": cm.getTokenAt(pos, true)
};
}
Returns the mode object and mode name string at a given position
function getModeAt(cm, pos, precise) {
precise = precise || true;
var modeData = cm.getMode(),
name;
if (modeData.innerMode) {
modeData = CodeMirror.innerMode(modeData, getTokenAt(cm, pos, precise).state).mode;
}
name = (modeData.name === "xml") ?
modeData.configuration : modeData.name;
return {mode: modeData, name: name};
}
exports.getTokenAt = getTokenAt;
exports.movePrevToken = movePrevToken;
exports.moveNextToken = moveNextToken;
exports.isAtStart = isAtStart;
exports.isAtEnd = isAtEnd;
exports.moveSkippingWhitespace = moveSkippingWhitespace;
exports.getInitialContext = getInitialContext;
exports.offsetInToken = offsetInToken;
exports.getModeAt = getModeAt;
});
Like cm.getTokenAt, but with caching. Way more performant for long lines.
function getTokenAt(cm, pos, precise) {
if (precise) {
_clearCache(); // reset cache
return cm.getTokenAt(pos, precise);
}
var cachedTokens = _manageCache(cm, pos.line),
tokenIndex = _.sortedIndex(cachedTokens, {end: pos.ch}, "end"), // binary search is faster for long arrays
token = cachedTokens[tokenIndex];
return token || cm.getTokenAt(pos, precise); // fall back to CMs getTokenAt, for example in an empty line
}
function isAtEnd(ctx) {
var eol = ctx.editor.getLine(ctx.pos.line).length;
return (ctx.pos.ch >= eol || ctx.token.end >= eol) && (ctx.pos.line >= ctx.editor.lineCount() - 1);
}
function isAtStart(ctx) {
return (ctx.pos.ch <= 0 || ctx.token.start <= 0) && (ctx.pos.line <= 0);
}
Moves the given context forward by one token.
function moveNextToken(ctx, precise) {
var eol = ctx.editor.getLine(ctx.pos.line).length;
if (precise === undefined) {
precise = true;
}
if (ctx.pos.ch >= eol || ctx.token.end >= eol) {
//move down a line
if (ctx.pos.line >= ctx.editor.lineCount() - 1) {
return false; //at the bottom
}
ctx.pos.line++;
ctx.pos.ch = 0;
} else {
ctx.pos.ch = ctx.token.end + 1;
}
ctx.token = getTokenAt(ctx.editor, ctx.pos, precise);
return true;
}
Moves the given context backwards by one token.
function movePrevToken(ctx, precise) {
if (precise === undefined) {
precise = true;
}
if (ctx.pos.ch <= 0 || ctx.token.start <= 0) {
//move up a line
if (ctx.pos.line <= 0) {
return false; //at the top already
}
ctx.pos.line--;
ctx.pos.ch = ctx.editor.getLine(ctx.pos.line).length;
} else {
ctx.pos.ch = ctx.token.start;
}
ctx.token = getTokenAt(ctx.editor, ctx.pos, precise);
return true;
}
Moves the given context in the given direction, skipping any whitespace it hits.
function moveSkippingWhitespace(moveFxn, ctx) {
if (!moveFxn(ctx)) {
return false;
}
while (!ctx.token.type && !/\S/.test(ctx.token.string)) {
if (!moveFxn(ctx)) {
return false;
}
}
return true;
}
In the given context, get the character offset of pos from the start of the token.
function offsetInToken(ctx) {
var offset = ctx.pos.ch - ctx.token.start;
if (offset < 0) {
console.log("CodeHintUtils: _offsetInToken - Invalid context: pos not in the current token!");
}
return offset;
}