12480 lines
412 KiB
JavaScript
Executable File
12480 lines
412 KiB
JavaScript
Executable File
define("ace/ext/spellcheck",["require","exports","module","ace/lib/event","ace/editor","ace/config"], function(require, exports, module) {
|
|
"use strict";
|
|
var event = require("../lib/event");
|
|
|
|
exports.contextMenuHandler = function(e){
|
|
var host = e.target;
|
|
var text = host.textInput.getElement();
|
|
if (!host.selection.isEmpty())
|
|
return;
|
|
var c = host.getCursorPosition();
|
|
var r = host.session.getWordRange(c.row, c.column);
|
|
var w = host.session.getTextRange(r);
|
|
|
|
host.session.tokenRe.lastIndex = 0;
|
|
if (!host.session.tokenRe.test(w))
|
|
return;
|
|
var PLACEHOLDER = "\x01\x01";
|
|
var value = w + " " + PLACEHOLDER;
|
|
text.value = value;
|
|
text.setSelectionRange(w.length, w.length + 1);
|
|
text.setSelectionRange(0, 0);
|
|
text.setSelectionRange(0, w.length);
|
|
|
|
var afterKeydown = false;
|
|
event.addListener(text, "keydown", function onKeydown() {
|
|
event.removeListener(text, "keydown", onKeydown);
|
|
afterKeydown = true;
|
|
});
|
|
|
|
host.textInput.setInputHandler(function(newVal) {
|
|
console.log(newVal , value, text.selectionStart, text.selectionEnd)
|
|
if (newVal == value)
|
|
return '';
|
|
if (newVal.lastIndexOf(value, 0) === 0)
|
|
return newVal.slice(value.length);
|
|
if (newVal.substr(text.selectionEnd) == value)
|
|
return newVal.slice(0, -value.length);
|
|
if (newVal.slice(-2) == PLACEHOLDER) {
|
|
var val = newVal.slice(0, -2);
|
|
if (val.slice(-1) == " ") {
|
|
if (afterKeydown)
|
|
return val.substring(0, text.selectionEnd);
|
|
val = val.slice(0, -1);
|
|
host.session.replace(r, val);
|
|
return "";
|
|
}
|
|
}
|
|
|
|
return newVal;
|
|
});
|
|
};
|
|
var Editor = require("../editor").Editor;
|
|
require("../config").defineOptions(Editor.prototype, "editor", {
|
|
spellcheck: {
|
|
set: function(val) {
|
|
var text = this.textInput.getElement();
|
|
text.spellcheck = !!val;
|
|
if (!val)
|
|
this.removeListener("nativecontextmenu", exports.contextMenuHandler);
|
|
else
|
|
this.on("nativecontextmenu", exports.contextMenuHandler);
|
|
},
|
|
value: true
|
|
}
|
|
});
|
|
|
|
});
|
|
|
|
define("kitchen-sink/inline_editor",["require","exports","module","ace/line_widgets","ace/editor","ace/virtual_renderer","ace/lib/dom","ace/commands/default_commands"], function(require, exports, module) {
|
|
"use strict";
|
|
|
|
var LineWidgets = require("ace/line_widgets").LineWidgets;
|
|
var Editor = require("ace/editor").Editor;
|
|
var Renderer = require("ace/virtual_renderer").VirtualRenderer;
|
|
var dom = require("ace/lib/dom");
|
|
|
|
|
|
require("ace/commands/default_commands").commands.push({
|
|
name: "openInlineEditor",
|
|
bindKey: "F3",
|
|
exec: function(editor) {
|
|
var split = window.env.split;
|
|
var s = editor.session;
|
|
var inlineEditor = new Editor(new Renderer());
|
|
var splitSession = split.$cloneSession(s);
|
|
|
|
var row = editor.getCursorPosition().row;
|
|
if (editor.session.lineWidgets && editor.session.lineWidgets[row]) {
|
|
editor.session.lineWidgets[row].destroy();
|
|
return;
|
|
}
|
|
|
|
var rowCount = 10;
|
|
var w = {
|
|
row: row,
|
|
fixedWidth: true,
|
|
el: dom.createElement("div"),
|
|
editor: inlineEditor
|
|
};
|
|
var el = w.el;
|
|
el.appendChild(inlineEditor.container);
|
|
|
|
if (!editor.session.widgetManager) {
|
|
editor.session.widgetManager = new LineWidgets(editor.session);
|
|
editor.session.widgetManager.attach(editor);
|
|
}
|
|
|
|
var h = rowCount*editor.renderer.layerConfig.lineHeight;
|
|
inlineEditor.container.style.height = h + "px";
|
|
|
|
el.style.position = "absolute";
|
|
el.style.zIndex = "4";
|
|
el.style.borderTop = "solid blue 2px";
|
|
el.style.borderBottom = "solid blue 2px";
|
|
|
|
inlineEditor.setSession(splitSession);
|
|
editor.session.widgetManager.addLineWidget(w);
|
|
|
|
var kb = {
|
|
handleKeyboard:function(_,hashId, keyString) {
|
|
if (hashId === 0 && keyString === "esc") {
|
|
w.destroy();
|
|
return true;
|
|
}
|
|
}
|
|
};
|
|
|
|
w.destroy = function() {
|
|
editor.keyBinding.removeKeyboardHandler(kb);
|
|
s.widgetManager.removeLineWidget(w);
|
|
};
|
|
|
|
editor.keyBinding.addKeyboardHandler(kb);
|
|
inlineEditor.keyBinding.addKeyboardHandler(kb);
|
|
inlineEditor.setTheme("ace/theme/solarized_light");
|
|
}
|
|
});
|
|
});
|
|
|
|
define("ace/test/asyncjs/assert",["require","exports","module","ace/lib/oop"], function(require, exports, module) {
|
|
var oop = require("ace/lib/oop");
|
|
var pSlice = Array.prototype.slice;
|
|
|
|
var assert = exports;
|
|
|
|
assert.AssertionError = function AssertionError(options) {
|
|
this.name = 'AssertionError';
|
|
this.message = options.message;
|
|
this.actual = options.actual;
|
|
this.expected = options.expected;
|
|
this.operator = options.operator;
|
|
var stackStartFunction = options.stackStartFunction || fail;
|
|
|
|
if (Error.captureStackTrace) {
|
|
Error.captureStackTrace(this, stackStartFunction);
|
|
}
|
|
};
|
|
oop.inherits(assert.AssertionError, Error);
|
|
|
|
toJSON = function(obj) {
|
|
if (typeof JSON !== "undefined")
|
|
return JSON.stringify(obj);
|
|
else
|
|
return obj.toString();
|
|
}
|
|
|
|
assert.AssertionError.prototype.toString = function() {
|
|
if (this.message) {
|
|
return [this.name + ':', this.message].join(' ');
|
|
} else {
|
|
return [this.name + ':',
|
|
toJSON(this.expected),
|
|
this.operator,
|
|
toJSON(this.actual)].join(' ');
|
|
}
|
|
};
|
|
|
|
assert.AssertionError.__proto__ = Error.prototype;
|
|
|
|
function fail(actual, expected, message, operator, stackStartFunction) {
|
|
throw new assert.AssertionError({
|
|
message: message,
|
|
actual: actual,
|
|
expected: expected,
|
|
operator: operator,
|
|
stackStartFunction: stackStartFunction
|
|
});
|
|
}
|
|
assert.fail = fail;
|
|
|
|
assert.ok = function ok(value, message) {
|
|
if (!!!value) fail(value, true, message, '==', assert.ok);
|
|
};
|
|
|
|
assert.equal = function equal(actual, expected, message) {
|
|
if (actual != expected) fail(actual, expected, message, '==', assert.equal);
|
|
};
|
|
|
|
assert.notEqual = function notEqual(actual, expected, message) {
|
|
if (actual == expected) {
|
|
fail(actual, expected, message, '!=', assert.notEqual);
|
|
}
|
|
};
|
|
|
|
assert.deepEqual = function deepEqual(actual, expected, message) {
|
|
if (!_deepEqual(actual, expected)) {
|
|
fail(actual, expected, message, 'deepEqual', assert.deepEqual);
|
|
}
|
|
};
|
|
|
|
function _deepEqual(actual, expected) {
|
|
if (actual === expected) {
|
|
return true;
|
|
|
|
} else if (typeof Buffer !== "undefined" && Buffer.isBuffer(actual) && Buffer.isBuffer(expected)) {
|
|
if (actual.length != expected.length) return false;
|
|
|
|
for (var i = 0; i < actual.length; i++) {
|
|
if (actual[i] !== expected[i]) return false;
|
|
}
|
|
|
|
return true;
|
|
} else if (actual instanceof Date && expected instanceof Date) {
|
|
return actual.getTime() === expected.getTime();
|
|
} else if (typeof actual != 'object' && typeof expected != 'object') {
|
|
return actual == expected;
|
|
} else {
|
|
return objEquiv(actual, expected);
|
|
}
|
|
}
|
|
|
|
function isUndefinedOrNull(value) {
|
|
return value === null || value === undefined;
|
|
}
|
|
|
|
function isArguments(object) {
|
|
return Object.prototype.toString.call(object) == '[object Arguments]';
|
|
}
|
|
|
|
function objEquiv(a, b) {
|
|
if (isUndefinedOrNull(a) || isUndefinedOrNull(b))
|
|
return false;
|
|
if (a.prototype !== b.prototype) return false;
|
|
if (isArguments(a)) {
|
|
if (!isArguments(b)) {
|
|
return false;
|
|
}
|
|
a = pSlice.call(a);
|
|
b = pSlice.call(b);
|
|
return _deepEqual(a, b);
|
|
}
|
|
try {
|
|
var ka = Object.keys(a),
|
|
kb = Object.keys(b),
|
|
key, i;
|
|
} catch (e) {//happens when one is a string literal and the other isn't
|
|
return false;
|
|
}
|
|
if (ka.length != kb.length)
|
|
return false;
|
|
ka.sort();
|
|
kb.sort();
|
|
for (i = ka.length - 1; i >= 0; i--) {
|
|
if (ka[i] != kb[i])
|
|
return false;
|
|
}
|
|
for (i = ka.length - 1; i >= 0; i--) {
|
|
key = ka[i];
|
|
if (!_deepEqual(a[key], b[key])) return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
assert.notDeepEqual = function notDeepEqual(actual, expected, message) {
|
|
if (_deepEqual(actual, expected)) {
|
|
fail(actual, expected, message, 'notDeepEqual', assert.notDeepEqual);
|
|
}
|
|
};
|
|
|
|
assert.strictEqual = function strictEqual(actual, expected, message) {
|
|
if (actual !== expected) {
|
|
fail(actual, expected, message, '===', assert.strictEqual);
|
|
}
|
|
};
|
|
|
|
assert.notStrictEqual = function notStrictEqual(actual, expected, message) {
|
|
if (actual === expected) {
|
|
fail(actual, expected, message, '!==', assert.notStrictEqual);
|
|
}
|
|
};
|
|
|
|
function expectedException(actual, expected) {
|
|
if (!actual || !expected) {
|
|
return false;
|
|
}
|
|
|
|
if (expected instanceof RegExp) {
|
|
return expected.test(actual);
|
|
} else if (actual instanceof expected) {
|
|
return true;
|
|
} else if (expected.call({}, actual) === true) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
function _throws(shouldThrow, block, expected, message) {
|
|
var actual;
|
|
|
|
if (typeof expected === 'string') {
|
|
message = expected;
|
|
expected = null;
|
|
}
|
|
|
|
try {
|
|
block();
|
|
} catch (e) {
|
|
actual = e;
|
|
}
|
|
|
|
message = (expected && expected.name ? ' (' + expected.name + ').' : '.') +
|
|
(message ? ' ' + message : '.');
|
|
|
|
if (shouldThrow && !actual) {
|
|
fail('Missing expected exception' + message);
|
|
}
|
|
|
|
if (!shouldThrow && expectedException(actual, expected)) {
|
|
fail('Got unwanted exception' + message);
|
|
}
|
|
|
|
if ((shouldThrow && actual && expected &&
|
|
!expectedException(actual, expected)) || (!shouldThrow && actual)) {
|
|
throw actual;
|
|
}
|
|
}
|
|
|
|
assert.throws = function(block, /*optional*/error, /*optional*/message) {
|
|
_throws.apply(this, [true].concat(pSlice.call(arguments)));
|
|
};
|
|
assert.doesNotThrow = function(block, /*optional*/error, /*optional*/message) {
|
|
_throws.apply(this, [false].concat(pSlice.call(arguments)));
|
|
};
|
|
|
|
assert.ifError = function(err) { if (err) {throw err;}};
|
|
|
|
});
|
|
|
|
define("ace/test/asyncjs/async",["require","exports","module"], function(require, exports, module) {
|
|
|
|
var STOP = exports.STOP = {}
|
|
|
|
exports.Generator = function(source) {
|
|
if (typeof source == "function")
|
|
this.source = {
|
|
next: source
|
|
}
|
|
else
|
|
this.source = source
|
|
}
|
|
|
|
;(function() {
|
|
this.next = function(callback) {
|
|
this.source.next(callback)
|
|
}
|
|
|
|
this.map = function(mapper) {
|
|
if (!mapper)
|
|
return this
|
|
|
|
mapper = makeAsync(1, mapper)
|
|
|
|
var source = this.source
|
|
this.next = function(callback) {
|
|
source.next(function(err, value) {
|
|
if (err)
|
|
callback(err)
|
|
else {
|
|
mapper(value, function(err, value) {
|
|
if (err)
|
|
callback(err)
|
|
else
|
|
callback(null, value)
|
|
})
|
|
}
|
|
})
|
|
}
|
|
return new this.constructor(this)
|
|
}
|
|
|
|
this.filter = function(filter) {
|
|
if (!filter)
|
|
return this
|
|
|
|
filter = makeAsync(1, filter)
|
|
|
|
var source = this.source
|
|
this.next = function(callback) {
|
|
source.next(function handler(err, value) {
|
|
if (err)
|
|
callback(err)
|
|
else {
|
|
filter(value, function(err, takeIt) {
|
|
if (err)
|
|
callback(err)
|
|
else if (takeIt)
|
|
callback(null, value)
|
|
else
|
|
source.next(handler)
|
|
})
|
|
}
|
|
})
|
|
}
|
|
return new this.constructor(this)
|
|
}
|
|
|
|
this.slice = function(begin, end) {
|
|
var count = -1
|
|
if (!end || end < 0)
|
|
var end = Infinity
|
|
|
|
var source = this.source
|
|
this.next = function(callback) {
|
|
source.next(function handler(err, value) {
|
|
count++
|
|
if (err)
|
|
callback(err)
|
|
else if (count >= begin && count < end)
|
|
callback(null, value)
|
|
else if (count >= end)
|
|
callback(STOP)
|
|
else
|
|
source.next(handler)
|
|
})
|
|
}
|
|
return new this.constructor(this)
|
|
}
|
|
|
|
this.reduce = function(reduce, initialValue) {
|
|
reduce = makeAsync(3, reduce)
|
|
|
|
var index = 0
|
|
var done = false
|
|
var previousValue = initialValue
|
|
|
|
var source = this.source
|
|
this.next = function(callback) {
|
|
if (done)
|
|
return callback(STOP)
|
|
|
|
if (initialValue === undefined) {
|
|
source.next(function(err, currentValue) {
|
|
if (err)
|
|
return callback(err, previousValue)
|
|
|
|
previousValue = currentValue
|
|
reduceAll()
|
|
})
|
|
}
|
|
else
|
|
reduceAll()
|
|
|
|
function reduceAll() {
|
|
source.next(function handler(err, currentValue) {
|
|
if (err) {
|
|
done = true
|
|
if (err == STOP)
|
|
return callback(null, previousValue)
|
|
else
|
|
return(err)
|
|
}
|
|
reduce(previousValue, currentValue, index++, function(err, value) {
|
|
previousValue = value
|
|
source.next(handler)
|
|
})
|
|
})
|
|
}
|
|
}
|
|
return new this.constructor(this)
|
|
}
|
|
|
|
this.forEach =
|
|
this.each = function(fn) {
|
|
fn = makeAsync(1, fn)
|
|
|
|
var source = this.source
|
|
this.next = function(callback) {
|
|
source.next(function handler(err, value) {
|
|
if (err)
|
|
callback(err)
|
|
else {
|
|
fn(value, function(err) {
|
|
callback(err, value)
|
|
})
|
|
}
|
|
})
|
|
}
|
|
return new this.constructor(this)
|
|
}
|
|
|
|
this.some = function(condition) {
|
|
condition = makeAsync(1, condition)
|
|
|
|
var source = this.source
|
|
var done = false
|
|
this.next = function(callback) {
|
|
if (done)
|
|
return callback(STOP)
|
|
|
|
source.next(function handler(err, value) {
|
|
if (err)
|
|
return callback(err)
|
|
|
|
condition(value, function(err, result) {
|
|
if (err) {
|
|
done = true
|
|
if (err == STOP)
|
|
callback(null, false)
|
|
else
|
|
callback(err)
|
|
}
|
|
else if (result) {
|
|
done = true
|
|
callback(null, true)
|
|
}
|
|
else
|
|
source.next(handler)
|
|
})
|
|
})
|
|
}
|
|
return new this.constructor(this)
|
|
}
|
|
|
|
this.every = function(condition) {
|
|
condition = makeAsync(1, condition)
|
|
|
|
var source = this.source
|
|
var done = false
|
|
this.next = function(callback) {
|
|
if (done)
|
|
return callback(STOP)
|
|
|
|
source.next(function handler(err, value) {
|
|
if (err)
|
|
return callback(err)
|
|
|
|
condition(value, function(err, result) {
|
|
if (err) {
|
|
done = true
|
|
if (err == STOP)
|
|
callback(null, true)
|
|
else
|
|
callback(err)
|
|
}
|
|
else if (!result) {
|
|
done = true
|
|
callback(null, false)
|
|
}
|
|
else
|
|
source.next(handler)
|
|
})
|
|
})
|
|
}
|
|
return new this.constructor(this)
|
|
}
|
|
|
|
this.call = function(context) {
|
|
var source = this.source
|
|
return this.map(function(fn, next) {
|
|
fn = makeAsync(0, fn, context)
|
|
fn.call(context, function(err, value) {
|
|
next(err, value)
|
|
})
|
|
})
|
|
}
|
|
|
|
this.concat = function(generator) {
|
|
var generators = [this]
|
|
generators.push.apply(generators, arguments)
|
|
var index = 0
|
|
var source = generators[index++]
|
|
|
|
return new this.constructor(function(callback) {
|
|
source.next(function handler(err, value) {
|
|
if (err) {
|
|
if (err == STOP) {
|
|
source = generators[index++]
|
|
if (!source)
|
|
return callback(STOP)
|
|
else
|
|
return source.next(handler)
|
|
}
|
|
else
|
|
return callback(err)
|
|
}
|
|
else
|
|
return callback(null, value)
|
|
})
|
|
})
|
|
}
|
|
|
|
this.zip = function(generator) {
|
|
var generators = [this]
|
|
generators.push.apply(generators, arguments)
|
|
|
|
return new this.constructor(function(callback) {
|
|
exports.list(generators)
|
|
.map(function(gen, next) {
|
|
gen.next(next)
|
|
})
|
|
.toArray(callback)
|
|
})
|
|
}
|
|
|
|
this.expand = function(inserter, constructor) {
|
|
if (!inserter)
|
|
return this
|
|
|
|
var inserter = makeAsync(1, inserter)
|
|
var constructor = constructor || this.constructor
|
|
var source = this.source;
|
|
var spliced = null;
|
|
|
|
return new constructor(function next(callback) {
|
|
if (!spliced) {
|
|
source.next(function(err, value) {
|
|
if (err)
|
|
return callback(err)
|
|
|
|
inserter(value, function(err, toInsert) {
|
|
if (err)
|
|
return callback(err)
|
|
|
|
spliced = toInsert
|
|
next(callback)
|
|
})
|
|
|
|
})
|
|
}
|
|
else {
|
|
spliced.next(function(err, value) {
|
|
if (err == STOP) {
|
|
spliced = null
|
|
return next(callback)
|
|
}
|
|
else if (err)
|
|
return callback(err)
|
|
|
|
callback(err, value)
|
|
})
|
|
}
|
|
})
|
|
}
|
|
|
|
this.sort = function(compare) {
|
|
var self = this
|
|
var arrGen
|
|
this.next = function(callback) {
|
|
if (arrGen)
|
|
return arrGen.next(callback)
|
|
|
|
self.toArray(function(err, arr) {
|
|
if (err)
|
|
callback(err)
|
|
else {
|
|
arrGen = exports.list(arr.sort(compare))
|
|
arrGen.next(callback)
|
|
}
|
|
})
|
|
}
|
|
return new this.constructor(this)
|
|
}
|
|
|
|
this.join = function(separator) {
|
|
return this.$arrayOp(Array.prototype.join, separator !== undefined ? [separator] : null)
|
|
}
|
|
|
|
this.reverse = function() {
|
|
return this.$arrayOp(Array.prototype.reverse)
|
|
}
|
|
|
|
this.$arrayOp = function(arrayMethod, args) {
|
|
var self = this
|
|
var i = 0
|
|
this.next = function(callback) {
|
|
if (i++ > 0)
|
|
return callback(STOP)
|
|
|
|
self.toArray(function(err, arr) {
|
|
if (err)
|
|
callback(err, "")
|
|
else {
|
|
if (args)
|
|
callback(null, arrayMethod.apply(arr, args))
|
|
else
|
|
callback(null, arrayMethod.call(arr))
|
|
}
|
|
})
|
|
}
|
|
return new this.constructor(this)
|
|
|
|
}
|
|
|
|
this.end = function(breakOnError, callback) {
|
|
if (!callback) {
|
|
callback = arguments[0]
|
|
breakOnError = true
|
|
}
|
|
|
|
var source = this.source
|
|
var last
|
|
var lastError
|
|
source.next(function handler(err, value) {
|
|
if (err) {
|
|
if (err == STOP)
|
|
callback && callback(lastError, last)
|
|
else if (!breakOnError) {
|
|
lastError = err
|
|
source.next(handler)
|
|
}
|
|
else
|
|
callback && callback(err, value)
|
|
}
|
|
else {
|
|
last = value
|
|
source.next(handler)
|
|
}
|
|
})
|
|
}
|
|
|
|
this.toArray = function(breakOnError, callback) {
|
|
if (!callback) {
|
|
callback = arguments[0]
|
|
breakOnError = true
|
|
}
|
|
|
|
var values = []
|
|
var errors = []
|
|
var source = this.source
|
|
|
|
source.next(function handler(err, value) {
|
|
if (err) {
|
|
if (err == STOP) {
|
|
if (breakOnError)
|
|
return callback(null, values)
|
|
else {
|
|
errors.length = values.length
|
|
return callback(errors, values)
|
|
}
|
|
}
|
|
else {
|
|
if (breakOnError)
|
|
return callback(err)
|
|
else
|
|
errors[values.length] = err
|
|
}
|
|
}
|
|
|
|
values.push(value)
|
|
source.next(handler)
|
|
})
|
|
}
|
|
|
|
}).call(exports.Generator.prototype)
|
|
|
|
var makeAsync = exports.makeAsync = function(args, fn, context) {
|
|
if (fn.length > args)
|
|
return fn
|
|
else {
|
|
return function() {
|
|
var value
|
|
var next = arguments[args]
|
|
try {
|
|
value = fn.apply(context || this, arguments)
|
|
} catch(e) {
|
|
return next(e)
|
|
}
|
|
next(null, value)
|
|
}
|
|
}
|
|
}
|
|
|
|
exports.list = function(arr, construct) {
|
|
var construct = construct || exports.Generator
|
|
var i = 0
|
|
var len = arr.length
|
|
|
|
return new construct(function(callback) {
|
|
if (i < len)
|
|
callback(null, arr[i++])
|
|
else
|
|
callback(STOP)
|
|
})
|
|
}
|
|
|
|
exports.values = function(map, construct) {
|
|
var values = []
|
|
for (var key in map)
|
|
values.push(map[key])
|
|
|
|
return exports.list(values, construct)
|
|
}
|
|
|
|
exports.keys = function(map, construct) {
|
|
var keys = []
|
|
for (var key in map)
|
|
keys.push(key)
|
|
|
|
return exports.list(keys, construct)
|
|
}
|
|
exports.range = function(start, stop, step, construct) {
|
|
var construct = construct || exports.Generator
|
|
start = start || 0
|
|
step = step || 1
|
|
|
|
if (stop === undefined || stop === null)
|
|
stop = step > 0 ? Infinity : -Infinity
|
|
|
|
var value = start
|
|
|
|
return new construct(function(callback) {
|
|
if (step > 0 && value >= stop || step < 0 && value <= stop)
|
|
callback(STOP)
|
|
else {
|
|
var current = value
|
|
value += step
|
|
callback(null, current)
|
|
}
|
|
})
|
|
}
|
|
|
|
exports.concat = function(first, varargs) {
|
|
if (arguments.length > 1)
|
|
return first.concat.apply(first, Array.prototype.slice.call(arguments, 1))
|
|
else
|
|
return first
|
|
}
|
|
|
|
exports.zip = function(first, varargs) {
|
|
if (arguments.length > 1)
|
|
return first.zip.apply(first, Array.prototype.slice.call(arguments, 1))
|
|
else
|
|
return first.map(function(item, next) {
|
|
next(null, [item])
|
|
})
|
|
}
|
|
|
|
|
|
exports.plugin = function(members, constructors) {
|
|
if (members) {
|
|
for (var key in members) {
|
|
exports.Generator.prototype[key] = members[key]
|
|
}
|
|
}
|
|
|
|
if (constructors) {
|
|
for (var key in constructors) {
|
|
exports[key] = constructors[key]
|
|
}
|
|
}
|
|
}
|
|
|
|
})
|
|
|
|
define("ace/test/mockrenderer",["require","exports","module"], function(require, exports, module) {
|
|
"use strict";
|
|
|
|
var MockRenderer = exports.MockRenderer = function(visibleRowCount) {
|
|
if (typeof document == "object") {
|
|
this.container = document.createElement("div");
|
|
this.scroller = document.createElement("div");
|
|
}
|
|
this.visibleRowCount = visibleRowCount || 20;
|
|
|
|
this.layerConfig = {
|
|
firstVisibleRow : 0,
|
|
lastVisibleRow : this.visibleRowCount
|
|
};
|
|
|
|
this.isMockRenderer = true;
|
|
|
|
this.$gutter = {};
|
|
};
|
|
|
|
|
|
MockRenderer.prototype.getFirstVisibleRow = function() {
|
|
return this.layerConfig.firstVisibleRow;
|
|
};
|
|
|
|
MockRenderer.prototype.getLastVisibleRow = function() {
|
|
return this.layerConfig.lastVisibleRow;
|
|
};
|
|
|
|
MockRenderer.prototype.getFirstFullyVisibleRow = function() {
|
|
return this.layerConfig.firstVisibleRow;
|
|
};
|
|
|
|
MockRenderer.prototype.getLastFullyVisibleRow = function() {
|
|
return this.layerConfig.lastVisibleRow;
|
|
};
|
|
|
|
MockRenderer.prototype.getContainerElement = function() {
|
|
return this.container;
|
|
};
|
|
|
|
MockRenderer.prototype.getMouseEventTarget = function() {
|
|
return this.container;
|
|
};
|
|
|
|
MockRenderer.prototype.getTextAreaContainer = function() {
|
|
return this.container;
|
|
};
|
|
|
|
MockRenderer.prototype.addGutterDecoration = function() {
|
|
};
|
|
|
|
MockRenderer.prototype.removeGutterDecoration = function() {
|
|
};
|
|
|
|
MockRenderer.prototype.moveTextAreaToCursor = function() {
|
|
};
|
|
|
|
MockRenderer.prototype.setSession = function(session) {
|
|
this.session = session;
|
|
};
|
|
|
|
MockRenderer.prototype.getSession = function(session) {
|
|
return this.session;
|
|
};
|
|
|
|
MockRenderer.prototype.setTokenizer = function() {
|
|
};
|
|
|
|
MockRenderer.prototype.on = function() {
|
|
};
|
|
|
|
MockRenderer.prototype.updateCursor = function() {
|
|
};
|
|
|
|
MockRenderer.prototype.animateScrolling = function(fromValue, callback) {
|
|
callback && callback();
|
|
};
|
|
|
|
MockRenderer.prototype.scrollToX = function(scrollTop) {};
|
|
MockRenderer.prototype.scrollToY = function(scrollLeft) {};
|
|
|
|
MockRenderer.prototype.scrollToLine = function(line, center) {
|
|
var lineHeight = 16;
|
|
var row = 0;
|
|
for (var l = 1; l < line; l++) {
|
|
row += this.session.getRowLength(l-1);
|
|
}
|
|
|
|
if (center) {
|
|
row -= this.visibleRowCount / 2;
|
|
}
|
|
this.scrollToRow(row);
|
|
};
|
|
|
|
MockRenderer.prototype.scrollSelectionIntoView = function() {
|
|
};
|
|
|
|
MockRenderer.prototype.scrollCursorIntoView = function() {
|
|
var cursor = this.session.getSelection().getCursor();
|
|
if (cursor.row < this.layerConfig.firstVisibleRow) {
|
|
this.scrollToRow(cursor.row);
|
|
}
|
|
else if (cursor.row > this.layerConfig.lastVisibleRow) {
|
|
this.scrollToRow(cursor.row);
|
|
}
|
|
};
|
|
|
|
MockRenderer.prototype.scrollToRow = function(row) {
|
|
var row = Math.min(this.session.getLength() - this.visibleRowCount, Math.max(0,
|
|
row));
|
|
this.layerConfig.firstVisibleRow = row;
|
|
this.layerConfig.lastVisibleRow = row + this.visibleRowCount;
|
|
};
|
|
|
|
MockRenderer.prototype.getScrollTopRow = function() {
|
|
return this.layerConfig.firstVisibleRow;
|
|
};
|
|
|
|
MockRenderer.prototype.draw = function() {
|
|
};
|
|
|
|
MockRenderer.prototype.onChangeTabSize = function(startRow, endRow) {
|
|
};
|
|
|
|
MockRenderer.prototype.updateLines = function(startRow, endRow) {
|
|
};
|
|
|
|
MockRenderer.prototype.updateBackMarkers = function() {
|
|
};
|
|
|
|
MockRenderer.prototype.updateFrontMarkers = function() {
|
|
};
|
|
|
|
MockRenderer.prototype.updateBreakpoints = function() {
|
|
};
|
|
|
|
MockRenderer.prototype.onResize = function() {
|
|
};
|
|
|
|
MockRenderer.prototype.updateFull = function() {
|
|
};
|
|
|
|
MockRenderer.prototype.updateText = function() {
|
|
};
|
|
|
|
MockRenderer.prototype.showCursor = function() {
|
|
};
|
|
|
|
MockRenderer.prototype.visualizeFocus = function() {
|
|
};
|
|
|
|
MockRenderer.prototype.setAnnotations = function() {
|
|
};
|
|
|
|
MockRenderer.prototype.setStyle = function() {
|
|
};
|
|
|
|
MockRenderer.prototype.unsetStyle = function() {
|
|
};
|
|
|
|
MockRenderer.prototype.textToScreenCoordinates = function() {
|
|
return {
|
|
pageX: 0,
|
|
pageY: 0
|
|
}
|
|
};
|
|
|
|
MockRenderer.prototype.screenToTextCoordinates = function() {
|
|
return {
|
|
row: 0,
|
|
column: 0
|
|
}
|
|
};
|
|
|
|
MockRenderer.prototype.adjustWrapLimit = function () {
|
|
|
|
};
|
|
|
|
});
|
|
|
|
define("kitchen-sink/dev_util",["require","exports","module","ace/lib/dom","ace/range","ace/lib/oop","ace/lib/dom","ace/range","ace/editor","ace/test/asyncjs/assert","ace/test/asyncjs/async","ace/undomanager","ace/edit_session","ace/test/mockrenderer","ace/lib/event_emitter"], function(require, exports, module) {
|
|
var dom = require("ace/lib/dom");
|
|
var Range = require("ace/range").Range;
|
|
function warn() {
|
|
var s = (new Error()).stack || "";
|
|
s = s.split("\n");
|
|
if (s[1] == "Error") s.shift(); // remove error description on chrome
|
|
s.shift(); // remove warn
|
|
s.shift(); // remove the getter
|
|
s = s.join("\n");
|
|
if (!/at Object.InjectedScript.|@debugger eval|snippets:\/{3}|<anonymous>:\d+:\d+/.test(s)) {
|
|
console.error("trying to access to global variable");
|
|
}
|
|
}
|
|
function def(o, key, get) {
|
|
try {
|
|
Object.defineProperty(o, key, {
|
|
configurable: true,
|
|
get: get,
|
|
set: function(val) {
|
|
delete o[key];
|
|
o[key] = val;
|
|
}
|
|
});
|
|
} catch(e) {
|
|
console.error(e);
|
|
}
|
|
}
|
|
def(window, "ace", function(){ warn(); return window.env.editor });
|
|
def(window, "editor", function(){ warn(); return window.env.editor });
|
|
def(window, "session", function(){ warn(); return window.env.editor.session });
|
|
def(window, "split", function(){ warn(); return window.env.split });
|
|
|
|
|
|
def(window, "devUtil", function(){ warn(); return exports });
|
|
exports.showTextArea = function(argument) {
|
|
dom.importCssString("\
|
|
.ace_text-input {\
|
|
position: absolute;\
|
|
z-index: 10!important;\
|
|
width: 6em!important;\
|
|
height: 1em;\
|
|
opacity: 1!important;\
|
|
background: rgba(0, 92, 255, 0.11);\
|
|
border: none;\
|
|
font: inherit;\
|
|
padding: 0 1px;\
|
|
margin: 0 -1px;\
|
|
text-indent: 0em;\
|
|
}\
|
|
");
|
|
};
|
|
|
|
exports.addGlobals = function() {
|
|
window.oop = require("ace/lib/oop");
|
|
window.dom = require("ace/lib/dom");
|
|
window.Range = require("ace/range").Range;
|
|
window.Editor = require("ace/editor").Editor;
|
|
window.assert = require("ace/test/asyncjs/assert");
|
|
window.asyncjs = require("ace/test/asyncjs/async");
|
|
window.UndoManager = require("ace/undomanager").UndoManager;
|
|
window.EditSession = require("ace/edit_session").EditSession;
|
|
window.MockRenderer = require("ace/test/mockrenderer").MockRenderer;
|
|
window.EventEmitter = require("ace/lib/event_emitter").EventEmitter;
|
|
|
|
window.getSelection = getSelection;
|
|
window.setSelection = setSelection;
|
|
window.testSelection = testSelection;
|
|
};
|
|
|
|
function getSelection(editor) {
|
|
var data = editor.multiSelect.toJSON();
|
|
if (!data.length) data = [data];
|
|
data = data.map(function(x) {
|
|
var a, c;
|
|
if (x.isBackwards) {
|
|
a = x.end;
|
|
c = x.start;
|
|
} else {
|
|
c = x.end;
|
|
a = x.start;
|
|
}
|
|
return Range.comparePoints(a, c)
|
|
? [a.row, a.column, c.row, c.column]
|
|
: [a.row, a.column];
|
|
});
|
|
return data.length > 1 ? data : data[0];
|
|
}
|
|
function setSelection(editor, data) {
|
|
if (typeof data[0] == "number")
|
|
data = [data];
|
|
editor.selection.fromJSON(data.map(function(x) {
|
|
var start = {row: x[0], column: x[1]};
|
|
var end = x.length == 2 ? start : {row: x[2], column: x[3]};
|
|
var isBackwards = Range.comparePoints(start, end) > 0;
|
|
return isBackwards ? {
|
|
start: end,
|
|
end: start,
|
|
isBackwards: true
|
|
} : {
|
|
start: start,
|
|
end: end,
|
|
isBackwards: true
|
|
};
|
|
}));
|
|
}
|
|
function testSelection(editor, data) {
|
|
assert.equal(getSelection(editor) + "", data + "");
|
|
}
|
|
|
|
exports.recordTestCase = function() {
|
|
exports.addGlobals();
|
|
var editor = window.editor;
|
|
var testcase = window.testcase = [];
|
|
var assert;
|
|
|
|
testcase.push({
|
|
type: "setValue",
|
|
data: editor.getValue()
|
|
}, {
|
|
type: "setSelection",
|
|
data: getSelection(editor)
|
|
});
|
|
editor.commands.on("afterExec", function(e) {
|
|
testcase.push({
|
|
type: "exec",
|
|
data: e
|
|
});
|
|
testcase.push({
|
|
type: "value",
|
|
data: editor.getValue()
|
|
});
|
|
testcase.push({
|
|
type: "selection",
|
|
data: getSelection(editor)
|
|
});
|
|
});
|
|
editor.on("mouseup", function() {
|
|
testcase.push({
|
|
type: "setSelection",
|
|
data: getSelection(editor)
|
|
});
|
|
});
|
|
|
|
testcase.toString = function() {
|
|
var lastValue = "";
|
|
var str = this.map(function(x) {
|
|
var data = x.data;
|
|
switch (x.type) {
|
|
case "exec":
|
|
return 'editor.execCommand("'
|
|
+ data.command.name
|
|
+ (data.args ? '", ' + JSON.stringify(data.args) : '"')
|
|
+ ')';
|
|
case "setSelection":
|
|
return 'setSelection(editor, ' + JSON.stringify(data) + ')';
|
|
case "setValue":
|
|
if (lastValue != data) {
|
|
lastValue = data;
|
|
return 'editor.setValue(' + JSON.stringify(data) + ', -1)';
|
|
}
|
|
return;
|
|
case "selection":
|
|
return 'testSelection(editor, ' + JSON.stringify(data) + ')';
|
|
case "value":
|
|
if (lastValue != data) {
|
|
lastValue = data;
|
|
return 'assert.equal('
|
|
+ 'editor.getValue(),'
|
|
+ JSON.stringify(data)
|
|
+ ')';
|
|
}
|
|
return;
|
|
}
|
|
}).filter(Boolean).join("\n");
|
|
|
|
return getSelection + "\n"
|
|
+ testSelection + "\n"
|
|
+ setSelection + "\n"
|
|
+ "\n" + str + "\n";
|
|
};
|
|
};
|
|
|
|
|
|
});
|
|
|
|
define("ace/ext/modelist",["require","exports","module"], function(require, exports, module) {
|
|
"use strict";
|
|
|
|
var modes = [];
|
|
function getModeForPath(path) {
|
|
var mode = modesByName.text;
|
|
var fileName = path.split(/[\/\\]/).pop();
|
|
for (var i = 0; i < modes.length; i++) {
|
|
if (modes[i].supportsFile(fileName)) {
|
|
mode = modes[i];
|
|
break;
|
|
}
|
|
}
|
|
return mode;
|
|
}
|
|
|
|
var Mode = function(name, caption, extensions) {
|
|
this.name = name;
|
|
this.caption = caption;
|
|
this.mode = "ace/mode/" + name;
|
|
this.extensions = extensions;
|
|
var re;
|
|
if (/\^/.test(extensions)) {
|
|
re = extensions.replace(/\|(\^)?/g, function(a, b){
|
|
return "$|" + (b ? "^" : "^.*\\.");
|
|
}) + "$";
|
|
} else {
|
|
re = "^.*\\.(" + extensions + ")$";
|
|
}
|
|
|
|
this.extRe = new RegExp(re, "gi");
|
|
};
|
|
|
|
Mode.prototype.supportsFile = function(filename) {
|
|
return filename.match(this.extRe);
|
|
};
|
|
var supportedModes = {
|
|
ABAP: ["abap"],
|
|
ABC: ["abc"],
|
|
ActionScript:["as"],
|
|
ADA: ["ada|adb"],
|
|
Apache_Conf: ["^htaccess|^htgroups|^htpasswd|^conf|htaccess|htgroups|htpasswd"],
|
|
AsciiDoc: ["asciidoc|adoc"],
|
|
Assembly_x86:["asm|a"],
|
|
AutoHotKey: ["ahk"],
|
|
BatchFile: ["bat|cmd"],
|
|
Bro: ["bro"],
|
|
C_Cpp: ["cpp|c|cc|cxx|h|hh|hpp|ino"],
|
|
C9Search: ["c9search_results"],
|
|
Cirru: ["cirru|cr"],
|
|
Clojure: ["clj|cljs"],
|
|
Cobol: ["CBL|COB"],
|
|
coffee: ["coffee|cf|cson|^Cakefile"],
|
|
ColdFusion: ["cfm"],
|
|
CSharp: ["cs"],
|
|
CSS: ["css"],
|
|
Curly: ["curly"],
|
|
D: ["d|di"],
|
|
Dart: ["dart"],
|
|
Diff: ["diff|patch"],
|
|
Dockerfile: ["^Dockerfile"],
|
|
Dot: ["dot"],
|
|
Drools: ["drl"],
|
|
Dummy: ["dummy"],
|
|
DummySyntax: ["dummy"],
|
|
Eiffel: ["e|ge"],
|
|
EJS: ["ejs"],
|
|
Elixir: ["ex|exs"],
|
|
Elm: ["elm"],
|
|
Erlang: ["erl|hrl"],
|
|
Forth: ["frt|fs|ldr|fth|4th"],
|
|
Fortran: ["f|f90"],
|
|
FTL: ["ftl"],
|
|
Gcode: ["gcode"],
|
|
Gherkin: ["feature"],
|
|
Gitignore: ["^.gitignore"],
|
|
Glsl: ["glsl|frag|vert"],
|
|
Gobstones: ["gbs"],
|
|
golang: ["go"],
|
|
GraphQLSchema: ["gql"],
|
|
Groovy: ["groovy"],
|
|
HAML: ["haml"],
|
|
Handlebars: ["hbs|handlebars|tpl|mustache"],
|
|
Haskell: ["hs"],
|
|
Haskell_Cabal: ["cabal"],
|
|
haXe: ["hx"],
|
|
Hjson: ["hjson"],
|
|
HTML: ["html|htm|xhtml"],
|
|
HTML_Elixir: ["eex|html.eex"],
|
|
HTML_Ruby: ["erb|rhtml|html.erb"],
|
|
INI: ["ini|conf|cfg|prefs"],
|
|
Io: ["io"],
|
|
Jack: ["jack"],
|
|
Jade: ["jade|pug"],
|
|
Java: ["java"],
|
|
JavaScript: ["js|jsm|jsx"],
|
|
JSON: ["json"],
|
|
JSONiq: ["jq"],
|
|
JSP: ["jsp"],
|
|
JSX: ["jsx"],
|
|
Julia: ["jl"],
|
|
Kotlin: ["kt|kts"],
|
|
LaTeX: ["tex|latex|ltx|bib"],
|
|
LESS: ["less"],
|
|
Liquid: ["liquid"],
|
|
Lisp: ["lisp"],
|
|
LiveScript: ["ls"],
|
|
LogiQL: ["logic|lql"],
|
|
LSL: ["lsl"],
|
|
Lua: ["lua"],
|
|
LuaPage: ["lp"],
|
|
Lucene: ["lucene"],
|
|
Makefile: ["^Makefile|^GNUmakefile|^makefile|^OCamlMakefile|make"],
|
|
Markdown: ["md|markdown"],
|
|
Mask: ["mask"],
|
|
MATLAB: ["matlab"],
|
|
Maze: ["mz"],
|
|
MEL: ["mel"],
|
|
MUSHCode: ["mc|mush"],
|
|
MySQL: ["mysql"],
|
|
Nix: ["nix"],
|
|
NSIS: ["nsi|nsh"],
|
|
ObjectiveC: ["m|mm"],
|
|
OCaml: ["ml|mli"],
|
|
Pascal: ["pas|p"],
|
|
Perl: ["pl|pm"],
|
|
pgSQL: ["pgsql"],
|
|
PHP: ["php|phtml|shtml|php3|php4|php5|phps|phpt|aw|ctp|module"],
|
|
Pig: ["pig"],
|
|
Powershell: ["ps1"],
|
|
Praat: ["praat|praatscript|psc|proc"],
|
|
Prolog: ["plg|prolog"],
|
|
Properties: ["properties"],
|
|
Protobuf: ["proto"],
|
|
Python: ["py"],
|
|
R: ["r"],
|
|
Razor: ["cshtml|asp"],
|
|
RDoc: ["Rd"],
|
|
RHTML: ["Rhtml"],
|
|
RST: ["rst"],
|
|
Ruby: ["rb|ru|gemspec|rake|^Guardfile|^Rakefile|^Gemfile"],
|
|
Rust: ["rs"],
|
|
SASS: ["sass"],
|
|
SCAD: ["scad"],
|
|
Scala: ["scala"],
|
|
Scheme: ["scm|sm|rkt|oak|scheme"],
|
|
SCSS: ["scss"],
|
|
SH: ["sh|bash|^.bashrc"],
|
|
SJS: ["sjs"],
|
|
Smarty: ["smarty|tpl"],
|
|
snippets: ["snippets"],
|
|
Soy_Template:["soy"],
|
|
Space: ["space"],
|
|
SQL: ["sql"],
|
|
SQLServer: ["sqlserver"],
|
|
Stylus: ["styl|stylus"],
|
|
SVG: ["svg"],
|
|
Swift: ["swift"],
|
|
Tcl: ["tcl"],
|
|
Tex: ["tex"],
|
|
Text: ["txt"],
|
|
Textile: ["textile"],
|
|
Toml: ["toml"],
|
|
TSX: ["tsx"],
|
|
Twig: ["twig|swig"],
|
|
Typescript: ["ts|typescript|str"],
|
|
Vala: ["vala"],
|
|
VBScript: ["vbs|vb"],
|
|
Velocity: ["vm"],
|
|
Verilog: ["v|vh|sv|svh"],
|
|
VHDL: ["vhd|vhdl"],
|
|
Wollok: ["wlk|wpgm|wtest"],
|
|
XML: ["xml|rdf|rss|wsdl|xslt|atom|mathml|mml|xul|xbl|xaml"],
|
|
XQuery: ["xq"],
|
|
YAML: ["yaml|yml"],
|
|
Django: ["html"]
|
|
};
|
|
|
|
var nameOverrides = {
|
|
ObjectiveC: "Objective-C",
|
|
CSharp: "C#",
|
|
golang: "Go",
|
|
C_Cpp: "C and C++",
|
|
coffee: "CoffeeScript",
|
|
HTML_Ruby: "HTML (Ruby)",
|
|
HTML_Elixir: "HTML (Elixir)",
|
|
FTL: "FreeMarker"
|
|
};
|
|
var modesByName = {};
|
|
for (var name in supportedModes) {
|
|
var data = supportedModes[name];
|
|
var displayName = (nameOverrides[name] || name).replace(/_/g, " ");
|
|
var filename = name.toLowerCase();
|
|
var mode = new Mode(filename, displayName, data[0]);
|
|
modesByName[filename] = mode;
|
|
modes.push(mode);
|
|
}
|
|
|
|
module.exports = {
|
|
getModeForPath: getModeForPath,
|
|
modes: modes,
|
|
modesByName: modesByName
|
|
};
|
|
|
|
});
|
|
|
|
define("kitchen-sink/file_drop",["require","exports","module","ace/config","ace/lib/event","ace/ext/modelist","ace/editor"], function(require, exports, module) {
|
|
|
|
var config = require("ace/config");
|
|
var event = require("ace/lib/event");
|
|
var modelist = require("ace/ext/modelist");
|
|
|
|
module.exports = function(editor) {
|
|
event.addListener(editor.container, "dragover", function(e) {
|
|
var types = e.dataTransfer.types;
|
|
if (types && Array.prototype.indexOf.call(types, 'Files') !== -1)
|
|
return event.preventDefault(e);
|
|
});
|
|
|
|
event.addListener(editor.container, "drop", function(e) {
|
|
var file;
|
|
try {
|
|
file = e.dataTransfer.files[0];
|
|
if (window.FileReader) {
|
|
var reader = new FileReader();
|
|
reader.onload = function() {
|
|
var mode = modelist.getModeForPath(file.name);
|
|
editor.session.doc.setValue(reader.result);
|
|
editor.session.setMode(mode.mode);
|
|
editor.session.modeName = mode.name;
|
|
};
|
|
reader.readAsText(file);
|
|
}
|
|
return event.preventDefault(e);
|
|
} catch(err) {
|
|
return event.stopEvent(e);
|
|
}
|
|
});
|
|
};
|
|
|
|
var Editor = require("ace/editor").Editor;
|
|
config.defineOptions(Editor.prototype, "editor", {
|
|
loadDroppedFile: {
|
|
set: function() { module.exports(this); },
|
|
value: true
|
|
}
|
|
});
|
|
|
|
});
|
|
|
|
define("ace/theme/textmate",["require","exports","module","ace/lib/dom"], function(require, exports, module) {
|
|
"use strict";
|
|
|
|
exports.isDark = false;
|
|
exports.cssClass = "ace-tm";
|
|
exports.cssText = ".ace-tm .ace_gutter {\
|
|
background: #f0f0f0;\
|
|
color: #333;\
|
|
}\
|
|
.ace-tm .ace_print-margin {\
|
|
width: 1px;\
|
|
background: #e8e8e8;\
|
|
}\
|
|
.ace-tm .ace_fold {\
|
|
background-color: #6B72E6;\
|
|
}\
|
|
.ace-tm {\
|
|
background-color: #FFFFFF;\
|
|
color: black;\
|
|
}\
|
|
.ace-tm .ace_cursor {\
|
|
color: black;\
|
|
}\
|
|
.ace-tm .ace_invisible {\
|
|
color: rgb(191, 191, 191);\
|
|
}\
|
|
.ace-tm .ace_storage,\
|
|
.ace-tm .ace_keyword {\
|
|
color: blue;\
|
|
}\
|
|
.ace-tm .ace_constant {\
|
|
color: rgb(197, 6, 11);\
|
|
}\
|
|
.ace-tm .ace_constant.ace_buildin {\
|
|
color: rgb(88, 72, 246);\
|
|
}\
|
|
.ace-tm .ace_constant.ace_language {\
|
|
color: rgb(88, 92, 246);\
|
|
}\
|
|
.ace-tm .ace_constant.ace_library {\
|
|
color: rgb(6, 150, 14);\
|
|
}\
|
|
.ace-tm .ace_invalid {\
|
|
background-color: rgba(255, 0, 0, 0.1);\
|
|
color: red;\
|
|
}\
|
|
.ace-tm .ace_support.ace_function {\
|
|
color: rgb(60, 76, 114);\
|
|
}\
|
|
.ace-tm .ace_support.ace_constant {\
|
|
color: rgb(6, 150, 14);\
|
|
}\
|
|
.ace-tm .ace_support.ace_type,\
|
|
.ace-tm .ace_support.ace_class {\
|
|
color: rgb(109, 121, 222);\
|
|
}\
|
|
.ace-tm .ace_keyword.ace_operator {\
|
|
color: rgb(104, 118, 135);\
|
|
}\
|
|
.ace-tm .ace_string {\
|
|
color: rgb(3, 106, 7);\
|
|
}\
|
|
.ace-tm .ace_comment {\
|
|
color: rgb(76, 136, 107);\
|
|
}\
|
|
.ace-tm .ace_comment.ace_doc {\
|
|
color: rgb(0, 102, 255);\
|
|
}\
|
|
.ace-tm .ace_comment.ace_doc.ace_tag {\
|
|
color: rgb(128, 159, 191);\
|
|
}\
|
|
.ace-tm .ace_constant.ace_numeric {\
|
|
color: rgb(0, 0, 205);\
|
|
}\
|
|
.ace-tm .ace_variable {\
|
|
color: rgb(49, 132, 149);\
|
|
}\
|
|
.ace-tm .ace_xml-pe {\
|
|
color: rgb(104, 104, 91);\
|
|
}\
|
|
.ace-tm .ace_entity.ace_name.ace_function {\
|
|
color: #0000A2;\
|
|
}\
|
|
.ace-tm .ace_heading {\
|
|
color: rgb(12, 7, 255);\
|
|
}\
|
|
.ace-tm .ace_list {\
|
|
color:rgb(185, 6, 144);\
|
|
}\
|
|
.ace-tm .ace_meta.ace_tag {\
|
|
color:rgb(0, 22, 142);\
|
|
}\
|
|
.ace-tm .ace_string.ace_regex {\
|
|
color: rgb(255, 0, 0)\
|
|
}\
|
|
.ace-tm .ace_marker-layer .ace_selection {\
|
|
background: rgb(181, 213, 255);\
|
|
}\
|
|
.ace-tm.ace_multiselect .ace_selection.ace_start {\
|
|
box-shadow: 0 0 3px 0px white;\
|
|
}\
|
|
.ace-tm .ace_marker-layer .ace_step {\
|
|
background: rgb(252, 255, 0);\
|
|
}\
|
|
.ace-tm .ace_marker-layer .ace_stack {\
|
|
background: rgb(164, 229, 101);\
|
|
}\
|
|
.ace-tm .ace_marker-layer .ace_bracket {\
|
|
margin: -1px 0 0 -1px;\
|
|
border: 1px solid rgb(192, 192, 192);\
|
|
}\
|
|
.ace-tm .ace_marker-layer .ace_active-line {\
|
|
background: rgba(0, 0, 0, 0.07);\
|
|
}\
|
|
.ace-tm .ace_gutter-active-line {\
|
|
background-color : #dcdcdc;\
|
|
}\
|
|
.ace-tm .ace_marker-layer .ace_selected-word {\
|
|
background: rgb(250, 250, 255);\
|
|
border: 1px solid rgb(200, 200, 250);\
|
|
}\
|
|
.ace-tm .ace_indent-guide {\
|
|
background: url(\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAACCAYAAACZgbYnAAAAE0lEQVQImWP4////f4bLly//BwAmVgd1/w11/gAAAABJRU5ErkJggg==\") right repeat-y;\
|
|
}\
|
|
";
|
|
|
|
var dom = require("../lib/dom");
|
|
dom.importCssString(exports.cssText, exports.cssClass);
|
|
});
|
|
|
|
define("ace/ext/whitespace",["require","exports","module","ace/lib/lang"], function(require, exports, module) {
|
|
"use strict";
|
|
|
|
var lang = require("../lib/lang");
|
|
exports.$detectIndentation = function(lines, fallback) {
|
|
var stats = [];
|
|
var changes = [];
|
|
var tabIndents = 0;
|
|
var prevSpaces = 0;
|
|
var max = Math.min(lines.length, 1000);
|
|
for (var i = 0; i < max; i++) {
|
|
var line = lines[i];
|
|
if (!/^\s*[^*+\-\s]/.test(line))
|
|
continue;
|
|
|
|
if (line[0] == "\t") {
|
|
tabIndents++;
|
|
prevSpaces = -Number.MAX_VALUE;
|
|
} else {
|
|
var spaces = line.match(/^ */)[0].length;
|
|
if (spaces && line[spaces] != "\t") {
|
|
var diff = spaces - prevSpaces;
|
|
if (diff > 0 && !(prevSpaces%diff) && !(spaces%diff))
|
|
changes[diff] = (changes[diff] || 0) + 1;
|
|
|
|
stats[spaces] = (stats[spaces] || 0) + 1;
|
|
}
|
|
prevSpaces = spaces;
|
|
}
|
|
while (i < max && line[line.length - 1] == "\\")
|
|
line = lines[i++];
|
|
}
|
|
|
|
function getScore(indent) {
|
|
var score = 0;
|
|
for (var i = indent; i < stats.length; i += indent)
|
|
score += stats[i] || 0;
|
|
return score;
|
|
}
|
|
|
|
var changesTotal = changes.reduce(function(a,b){return a+b}, 0);
|
|
|
|
var first = {score: 0, length: 0};
|
|
var spaceIndents = 0;
|
|
for (var i = 1; i < 12; i++) {
|
|
var score = getScore(i);
|
|
if (i == 1) {
|
|
spaceIndents = score;
|
|
score = stats[1] ? 0.9 : 0.8;
|
|
if (!stats.length)
|
|
score = 0;
|
|
} else
|
|
score /= spaceIndents;
|
|
|
|
if (changes[i])
|
|
score += changes[i] / changesTotal;
|
|
|
|
if (score > first.score)
|
|
first = {score: score, length: i};
|
|
}
|
|
|
|
if (first.score && first.score > 1.4)
|
|
var tabLength = first.length;
|
|
|
|
if (tabIndents > spaceIndents + 1) {
|
|
if (tabLength == 1 || spaceIndents < tabIndents / 4 || first.score < 1.8)
|
|
tabLength = undefined;
|
|
return {ch: "\t", length: tabLength};
|
|
}
|
|
if (spaceIndents > tabIndents + 1)
|
|
return {ch: " ", length: tabLength};
|
|
};
|
|
|
|
exports.detectIndentation = function(session) {
|
|
var lines = session.getLines(0, 1000);
|
|
var indent = exports.$detectIndentation(lines) || {};
|
|
|
|
if (indent.ch)
|
|
session.setUseSoftTabs(indent.ch == " ");
|
|
|
|
if (indent.length)
|
|
session.setTabSize(indent.length);
|
|
return indent;
|
|
};
|
|
exports.trimTrailingSpace = function(session, options) {
|
|
var doc = session.getDocument();
|
|
var lines = doc.getAllLines();
|
|
|
|
var min = options && options.trimEmpty ? -1 : 0;
|
|
var cursors = [], ci = -1;
|
|
if (options && options.keepCursorPosition) {
|
|
if (session.selection.rangeCount) {
|
|
session.selection.rangeList.ranges.forEach(function(x, i, ranges) {
|
|
var next = ranges[i + 1];
|
|
if (next && next.cursor.row == x.cursor.row)
|
|
return;
|
|
cursors.push(x.cursor);
|
|
});
|
|
} else {
|
|
cursors.push(session.selection.getCursor());
|
|
}
|
|
ci = 0;
|
|
}
|
|
var cursorRow = cursors[ci] && cursors[ci].row;
|
|
|
|
for (var i = 0, l=lines.length; i < l; i++) {
|
|
var line = lines[i];
|
|
var index = line.search(/\s+$/);
|
|
|
|
if (i == cursorRow) {
|
|
if (index < cursors[ci].column && index > min)
|
|
index = cursors[ci].column;
|
|
ci++;
|
|
cursorRow = cursors[ci] ? cursors[ci].row : -1;
|
|
}
|
|
|
|
if (index > min)
|
|
doc.removeInLine(i, index, line.length);
|
|
}
|
|
};
|
|
|
|
exports.convertIndentation = function(session, ch, len) {
|
|
var oldCh = session.getTabString()[0];
|
|
var oldLen = session.getTabSize();
|
|
if (!len) len = oldLen;
|
|
if (!ch) ch = oldCh;
|
|
|
|
var tab = ch == "\t" ? ch: lang.stringRepeat(ch, len);
|
|
|
|
var doc = session.doc;
|
|
var lines = doc.getAllLines();
|
|
|
|
var cache = {};
|
|
var spaceCache = {};
|
|
for (var i = 0, l=lines.length; i < l; i++) {
|
|
var line = lines[i];
|
|
var match = line.match(/^\s*/)[0];
|
|
if (match) {
|
|
var w = session.$getStringScreenWidth(match)[0];
|
|
var tabCount = Math.floor(w/oldLen);
|
|
var reminder = w%oldLen;
|
|
var toInsert = cache[tabCount] || (cache[tabCount] = lang.stringRepeat(tab, tabCount));
|
|
toInsert += spaceCache[reminder] || (spaceCache[reminder] = lang.stringRepeat(" ", reminder));
|
|
|
|
if (toInsert != match) {
|
|
doc.removeInLine(i, 0, match.length);
|
|
doc.insertInLine({row: i, column: 0}, toInsert);
|
|
}
|
|
}
|
|
}
|
|
session.setTabSize(len);
|
|
session.setUseSoftTabs(ch == " ");
|
|
};
|
|
|
|
exports.$parseStringArg = function(text) {
|
|
var indent = {};
|
|
if (/t/.test(text))
|
|
indent.ch = "\t";
|
|
else if (/s/.test(text))
|
|
indent.ch = " ";
|
|
var m = text.match(/\d+/);
|
|
if (m)
|
|
indent.length = parseInt(m[0], 10);
|
|
return indent;
|
|
};
|
|
|
|
exports.$parseArg = function(arg) {
|
|
if (!arg)
|
|
return {};
|
|
if (typeof arg == "string")
|
|
return exports.$parseStringArg(arg);
|
|
if (typeof arg.text == "string")
|
|
return exports.$parseStringArg(arg.text);
|
|
return arg;
|
|
};
|
|
|
|
exports.commands = [{
|
|
name: "detectIndentation",
|
|
exec: function(editor) {
|
|
exports.detectIndentation(editor.session);
|
|
}
|
|
}, {
|
|
name: "trimTrailingSpace",
|
|
exec: function(editor) {
|
|
exports.trimTrailingSpace(editor.session);
|
|
}
|
|
}, {
|
|
name: "convertIndentation",
|
|
exec: function(editor, arg) {
|
|
var indent = exports.$parseArg(arg);
|
|
exports.convertIndentation(editor.session, indent.ch, indent.length);
|
|
}
|
|
}, {
|
|
name: "setIndentation",
|
|
exec: function(editor, arg) {
|
|
var indent = exports.$parseArg(arg);
|
|
indent.length && editor.session.setTabSize(indent.length);
|
|
indent.ch && editor.session.setUseSoftTabs(indent.ch == " ");
|
|
}
|
|
}];
|
|
|
|
});
|
|
|
|
define("kitchen-sink/doclist",["require","exports","module","ace/edit_session","ace/undomanager","ace/lib/net","ace/ext/modelist"], function(require, exports, module) {
|
|
"use strict";
|
|
|
|
var EditSession = require("ace/edit_session").EditSession;
|
|
var UndoManager = require("ace/undomanager").UndoManager;
|
|
var net = require("ace/lib/net");
|
|
|
|
var modelist = require("ace/ext/modelist");
|
|
var fileCache = {};
|
|
|
|
function initDoc(file, path, doc) {
|
|
if (doc.prepare)
|
|
file = doc.prepare(file);
|
|
|
|
var session = new EditSession(file);
|
|
session.setUndoManager(new UndoManager());
|
|
doc.session = session;
|
|
doc.path = path;
|
|
session.name = doc.name;
|
|
if (doc.wrapped) {
|
|
session.setUseWrapMode(true);
|
|
session.setWrapLimitRange(80, 80);
|
|
}
|
|
var mode = modelist.getModeForPath(path);
|
|
session.modeName = mode.name;
|
|
session.setMode(mode.mode);
|
|
return session;
|
|
}
|
|
|
|
|
|
function makeHuge(txt) {
|
|
for (var i = 0; i < 5; i++)
|
|
txt += txt;
|
|
return txt;
|
|
}
|
|
|
|
var docs = {
|
|
"docs/javascript.js": {order: 1, name: "JavaScript"},
|
|
|
|
"docs/latex.tex": {name: "LaTeX", wrapped: true},
|
|
"docs/markdown.md": {name: "Markdown", wrapped: true},
|
|
"docs/mushcode.mc": {name: "MUSHCode", wrapped: true},
|
|
"docs/pgsql.pgsql": {name: "pgSQL", wrapped: true},
|
|
"docs/plaintext.txt": {name: "Plain Text", prepare: makeHuge, wrapped: true},
|
|
"docs/sql.sql": {name: "SQL", wrapped: true},
|
|
|
|
"docs/textile.textile": {name: "Textile", wrapped: true},
|
|
|
|
"docs/c9search.c9search_results": "C9 Search Results",
|
|
"docs/mel.mel": "MEL",
|
|
"docs/Nix.nix": "Nix"
|
|
};
|
|
|
|
var ownSource = {
|
|
};
|
|
|
|
var hugeDocs = require.toUrl ? {
|
|
"build/src/ace.js": "",
|
|
"build/src-min/ace.js": ""
|
|
} : {
|
|
"src/ace.js": "",
|
|
"src-min/ace.js": ""
|
|
};
|
|
|
|
modelist.modes.forEach(function(m) {
|
|
var ext = m.extensions.split("|")[0];
|
|
if (ext[0] === "^") {
|
|
path = ext.substr(1);
|
|
} else {
|
|
var path = m.name + "." + ext;
|
|
}
|
|
path = "docs/" + path;
|
|
if (!docs[path]) {
|
|
docs[path] = {name: m.caption};
|
|
} else if (typeof docs[path] == "object" && !docs[path].name) {
|
|
docs[path].name = m.caption;
|
|
}
|
|
});
|
|
|
|
|
|
|
|
if (window.require && window.require.s) try {
|
|
for (var path in window.require.s.contexts._.defined) {
|
|
if (path.indexOf("!") != -1)
|
|
path = path.split("!").pop();
|
|
else
|
|
path = path + ".js";
|
|
ownSource[path] = "";
|
|
}
|
|
} catch(e) {}
|
|
|
|
function sort(list) {
|
|
return list.sort(function(a, b) {
|
|
var cmp = (b.order || 0) - (a.order || 0);
|
|
return cmp || a.name && a.name.localeCompare(b.name);
|
|
});
|
|
}
|
|
|
|
function prepareDocList(docs) {
|
|
var list = [];
|
|
for (var path in docs) {
|
|
var doc = docs[path];
|
|
if (typeof doc != "object")
|
|
doc = {name: doc || path};
|
|
|
|
doc.path = path;
|
|
doc.desc = doc.name.replace(/^(ace|docs|demo|build)\//, "");
|
|
if (doc.desc.length > 18)
|
|
doc.desc = doc.desc.slice(0, 7) + ".." + doc.desc.slice(-9);
|
|
|
|
fileCache[doc.name] = doc;
|
|
list.push(doc);
|
|
}
|
|
|
|
return list;
|
|
}
|
|
|
|
function loadDoc(name, callback) {
|
|
var doc = fileCache[name];
|
|
if (!doc)
|
|
return callback(null);
|
|
|
|
if (doc.session)
|
|
return callback(doc.session);
|
|
var path = doc.path;
|
|
var parts = path.split("/");
|
|
if (parts[0] == "docs")
|
|
path = "demo/kitchen-sink/" + path;
|
|
else if (parts[0] == "ace")
|
|
path = "lib/" + path;
|
|
|
|
net.get(path, function(x) {
|
|
initDoc(x, path, doc);
|
|
callback(doc.session);
|
|
});
|
|
}
|
|
|
|
function saveDoc(name, callback) {
|
|
var doc = fileCache[name] || name;
|
|
if (!doc || !doc.session)
|
|
return callback("Unknown document: " + name);
|
|
|
|
var path = doc.path;
|
|
var parts = path.split("/");
|
|
if (parts[0] == "docs")
|
|
path = "demo/kitchen-sink/" + path;
|
|
else if (parts[0] == "ace")
|
|
path = "lib/" + path;
|
|
|
|
upload(path, doc.session.getValue(), callback);
|
|
}
|
|
|
|
function upload(url, data, callback) {
|
|
var absUrl = net.qualifyURL(url);
|
|
if (/^file:/.test(absUrl))
|
|
absUrl = "http://localhost:8888/" + url;
|
|
url = absUrl
|
|
if (!/^https?:/.test(url))
|
|
return callback(new Error("Unsupported url scheme"));
|
|
var xhr = new XMLHttpRequest();
|
|
xhr.open("PUT", url, true);
|
|
xhr.onreadystatechange = function () {
|
|
if (xhr.readyState === 4) {
|
|
callback(!/^2../.test(xhr.status));
|
|
}
|
|
};
|
|
xhr.send(data);
|
|
};
|
|
|
|
|
|
module.exports = {
|
|
fileCache: fileCache,
|
|
docs: sort(prepareDocList(docs)),
|
|
ownSource: prepareDocList(ownSource),
|
|
hugeDocs: prepareDocList(hugeDocs),
|
|
initDoc: initDoc,
|
|
loadDoc: loadDoc,
|
|
saveDoc: saveDoc
|
|
};
|
|
module.exports.all = {
|
|
"Mode Examples": module.exports.docs,
|
|
"Huge documents": module.exports.hugeDocs,
|
|
"own source": module.exports.ownSource
|
|
};
|
|
|
|
});
|
|
|
|
define("ace/ext/themelist",["require","exports","module","ace/lib/fixoldbrowsers"], function(require, exports, module) {
|
|
"use strict";
|
|
require("ace/lib/fixoldbrowsers");
|
|
|
|
var themeData = [
|
|
["Chrome" ],
|
|
["Clouds" ],
|
|
["Crimson Editor" ],
|
|
["Dawn" ],
|
|
["Dreamweaver" ],
|
|
["Eclipse" ],
|
|
["GitHub" ],
|
|
["IPlastic" ],
|
|
["Solarized Light"],
|
|
["TextMate" ],
|
|
["Tomorrow" ],
|
|
["XCode" ],
|
|
["Kuroir"],
|
|
["KatzenMilch"],
|
|
["SQL Server" ,"sqlserver" , "light"],
|
|
["Ambiance" ,"ambiance" , "dark"],
|
|
["Chaos" ,"chaos" , "dark"],
|
|
["Clouds Midnight" ,"clouds_midnight" , "dark"],
|
|
["Cobalt" ,"cobalt" , "dark"],
|
|
["Gruvbox" ,"gruvbox" , "dark"],
|
|
["Green on Black" ,"gob" , "dark"],
|
|
["idle Fingers" ,"idle_fingers" , "dark"],
|
|
["krTheme" ,"kr_theme" , "dark"],
|
|
["Merbivore" ,"merbivore" , "dark"],
|
|
["Merbivore Soft" ,"merbivore_soft" , "dark"],
|
|
["Mono Industrial" ,"mono_industrial" , "dark"],
|
|
["Monokai" ,"monokai" , "dark"],
|
|
["Pastel on dark" ,"pastel_on_dark" , "dark"],
|
|
["Solarized Dark" ,"solarized_dark" , "dark"],
|
|
["Terminal" ,"terminal" , "dark"],
|
|
["Tomorrow Night" ,"tomorrow_night" , "dark"],
|
|
["Tomorrow Night Blue" ,"tomorrow_night_blue" , "dark"],
|
|
["Tomorrow Night Bright","tomorrow_night_bright" , "dark"],
|
|
["Tomorrow Night 80s" ,"tomorrow_night_eighties" , "dark"],
|
|
["Twilight" ,"twilight" , "dark"],
|
|
["Vibrant Ink" ,"vibrant_ink" , "dark"]
|
|
];
|
|
|
|
|
|
exports.themesByName = {};
|
|
exports.themes = themeData.map(function(data) {
|
|
var name = data[1] || data[0].replace(/ /g, "_").toLowerCase();
|
|
var theme = {
|
|
caption: data[0],
|
|
theme: "ace/theme/" + name,
|
|
isDark: data[2] == "dark",
|
|
name: name
|
|
};
|
|
exports.themesByName[name] = theme;
|
|
return theme;
|
|
});
|
|
|
|
});
|
|
|
|
define("kitchen-sink/layout",["require","exports","module","ace/lib/dom","ace/lib/event","ace/edit_session","ace/undomanager","ace/virtual_renderer","ace/editor","ace/multi_select","ace/theme/textmate"], function(require, exports, module) {
|
|
"use strict";
|
|
|
|
var dom = require("ace/lib/dom");
|
|
var event = require("ace/lib/event");
|
|
|
|
var EditSession = require("ace/edit_session").EditSession;
|
|
var UndoManager = require("ace/undomanager").UndoManager;
|
|
var Renderer = require("ace/virtual_renderer").VirtualRenderer;
|
|
var Editor = require("ace/editor").Editor;
|
|
var MultiSelect = require("ace/multi_select").MultiSelect;
|
|
|
|
dom.importCssString("\
|
|
splitter {\
|
|
border: 1px solid #C6C6D2;\
|
|
width: 0px;\
|
|
cursor: ew-resize;\
|
|
z-index:10}\
|
|
splitter:hover {\
|
|
margin-left: -2px;\
|
|
width:3px;\
|
|
border-color: #B5B4E0;\
|
|
}\
|
|
", "splitEditor");
|
|
|
|
exports.edit = function(el) {
|
|
if (typeof(el) == "string")
|
|
el = document.getElementById(el);
|
|
|
|
var editor = new Editor(new Renderer(el, require("ace/theme/textmate")));
|
|
|
|
editor.resize();
|
|
event.addListener(window, "resize", function() {
|
|
editor.resize();
|
|
});
|
|
return editor;
|
|
};
|
|
|
|
|
|
var SplitRoot = function(el, theme, position, getSize) {
|
|
el.style.position = position || "relative";
|
|
this.container = el;
|
|
this.getSize = getSize || this.getSize;
|
|
this.resize = this.$resize.bind(this);
|
|
|
|
event.addListener(el.ownerDocument.defaultView, "resize", this.resize);
|
|
this.editor = this.createEditor();
|
|
};
|
|
|
|
(function(){
|
|
this.createEditor = function() {
|
|
var el = document.createElement("div");
|
|
el.className = this.$editorCSS;
|
|
el.style.cssText = "position: absolute; top:0px; bottom:0px";
|
|
this.$container.appendChild(el);
|
|
var session = new EditSession("");
|
|
var editor = new Editor(new Renderer(el, this.$theme));
|
|
|
|
this.$editors.push(editor);
|
|
editor.setFontSize(this.$fontSize);
|
|
return editor;
|
|
};
|
|
this.$resize = function() {
|
|
var size = this.getSize(this.container);
|
|
this.rect = {
|
|
x: size.left,
|
|
y: size.top,
|
|
w: size.width,
|
|
h: size.height
|
|
};
|
|
this.item.resize(this.rect);
|
|
};
|
|
this.getSize = function(el) {
|
|
return el.getBoundingClientRect();
|
|
};
|
|
this.destroy = function() {
|
|
var win = this.container.ownerDocument.defaultView;
|
|
event.removeListener(win, "resize", this.resize);
|
|
};
|
|
|
|
|
|
}).call(SplitRoot.prototype);
|
|
|
|
|
|
|
|
var Split = function(){
|
|
|
|
};
|
|
(function(){
|
|
this.execute = function(options) {
|
|
this.$u.execute(options);
|
|
};
|
|
|
|
}).call(Split.prototype);
|
|
|
|
|
|
|
|
exports.singleLineEditor = function(el) {
|
|
var renderer = new Renderer(el);
|
|
el.style.overflow = "hidden";
|
|
|
|
renderer.screenToTextCoordinates = function(x, y) {
|
|
var pos = this.pixelToScreenCoordinates(x, y);
|
|
return this.session.screenToDocumentPosition(
|
|
Math.min(this.session.getScreenLength() - 1, Math.max(pos.row, 0)),
|
|
Math.max(pos.column, 0)
|
|
);
|
|
};
|
|
|
|
renderer.$maxLines = 4;
|
|
|
|
renderer.setStyle("ace_one-line");
|
|
var editor = new Editor(renderer);
|
|
editor.session.setUndoManager(new UndoManager());
|
|
|
|
editor.setShowPrintMargin(false);
|
|
editor.renderer.setShowGutter(false);
|
|
editor.renderer.setHighlightGutterLine(false);
|
|
editor.$mouseHandler.$focusWaitTimout = 0;
|
|
|
|
return editor;
|
|
};
|
|
|
|
|
|
|
|
});
|
|
|
|
define("kitchen-sink/token_tooltip",["require","exports","module","ace/lib/dom","ace/lib/oop","ace/lib/event","ace/range","ace/tooltip"], function(require, exports, module) {
|
|
"use strict";
|
|
|
|
var dom = require("ace/lib/dom");
|
|
var oop = require("ace/lib/oop");
|
|
var event = require("ace/lib/event");
|
|
var Range = require("ace/range").Range;
|
|
var Tooltip = require("ace/tooltip").Tooltip;
|
|
|
|
function TokenTooltip (editor) {
|
|
if (editor.tokenTooltip)
|
|
return;
|
|
Tooltip.call(this, editor.container);
|
|
editor.tokenTooltip = this;
|
|
this.editor = editor;
|
|
|
|
this.update = this.update.bind(this);
|
|
this.onMouseMove = this.onMouseMove.bind(this);
|
|
this.onMouseOut = this.onMouseOut.bind(this);
|
|
event.addListener(editor.renderer.scroller, "mousemove", this.onMouseMove);
|
|
event.addListener(editor.renderer.content, "mouseout", this.onMouseOut);
|
|
}
|
|
|
|
oop.inherits(TokenTooltip, Tooltip);
|
|
|
|
(function(){
|
|
this.token = {};
|
|
this.range = new Range();
|
|
|
|
this.update = function() {
|
|
this.$timer = null;
|
|
|
|
var r = this.editor.renderer;
|
|
if (this.lastT - (r.timeStamp || 0) > 1000) {
|
|
r.rect = null;
|
|
r.timeStamp = this.lastT;
|
|
this.maxHeight = window.innerHeight;
|
|
this.maxWidth = window.innerWidth;
|
|
}
|
|
|
|
var canvasPos = r.rect || (r.rect = r.scroller.getBoundingClientRect());
|
|
var offset = (this.x + r.scrollLeft - canvasPos.left - r.$padding) / r.characterWidth;
|
|
var row = Math.floor((this.y + r.scrollTop - canvasPos.top) / r.lineHeight);
|
|
var col = Math.round(offset);
|
|
|
|
var screenPos = {row: row, column: col, side: offset - col > 0 ? 1 : -1};
|
|
var session = this.editor.session;
|
|
var docPos = session.screenToDocumentPosition(screenPos.row, screenPos.column);
|
|
var token = session.getTokenAt(docPos.row, docPos.column);
|
|
|
|
if (!token && !session.getLine(docPos.row)) {
|
|
token = {
|
|
type: "",
|
|
value: "",
|
|
state: session.bgTokenizer.getState(0)
|
|
};
|
|
}
|
|
if (!token) {
|
|
session.removeMarker(this.marker);
|
|
this.hide();
|
|
return;
|
|
}
|
|
|
|
var tokenText = token.type;
|
|
if (token.state)
|
|
tokenText += "|" + token.state;
|
|
if (token.merge)
|
|
tokenText += "\n merge";
|
|
if (token.stateTransitions)
|
|
tokenText += "\n " + token.stateTransitions.join("\n ");
|
|
|
|
if (this.tokenText != tokenText) {
|
|
this.setText(tokenText);
|
|
this.width = this.getWidth();
|
|
this.height = this.getHeight();
|
|
this.tokenText = tokenText;
|
|
}
|
|
|
|
this.show(null, this.x, this.y);
|
|
|
|
this.token = token;
|
|
session.removeMarker(this.marker);
|
|
this.range = new Range(docPos.row, token.start, docPos.row, token.start + token.value.length);
|
|
this.marker = session.addMarker(this.range, "ace_bracket", "text");
|
|
};
|
|
|
|
this.onMouseMove = function(e) {
|
|
this.x = e.clientX;
|
|
this.y = e.clientY;
|
|
if (this.isOpen) {
|
|
this.lastT = e.timeStamp;
|
|
this.setPosition(this.x, this.y);
|
|
}
|
|
if (!this.$timer)
|
|
this.$timer = setTimeout(this.update, 100);
|
|
};
|
|
|
|
this.onMouseOut = function(e) {
|
|
if (e && e.currentTarget.contains(e.relatedTarget))
|
|
return;
|
|
this.hide();
|
|
this.editor.session.removeMarker(this.marker);
|
|
this.$timer = clearTimeout(this.$timer);
|
|
};
|
|
|
|
this.setPosition = function(x, y) {
|
|
if (x + 10 + this.width > this.maxWidth)
|
|
x = window.innerWidth - this.width - 10;
|
|
if (y > window.innerHeight * 0.75 || y + 20 + this.height > this.maxHeight)
|
|
y = y - this.height - 30;
|
|
|
|
Tooltip.prototype.setPosition.call(this, x + 10, y + 20);
|
|
};
|
|
|
|
this.destroy = function() {
|
|
this.onMouseOut();
|
|
event.removeListener(this.editor.renderer.scroller, "mousemove", this.onMouseMove);
|
|
event.removeListener(this.editor.renderer.content, "mouseout", this.onMouseOut);
|
|
delete this.editor.tokenTooltip;
|
|
};
|
|
|
|
}).call(TokenTooltip.prototype);
|
|
|
|
exports.TokenTooltip = TokenTooltip;
|
|
|
|
});
|
|
|
|
define("kitchen-sink/util",["require","exports","module","ace/lib/dom","ace/lib/event","ace/edit_session","ace/undomanager","ace/virtual_renderer","ace/editor","ace/multi_select"], function(require, exports, module) {
|
|
"use strict";
|
|
|
|
var dom = require("ace/lib/dom");
|
|
var event = require("ace/lib/event");
|
|
|
|
var EditSession = require("ace/edit_session").EditSession;
|
|
var UndoManager = require("ace/undomanager").UndoManager;
|
|
var Renderer = require("ace/virtual_renderer").VirtualRenderer;
|
|
var Editor = require("ace/editor").Editor;
|
|
var MultiSelect = require("ace/multi_select").MultiSelect;
|
|
|
|
var urlOptions = {}
|
|
try {
|
|
window.location.search.slice(1).split(/[&]/).forEach(function(e) {
|
|
var parts = e.split("=");
|
|
urlOptions[decodeURIComponent(parts[0])] = decodeURIComponent(parts[1]);
|
|
});
|
|
} catch(e) {
|
|
console.error(e);
|
|
}
|
|
exports.createEditor = function(el) {
|
|
return new Editor(new Renderer(el));
|
|
};
|
|
|
|
exports.createSplitEditor = function(el) {
|
|
if (typeof(el) == "string")
|
|
el = document.getElementById(el);
|
|
|
|
var e0 = document.createElement("div");
|
|
var s = document.createElement("splitter");
|
|
var e1 = document.createElement("div");
|
|
el.appendChild(e0);
|
|
el.appendChild(e1);
|
|
el.appendChild(s);
|
|
e0.style.position = e1.style.position = s.style.position = "absolute";
|
|
el.style.position = "relative";
|
|
var split = {$container: el};
|
|
|
|
split.editor0 = split[0] = new Editor(new Renderer(e0));
|
|
split.editor1 = split[1] = new Editor(new Renderer(e1));
|
|
split.splitter = s;
|
|
|
|
s.ratio = 0.5;
|
|
|
|
split.resize = function resize(){
|
|
var height = el.parentNode.clientHeight - el.offsetTop;
|
|
var total = el.clientWidth;
|
|
var w1 = total * s.ratio;
|
|
var w2 = total * (1- s.ratio);
|
|
s.style.left = w1 - 1 + "px";
|
|
s.style.height = el.style.height = height + "px";
|
|
|
|
var st0 = split[0].container.style;
|
|
var st1 = split[1].container.style;
|
|
st0.width = w1 + "px";
|
|
st1.width = w2 + "px";
|
|
st0.left = 0 + "px";
|
|
st1.left = w1 + "px";
|
|
|
|
st0.top = st1.top = "0px";
|
|
st0.height = st1.height = height + "px";
|
|
|
|
split[0].resize();
|
|
split[1].resize();
|
|
};
|
|
|
|
split.onMouseDown = function(e) {
|
|
var rect = el.getBoundingClientRect();
|
|
var x = e.clientX;
|
|
var y = e.clientY;
|
|
|
|
var button = e.button;
|
|
if (button !== 0) {
|
|
return;
|
|
}
|
|
|
|
var onMouseMove = function(e) {
|
|
x = e.clientX;
|
|
y = e.clientY;
|
|
};
|
|
var onResizeEnd = function(e) {
|
|
clearInterval(timerId);
|
|
};
|
|
|
|
var onResizeInterval = function() {
|
|
s.ratio = (x - rect.left) / rect.width;
|
|
split.resize();
|
|
};
|
|
|
|
event.capture(s, onMouseMove, onResizeEnd);
|
|
var timerId = setInterval(onResizeInterval, 40);
|
|
|
|
return e.preventDefault();
|
|
};
|
|
|
|
|
|
|
|
event.addListener(s, "mousedown", split.onMouseDown);
|
|
event.addListener(window, "resize", split.resize);
|
|
split.resize();
|
|
return split;
|
|
};
|
|
exports.stripLeadingComments = function(str) {
|
|
if(str.slice(0,2)=='/*') {
|
|
var j = str.indexOf('*/')+2;
|
|
str = str.substr(j);
|
|
}
|
|
return str.trim() + "\n";
|
|
};
|
|
exports.saveOption = function(el, val) {
|
|
if (!el.onchange && !el.onclick)
|
|
return;
|
|
|
|
if ("checked" in el) {
|
|
if (val !== undefined)
|
|
el.checked = val;
|
|
|
|
localStorage && localStorage.setItem(el.id, el.checked ? 1 : 0);
|
|
}
|
|
else {
|
|
if (val !== undefined)
|
|
el.value = val;
|
|
|
|
localStorage && localStorage.setItem(el.id, el.value);
|
|
}
|
|
};
|
|
|
|
exports.bindCheckbox = function(id, callback, noInit) {
|
|
if (typeof id == "string")
|
|
var el = document.getElementById(id);
|
|
else {
|
|
var el = id;
|
|
id = el.id;
|
|
}
|
|
var el = document.getElementById(id);
|
|
|
|
if (urlOptions[id])
|
|
el.checked = urlOptions[id] == "1";
|
|
else if (localStorage && localStorage.getItem(id))
|
|
el.checked = localStorage.getItem(id) == "1";
|
|
|
|
var onCheck = function() {
|
|
callback(!!el.checked);
|
|
exports.saveOption(el);
|
|
};
|
|
el.onclick = onCheck;
|
|
noInit || onCheck();
|
|
return el;
|
|
};
|
|
|
|
exports.bindDropdown = function(id, callback, noInit) {
|
|
if (typeof id == "string")
|
|
var el = document.getElementById(id);
|
|
else {
|
|
var el = id;
|
|
id = el.id;
|
|
}
|
|
|
|
if (urlOptions[id])
|
|
el.value = urlOptions[id];
|
|
else if (localStorage && localStorage.getItem(id))
|
|
el.value = localStorage.getItem(id);
|
|
|
|
var onChange = function() {
|
|
callback(el.value);
|
|
exports.saveOption(el);
|
|
};
|
|
|
|
el.onchange = onChange;
|
|
noInit || onChange();
|
|
};
|
|
|
|
exports.fillDropdown = function(el, values) {
|
|
if (typeof el == "string")
|
|
el = document.getElementById(el);
|
|
|
|
dropdown(values).forEach(function(e) {
|
|
el.appendChild(e);
|
|
});
|
|
};
|
|
|
|
function elt(tag, attributes, content) {
|
|
var el = dom.createElement(tag);
|
|
if (typeof content == "string") {
|
|
el.appendChild(document.createTextNode(content));
|
|
} else if (content) {
|
|
content.forEach(function(ch) {
|
|
el.appendChild(ch);
|
|
});
|
|
}
|
|
|
|
for (var i in attributes)
|
|
el.setAttribute(i, attributes[i]);
|
|
return el;
|
|
}
|
|
|
|
function optgroup(values) {
|
|
return values.map(function(item) {
|
|
if (typeof item == "string")
|
|
item = {name: item, caption: item};
|
|
return elt("option", {value: item.value || item.name}, item.caption || item.desc);
|
|
});
|
|
}
|
|
|
|
function dropdown(values) {
|
|
if (Array.isArray(values))
|
|
return optgroup(values);
|
|
|
|
return Object.keys(values).map(function(i) {
|
|
return elt("optgroup", {"label": i}, optgroup(values[i]));
|
|
});
|
|
}
|
|
|
|
|
|
});
|
|
|
|
define("ace/ext/elastic_tabstops_lite",["require","exports","module","ace/editor","ace/config"], function(require, exports, module) {
|
|
"use strict";
|
|
|
|
var ElasticTabstopsLite = function(editor) {
|
|
this.$editor = editor;
|
|
var self = this;
|
|
var changedRows = [];
|
|
var recordChanges = false;
|
|
this.onAfterExec = function() {
|
|
recordChanges = false;
|
|
self.processRows(changedRows);
|
|
changedRows = [];
|
|
};
|
|
this.onExec = function() {
|
|
recordChanges = true;
|
|
};
|
|
this.onChange = function(delta) {
|
|
if (recordChanges) {
|
|
if (changedRows.indexOf(delta.start.row) == -1)
|
|
changedRows.push(delta.start.row);
|
|
if (delta.end.row != delta.start.row)
|
|
changedRows.push(delta.end.row);
|
|
}
|
|
};
|
|
};
|
|
|
|
(function() {
|
|
this.processRows = function(rows) {
|
|
this.$inChange = true;
|
|
var checkedRows = [];
|
|
|
|
for (var r = 0, rowCount = rows.length; r < rowCount; r++) {
|
|
var row = rows[r];
|
|
|
|
if (checkedRows.indexOf(row) > -1)
|
|
continue;
|
|
|
|
var cellWidthObj = this.$findCellWidthsForBlock(row);
|
|
var cellWidths = this.$setBlockCellWidthsToMax(cellWidthObj.cellWidths);
|
|
var rowIndex = cellWidthObj.firstRow;
|
|
|
|
for (var w = 0, l = cellWidths.length; w < l; w++) {
|
|
var widths = cellWidths[w];
|
|
checkedRows.push(rowIndex);
|
|
this.$adjustRow(rowIndex, widths);
|
|
rowIndex++;
|
|
}
|
|
}
|
|
this.$inChange = false;
|
|
};
|
|
|
|
this.$findCellWidthsForBlock = function(row) {
|
|
var cellWidths = [], widths;
|
|
var rowIter = row;
|
|
while (rowIter >= 0) {
|
|
widths = this.$cellWidthsForRow(rowIter);
|
|
if (widths.length == 0)
|
|
break;
|
|
|
|
cellWidths.unshift(widths);
|
|
rowIter--;
|
|
}
|
|
var firstRow = rowIter + 1;
|
|
rowIter = row;
|
|
var numRows = this.$editor.session.getLength();
|
|
|
|
while (rowIter < numRows - 1) {
|
|
rowIter++;
|
|
|
|
widths = this.$cellWidthsForRow(rowIter);
|
|
if (widths.length == 0)
|
|
break;
|
|
|
|
cellWidths.push(widths);
|
|
}
|
|
|
|
return { cellWidths: cellWidths, firstRow: firstRow };
|
|
};
|
|
|
|
this.$cellWidthsForRow = function(row) {
|
|
var selectionColumns = this.$selectionColumnsForRow(row);
|
|
|
|
var tabs = [-1].concat(this.$tabsForRow(row));
|
|
var widths = tabs.map(function(el) { return 0; } ).slice(1);
|
|
var line = this.$editor.session.getLine(row);
|
|
|
|
for (var i = 0, len = tabs.length - 1; i < len; i++) {
|
|
var leftEdge = tabs[i]+1;
|
|
var rightEdge = tabs[i+1];
|
|
|
|
var rightmostSelection = this.$rightmostSelectionInCell(selectionColumns, rightEdge);
|
|
var cell = line.substring(leftEdge, rightEdge);
|
|
widths[i] = Math.max(cell.replace(/\s+$/g,'').length, rightmostSelection - leftEdge);
|
|
}
|
|
|
|
return widths;
|
|
};
|
|
|
|
this.$selectionColumnsForRow = function(row) {
|
|
var selections = [], cursor = this.$editor.getCursorPosition();
|
|
if (this.$editor.session.getSelection().isEmpty()) {
|
|
if (row == cursor.row)
|
|
selections.push(cursor.column);
|
|
}
|
|
|
|
return selections;
|
|
};
|
|
|
|
this.$setBlockCellWidthsToMax = function(cellWidths) {
|
|
var startingNewBlock = true, blockStartRow, blockEndRow, maxWidth;
|
|
var columnInfo = this.$izip_longest(cellWidths);
|
|
|
|
for (var c = 0, l = columnInfo.length; c < l; c++) {
|
|
var column = columnInfo[c];
|
|
if (!column.push) {
|
|
console.error(column);
|
|
continue;
|
|
}
|
|
column.push(NaN);
|
|
|
|
for (var r = 0, s = column.length; r < s; r++) {
|
|
var width = column[r];
|
|
if (startingNewBlock) {
|
|
blockStartRow = r;
|
|
maxWidth = 0;
|
|
startingNewBlock = false;
|
|
}
|
|
if (isNaN(width)) {
|
|
blockEndRow = r;
|
|
|
|
for (var j = blockStartRow; j < blockEndRow; j++) {
|
|
cellWidths[j][c] = maxWidth;
|
|
}
|
|
startingNewBlock = true;
|
|
}
|
|
|
|
maxWidth = Math.max(maxWidth, width);
|
|
}
|
|
}
|
|
|
|
return cellWidths;
|
|
};
|
|
|
|
this.$rightmostSelectionInCell = function(selectionColumns, cellRightEdge) {
|
|
var rightmost = 0;
|
|
|
|
if (selectionColumns.length) {
|
|
var lengths = [];
|
|
for (var s = 0, length = selectionColumns.length; s < length; s++) {
|
|
if (selectionColumns[s] <= cellRightEdge)
|
|
lengths.push(s);
|
|
else
|
|
lengths.push(0);
|
|
}
|
|
rightmost = Math.max.apply(Math, lengths);
|
|
}
|
|
|
|
return rightmost;
|
|
};
|
|
|
|
this.$tabsForRow = function(row) {
|
|
var rowTabs = [], line = this.$editor.session.getLine(row),
|
|
re = /\t/g, match;
|
|
|
|
while ((match = re.exec(line)) != null) {
|
|
rowTabs.push(match.index);
|
|
}
|
|
|
|
return rowTabs;
|
|
};
|
|
|
|
this.$adjustRow = function(row, widths) {
|
|
var rowTabs = this.$tabsForRow(row);
|
|
|
|
if (rowTabs.length == 0)
|
|
return;
|
|
|
|
var bias = 0, location = -1;
|
|
var expandedSet = this.$izip(widths, rowTabs);
|
|
|
|
for (var i = 0, l = expandedSet.length; i < l; i++) {
|
|
var w = expandedSet[i][0], it = expandedSet[i][1];
|
|
location += 1 + w;
|
|
it += bias;
|
|
var difference = location - it;
|
|
|
|
if (difference == 0)
|
|
continue;
|
|
|
|
var partialLine = this.$editor.session.getLine(row).substr(0, it );
|
|
var strippedPartialLine = partialLine.replace(/\s*$/g, "");
|
|
var ispaces = partialLine.length - strippedPartialLine.length;
|
|
|
|
if (difference > 0) {
|
|
this.$editor.session.getDocument().insertInLine({row: row, column: it + 1}, Array(difference + 1).join(" ") + "\t");
|
|
this.$editor.session.getDocument().removeInLine(row, it, it + 1);
|
|
|
|
bias += difference;
|
|
}
|
|
|
|
if (difference < 0 && ispaces >= -difference) {
|
|
this.$editor.session.getDocument().removeInLine(row, it + difference, it);
|
|
bias += difference;
|
|
}
|
|
}
|
|
};
|
|
this.$izip_longest = function(iterables) {
|
|
if (!iterables[0])
|
|
return [];
|
|
var longest = iterables[0].length;
|
|
var iterablesLength = iterables.length;
|
|
|
|
for (var i = 1; i < iterablesLength; i++) {
|
|
var iLength = iterables[i].length;
|
|
if (iLength > longest)
|
|
longest = iLength;
|
|
}
|
|
|
|
var expandedSet = [];
|
|
|
|
for (var l = 0; l < longest; l++) {
|
|
var set = [];
|
|
for (var i = 0; i < iterablesLength; i++) {
|
|
if (iterables[i][l] === "")
|
|
set.push(NaN);
|
|
else
|
|
set.push(iterables[i][l]);
|
|
}
|
|
|
|
expandedSet.push(set);
|
|
}
|
|
|
|
|
|
return expandedSet;
|
|
};
|
|
this.$izip = function(widths, tabs) {
|
|
var size = widths.length >= tabs.length ? tabs.length : widths.length;
|
|
|
|
var expandedSet = [];
|
|
for (var i = 0; i < size; i++) {
|
|
var set = [ widths[i], tabs[i] ];
|
|
expandedSet.push(set);
|
|
}
|
|
return expandedSet;
|
|
};
|
|
|
|
}).call(ElasticTabstopsLite.prototype);
|
|
|
|
exports.ElasticTabstopsLite = ElasticTabstopsLite;
|
|
|
|
var Editor = require("../editor").Editor;
|
|
require("../config").defineOptions(Editor.prototype, "editor", {
|
|
useElasticTabstops: {
|
|
set: function(val) {
|
|
if (val) {
|
|
if (!this.elasticTabstops)
|
|
this.elasticTabstops = new ElasticTabstopsLite(this);
|
|
this.commands.on("afterExec", this.elasticTabstops.onAfterExec);
|
|
this.commands.on("exec", this.elasticTabstops.onExec);
|
|
this.on("change", this.elasticTabstops.onChange);
|
|
} else if (this.elasticTabstops) {
|
|
this.commands.removeListener("afterExec", this.elasticTabstops.onAfterExec);
|
|
this.commands.removeListener("exec", this.elasticTabstops.onExec);
|
|
this.removeListener("change", this.elasticTabstops.onChange);
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
});
|
|
|
|
define("ace/occur",["require","exports","module","ace/lib/oop","ace/range","ace/search","ace/edit_session","ace/search_highlight","ace/lib/dom"], function(require, exports, module) {
|
|
"use strict";
|
|
|
|
var oop = require("./lib/oop");
|
|
var Range = require("./range").Range;
|
|
var Search = require("./search").Search;
|
|
var EditSession = require("./edit_session").EditSession;
|
|
var SearchHighlight = require("./search_highlight").SearchHighlight;
|
|
function Occur() {}
|
|
|
|
oop.inherits(Occur, Search);
|
|
|
|
(function() {
|
|
this.enter = function(editor, options) {
|
|
if (!options.needle) return false;
|
|
var pos = editor.getCursorPosition();
|
|
this.displayOccurContent(editor, options);
|
|
var translatedPos = this.originalToOccurPosition(editor.session, pos);
|
|
editor.moveCursorToPosition(translatedPos);
|
|
return true;
|
|
}
|
|
this.exit = function(editor, options) {
|
|
var pos = options.translatePosition && editor.getCursorPosition();
|
|
var translatedPos = pos && this.occurToOriginalPosition(editor.session, pos);
|
|
this.displayOriginalContent(editor);
|
|
if (translatedPos)
|
|
editor.moveCursorToPosition(translatedPos);
|
|
return true;
|
|
}
|
|
|
|
this.highlight = function(sess, regexp) {
|
|
var hl = sess.$occurHighlight = sess.$occurHighlight || sess.addDynamicMarker(
|
|
new SearchHighlight(null, "ace_occur-highlight", "text"));
|
|
hl.setRegexp(regexp);
|
|
sess._emit("changeBackMarker"); // force highlight layer redraw
|
|
}
|
|
|
|
this.displayOccurContent = function(editor, options) {
|
|
this.$originalSession = editor.session;
|
|
var found = this.matchingLines(editor.session, options);
|
|
var lines = found.map(function(foundLine) { return foundLine.content; });
|
|
var occurSession = new EditSession(lines.join('\n'));
|
|
occurSession.$occur = this;
|
|
occurSession.$occurMatchingLines = found;
|
|
editor.setSession(occurSession);
|
|
this.$useEmacsStyleLineStart = this.$originalSession.$useEmacsStyleLineStart;
|
|
occurSession.$useEmacsStyleLineStart = this.$useEmacsStyleLineStart;
|
|
this.highlight(occurSession, options.re);
|
|
occurSession._emit('changeBackMarker');
|
|
}
|
|
|
|
this.displayOriginalContent = function(editor) {
|
|
editor.setSession(this.$originalSession);
|
|
this.$originalSession.$useEmacsStyleLineStart = this.$useEmacsStyleLineStart;
|
|
}
|
|
this.originalToOccurPosition = function(session, pos) {
|
|
var lines = session.$occurMatchingLines;
|
|
var nullPos = {row: 0, column: 0};
|
|
if (!lines) return nullPos;
|
|
for (var i = 0; i < lines.length; i++) {
|
|
if (lines[i].row === pos.row)
|
|
return {row: i, column: pos.column};
|
|
}
|
|
return nullPos;
|
|
}
|
|
this.occurToOriginalPosition = function(session, pos) {
|
|
var lines = session.$occurMatchingLines;
|
|
if (!lines || !lines[pos.row])
|
|
return pos;
|
|
return {row: lines[pos.row].row, column: pos.column};
|
|
}
|
|
|
|
this.matchingLines = function(session, options) {
|
|
options = oop.mixin({}, options);
|
|
if (!session || !options.needle) return [];
|
|
var search = new Search();
|
|
search.set(options);
|
|
return search.findAll(session).reduce(function(lines, range) {
|
|
var row = range.start.row;
|
|
var last = lines[lines.length-1];
|
|
return last && last.row === row ?
|
|
lines :
|
|
lines.concat({row: row, content: session.getLine(row)});
|
|
}, []);
|
|
}
|
|
|
|
}).call(Occur.prototype);
|
|
|
|
var dom = require('./lib/dom');
|
|
dom.importCssString(".ace_occur-highlight {\n\
|
|
border-radius: 4px;\n\
|
|
background-color: rgba(87, 255, 8, 0.25);\n\
|
|
position: absolute;\n\
|
|
z-index: 4;\n\
|
|
-moz-box-sizing: border-box;\n\
|
|
-webkit-box-sizing: border-box;\n\
|
|
box-sizing: border-box;\n\
|
|
box-shadow: 0 0 4px rgb(91, 255, 50);\n\
|
|
}\n\
|
|
.ace_dark .ace_occur-highlight {\n\
|
|
background-color: rgb(80, 140, 85);\n\
|
|
box-shadow: 0 0 4px rgb(60, 120, 70);\n\
|
|
}\n", "incremental-occur-highlighting");
|
|
|
|
exports.Occur = Occur;
|
|
|
|
});
|
|
|
|
define("ace/commands/occur_commands",["require","exports","module","ace/config","ace/occur","ace/keyboard/hash_handler","ace/lib/oop"], function(require, exports, module) {
|
|
|
|
var config = require("../config"),
|
|
Occur = require("../occur").Occur;
|
|
var occurStartCommand = {
|
|
name: "occur",
|
|
exec: function(editor, options) {
|
|
var alreadyInOccur = !!editor.session.$occur;
|
|
var occurSessionActive = new Occur().enter(editor, options);
|
|
if (occurSessionActive && !alreadyInOccur)
|
|
OccurKeyboardHandler.installIn(editor);
|
|
},
|
|
readOnly: true
|
|
};
|
|
|
|
var occurCommands = [{
|
|
name: "occurexit",
|
|
bindKey: 'esc|Ctrl-G',
|
|
exec: function(editor) {
|
|
var occur = editor.session.$occur;
|
|
if (!occur) return;
|
|
occur.exit(editor, {});
|
|
if (!editor.session.$occur) OccurKeyboardHandler.uninstallFrom(editor);
|
|
},
|
|
readOnly: true
|
|
}, {
|
|
name: "occuraccept",
|
|
bindKey: 'enter',
|
|
exec: function(editor) {
|
|
var occur = editor.session.$occur;
|
|
if (!occur) return;
|
|
occur.exit(editor, {translatePosition: true});
|
|
if (!editor.session.$occur) OccurKeyboardHandler.uninstallFrom(editor);
|
|
},
|
|
readOnly: true
|
|
}];
|
|
|
|
var HashHandler = require("../keyboard/hash_handler").HashHandler;
|
|
var oop = require("../lib/oop");
|
|
|
|
|
|
function OccurKeyboardHandler() {}
|
|
|
|
oop.inherits(OccurKeyboardHandler, HashHandler);
|
|
|
|
(function() {
|
|
|
|
this.isOccurHandler = true;
|
|
|
|
this.attach = function(editor) {
|
|
HashHandler.call(this, occurCommands, editor.commands.platform);
|
|
this.$editor = editor;
|
|
}
|
|
|
|
var handleKeyboard$super = this.handleKeyboard;
|
|
this.handleKeyboard = function(data, hashId, key, keyCode) {
|
|
var cmd = handleKeyboard$super.call(this, data, hashId, key, keyCode);
|
|
return (cmd && cmd.command) ? cmd : undefined;
|
|
}
|
|
|
|
}).call(OccurKeyboardHandler.prototype);
|
|
|
|
OccurKeyboardHandler.installIn = function(editor) {
|
|
var handler = new this();
|
|
editor.keyBinding.addKeyboardHandler(handler);
|
|
editor.commands.addCommands(occurCommands);
|
|
}
|
|
|
|
OccurKeyboardHandler.uninstallFrom = function(editor) {
|
|
editor.commands.removeCommands(occurCommands);
|
|
var handler = editor.getKeyboardHandler();
|
|
if (handler.isOccurHandler)
|
|
editor.keyBinding.removeKeyboardHandler(handler);
|
|
}
|
|
|
|
exports.occurStartCommand = occurStartCommand;
|
|
|
|
});
|
|
|
|
define("ace/commands/incremental_search_commands",["require","exports","module","ace/config","ace/lib/oop","ace/keyboard/hash_handler","ace/commands/occur_commands"], function(require, exports, module) {
|
|
|
|
var config = require("../config");
|
|
var oop = require("../lib/oop");
|
|
var HashHandler = require("../keyboard/hash_handler").HashHandler;
|
|
var occurStartCommand = require("./occur_commands").occurStartCommand;
|
|
exports.iSearchStartCommands = [{
|
|
name: "iSearch",
|
|
bindKey: {win: "Ctrl-F", mac: "Command-F"},
|
|
exec: function(editor, options) {
|
|
config.loadModule(["core", "ace/incremental_search"], function(e) {
|
|
var iSearch = e.iSearch = e.iSearch || new e.IncrementalSearch();
|
|
iSearch.activate(editor, options.backwards);
|
|
if (options.jumpToFirstMatch) iSearch.next(options);
|
|
});
|
|
},
|
|
readOnly: true
|
|
}, {
|
|
name: "iSearchBackwards",
|
|
exec: function(editor, jumpToNext) { editor.execCommand('iSearch', {backwards: true}); },
|
|
readOnly: true
|
|
}, {
|
|
name: "iSearchAndGo",
|
|
bindKey: {win: "Ctrl-K", mac: "Command-G"},
|
|
exec: function(editor, jumpToNext) { editor.execCommand('iSearch', {jumpToFirstMatch: true, useCurrentOrPrevSearch: true}); },
|
|
readOnly: true
|
|
}, {
|
|
name: "iSearchBackwardsAndGo",
|
|
bindKey: {win: "Ctrl-Shift-K", mac: "Command-Shift-G"},
|
|
exec: function(editor) { editor.execCommand('iSearch', {jumpToFirstMatch: true, backwards: true, useCurrentOrPrevSearch: true}); },
|
|
readOnly: true
|
|
}];
|
|
exports.iSearchCommands = [{
|
|
name: "restartSearch",
|
|
bindKey: {win: "Ctrl-F", mac: "Command-F"},
|
|
exec: function(iSearch) {
|
|
iSearch.cancelSearch(true);
|
|
}
|
|
}, {
|
|
name: "searchForward",
|
|
bindKey: {win: "Ctrl-S|Ctrl-K", mac: "Ctrl-S|Command-G"},
|
|
exec: function(iSearch, options) {
|
|
options.useCurrentOrPrevSearch = true;
|
|
iSearch.next(options);
|
|
}
|
|
}, {
|
|
name: "searchBackward",
|
|
bindKey: {win: "Ctrl-R|Ctrl-Shift-K", mac: "Ctrl-R|Command-Shift-G"},
|
|
exec: function(iSearch, options) {
|
|
options.useCurrentOrPrevSearch = true;
|
|
options.backwards = true;
|
|
iSearch.next(options);
|
|
}
|
|
}, {
|
|
name: "extendSearchTerm",
|
|
exec: function(iSearch, string) {
|
|
iSearch.addString(string);
|
|
}
|
|
}, {
|
|
name: "extendSearchTermSpace",
|
|
bindKey: "space",
|
|
exec: function(iSearch) { iSearch.addString(' '); }
|
|
}, {
|
|
name: "shrinkSearchTerm",
|
|
bindKey: "backspace",
|
|
exec: function(iSearch) {
|
|
iSearch.removeChar();
|
|
}
|
|
}, {
|
|
name: 'confirmSearch',
|
|
bindKey: 'return',
|
|
exec: function(iSearch) { iSearch.deactivate(); }
|
|
}, {
|
|
name: 'cancelSearch',
|
|
bindKey: 'esc|Ctrl-G',
|
|
exec: function(iSearch) { iSearch.deactivate(true); }
|
|
}, {
|
|
name: 'occurisearch',
|
|
bindKey: 'Ctrl-O',
|
|
exec: function(iSearch) {
|
|
var options = oop.mixin({}, iSearch.$options);
|
|
iSearch.deactivate();
|
|
occurStartCommand.exec(iSearch.$editor, options);
|
|
}
|
|
}, {
|
|
name: "yankNextWord",
|
|
bindKey: "Ctrl-w",
|
|
exec: function(iSearch) {
|
|
var ed = iSearch.$editor,
|
|
range = ed.selection.getRangeOfMovements(function(sel) { sel.moveCursorWordRight(); }),
|
|
string = ed.session.getTextRange(range);
|
|
iSearch.addString(string);
|
|
}
|
|
}, {
|
|
name: "yankNextChar",
|
|
bindKey: "Ctrl-Alt-y",
|
|
exec: function(iSearch) {
|
|
var ed = iSearch.$editor,
|
|
range = ed.selection.getRangeOfMovements(function(sel) { sel.moveCursorRight(); }),
|
|
string = ed.session.getTextRange(range);
|
|
iSearch.addString(string);
|
|
}
|
|
}, {
|
|
name: 'recenterTopBottom',
|
|
bindKey: 'Ctrl-l',
|
|
exec: function(iSearch) { iSearch.$editor.execCommand('recenterTopBottom'); }
|
|
}, {
|
|
name: 'selectAllMatches',
|
|
bindKey: 'Ctrl-space',
|
|
exec: function(iSearch) {
|
|
var ed = iSearch.$editor,
|
|
hl = ed.session.$isearchHighlight,
|
|
ranges = hl && hl.cache ? hl.cache
|
|
.reduce(function(ranges, ea) {
|
|
return ranges.concat(ea ? ea : []); }, []) : [];
|
|
iSearch.deactivate(false);
|
|
ranges.forEach(ed.selection.addRange.bind(ed.selection));
|
|
}
|
|
}, {
|
|
name: 'searchAsRegExp',
|
|
bindKey: 'Alt-r',
|
|
exec: function(iSearch) {
|
|
iSearch.convertNeedleToRegExp();
|
|
}
|
|
}].map(function(cmd) {
|
|
cmd.readOnly = true;
|
|
cmd.isIncrementalSearchCommand = true;
|
|
cmd.scrollIntoView = "animate-cursor";
|
|
return cmd;
|
|
});
|
|
|
|
function IncrementalSearchKeyboardHandler(iSearch) {
|
|
this.$iSearch = iSearch;
|
|
}
|
|
|
|
oop.inherits(IncrementalSearchKeyboardHandler, HashHandler);
|
|
|
|
(function() {
|
|
|
|
this.attach = function(editor) {
|
|
var iSearch = this.$iSearch;
|
|
HashHandler.call(this, exports.iSearchCommands, editor.commands.platform);
|
|
this.$commandExecHandler = editor.commands.addEventListener('exec', function(e) {
|
|
if (!e.command.isIncrementalSearchCommand)
|
|
return iSearch.deactivate();
|
|
e.stopPropagation();
|
|
e.preventDefault();
|
|
var scrollTop = editor.session.getScrollTop();
|
|
var result = e.command.exec(iSearch, e.args || {});
|
|
editor.renderer.scrollCursorIntoView(null, 0.5);
|
|
editor.renderer.animateScrolling(scrollTop);
|
|
return result;
|
|
});
|
|
};
|
|
|
|
this.detach = function(editor) {
|
|
if (!this.$commandExecHandler) return;
|
|
editor.commands.removeEventListener('exec', this.$commandExecHandler);
|
|
delete this.$commandExecHandler;
|
|
};
|
|
|
|
var handleKeyboard$super = this.handleKeyboard;
|
|
this.handleKeyboard = function(data, hashId, key, keyCode) {
|
|
if (((hashId === 1/*ctrl*/ || hashId === 8/*command*/) && key === 'v')
|
|
|| (hashId === 1/*ctrl*/ && key === 'y')) return null;
|
|
var cmd = handleKeyboard$super.call(this, data, hashId, key, keyCode);
|
|
if (cmd.command) { return cmd; }
|
|
if (hashId == -1) {
|
|
var extendCmd = this.commands.extendSearchTerm;
|
|
if (extendCmd) { return {command: extendCmd, args: key}; }
|
|
}
|
|
return false;
|
|
};
|
|
|
|
}).call(IncrementalSearchKeyboardHandler.prototype);
|
|
|
|
|
|
exports.IncrementalSearchKeyboardHandler = IncrementalSearchKeyboardHandler;
|
|
|
|
});
|
|
|
|
define("ace/incremental_search",["require","exports","module","ace/lib/oop","ace/range","ace/search","ace/search_highlight","ace/commands/incremental_search_commands","ace/lib/dom","ace/commands/command_manager","ace/editor","ace/config"], function(require, exports, module) {
|
|
"use strict";
|
|
|
|
var oop = require("./lib/oop");
|
|
var Range = require("./range").Range;
|
|
var Search = require("./search").Search;
|
|
var SearchHighlight = require("./search_highlight").SearchHighlight;
|
|
var iSearchCommandModule = require("./commands/incremental_search_commands");
|
|
var ISearchKbd = iSearchCommandModule.IncrementalSearchKeyboardHandler;
|
|
function IncrementalSearch() {
|
|
this.$options = {wrap: false, skipCurrent: false};
|
|
this.$keyboardHandler = new ISearchKbd(this);
|
|
}
|
|
|
|
oop.inherits(IncrementalSearch, Search);
|
|
|
|
function isRegExp(obj) {
|
|
return obj instanceof RegExp;
|
|
}
|
|
|
|
function regExpToObject(re) {
|
|
var string = String(re),
|
|
start = string.indexOf('/'),
|
|
flagStart = string.lastIndexOf('/');
|
|
return {
|
|
expression: string.slice(start+1, flagStart),
|
|
flags: string.slice(flagStart+1)
|
|
}
|
|
}
|
|
|
|
function stringToRegExp(string, flags) {
|
|
try {
|
|
return new RegExp(string, flags);
|
|
} catch (e) { return string; }
|
|
}
|
|
|
|
function objectToRegExp(obj) {
|
|
return stringToRegExp(obj.expression, obj.flags);
|
|
}
|
|
|
|
(function() {
|
|
|
|
this.activate = function(ed, backwards) {
|
|
this.$editor = ed;
|
|
this.$startPos = this.$currentPos = ed.getCursorPosition();
|
|
this.$options.needle = '';
|
|
this.$options.backwards = backwards;
|
|
ed.keyBinding.addKeyboardHandler(this.$keyboardHandler);
|
|
this.$originalEditorOnPaste = ed.onPaste; ed.onPaste = this.onPaste.bind(this);
|
|
this.$mousedownHandler = ed.addEventListener('mousedown', this.onMouseDown.bind(this));
|
|
this.selectionFix(ed);
|
|
this.statusMessage(true);
|
|
};
|
|
|
|
this.deactivate = function(reset) {
|
|
this.cancelSearch(reset);
|
|
var ed = this.$editor;
|
|
ed.keyBinding.removeKeyboardHandler(this.$keyboardHandler);
|
|
if (this.$mousedownHandler) {
|
|
ed.removeEventListener('mousedown', this.$mousedownHandler);
|
|
delete this.$mousedownHandler;
|
|
}
|
|
ed.onPaste = this.$originalEditorOnPaste;
|
|
this.message('');
|
|
};
|
|
|
|
this.selectionFix = function(editor) {
|
|
if (editor.selection.isEmpty() && !editor.session.$emacsMark) {
|
|
editor.clearSelection();
|
|
}
|
|
};
|
|
|
|
this.highlight = function(regexp) {
|
|
var sess = this.$editor.session,
|
|
hl = sess.$isearchHighlight = sess.$isearchHighlight || sess.addDynamicMarker(
|
|
new SearchHighlight(null, "ace_isearch-result", "text"));
|
|
hl.setRegexp(regexp);
|
|
sess._emit("changeBackMarker"); // force highlight layer redraw
|
|
};
|
|
|
|
this.cancelSearch = function(reset) {
|
|
var e = this.$editor;
|
|
this.$prevNeedle = this.$options.needle;
|
|
this.$options.needle = '';
|
|
if (reset) {
|
|
e.moveCursorToPosition(this.$startPos);
|
|
this.$currentPos = this.$startPos;
|
|
} else {
|
|
e.pushEmacsMark && e.pushEmacsMark(this.$startPos, false);
|
|
}
|
|
this.highlight(null);
|
|
return Range.fromPoints(this.$currentPos, this.$currentPos);
|
|
};
|
|
|
|
this.highlightAndFindWithNeedle = function(moveToNext, needleUpdateFunc) {
|
|
if (!this.$editor) return null;
|
|
var options = this.$options;
|
|
if (needleUpdateFunc) {
|
|
options.needle = needleUpdateFunc.call(this, options.needle || '') || '';
|
|
}
|
|
if (options.needle.length === 0) {
|
|
this.statusMessage(true);
|
|
return this.cancelSearch(true);
|
|
}
|
|
options.start = this.$currentPos;
|
|
var session = this.$editor.session,
|
|
found = this.find(session),
|
|
shouldSelect = this.$editor.emacsMark ?
|
|
!!this.$editor.emacsMark() : !this.$editor.selection.isEmpty();
|
|
if (found) {
|
|
if (options.backwards) found = Range.fromPoints(found.end, found.start);
|
|
this.$editor.selection.setRange(Range.fromPoints(shouldSelect ? this.$startPos : found.end, found.end));
|
|
if (moveToNext) this.$currentPos = found.end;
|
|
this.highlight(options.re);
|
|
}
|
|
|
|
this.statusMessage(found);
|
|
|
|
return found;
|
|
};
|
|
|
|
this.addString = function(s) {
|
|
return this.highlightAndFindWithNeedle(false, function(needle) {
|
|
if (!isRegExp(needle))
|
|
return needle + s;
|
|
var reObj = regExpToObject(needle);
|
|
reObj.expression += s;
|
|
return objectToRegExp(reObj);
|
|
});
|
|
};
|
|
|
|
this.removeChar = function(c) {
|
|
return this.highlightAndFindWithNeedle(false, function(needle) {
|
|
if (!isRegExp(needle))
|
|
return needle.substring(0, needle.length-1);
|
|
var reObj = regExpToObject(needle);
|
|
reObj.expression = reObj.expression.substring(0, reObj.expression.length-1);
|
|
return objectToRegExp(reObj);
|
|
});
|
|
};
|
|
|
|
this.next = function(options) {
|
|
options = options || {};
|
|
this.$options.backwards = !!options.backwards;
|
|
this.$currentPos = this.$editor.getCursorPosition();
|
|
return this.highlightAndFindWithNeedle(true, function(needle) {
|
|
return options.useCurrentOrPrevSearch && needle.length === 0 ?
|
|
this.$prevNeedle || '' : needle;
|
|
});
|
|
};
|
|
|
|
this.onMouseDown = function(evt) {
|
|
this.deactivate();
|
|
return true;
|
|
};
|
|
|
|
this.onPaste = function(text) {
|
|
this.addString(text);
|
|
};
|
|
|
|
this.convertNeedleToRegExp = function() {
|
|
return this.highlightAndFindWithNeedle(false, function(needle) {
|
|
return isRegExp(needle) ? needle : stringToRegExp(needle, 'ig');
|
|
});
|
|
};
|
|
|
|
this.convertNeedleToString = function() {
|
|
return this.highlightAndFindWithNeedle(false, function(needle) {
|
|
return isRegExp(needle) ? regExpToObject(needle).expression : needle;
|
|
});
|
|
};
|
|
|
|
this.statusMessage = function(found) {
|
|
var options = this.$options, msg = '';
|
|
msg += options.backwards ? 'reverse-' : '';
|
|
msg += 'isearch: ' + options.needle;
|
|
msg += found ? '' : ' (not found)';
|
|
this.message(msg);
|
|
};
|
|
|
|
this.message = function(msg) {
|
|
if (this.$editor.showCommandLine) {
|
|
this.$editor.showCommandLine(msg);
|
|
this.$editor.focus();
|
|
} else {
|
|
console.log(msg);
|
|
}
|
|
};
|
|
|
|
}).call(IncrementalSearch.prototype);
|
|
|
|
|
|
exports.IncrementalSearch = IncrementalSearch;
|
|
|
|
var dom = require('./lib/dom');
|
|
dom.importCssString && dom.importCssString("\
|
|
.ace_marker-layer .ace_isearch-result {\
|
|
position: absolute;\
|
|
z-index: 6;\
|
|
-moz-box-sizing: border-box;\
|
|
-webkit-box-sizing: border-box;\
|
|
box-sizing: border-box;\
|
|
}\
|
|
div.ace_isearch-result {\
|
|
border-radius: 4px;\
|
|
background-color: rgba(255, 200, 0, 0.5);\
|
|
box-shadow: 0 0 4px rgb(255, 200, 0);\
|
|
}\
|
|
.ace_dark div.ace_isearch-result {\
|
|
background-color: rgb(100, 110, 160);\
|
|
box-shadow: 0 0 4px rgb(80, 90, 140);\
|
|
}", "incremental-search-highlighting");
|
|
var commands = require("./commands/command_manager");
|
|
(function() {
|
|
this.setupIncrementalSearch = function(editor, val) {
|
|
if (this.usesIncrementalSearch == val) return;
|
|
this.usesIncrementalSearch = val;
|
|
var iSearchCommands = iSearchCommandModule.iSearchStartCommands;
|
|
var method = val ? 'addCommands' : 'removeCommands';
|
|
this[method](iSearchCommands);
|
|
};
|
|
}).call(commands.CommandManager.prototype);
|
|
var Editor = require("./editor").Editor;
|
|
require("./config").defineOptions(Editor.prototype, "editor", {
|
|
useIncrementalSearch: {
|
|
set: function(val) {
|
|
this.keyBinding.$handlers.forEach(function(handler) {
|
|
if (handler.setupIncrementalSearch) {
|
|
handler.setupIncrementalSearch(this, val);
|
|
}
|
|
});
|
|
this._emit('incrementalSearchSettingChanged', {isEnabled: val});
|
|
}
|
|
}
|
|
});
|
|
|
|
});
|
|
|
|
define("ace/split",["require","exports","module","ace/lib/oop","ace/lib/lang","ace/lib/event_emitter","ace/editor","ace/virtual_renderer","ace/edit_session"], function(require, exports, module) {
|
|
"use strict";
|
|
|
|
var oop = require("./lib/oop");
|
|
var lang = require("./lib/lang");
|
|
var EventEmitter = require("./lib/event_emitter").EventEmitter;
|
|
|
|
var Editor = require("./editor").Editor;
|
|
var Renderer = require("./virtual_renderer").VirtualRenderer;
|
|
var EditSession = require("./edit_session").EditSession;
|
|
|
|
|
|
var Split = function(container, theme, splits) {
|
|
this.BELOW = 1;
|
|
this.BESIDE = 0;
|
|
|
|
this.$container = container;
|
|
this.$theme = theme;
|
|
this.$splits = 0;
|
|
this.$editorCSS = "";
|
|
this.$editors = [];
|
|
this.$orientation = this.BESIDE;
|
|
|
|
this.setSplits(splits || 1);
|
|
this.$cEditor = this.$editors[0];
|
|
|
|
|
|
this.on("focus", function(editor) {
|
|
this.$cEditor = editor;
|
|
}.bind(this));
|
|
};
|
|
|
|
(function(){
|
|
|
|
oop.implement(this, EventEmitter);
|
|
|
|
this.$createEditor = function() {
|
|
var el = document.createElement("div");
|
|
el.className = this.$editorCSS;
|
|
el.style.cssText = "position: absolute; top:0px; bottom:0px";
|
|
this.$container.appendChild(el);
|
|
var editor = new Editor(new Renderer(el, this.$theme));
|
|
|
|
editor.on("focus", function() {
|
|
this._emit("focus", editor);
|
|
}.bind(this));
|
|
|
|
this.$editors.push(editor);
|
|
editor.setFontSize(this.$fontSize);
|
|
return editor;
|
|
};
|
|
|
|
this.setSplits = function(splits) {
|
|
var editor;
|
|
if (splits < 1) {
|
|
throw "The number of splits have to be > 0!";
|
|
}
|
|
|
|
if (splits == this.$splits) {
|
|
return;
|
|
} else if (splits > this.$splits) {
|
|
while (this.$splits < this.$editors.length && this.$splits < splits) {
|
|
editor = this.$editors[this.$splits];
|
|
this.$container.appendChild(editor.container);
|
|
editor.setFontSize(this.$fontSize);
|
|
this.$splits ++;
|
|
}
|
|
while (this.$splits < splits) {
|
|
this.$createEditor();
|
|
this.$splits ++;
|
|
}
|
|
} else {
|
|
while (this.$splits > splits) {
|
|
editor = this.$editors[this.$splits - 1];
|
|
this.$container.removeChild(editor.container);
|
|
this.$splits --;
|
|
}
|
|
}
|
|
this.resize();
|
|
};
|
|
this.getSplits = function() {
|
|
return this.$splits;
|
|
};
|
|
this.getEditor = function(idx) {
|
|
return this.$editors[idx];
|
|
};
|
|
this.getCurrentEditor = function() {
|
|
return this.$cEditor;
|
|
};
|
|
this.focus = function() {
|
|
this.$cEditor.focus();
|
|
};
|
|
this.blur = function() {
|
|
this.$cEditor.blur();
|
|
};
|
|
this.setTheme = function(theme) {
|
|
this.$editors.forEach(function(editor) {
|
|
editor.setTheme(theme);
|
|
});
|
|
};
|
|
this.setKeyboardHandler = function(keybinding) {
|
|
this.$editors.forEach(function(editor) {
|
|
editor.setKeyboardHandler(keybinding);
|
|
});
|
|
};
|
|
this.forEach = function(callback, scope) {
|
|
this.$editors.forEach(callback, scope);
|
|
};
|
|
|
|
|
|
this.$fontSize = "";
|
|
this.setFontSize = function(size) {
|
|
this.$fontSize = size;
|
|
this.forEach(function(editor) {
|
|
editor.setFontSize(size);
|
|
});
|
|
};
|
|
|
|
this.$cloneSession = function(session) {
|
|
var s = new EditSession(session.getDocument(), session.getMode());
|
|
|
|
var undoManager = session.getUndoManager();
|
|
if (undoManager) {
|
|
var undoManagerProxy = new UndoManagerProxy(undoManager, s);
|
|
s.setUndoManager(undoManagerProxy);
|
|
}
|
|
s.$informUndoManager = lang.delayedCall(function() { s.$deltas = []; });
|
|
s.setTabSize(session.getTabSize());
|
|
s.setUseSoftTabs(session.getUseSoftTabs());
|
|
s.setOverwrite(session.getOverwrite());
|
|
s.setBreakpoints(session.getBreakpoints());
|
|
s.setUseWrapMode(session.getUseWrapMode());
|
|
s.setUseWorker(session.getUseWorker());
|
|
s.setWrapLimitRange(session.$wrapLimitRange.min,
|
|
session.$wrapLimitRange.max);
|
|
s.$foldData = session.$cloneFoldData();
|
|
|
|
return s;
|
|
};
|
|
this.setSession = function(session, idx) {
|
|
var editor;
|
|
if (idx == null) {
|
|
editor = this.$cEditor;
|
|
} else {
|
|
editor = this.$editors[idx];
|
|
}
|
|
var isUsed = this.$editors.some(function(editor) {
|
|
return editor.session === session;
|
|
});
|
|
|
|
if (isUsed) {
|
|
session = this.$cloneSession(session);
|
|
}
|
|
editor.setSession(session);
|
|
return session;
|
|
};
|
|
this.getOrientation = function() {
|
|
return this.$orientation;
|
|
};
|
|
this.setOrientation = function(orientation) {
|
|
if (this.$orientation == orientation) {
|
|
return;
|
|
}
|
|
this.$orientation = orientation;
|
|
this.resize();
|
|
};
|
|
this.resize = function() {
|
|
var width = this.$container.clientWidth;
|
|
var height = this.$container.clientHeight;
|
|
var editor;
|
|
|
|
if (this.$orientation == this.BESIDE) {
|
|
var editorWidth = width / this.$splits;
|
|
for (var i = 0; i < this.$splits; i++) {
|
|
editor = this.$editors[i];
|
|
editor.container.style.width = editorWidth + "px";
|
|
editor.container.style.top = "0px";
|
|
editor.container.style.left = i * editorWidth + "px";
|
|
editor.container.style.height = height + "px";
|
|
editor.resize();
|
|
}
|
|
} else {
|
|
var editorHeight = height / this.$splits;
|
|
for (var i = 0; i < this.$splits; i++) {
|
|
editor = this.$editors[i];
|
|
editor.container.style.width = width + "px";
|
|
editor.container.style.top = i * editorHeight + "px";
|
|
editor.container.style.left = "0px";
|
|
editor.container.style.height = editorHeight + "px";
|
|
editor.resize();
|
|
}
|
|
}
|
|
};
|
|
|
|
}).call(Split.prototype);
|
|
|
|
|
|
function UndoManagerProxy(undoManager, session) {
|
|
this.$u = undoManager;
|
|
this.$doc = session;
|
|
}
|
|
|
|
(function() {
|
|
this.execute = function(options) {
|
|
this.$u.execute(options);
|
|
};
|
|
|
|
this.undo = function() {
|
|
var selectionRange = this.$u.undo(true);
|
|
if (selectionRange) {
|
|
this.$doc.selection.setSelectionRange(selectionRange);
|
|
}
|
|
};
|
|
|
|
this.redo = function() {
|
|
var selectionRange = this.$u.redo(true);
|
|
if (selectionRange) {
|
|
this.$doc.selection.setSelectionRange(selectionRange);
|
|
}
|
|
};
|
|
|
|
this.reset = function() {
|
|
this.$u.reset();
|
|
};
|
|
|
|
this.hasUndo = function() {
|
|
return this.$u.hasUndo();
|
|
};
|
|
|
|
this.hasRedo = function() {
|
|
return this.$u.hasRedo();
|
|
};
|
|
}).call(UndoManagerProxy.prototype);
|
|
|
|
exports.Split = Split;
|
|
});
|
|
|
|
define("ace/keyboard/vim",["require","exports","module","ace/range","ace/lib/event_emitter","ace/lib/dom","ace/lib/oop","ace/lib/keys","ace/lib/event","ace/search","ace/lib/useragent","ace/search_highlight","ace/commands/multi_select_commands","ace/mode/text","ace/multi_select"], function(require, exports, module) {
|
|
'use strict';
|
|
|
|
function log() {
|
|
var d = "";
|
|
function format(p) {
|
|
if (typeof p != "object")
|
|
return p + "";
|
|
if ("line" in p) {
|
|
return p.line + ":" + p.ch;
|
|
}
|
|
if ("anchor" in p) {
|
|
return format(p.anchor) + "->" + format(p.head);
|
|
}
|
|
if (Array.isArray(p))
|
|
return "[" + p.map(function(x) {
|
|
return format(x);
|
|
}) + "]";
|
|
return JSON.stringify(p);
|
|
}
|
|
for (var i = 0; i < arguments.length; i++) {
|
|
var p = arguments[i];
|
|
var f = format(p);
|
|
d += f + " ";
|
|
}
|
|
console.log(d);
|
|
}
|
|
var Range = require("../range").Range;
|
|
var EventEmitter = require("../lib/event_emitter").EventEmitter;
|
|
var dom = require("../lib/dom");
|
|
var oop = require("../lib/oop");
|
|
var KEYS = require("../lib/keys");
|
|
var event = require("../lib/event");
|
|
var Search = require("../search").Search;
|
|
var useragent = require("../lib/useragent");
|
|
var SearchHighlight = require("../search_highlight").SearchHighlight;
|
|
var multiSelectCommands = require("../commands/multi_select_commands");
|
|
var TextModeTokenRe = require("../mode/text").Mode.prototype.tokenRe;
|
|
require("../multi_select");
|
|
|
|
var CodeMirror = function(ace) {
|
|
this.ace = ace;
|
|
this.state = {};
|
|
this.marks = {};
|
|
this.$uid = 0;
|
|
this.onChange = this.onChange.bind(this);
|
|
this.onSelectionChange = this.onSelectionChange.bind(this);
|
|
this.onBeforeEndOperation = this.onBeforeEndOperation.bind(this);
|
|
this.ace.on('change', this.onChange);
|
|
this.ace.on('changeSelection', this.onSelectionChange);
|
|
this.ace.on('beforeEndOperation', this.onBeforeEndOperation);
|
|
};
|
|
CodeMirror.Pos = function(line, ch) {
|
|
if (!(this instanceof Pos)) return new Pos(line, ch);
|
|
this.line = line; this.ch = ch;
|
|
};
|
|
CodeMirror.defineOption = function(name, val, setter) {};
|
|
CodeMirror.commands = {
|
|
redo: function(cm) { cm.ace.redo(); },
|
|
undo: function(cm) { cm.ace.undo(); },
|
|
newlineAndIndent: function(cm) { cm.ace.insert("\n"); }
|
|
};
|
|
CodeMirror.keyMap = {};
|
|
CodeMirror.addClass = CodeMirror.rmClass =
|
|
CodeMirror.e_stop = function() {};
|
|
CodeMirror.keyName = function(e) {
|
|
if (e.key) return e.key;
|
|
var key = (KEYS[e.keyCode] || "");
|
|
if (key.length == 1) key = key.toUpperCase();
|
|
key = event.getModifierString(e).replace(/(^|-)\w/g, function(m) {
|
|
return m.toUpperCase();
|
|
}) + key;
|
|
return key;
|
|
};
|
|
CodeMirror.keyMap['default'] = function(key) {
|
|
return function(cm) {
|
|
var cmd = cm.ace.commands.commandKeyBinding[key.toLowerCase()];
|
|
return cmd && cm.ace.execCommand(cmd) !== false;
|
|
};
|
|
};
|
|
CodeMirror.lookupKey = function lookupKey(key, map, handle) {
|
|
if (typeof map == "string")
|
|
map = CodeMirror.keyMap[map];
|
|
var found = typeof map == "function" ? map(key) : map[key];
|
|
if (found === false) return "nothing";
|
|
if (found === "...") return "multi";
|
|
if (found != null && handle(found)) return "handled";
|
|
|
|
if (map.fallthrough) {
|
|
if (!Array.isArray(map.fallthrough))
|
|
return lookupKey(key, map.fallthrough, handle);
|
|
for (var i = 0; i < map.fallthrough.length; i++) {
|
|
var result = lookupKey(key, map.fallthrough[i], handle);
|
|
if (result) return result;
|
|
}
|
|
}
|
|
};
|
|
|
|
CodeMirror.signal = function(o, name, e) { return o._signal(name, e) };
|
|
CodeMirror.on = event.addListener;
|
|
CodeMirror.off = event.removeListener;
|
|
CodeMirror.isWordChar = function(ch) {
|
|
if (ch < "\x7f") return /^\w$/.test(ch);
|
|
TextModeTokenRe.lastIndex = 0;
|
|
return TextModeTokenRe.test(ch);
|
|
};
|
|
|
|
(function() {
|
|
oop.implement(CodeMirror.prototype, EventEmitter);
|
|
|
|
this.destroy = function() {
|
|
this.ace.off('change', this.onChange);
|
|
this.ace.off('changeSelection', this.onSelectionChange);
|
|
this.ace.off('beforeEndOperation', this.onBeforeEndOperation);
|
|
this.removeOverlay();
|
|
};
|
|
this.virtualSelectionMode = function() {
|
|
return this.ace.inVirtualSelectionMode && this.ace.selection.index;
|
|
};
|
|
this.onChange = function(delta) {
|
|
var change = { text: delta.action[0] == 'i' ? delta.lines : [] };
|
|
var curOp = this.curOp = this.curOp || {};
|
|
if (!curOp.changeHandlers)
|
|
curOp.changeHandlers = this._eventRegistry["change"] && this._eventRegistry["change"].slice();
|
|
if (this.virtualSelectionMode()) return;
|
|
if (!curOp.lastChange) {
|
|
curOp.lastChange = curOp.change = change;
|
|
} else {
|
|
curOp.lastChange.next = curOp.lastChange = change;
|
|
}
|
|
this.$updateMarkers(delta);
|
|
};
|
|
this.onSelectionChange = function() {
|
|
var curOp = this.curOp = this.curOp || {};
|
|
if (!curOp.cursorActivityHandlers)
|
|
curOp.cursorActivityHandlers = this._eventRegistry["cursorActivity"] && this._eventRegistry["cursorActivity"].slice();
|
|
this.curOp.cursorActivity = true;
|
|
if (this.ace.inMultiSelectMode) {
|
|
this.ace.keyBinding.removeKeyboardHandler(multiSelectCommands.keyboardHandler);
|
|
}
|
|
};
|
|
this.operation = function(fn, force) {
|
|
if (!force && this.curOp || force && this.curOp && this.curOp.force) {
|
|
return fn();
|
|
}
|
|
if (force || !this.ace.curOp) {
|
|
if (this.curOp)
|
|
this.onBeforeEndOperation();
|
|
}
|
|
if (!this.ace.curOp) {
|
|
var prevOp = this.ace.prevOp;
|
|
this.ace.startOperation({
|
|
command: { name: "vim", scrollIntoView: "cursor" }
|
|
});
|
|
}
|
|
var curOp = this.curOp = this.curOp || {};
|
|
this.curOp.force = force;
|
|
var result = fn();
|
|
if (this.ace.curOp && this.ace.curOp.command.name == "vim") {
|
|
this.ace.endOperation();
|
|
if (!curOp.cursorActivity && !curOp.lastChange && prevOp)
|
|
this.ace.prevOp = prevOp;
|
|
}
|
|
if (force || !this.ace.curOp) {
|
|
if (this.curOp)
|
|
this.onBeforeEndOperation();
|
|
}
|
|
return result;
|
|
};
|
|
this.onBeforeEndOperation = function() {
|
|
var op = this.curOp;
|
|
if (op) {
|
|
if (op.change) { this.signal("change", op.change, op); }
|
|
if (op && op.cursorActivity) { this.signal("cursorActivity", null, op); }
|
|
this.curOp = null;
|
|
}
|
|
};
|
|
|
|
this.signal = function(eventName, e, handlers) {
|
|
var listeners = handlers ? handlers[eventName + "Handlers"]
|
|
: (this._eventRegistry || {})[eventName];
|
|
if (!listeners)
|
|
return;
|
|
listeners = listeners.slice();
|
|
for (var i=0; i<listeners.length; i++)
|
|
listeners[i](this, e);
|
|
};
|
|
this.firstLine = function() { return 0; };
|
|
this.lastLine = function() { return this.ace.session.getLength() - 1; };
|
|
this.lineCount = function() { return this.ace.session.getLength(); };
|
|
this.setCursor = function(line, ch) {
|
|
if (typeof line === 'object') {
|
|
ch = line.ch;
|
|
line = line.line;
|
|
}
|
|
if (!this.ace.inVirtualSelectionMode)
|
|
this.ace.exitMultiSelectMode();
|
|
this.ace.session.unfold({row: line, column: ch});
|
|
this.ace.selection.moveTo(line, ch);
|
|
};
|
|
this.getCursor = function(p) {
|
|
var sel = this.ace.selection;
|
|
var pos = p == 'anchor' ? (sel.isEmpty() ? sel.lead : sel.anchor) :
|
|
p == 'head' || !p ? sel.lead : sel.getRange()[p];
|
|
return toCmPos(pos);
|
|
};
|
|
this.listSelections = function(p) {
|
|
var ranges = this.ace.multiSelect.rangeList.ranges;
|
|
if (!ranges.length || this.ace.inVirtualSelectionMode)
|
|
return [{anchor: this.getCursor('anchor'), head: this.getCursor('head')}];
|
|
return ranges.map(function(r) {
|
|
return {
|
|
anchor: this.clipPos(toCmPos(r.cursor == r.end ? r.start : r.end)),
|
|
head: this.clipPos(toCmPos(r.cursor))
|
|
};
|
|
}, this);
|
|
};
|
|
this.setSelections = function(p, primIndex) {
|
|
var sel = this.ace.multiSelect;
|
|
var ranges = p.map(function(x) {
|
|
var anchor = toAcePos(x.anchor);
|
|
var head = toAcePos(x.head);
|
|
var r = Range.comparePoints(anchor, head) < 0
|
|
? new Range.fromPoints(anchor, head)
|
|
: new Range.fromPoints(head, anchor);
|
|
r.cursor = Range.comparePoints(r.start, head) ? r.end : r.start;
|
|
return r;
|
|
});
|
|
|
|
if (this.ace.inVirtualSelectionMode) {
|
|
this.ace.selection.fromOrientedRange(ranges[0]);
|
|
return;
|
|
}
|
|
if (!primIndex) {
|
|
ranges = ranges.reverse();
|
|
} else if (ranges[primIndex]) {
|
|
ranges.push(ranges.splice(primIndex, 1)[0]);
|
|
}
|
|
sel.toSingleRange(ranges[0].clone());
|
|
var session = this.ace.session;
|
|
for (var i = 0; i < ranges.length; i++) {
|
|
var range = session.$clipRangeToDocument(ranges[i]); // todo why ace doesn't do this?
|
|
sel.addRange(range);
|
|
}
|
|
};
|
|
this.setSelection = function(a, h, options) {
|
|
var sel = this.ace.selection;
|
|
sel.moveTo(a.line, a.ch);
|
|
sel.selectTo(h.line, h.ch);
|
|
if (options && options.origin == '*mouse') {
|
|
this.onBeforeEndOperation();
|
|
}
|
|
};
|
|
this.somethingSelected = function(p) {
|
|
return !this.ace.selection.isEmpty();
|
|
};
|
|
this.clipPos = function(p) {
|
|
var pos = this.ace.session.$clipPositionToDocument(p.line, p.ch);
|
|
return toCmPos(pos);
|
|
};
|
|
this.markText = function(cursor) {
|
|
return {clear: function() {}, find: function() {}};
|
|
};
|
|
this.$updateMarkers = function(delta) {
|
|
var isInsert = delta.action == "insert";
|
|
var start = delta.start;
|
|
var end = delta.end;
|
|
var rowShift = (end.row - start.row) * (isInsert ? 1 : -1);
|
|
var colShift = (end.column - start.column) * (isInsert ? 1 : -1);
|
|
if (isInsert) end = start;
|
|
|
|
for (var i in this.marks) {
|
|
var point = this.marks[i];
|
|
var cmp = Range.comparePoints(point, start);
|
|
if (cmp < 0) {
|
|
continue; // delta starts after the range
|
|
}
|
|
if (cmp === 0) {
|
|
if (isInsert) {
|
|
if (point.bias == 1) {
|
|
cmp = 1;
|
|
} else {
|
|
point.bias == -1;
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
var cmp2 = isInsert ? cmp : Range.comparePoints(point, end);
|
|
if (cmp2 > 0) {
|
|
point.row += rowShift;
|
|
point.column += point.row == end.row ? colShift : 0;
|
|
continue;
|
|
}
|
|
if (!isInsert && cmp2 <= 0) {
|
|
point.row = start.row;
|
|
point.column = start.column;
|
|
if (cmp2 === 0)
|
|
point.bias = 1;
|
|
}
|
|
}
|
|
};
|
|
var Marker = function(cm, id, row, column) {
|
|
this.cm = cm;
|
|
this.id = id;
|
|
this.row = row;
|
|
this.column = column;
|
|
cm.marks[this.id] = this;
|
|
};
|
|
Marker.prototype.clear = function() { delete this.cm.marks[this.id] };
|
|
Marker.prototype.find = function() { return toCmPos(this) };
|
|
this.setBookmark = function(cursor, options) {
|
|
var bm = new Marker(this, this.$uid++, cursor.line, cursor.ch);
|
|
if (!options || !options.insertLeft)
|
|
bm.$insertRight = true;
|
|
this.marks[bm.id] = bm;
|
|
return bm;
|
|
};
|
|
this.moveH = function(increment, unit) {
|
|
if (unit == 'char') {
|
|
var sel = this.ace.selection;
|
|
sel.clearSelection();
|
|
sel.moveCursorBy(0, increment);
|
|
}
|
|
};
|
|
this.findPosV = function(start, amount, unit, goalColumn) {
|
|
if (unit == 'page') {
|
|
var renderer = this.ace.renderer;
|
|
var config = renderer.layerConfig;
|
|
amount = amount * Math.floor(config.height / config.lineHeight);
|
|
unit = 'line';
|
|
}
|
|
if (unit == 'line') {
|
|
var screenPos = this.ace.session.documentToScreenPosition(start.line, start.ch);
|
|
if (goalColumn != null)
|
|
screenPos.column = goalColumn;
|
|
screenPos.row += amount;
|
|
screenPos.row = Math.min(Math.max(0, screenPos.row), this.ace.session.getScreenLength() - 1);
|
|
var pos = this.ace.session.screenToDocumentPosition(screenPos.row, screenPos.column);
|
|
return toCmPos(pos);
|
|
} else {
|
|
debugger;
|
|
}
|
|
};
|
|
this.charCoords = function(pos, mode) {
|
|
if (mode == 'div' || !mode) {
|
|
var sc = this.ace.session.documentToScreenPosition(pos.line, pos.ch);
|
|
return {left: sc.column, top: sc.row};
|
|
}if (mode == 'local') {
|
|
var renderer = this.ace.renderer;
|
|
var sc = this.ace.session.documentToScreenPosition(pos.line, pos.ch);
|
|
var lh = renderer.layerConfig.lineHeight;
|
|
var cw = renderer.layerConfig.characterWidth;
|
|
var top = lh * sc.row;
|
|
return {left: sc.column * cw, top: top, bottom: top + lh};
|
|
}
|
|
};
|
|
this.coordsChar = function(pos, mode) {
|
|
var renderer = this.ace.renderer;
|
|
if (mode == 'local') {
|
|
var row = Math.max(0, Math.floor(pos.top / renderer.lineHeight));
|
|
var col = Math.max(0, Math.floor(pos.left / renderer.characterWidth));
|
|
var ch = renderer.session.screenToDocumentPosition(row, col);
|
|
return toCmPos(ch);
|
|
} else if (mode == 'div') {
|
|
throw "not implemented";
|
|
}
|
|
};
|
|
this.getSearchCursor = function(query, pos, caseFold) {
|
|
var caseSensitive = false;
|
|
var isRegexp = false;
|
|
if (query instanceof RegExp && !query.global) {
|
|
caseSensitive = !query.ignoreCase;
|
|
query = query.source;
|
|
isRegexp = true;
|
|
}
|
|
var search = new Search();
|
|
if (pos.ch == undefined) pos.ch = Number.MAX_VALUE;
|
|
var acePos = {row: pos.line, column: pos.ch};
|
|
var cm = this;
|
|
var last = null;
|
|
return {
|
|
findNext: function() { return this.find(false) },
|
|
findPrevious: function() {return this.find(true) },
|
|
find: function(back) {
|
|
search.setOptions({
|
|
needle: query,
|
|
caseSensitive: caseSensitive,
|
|
wrap: false,
|
|
backwards: back,
|
|
regExp: isRegexp,
|
|
start: last || acePos
|
|
});
|
|
var range = search.find(cm.ace.session);
|
|
if (range && range.isEmpty()) {
|
|
if (cm.getLine(range.start.row).length == range.start.column) {
|
|
search.$options.start = range;
|
|
range = search.find(cm.ace.session);
|
|
}
|
|
}
|
|
last = range;
|
|
return last;
|
|
},
|
|
from: function() { return last && toCmPos(last.start) },
|
|
to: function() { return last && toCmPos(last.end) },
|
|
replace: function(text) {
|
|
if (last) {
|
|
last.end = cm.ace.session.doc.replace(last, text);
|
|
}
|
|
}
|
|
};
|
|
};
|
|
this.scrollTo = function(x, y) {
|
|
var renderer = this.ace.renderer;
|
|
var config = renderer.layerConfig;
|
|
var maxHeight = config.maxHeight;
|
|
maxHeight -= (renderer.$size.scrollerHeight - renderer.lineHeight) * renderer.$scrollPastEnd;
|
|
if (y != null) this.ace.session.setScrollTop(Math.max(0, Math.min(y, maxHeight)));
|
|
if (x != null) this.ace.session.setScrollLeft(Math.max(0, Math.min(x, config.width)));
|
|
};
|
|
this.scrollInfo = function() { return 0; };
|
|
this.scrollIntoView = function(pos, margin) {
|
|
if (pos) {
|
|
var renderer = this.ace.renderer;
|
|
var viewMargin = { "top": 0, "bottom": margin };
|
|
renderer.scrollCursorIntoView(toAcePos(pos),
|
|
(renderer.lineHeight * 2) / renderer.$size.scrollerHeight, viewMargin);
|
|
}
|
|
};
|
|
this.getLine = function(row) { return this.ace.session.getLine(row) };
|
|
this.getRange = function(s, e) {
|
|
return this.ace.session.getTextRange(new Range(s.line, s.ch, e.line, e.ch));
|
|
};
|
|
this.replaceRange = function(text, s, e) {
|
|
if (!e) e = s;
|
|
return this.ace.session.replace(new Range(s.line, s.ch, e.line, e.ch), text);
|
|
};
|
|
this.replaceSelections = function(p) {
|
|
var sel = this.ace.selection;
|
|
if (this.ace.inVirtualSelectionMode) {
|
|
this.ace.session.replace(sel.getRange(), p[0] || "");
|
|
return;
|
|
}
|
|
sel.inVirtualSelectionMode = true;
|
|
var ranges = sel.rangeList.ranges;
|
|
if (!ranges.length) ranges = [this.ace.multiSelect.getRange()];
|
|
for (var i = ranges.length; i--;)
|
|
this.ace.session.replace(ranges[i], p[i] || "");
|
|
sel.inVirtualSelectionMode = false;
|
|
};
|
|
this.getSelection = function() {
|
|
return this.ace.getSelectedText();
|
|
};
|
|
this.getSelections = function() {
|
|
return this.listSelections().map(function(x) {
|
|
return this.getRange(x.anchor, x.head);
|
|
}, this);
|
|
};
|
|
this.getInputField = function() {
|
|
return this.ace.textInput.getElement();
|
|
};
|
|
this.getWrapperElement = function() {
|
|
return this.ace.containter;
|
|
};
|
|
var optMap = {
|
|
indentWithTabs: "useSoftTabs",
|
|
indentUnit: "tabSize",
|
|
tabSize: "tabSize",
|
|
firstLineNumber: "firstLineNumber",
|
|
readOnly: "readOnly"
|
|
};
|
|
this.setOption = function(name, val) {
|
|
this.state[name] = val;
|
|
switch (name) {
|
|
case 'indentWithTabs':
|
|
name = optMap[name];
|
|
val = !val;
|
|
break;
|
|
default:
|
|
name = optMap[name];
|
|
}
|
|
if (name)
|
|
this.ace.setOption(name, val);
|
|
};
|
|
this.getOption = function(name, val) {
|
|
var aceOpt = optMap[name];
|
|
if (aceOpt)
|
|
val = this.ace.getOption(aceOpt);
|
|
switch (name) {
|
|
case 'indentWithTabs':
|
|
name = optMap[name];
|
|
return !val;
|
|
}
|
|
return aceOpt ? val : this.state[name];
|
|
};
|
|
this.toggleOverwrite = function(on) {
|
|
this.state.overwrite = on;
|
|
return this.ace.setOverwrite(on);
|
|
};
|
|
this.addOverlay = function(o) {
|
|
if (!this.$searchHighlight || !this.$searchHighlight.session) {
|
|
var highlight = new SearchHighlight(null, "ace_highlight-marker", "text");
|
|
var marker = this.ace.session.addDynamicMarker(highlight);
|
|
highlight.id = marker.id;
|
|
highlight.session = this.ace.session;
|
|
highlight.destroy = function(o) {
|
|
highlight.session.off("change", highlight.updateOnChange);
|
|
highlight.session.off("changeEditor", highlight.destroy);
|
|
highlight.session.removeMarker(highlight.id);
|
|
highlight.session = null;
|
|
};
|
|
highlight.updateOnChange = function(delta) {
|
|
var row = delta.start.row;
|
|
if (row == delta.end.row) highlight.cache[row] = undefined;
|
|
else highlight.cache.splice(row, highlight.cache.length);
|
|
};
|
|
highlight.session.on("changeEditor", highlight.destroy);
|
|
highlight.session.on("change", highlight.updateOnChange);
|
|
}
|
|
var re = new RegExp(o.query.source, "gmi");
|
|
this.$searchHighlight = o.highlight = highlight;
|
|
this.$searchHighlight.setRegexp(re);
|
|
this.ace.renderer.updateBackMarkers();
|
|
};
|
|
this.removeOverlay = function(o) {
|
|
if (this.$searchHighlight && this.$searchHighlight.session) {
|
|
this.$searchHighlight.destroy();
|
|
}
|
|
};
|
|
this.getScrollInfo = function() {
|
|
var renderer = this.ace.renderer;
|
|
var config = renderer.layerConfig;
|
|
return {
|
|
left: renderer.scrollLeft,
|
|
top: renderer.scrollTop,
|
|
height: config.maxHeight,
|
|
width: config.width,
|
|
clientHeight: config.height,
|
|
clientWidth: config.width
|
|
};
|
|
};
|
|
this.getValue = function() {
|
|
return this.ace.getValue();
|
|
};
|
|
this.setValue = function(v) {
|
|
return this.ace.setValue(v);
|
|
};
|
|
this.getTokenTypeAt = function(pos) {
|
|
var token = this.ace.session.getTokenAt(pos.line, pos.ch);
|
|
return token && /comment|string/.test(token.type) ? "string" : "";
|
|
};
|
|
this.findMatchingBracket = function(pos) {
|
|
var m = this.ace.session.findMatchingBracket(toAcePos(pos));
|
|
return {to: m && toCmPos(m)};
|
|
};
|
|
this.indentLine = function(line, method) {
|
|
if (method === true)
|
|
this.ace.session.indentRows(line, line, "\t");
|
|
else if (method === false)
|
|
this.ace.session.outdentRows(new Range(line, 0, line, 0));
|
|
};
|
|
this.indexFromPos = function(pos) {
|
|
return this.ace.session.doc.positionToIndex(toAcePos(pos));
|
|
};
|
|
this.posFromIndex = function(index) {
|
|
return toCmPos(this.ace.session.doc.indexToPosition(index));
|
|
};
|
|
this.focus = function(index) {
|
|
return this.ace.focus();
|
|
};
|
|
this.blur = function(index) {
|
|
return this.ace.blur();
|
|
};
|
|
this.defaultTextHeight = function(index) {
|
|
return this.ace.renderer.layerConfig.lineHeight;
|
|
};
|
|
this.scanForBracket = function(pos, dir, _, options) {
|
|
var re = options.bracketRegex.source;
|
|
if (dir == 1) {
|
|
var m = this.ace.session.$findClosingBracket(re.slice(1, 2), toAcePos(pos), /paren|text/);
|
|
} else {
|
|
var m = this.ace.session.$findOpeningBracket(re.slice(-2, -1), {row: pos.line, column: pos.ch + 1}, /paren|text/);
|
|
}
|
|
return m && {pos: toCmPos(m)};
|
|
};
|
|
this.refresh = function() {
|
|
return this.ace.resize(true);
|
|
};
|
|
this.getMode = function() {
|
|
return { name : this.getOption("mode") };
|
|
}
|
|
}).call(CodeMirror.prototype);
|
|
function toAcePos(cmPos) {
|
|
return {row: cmPos.line, column: cmPos.ch};
|
|
}
|
|
function toCmPos(acePos) {
|
|
return new Pos(acePos.row, acePos.column);
|
|
}
|
|
|
|
var StringStream = CodeMirror.StringStream = function(string, tabSize) {
|
|
this.pos = this.start = 0;
|
|
this.string = string;
|
|
this.tabSize = tabSize || 8;
|
|
this.lastColumnPos = this.lastColumnValue = 0;
|
|
this.lineStart = 0;
|
|
};
|
|
|
|
StringStream.prototype = {
|
|
eol: function() {return this.pos >= this.string.length;},
|
|
sol: function() {return this.pos == this.lineStart;},
|
|
peek: function() {return this.string.charAt(this.pos) || undefined;},
|
|
next: function() {
|
|
if (this.pos < this.string.length)
|
|
return this.string.charAt(this.pos++);
|
|
},
|
|
eat: function(match) {
|
|
var ch = this.string.charAt(this.pos);
|
|
if (typeof match == "string") var ok = ch == match;
|
|
else var ok = ch && (match.test ? match.test(ch) : match(ch));
|
|
if (ok) {++this.pos; return ch;}
|
|
},
|
|
eatWhile: function(match) {
|
|
var start = this.pos;
|
|
while (this.eat(match)){}
|
|
return this.pos > start;
|
|
},
|
|
eatSpace: function() {
|
|
var start = this.pos;
|
|
while (/[\s\u00a0]/.test(this.string.charAt(this.pos))) ++this.pos;
|
|
return this.pos > start;
|
|
},
|
|
skipToEnd: function() {this.pos = this.string.length;},
|
|
skipTo: function(ch) {
|
|
var found = this.string.indexOf(ch, this.pos);
|
|
if (found > -1) {this.pos = found; return true;}
|
|
},
|
|
backUp: function(n) {this.pos -= n;},
|
|
column: function() {
|
|
throw "not implemented";
|
|
},
|
|
indentation: function() {
|
|
throw "not implemented";
|
|
},
|
|
match: function(pattern, consume, caseInsensitive) {
|
|
if (typeof pattern == "string") {
|
|
var cased = function(str) {return caseInsensitive ? str.toLowerCase() : str;};
|
|
var substr = this.string.substr(this.pos, pattern.length);
|
|
if (cased(substr) == cased(pattern)) {
|
|
if (consume !== false) this.pos += pattern.length;
|
|
return true;
|
|
}
|
|
} else {
|
|
var match = this.string.slice(this.pos).match(pattern);
|
|
if (match && match.index > 0) return null;
|
|
if (match && consume !== false) this.pos += match[0].length;
|
|
return match;
|
|
}
|
|
},
|
|
current: function(){return this.string.slice(this.start, this.pos);},
|
|
hideFirstChars: function(n, inner) {
|
|
this.lineStart += n;
|
|
try { return inner(); }
|
|
finally { this.lineStart -= n; }
|
|
}
|
|
};
|
|
CodeMirror.defineExtension = function(name, fn) {
|
|
CodeMirror.prototype[name] = fn;
|
|
};
|
|
dom.importCssString(".normal-mode .ace_cursor{\
|
|
border: 1px solid red;\
|
|
background-color: red;\
|
|
opacity: 0.5;\
|
|
}\
|
|
.normal-mode .ace_hidden-cursors .ace_cursor{\
|
|
background-color: transparent;\
|
|
}\
|
|
.ace_dialog {\
|
|
position: absolute;\
|
|
left: 0; right: 0;\
|
|
background: white;\
|
|
z-index: 15;\
|
|
padding: .1em .8em;\
|
|
overflow: hidden;\
|
|
color: #333;\
|
|
}\
|
|
.ace_dialog-top {\
|
|
border-bottom: 1px solid #eee;\
|
|
top: 0;\
|
|
}\
|
|
.ace_dialog-bottom {\
|
|
border-top: 1px solid #eee;\
|
|
bottom: 0;\
|
|
}\
|
|
.ace_dialog input {\
|
|
border: none;\
|
|
outline: none;\
|
|
background: transparent;\
|
|
width: 20em;\
|
|
color: inherit;\
|
|
font-family: monospace;\
|
|
}", "vimMode");
|
|
(function() {
|
|
function dialogDiv(cm, template, bottom) {
|
|
var wrap = cm.ace.container;
|
|
var dialog;
|
|
dialog = wrap.appendChild(document.createElement("div"));
|
|
if (bottom)
|
|
dialog.className = "ace_dialog ace_dialog-bottom";
|
|
else
|
|
dialog.className = "ace_dialog ace_dialog-top";
|
|
|
|
if (typeof template == "string") {
|
|
dialog.innerHTML = template;
|
|
} else { // Assuming it's a detached DOM element.
|
|
dialog.appendChild(template);
|
|
}
|
|
return dialog;
|
|
}
|
|
|
|
function closeNotification(cm, newVal) {
|
|
if (cm.state.currentNotificationClose)
|
|
cm.state.currentNotificationClose();
|
|
cm.state.currentNotificationClose = newVal;
|
|
}
|
|
|
|
CodeMirror.defineExtension("openDialog", function(template, callback, options) {
|
|
if (this.virtualSelectionMode()) return;
|
|
if (!options) options = {};
|
|
|
|
closeNotification(this, null);
|
|
|
|
var dialog = dialogDiv(this, template, options.bottom);
|
|
var closed = false, me = this;
|
|
function close(newVal) {
|
|
if (typeof newVal == 'string') {
|
|
inp.value = newVal;
|
|
} else {
|
|
if (closed) return;
|
|
closed = true;
|
|
dialog.parentNode.removeChild(dialog);
|
|
me.focus();
|
|
|
|
if (options.onClose) options.onClose(dialog);
|
|
}
|
|
}
|
|
|
|
var inp = dialog.getElementsByTagName("input")[0], button;
|
|
if (inp) {
|
|
if (options.value) {
|
|
inp.value = options.value;
|
|
if (options.select !== false) inp.select();
|
|
}
|
|
|
|
if (options.onInput)
|
|
CodeMirror.on(inp, "input", function(e) { options.onInput(e, inp.value, close);});
|
|
if (options.onKeyUp)
|
|
CodeMirror.on(inp, "keyup", function(e) {options.onKeyUp(e, inp.value, close);});
|
|
|
|
CodeMirror.on(inp, "keydown", function(e) {
|
|
if (options && options.onKeyDown && options.onKeyDown(e, inp.value, close)) { return; }
|
|
if (e.keyCode == 27 || (options.closeOnEnter !== false && e.keyCode == 13)) {
|
|
inp.blur();
|
|
CodeMirror.e_stop(e);
|
|
close();
|
|
}
|
|
if (e.keyCode == 13) callback(inp.value);
|
|
});
|
|
|
|
if (options.closeOnBlur !== false) CodeMirror.on(inp, "blur", close);
|
|
|
|
inp.focus();
|
|
} else if (button = dialog.getElementsByTagName("button")[0]) {
|
|
CodeMirror.on(button, "click", function() {
|
|
close();
|
|
me.focus();
|
|
});
|
|
|
|
if (options.closeOnBlur !== false) CodeMirror.on(button, "blur", close);
|
|
|
|
button.focus();
|
|
}
|
|
return close;
|
|
});
|
|
|
|
CodeMirror.defineExtension("openNotification", function(template, options) {
|
|
if (this.virtualSelectionMode()) return;
|
|
closeNotification(this, close);
|
|
var dialog = dialogDiv(this, template, options && options.bottom);
|
|
var closed = false, doneTimer;
|
|
var duration = options && typeof options.duration !== "undefined" ? options.duration : 5000;
|
|
|
|
function close() {
|
|
if (closed) return;
|
|
closed = true;
|
|
clearTimeout(doneTimer);
|
|
dialog.parentNode.removeChild(dialog);
|
|
}
|
|
|
|
CodeMirror.on(dialog, 'click', function(e) {
|
|
CodeMirror.e_preventDefault(e);
|
|
close();
|
|
});
|
|
|
|
if (duration)
|
|
doneTimer = setTimeout(close, duration);
|
|
|
|
return close;
|
|
});
|
|
})();
|
|
|
|
|
|
var defaultKeymap = [
|
|
{ keys: '<Left>', type: 'keyToKey', toKeys: 'h' },
|
|
{ keys: '<Right>', type: 'keyToKey', toKeys: 'l' },
|
|
{ keys: '<Up>', type: 'keyToKey', toKeys: 'k' },
|
|
{ keys: '<Down>', type: 'keyToKey', toKeys: 'j' },
|
|
{ keys: '<Space>', type: 'keyToKey', toKeys: 'l' },
|
|
{ keys: '<BS>', type: 'keyToKey', toKeys: 'h', context: 'normal'},
|
|
{ keys: '<C-Space>', type: 'keyToKey', toKeys: 'W' },
|
|
{ keys: '<C-BS>', type: 'keyToKey', toKeys: 'B', context: 'normal' },
|
|
{ keys: '<S-Space>', type: 'keyToKey', toKeys: 'w' },
|
|
{ keys: '<S-BS>', type: 'keyToKey', toKeys: 'b', context: 'normal' },
|
|
{ keys: '<C-n>', type: 'keyToKey', toKeys: 'j' },
|
|
{ keys: '<C-p>', type: 'keyToKey', toKeys: 'k' },
|
|
{ keys: '<C-[>', type: 'keyToKey', toKeys: '<Esc>' },
|
|
{ keys: '<C-c>', type: 'keyToKey', toKeys: '<Esc>' },
|
|
{ keys: '<C-[>', type: 'keyToKey', toKeys: '<Esc>', context: 'insert' },
|
|
{ keys: '<C-c>', type: 'keyToKey', toKeys: '<Esc>', context: 'insert' },
|
|
{ keys: 's', type: 'keyToKey', toKeys: 'cl', context: 'normal' },
|
|
{ keys: 's', type: 'keyToKey', toKeys: 'c', context: 'visual'},
|
|
{ keys: 'S', type: 'keyToKey', toKeys: 'cc', context: 'normal' },
|
|
{ keys: 'S', type: 'keyToKey', toKeys: 'VdO', context: 'visual' },
|
|
{ keys: '<Home>', type: 'keyToKey', toKeys: '0' },
|
|
{ keys: '<End>', type: 'keyToKey', toKeys: '$' },
|
|
{ keys: '<PageUp>', type: 'keyToKey', toKeys: '<C-b>' },
|
|
{ keys: '<PageDown>', type: 'keyToKey', toKeys: '<C-f>' },
|
|
{ keys: '<CR>', type: 'keyToKey', toKeys: 'j^', context: 'normal' },
|
|
{ keys: 'H', type: 'motion', motion: 'moveToTopLine', motionArgs: { linewise: true, toJumplist: true }},
|
|
{ keys: 'M', type: 'motion', motion: 'moveToMiddleLine', motionArgs: { linewise: true, toJumplist: true }},
|
|
{ keys: 'L', type: 'motion', motion: 'moveToBottomLine', motionArgs: { linewise: true, toJumplist: true }},
|
|
{ keys: 'h', type: 'motion', motion: 'moveByCharacters', motionArgs: { forward: false }},
|
|
{ keys: 'l', type: 'motion', motion: 'moveByCharacters', motionArgs: { forward: true }},
|
|
{ keys: 'j', type: 'motion', motion: 'moveByLines', motionArgs: { forward: true, linewise: true }},
|
|
{ keys: 'k', type: 'motion', motion: 'moveByLines', motionArgs: { forward: false, linewise: true }},
|
|
{ keys: 'gj', type: 'motion', motion: 'moveByDisplayLines', motionArgs: { forward: true }},
|
|
{ keys: 'gk', type: 'motion', motion: 'moveByDisplayLines', motionArgs: { forward: false }},
|
|
{ keys: 'w', type: 'motion', motion: 'moveByWords', motionArgs: { forward: true, wordEnd: false }},
|
|
{ keys: 'W', type: 'motion', motion: 'moveByWords', motionArgs: { forward: true, wordEnd: false, bigWord: true }},
|
|
{ keys: 'e', type: 'motion', motion: 'moveByWords', motionArgs: { forward: true, wordEnd: true, inclusive: true }},
|
|
{ keys: 'E', type: 'motion', motion: 'moveByWords', motionArgs: { forward: true, wordEnd: true, bigWord: true, inclusive: true }},
|
|
{ keys: 'b', type: 'motion', motion: 'moveByWords', motionArgs: { forward: false, wordEnd: false }},
|
|
{ keys: 'B', type: 'motion', motion: 'moveByWords', motionArgs: { forward: false, wordEnd: false, bigWord: true }},
|
|
{ keys: 'ge', type: 'motion', motion: 'moveByWords', motionArgs: { forward: false, wordEnd: true, inclusive: true }},
|
|
{ keys: 'gE', type: 'motion', motion: 'moveByWords', motionArgs: { forward: false, wordEnd: true, bigWord: true, inclusive: true }},
|
|
{ keys: '{', type: 'motion', motion: 'moveByParagraph', motionArgs: { forward: false, toJumplist: true }},
|
|
{ keys: '}', type: 'motion', motion: 'moveByParagraph', motionArgs: { forward: true, toJumplist: true }},
|
|
{ keys: '<C-f>', type: 'motion', motion: 'moveByPage', motionArgs: { forward: true }},
|
|
{ keys: '<C-b>', type: 'motion', motion: 'moveByPage', motionArgs: { forward: false }},
|
|
{ keys: '<C-d>', type: 'motion', motion: 'moveByScroll', motionArgs: { forward: true, explicitRepeat: true }},
|
|
{ keys: '<C-u>', type: 'motion', motion: 'moveByScroll', motionArgs: { forward: false, explicitRepeat: true }},
|
|
{ keys: 'gg', type: 'motion', motion: 'moveToLineOrEdgeOfDocument', motionArgs: { forward: false, explicitRepeat: true, linewise: true, toJumplist: true }},
|
|
{ keys: 'G', type: 'motion', motion: 'moveToLineOrEdgeOfDocument', motionArgs: { forward: true, explicitRepeat: true, linewise: true, toJumplist: true }},
|
|
{ keys: '0', type: 'motion', motion: 'moveToStartOfLine' },
|
|
{ keys: '^', type: 'motion', motion: 'moveToFirstNonWhiteSpaceCharacter' },
|
|
{ keys: '+', type: 'motion', motion: 'moveByLines', motionArgs: { forward: true, toFirstChar:true }},
|
|
{ keys: '-', type: 'motion', motion: 'moveByLines', motionArgs: { forward: false, toFirstChar:true }},
|
|
{ keys: '_', type: 'motion', motion: 'moveByLines', motionArgs: { forward: true, toFirstChar:true, repeatOffset:-1 }},
|
|
{ keys: '$', type: 'motion', motion: 'moveToEol', motionArgs: { inclusive: true }},
|
|
{ keys: '%', type: 'motion', motion: 'moveToMatchedSymbol', motionArgs: { inclusive: true, toJumplist: true }},
|
|
{ keys: 'f<character>', type: 'motion', motion: 'moveToCharacter', motionArgs: { forward: true , inclusive: true }},
|
|
{ keys: 'F<character>', type: 'motion', motion: 'moveToCharacter', motionArgs: { forward: false }},
|
|
{ keys: 't<character>', type: 'motion', motion: 'moveTillCharacter', motionArgs: { forward: true, inclusive: true }},
|
|
{ keys: 'T<character>', type: 'motion', motion: 'moveTillCharacter', motionArgs: { forward: false }},
|
|
{ keys: ';', type: 'motion', motion: 'repeatLastCharacterSearch', motionArgs: { forward: true }},
|
|
{ keys: ',', type: 'motion', motion: 'repeatLastCharacterSearch', motionArgs: { forward: false }},
|
|
{ keys: '\'<character>', type: 'motion', motion: 'goToMark', motionArgs: {toJumplist: true, linewise: true}},
|
|
{ keys: '`<character>', type: 'motion', motion: 'goToMark', motionArgs: {toJumplist: true}},
|
|
{ keys: ']`', type: 'motion', motion: 'jumpToMark', motionArgs: { forward: true } },
|
|
{ keys: '[`', type: 'motion', motion: 'jumpToMark', motionArgs: { forward: false } },
|
|
{ keys: ']\'', type: 'motion', motion: 'jumpToMark', motionArgs: { forward: true, linewise: true } },
|
|
{ keys: '[\'', type: 'motion', motion: 'jumpToMark', motionArgs: { forward: false, linewise: true } },
|
|
{ keys: ']p', type: 'action', action: 'paste', isEdit: true, actionArgs: { after: true, isEdit: true, matchIndent: true}},
|
|
{ keys: '[p', type: 'action', action: 'paste', isEdit: true, actionArgs: { after: false, isEdit: true, matchIndent: true}},
|
|
{ keys: ']<character>', type: 'motion', motion: 'moveToSymbol', motionArgs: { forward: true, toJumplist: true}},
|
|
{ keys: '[<character>', type: 'motion', motion: 'moveToSymbol', motionArgs: { forward: false, toJumplist: true}},
|
|
{ keys: '|', type: 'motion', motion: 'moveToColumn'},
|
|
{ keys: 'o', type: 'motion', motion: 'moveToOtherHighlightedEnd', context:'visual'},
|
|
{ keys: 'O', type: 'motion', motion: 'moveToOtherHighlightedEnd', motionArgs: {sameLine: true}, context:'visual'},
|
|
{ keys: 'd', type: 'operator', operator: 'delete' },
|
|
{ keys: 'y', type: 'operator', operator: 'yank' },
|
|
{ keys: 'c', type: 'operator', operator: 'change' },
|
|
{ keys: '>', type: 'operator', operator: 'indent', operatorArgs: { indentRight: true }},
|
|
{ keys: '<', type: 'operator', operator: 'indent', operatorArgs: { indentRight: false }},
|
|
{ keys: 'g~', type: 'operator', operator: 'changeCase' },
|
|
{ keys: 'gu', type: 'operator', operator: 'changeCase', operatorArgs: {toLower: true}, isEdit: true },
|
|
{ keys: 'gU', type: 'operator', operator: 'changeCase', operatorArgs: {toLower: false}, isEdit: true },
|
|
{ keys: 'n', type: 'motion', motion: 'findNext', motionArgs: { forward: true, toJumplist: true }},
|
|
{ keys: 'N', type: 'motion', motion: 'findNext', motionArgs: { forward: false, toJumplist: true }},
|
|
{ keys: 'x', type: 'operatorMotion', operator: 'delete', motion: 'moveByCharacters', motionArgs: { forward: true }, operatorMotionArgs: { visualLine: false }},
|
|
{ keys: 'X', type: 'operatorMotion', operator: 'delete', motion: 'moveByCharacters', motionArgs: { forward: false }, operatorMotionArgs: { visualLine: true }},
|
|
{ keys: 'D', type: 'operatorMotion', operator: 'delete', motion: 'moveToEol', motionArgs: { inclusive: true }, context: 'normal'},
|
|
{ keys: 'D', type: 'operator', operator: 'delete', operatorArgs: { linewise: true }, context: 'visual'},
|
|
{ keys: 'Y', type: 'operatorMotion', operator: 'yank', motion: 'moveToEol', motionArgs: { inclusive: true }, context: 'normal'},
|
|
{ keys: 'Y', type: 'operator', operator: 'yank', operatorArgs: { linewise: true }, context: 'visual'},
|
|
{ keys: 'C', type: 'operatorMotion', operator: 'change', motion: 'moveToEol', motionArgs: { inclusive: true }, context: 'normal'},
|
|
{ keys: 'C', type: 'operator', operator: 'change', operatorArgs: { linewise: true }, context: 'visual'},
|
|
{ keys: '~', type: 'operatorMotion', operator: 'changeCase', motion: 'moveByCharacters', motionArgs: { forward: true }, operatorArgs: { shouldMoveCursor: true }, context: 'normal'},
|
|
{ keys: '~', type: 'operator', operator: 'changeCase', context: 'visual'},
|
|
{ keys: '<C-w>', type: 'operatorMotion', operator: 'delete', motion: 'moveByWords', motionArgs: { forward: false, wordEnd: false }, context: 'insert' },
|
|
{ keys: '<C-i>', type: 'action', action: 'jumpListWalk', actionArgs: { forward: true }},
|
|
{ keys: '<C-o>', type: 'action', action: 'jumpListWalk', actionArgs: { forward: false }},
|
|
{ keys: '<C-e>', type: 'action', action: 'scroll', actionArgs: { forward: true, linewise: true }},
|
|
{ keys: '<C-y>', type: 'action', action: 'scroll', actionArgs: { forward: false, linewise: true }},
|
|
{ keys: 'a', type: 'action', action: 'enterInsertMode', isEdit: true, actionArgs: { insertAt: 'charAfter' }, context: 'normal' },
|
|
{ keys: 'A', type: 'action', action: 'enterInsertMode', isEdit: true, actionArgs: { insertAt: 'eol' }, context: 'normal' },
|
|
{ keys: 'A', type: 'action', action: 'enterInsertMode', isEdit: true, actionArgs: { insertAt: 'endOfSelectedArea' }, context: 'visual' },
|
|
{ keys: 'i', type: 'action', action: 'enterInsertMode', isEdit: true, actionArgs: { insertAt: 'inplace' }, context: 'normal' },
|
|
{ keys: 'I', type: 'action', action: 'enterInsertMode', isEdit: true, actionArgs: { insertAt: 'firstNonBlank'}, context: 'normal' },
|
|
{ keys: 'I', type: 'action', action: 'enterInsertMode', isEdit: true, actionArgs: { insertAt: 'startOfSelectedArea' }, context: 'visual' },
|
|
{ keys: 'o', type: 'action', action: 'newLineAndEnterInsertMode', isEdit: true, interlaceInsertRepeat: true, actionArgs: { after: true }, context: 'normal' },
|
|
{ keys: 'O', type: 'action', action: 'newLineAndEnterInsertMode', isEdit: true, interlaceInsertRepeat: true, actionArgs: { after: false }, context: 'normal' },
|
|
{ keys: 'v', type: 'action', action: 'toggleVisualMode' },
|
|
{ keys: 'V', type: 'action', action: 'toggleVisualMode', actionArgs: { linewise: true }},
|
|
{ keys: '<C-v>', type: 'action', action: 'toggleVisualMode', actionArgs: { blockwise: true }},
|
|
{ keys: '<C-q>', type: 'action', action: 'toggleVisualMode', actionArgs: { blockwise: true }},
|
|
{ keys: 'gv', type: 'action', action: 'reselectLastSelection' },
|
|
{ keys: 'J', type: 'action', action: 'joinLines', isEdit: true },
|
|
{ keys: 'p', type: 'action', action: 'paste', isEdit: true, actionArgs: { after: true, isEdit: true }},
|
|
{ keys: 'P', type: 'action', action: 'paste', isEdit: true, actionArgs: { after: false, isEdit: true }},
|
|
{ keys: 'r<character>', type: 'action', action: 'replace', isEdit: true },
|
|
{ keys: '@<character>', type: 'action', action: 'replayMacro' },
|
|
{ keys: 'q<character>', type: 'action', action: 'enterMacroRecordMode' },
|
|
{ keys: 'R', type: 'action', action: 'enterInsertMode', isEdit: true, actionArgs: { replace: true }},
|
|
{ keys: 'u', type: 'action', action: 'undo', context: 'normal' },
|
|
{ keys: 'u', type: 'operator', operator: 'changeCase', operatorArgs: {toLower: true}, context: 'visual', isEdit: true },
|
|
{ keys: 'U', type: 'operator', operator: 'changeCase', operatorArgs: {toLower: false}, context: 'visual', isEdit: true },
|
|
{ keys: '<C-r>', type: 'action', action: 'redo' },
|
|
{ keys: 'm<character>', type: 'action', action: 'setMark' },
|
|
{ keys: '"<character>', type: 'action', action: 'setRegister' },
|
|
{ keys: 'zz', type: 'action', action: 'scrollToCursor', actionArgs: { position: 'center' }},
|
|
{ keys: 'z.', type: 'action', action: 'scrollToCursor', actionArgs: { position: 'center' }, motion: 'moveToFirstNonWhiteSpaceCharacter' },
|
|
{ keys: 'zt', type: 'action', action: 'scrollToCursor', actionArgs: { position: 'top' }},
|
|
{ keys: 'z<CR>', type: 'action', action: 'scrollToCursor', actionArgs: { position: 'top' }, motion: 'moveToFirstNonWhiteSpaceCharacter' },
|
|
{ keys: 'z-', type: 'action', action: 'scrollToCursor', actionArgs: { position: 'bottom' }},
|
|
{ keys: 'zb', type: 'action', action: 'scrollToCursor', actionArgs: { position: 'bottom' }, motion: 'moveToFirstNonWhiteSpaceCharacter' },
|
|
{ keys: '.', type: 'action', action: 'repeatLastEdit' },
|
|
{ keys: '<C-a>', type: 'action', action: 'incrementNumberToken', isEdit: true, actionArgs: {increase: true, backtrack: false}},
|
|
{ keys: '<C-x>', type: 'action', action: 'incrementNumberToken', isEdit: true, actionArgs: {increase: false, backtrack: false}},
|
|
{ keys: 'a<character>', type: 'motion', motion: 'textObjectManipulation' },
|
|
{ keys: 'i<character>', type: 'motion', motion: 'textObjectManipulation', motionArgs: { textObjectInner: true }},
|
|
{ keys: '/', type: 'search', searchArgs: { forward: true, querySrc: 'prompt', toJumplist: true }},
|
|
{ keys: '?', type: 'search', searchArgs: { forward: false, querySrc: 'prompt', toJumplist: true }},
|
|
{ keys: '*', type: 'search', searchArgs: { forward: true, querySrc: 'wordUnderCursor', wholeWordOnly: true, toJumplist: true }},
|
|
{ keys: '#', type: 'search', searchArgs: { forward: false, querySrc: 'wordUnderCursor', wholeWordOnly: true, toJumplist: true }},
|
|
{ keys: 'g*', type: 'search', searchArgs: { forward: true, querySrc: 'wordUnderCursor', toJumplist: true }},
|
|
{ keys: 'g#', type: 'search', searchArgs: { forward: false, querySrc: 'wordUnderCursor', toJumplist: true }},
|
|
{ keys: ':', type: 'ex' }
|
|
];
|
|
var defaultExCommandMap = [
|
|
{ name: 'colorscheme', shortName: 'colo' },
|
|
{ name: 'map' },
|
|
{ name: 'imap', shortName: 'im' },
|
|
{ name: 'nmap', shortName: 'nm' },
|
|
{ name: 'vmap', shortName: 'vm' },
|
|
{ name: 'unmap' },
|
|
{ name: 'write', shortName: 'w' },
|
|
{ name: 'undo', shortName: 'u' },
|
|
{ name: 'redo', shortName: 'red' },
|
|
{ name: 'set', shortName: 'se' },
|
|
{ name: 'set', shortName: 'se' },
|
|
{ name: 'setlocal', shortName: 'setl' },
|
|
{ name: 'setglobal', shortName: 'setg' },
|
|
{ name: 'sort', shortName: 'sor' },
|
|
{ name: 'substitute', shortName: 's', possiblyAsync: true },
|
|
{ name: 'nohlsearch', shortName: 'noh' },
|
|
{ name: 'delmarks', shortName: 'delm' },
|
|
{ name: 'registers', shortName: 'reg', excludeFromCommandHistory: true },
|
|
{ name: 'global', shortName: 'g' }
|
|
];
|
|
|
|
var Pos = CodeMirror.Pos;
|
|
|
|
var Vim = function() { return vimApi; } //{
|
|
function enterVimMode(cm) {
|
|
cm.setOption('disableInput', true);
|
|
cm.setOption('showCursorWhenSelecting', false);
|
|
CodeMirror.signal(cm, "vim-mode-change", {mode: "normal"});
|
|
cm.on('cursorActivity', onCursorActivity);
|
|
maybeInitVimState(cm);
|
|
CodeMirror.on(cm.getInputField(), 'paste', getOnPasteFn(cm));
|
|
}
|
|
|
|
function leaveVimMode(cm) {
|
|
cm.setOption('disableInput', false);
|
|
cm.off('cursorActivity', onCursorActivity);
|
|
CodeMirror.off(cm.getInputField(), 'paste', getOnPasteFn(cm));
|
|
cm.state.vim = null;
|
|
}
|
|
|
|
function detachVimMap(cm, next) {
|
|
if (this == CodeMirror.keyMap.vim)
|
|
CodeMirror.rmClass(cm.getWrapperElement(), "cm-fat-cursor");
|
|
|
|
if (!next || next.attach != attachVimMap)
|
|
leaveVimMode(cm, false);
|
|
}
|
|
function attachVimMap(cm, prev) {
|
|
if (this == CodeMirror.keyMap.vim)
|
|
CodeMirror.addClass(cm.getWrapperElement(), "cm-fat-cursor");
|
|
|
|
if (!prev || prev.attach != attachVimMap)
|
|
enterVimMode(cm);
|
|
}
|
|
CodeMirror.defineOption('vimMode', false, function(cm, val, prev) {
|
|
if (val && cm.getOption("keyMap") != "vim")
|
|
cm.setOption("keyMap", "vim");
|
|
else if (!val && prev != CodeMirror.Init && /^vim/.test(cm.getOption("keyMap")))
|
|
cm.setOption("keyMap", "default");
|
|
});
|
|
|
|
function cmKey(key, cm) {
|
|
if (!cm) { return undefined; }
|
|
var vimKey = cmKeyToVimKey(key);
|
|
if (!vimKey) {
|
|
return false;
|
|
}
|
|
var cmd = CodeMirror.Vim.findKey(cm, vimKey);
|
|
if (typeof cmd == 'function') {
|
|
CodeMirror.signal(cm, 'vim-keypress', vimKey);
|
|
}
|
|
return cmd;
|
|
}
|
|
|
|
var modifiers = {'Shift': 'S', 'Ctrl': 'C', 'Alt': 'A', 'Cmd': 'D', 'Mod': 'A'};
|
|
var specialKeys = {Enter:'CR',Backspace:'BS',Delete:'Del'};
|
|
function cmKeyToVimKey(key) {
|
|
if (key.charAt(0) == '\'') {
|
|
return key.charAt(1);
|
|
}
|
|
var pieces = key.split(/-(?!$)/);
|
|
var lastPiece = pieces[pieces.length - 1];
|
|
if (pieces.length == 1 && pieces[0].length == 1) {
|
|
return false;
|
|
} else if (pieces.length == 2 && pieces[0] == 'Shift' && lastPiece.length == 1) {
|
|
return false;
|
|
}
|
|
var hasCharacter = false;
|
|
for (var i = 0; i < pieces.length; i++) {
|
|
var piece = pieces[i];
|
|
if (piece in modifiers) { pieces[i] = modifiers[piece]; }
|
|
else { hasCharacter = true; }
|
|
if (piece in specialKeys) { pieces[i] = specialKeys[piece]; }
|
|
}
|
|
if (!hasCharacter) {
|
|
return false;
|
|
}
|
|
if (isUpperCase(lastPiece)) {
|
|
pieces[pieces.length - 1] = lastPiece.toLowerCase();
|
|
}
|
|
return '<' + pieces.join('-') + '>';
|
|
}
|
|
|
|
function getOnPasteFn(cm) {
|
|
var vim = cm.state.vim;
|
|
if (!vim.onPasteFn) {
|
|
vim.onPasteFn = function() {
|
|
if (!vim.insertMode) {
|
|
cm.setCursor(offsetCursor(cm.getCursor(), 0, 1));
|
|
actions.enterInsertMode(cm, {}, vim);
|
|
}
|
|
};
|
|
}
|
|
return vim.onPasteFn;
|
|
}
|
|
|
|
var numberRegex = /[\d]/;
|
|
var wordCharTest = [CodeMirror.isWordChar, function(ch) {
|
|
return ch && !CodeMirror.isWordChar(ch) && !/\s/.test(ch);
|
|
}], bigWordCharTest = [function(ch) {
|
|
return /\S/.test(ch);
|
|
}];
|
|
function makeKeyRange(start, size) {
|
|
var keys = [];
|
|
for (var i = start; i < start + size; i++) {
|
|
keys.push(String.fromCharCode(i));
|
|
}
|
|
return keys;
|
|
}
|
|
var upperCaseAlphabet = makeKeyRange(65, 26);
|
|
var lowerCaseAlphabet = makeKeyRange(97, 26);
|
|
var numbers = makeKeyRange(48, 10);
|
|
var validMarks = [].concat(upperCaseAlphabet, lowerCaseAlphabet, numbers, ['<', '>']);
|
|
var validRegisters = [].concat(upperCaseAlphabet, lowerCaseAlphabet, numbers, ['-', '"', '.', ':', '/']);
|
|
|
|
function isLine(cm, line) {
|
|
return line >= cm.firstLine() && line <= cm.lastLine();
|
|
}
|
|
function isLowerCase(k) {
|
|
return (/^[a-z]$/).test(k);
|
|
}
|
|
function isMatchableSymbol(k) {
|
|
return '()[]{}'.indexOf(k) != -1;
|
|
}
|
|
function isNumber(k) {
|
|
return numberRegex.test(k);
|
|
}
|
|
function isUpperCase(k) {
|
|
return (/^[A-Z]$/).test(k);
|
|
}
|
|
function isWhiteSpaceString(k) {
|
|
return (/^\s*$/).test(k);
|
|
}
|
|
function inArray(val, arr) {
|
|
for (var i = 0; i < arr.length; i++) {
|
|
if (arr[i] == val) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
var options = {};
|
|
function defineOption(name, defaultValue, type, aliases, callback) {
|
|
if (defaultValue === undefined && !callback) {
|
|
throw Error('defaultValue is required unless callback is provided');
|
|
}
|
|
if (!type) { type = 'string'; }
|
|
options[name] = {
|
|
type: type,
|
|
defaultValue: defaultValue,
|
|
callback: callback
|
|
};
|
|
if (aliases) {
|
|
for (var i = 0; i < aliases.length; i++) {
|
|
options[aliases[i]] = options[name];
|
|
}
|
|
}
|
|
if (defaultValue) {
|
|
setOption(name, defaultValue);
|
|
}
|
|
}
|
|
|
|
function setOption(name, value, cm, cfg) {
|
|
var option = options[name];
|
|
cfg = cfg || {};
|
|
var scope = cfg.scope;
|
|
if (!option) {
|
|
throw Error('Unknown option: ' + name);
|
|
}
|
|
if (option.type == 'boolean') {
|
|
if (value && value !== true) {
|
|
throw Error('Invalid argument: ' + name + '=' + value);
|
|
} else if (value !== false) {
|
|
value = true;
|
|
}
|
|
}
|
|
if (option.callback) {
|
|
if (scope !== 'local') {
|
|
option.callback(value, undefined);
|
|
}
|
|
if (scope !== 'global' && cm) {
|
|
option.callback(value, cm);
|
|
}
|
|
} else {
|
|
if (scope !== 'local') {
|
|
option.value = option.type == 'boolean' ? !!value : value;
|
|
}
|
|
if (scope !== 'global' && cm) {
|
|
cm.state.vim.options[name] = {value: value};
|
|
}
|
|
}
|
|
}
|
|
|
|
function getOption(name, cm, cfg) {
|
|
var option = options[name];
|
|
cfg = cfg || {};
|
|
var scope = cfg.scope;
|
|
if (!option) {
|
|
throw Error('Unknown option: ' + name);
|
|
}
|
|
if (option.callback) {
|
|
var local = cm && option.callback(undefined, cm);
|
|
if (scope !== 'global' && local !== undefined) {
|
|
return local;
|
|
}
|
|
if (scope !== 'local') {
|
|
return option.callback();
|
|
}
|
|
return;
|
|
} else {
|
|
var local = (scope !== 'global') && (cm && cm.state.vim.options[name]);
|
|
return (local || (scope !== 'local') && option || {}).value;
|
|
}
|
|
}
|
|
|
|
defineOption('filetype', undefined, 'string', ['ft'], function(name, cm) {
|
|
if (cm === undefined) {
|
|
return;
|
|
}
|
|
if (name === undefined) {
|
|
var mode = cm.getOption('mode');
|
|
return mode == 'null' ? '' : mode;
|
|
} else {
|
|
var mode = name == '' ? 'null' : name;
|
|
cm.setOption('mode', mode);
|
|
}
|
|
});
|
|
|
|
var createCircularJumpList = function() {
|
|
var size = 100;
|
|
var pointer = -1;
|
|
var head = 0;
|
|
var tail = 0;
|
|
var buffer = new Array(size);
|
|
function add(cm, oldCur, newCur) {
|
|
var current = pointer % size;
|
|
var curMark = buffer[current];
|
|
function useNextSlot(cursor) {
|
|
var next = ++pointer % size;
|
|
var trashMark = buffer[next];
|
|
if (trashMark) {
|
|
trashMark.clear();
|
|
}
|
|
buffer[next] = cm.setBookmark(cursor);
|
|
}
|
|
if (curMark) {
|
|
var markPos = curMark.find();
|
|
if (markPos && !cursorEqual(markPos, oldCur)) {
|
|
useNextSlot(oldCur);
|
|
}
|
|
} else {
|
|
useNextSlot(oldCur);
|
|
}
|
|
useNextSlot(newCur);
|
|
head = pointer;
|
|
tail = pointer - size + 1;
|
|
if (tail < 0) {
|
|
tail = 0;
|
|
}
|
|
}
|
|
function move(cm, offset) {
|
|
pointer += offset;
|
|
if (pointer > head) {
|
|
pointer = head;
|
|
} else if (pointer < tail) {
|
|
pointer = tail;
|
|
}
|
|
var mark = buffer[(size + pointer) % size];
|
|
if (mark && !mark.find()) {
|
|
var inc = offset > 0 ? 1 : -1;
|
|
var newCur;
|
|
var oldCur = cm.getCursor();
|
|
do {
|
|
pointer += inc;
|
|
mark = buffer[(size + pointer) % size];
|
|
if (mark &&
|
|
(newCur = mark.find()) &&
|
|
!cursorEqual(oldCur, newCur)) {
|
|
break;
|
|
}
|
|
} while (pointer < head && pointer > tail);
|
|
}
|
|
return mark;
|
|
}
|
|
return {
|
|
cachedCursor: undefined, //used for # and * jumps
|
|
add: add,
|
|
move: move
|
|
};
|
|
};
|
|
var createInsertModeChanges = function(c) {
|
|
if (c) {
|
|
return {
|
|
changes: c.changes,
|
|
expectCursorActivityForChange: c.expectCursorActivityForChange
|
|
};
|
|
}
|
|
return {
|
|
changes: [],
|
|
expectCursorActivityForChange: false
|
|
};
|
|
};
|
|
|
|
function MacroModeState() {
|
|
this.latestRegister = undefined;
|
|
this.isPlaying = false;
|
|
this.isRecording = false;
|
|
this.replaySearchQueries = [];
|
|
this.onRecordingDone = undefined;
|
|
this.lastInsertModeChanges = createInsertModeChanges();
|
|
}
|
|
MacroModeState.prototype = {
|
|
exitMacroRecordMode: function() {
|
|
var macroModeState = vimGlobalState.macroModeState;
|
|
if (macroModeState.onRecordingDone) {
|
|
macroModeState.onRecordingDone(); // close dialog
|
|
}
|
|
macroModeState.onRecordingDone = undefined;
|
|
macroModeState.isRecording = false;
|
|
},
|
|
enterMacroRecordMode: function(cm, registerName) {
|
|
var register =
|
|
vimGlobalState.registerController.getRegister(registerName);
|
|
if (register) {
|
|
register.clear();
|
|
this.latestRegister = registerName;
|
|
if (cm.openDialog) {
|
|
this.onRecordingDone = cm.openDialog(
|
|
'(recording)['+registerName+']', null, {bottom:true});
|
|
}
|
|
this.isRecording = true;
|
|
}
|
|
}
|
|
};
|
|
|
|
function maybeInitVimState(cm) {
|
|
if (!cm.state.vim) {
|
|
cm.state.vim = {
|
|
inputState: new InputState(),
|
|
lastEditInputState: undefined,
|
|
lastEditActionCommand: undefined,
|
|
lastHPos: -1,
|
|
lastHSPos: -1,
|
|
lastMotion: null,
|
|
marks: {},
|
|
fakeCursor: null,
|
|
insertMode: false,
|
|
insertModeRepeat: undefined,
|
|
visualMode: false,
|
|
visualLine: false,
|
|
visualBlock: false,
|
|
lastSelection: null,
|
|
lastPastedText: null,
|
|
sel: {},
|
|
options: {}
|
|
};
|
|
}
|
|
return cm.state.vim;
|
|
}
|
|
var vimGlobalState;
|
|
function resetVimGlobalState() {
|
|
vimGlobalState = {
|
|
searchQuery: null,
|
|
searchIsReversed: false,
|
|
lastSubstituteReplacePart: undefined,
|
|
jumpList: createCircularJumpList(),
|
|
macroModeState: new MacroModeState,
|
|
lastChararacterSearch: {increment:0, forward:true, selectedCharacter:''},
|
|
registerController: new RegisterController({}),
|
|
searchHistoryController: new HistoryController({}),
|
|
exCommandHistoryController : new HistoryController({})
|
|
};
|
|
for (var optionName in options) {
|
|
var option = options[optionName];
|
|
option.value = option.defaultValue;
|
|
}
|
|
}
|
|
|
|
var lastInsertModeKeyTimer;
|
|
var vimApi= {
|
|
buildKeyMap: function() {
|
|
},
|
|
getRegisterController: function() {
|
|
return vimGlobalState.registerController;
|
|
},
|
|
resetVimGlobalState_: resetVimGlobalState,
|
|
getVimGlobalState_: function() {
|
|
return vimGlobalState;
|
|
},
|
|
maybeInitVimState_: maybeInitVimState,
|
|
|
|
suppressErrorLogging: false,
|
|
|
|
InsertModeKey: InsertModeKey,
|
|
map: function(lhs, rhs, ctx) {
|
|
exCommandDispatcher.map(lhs, rhs, ctx);
|
|
},
|
|
unmap: function(lhs, ctx) {
|
|
exCommandDispatcher.unmap(lhs, ctx);
|
|
},
|
|
setOption: setOption,
|
|
getOption: getOption,
|
|
defineOption: defineOption,
|
|
defineEx: function(name, prefix, func){
|
|
if (!prefix) {
|
|
prefix = name;
|
|
} else if (name.indexOf(prefix) !== 0) {
|
|
throw new Error('(Vim.defineEx) "'+prefix+'" is not a prefix of "'+name+'", command not registered');
|
|
}
|
|
exCommands[name]=func;
|
|
exCommandDispatcher.commandMap_[prefix]={name:name, shortName:prefix, type:'api'};
|
|
},
|
|
handleKey: function (cm, key, origin) {
|
|
var command = this.findKey(cm, key, origin);
|
|
if (typeof command === 'function') {
|
|
return command();
|
|
}
|
|
},
|
|
findKey: function(cm, key, origin) {
|
|
var vim = maybeInitVimState(cm);
|
|
function handleMacroRecording() {
|
|
var macroModeState = vimGlobalState.macroModeState;
|
|
if (macroModeState.isRecording) {
|
|
if (key == 'q') {
|
|
macroModeState.exitMacroRecordMode();
|
|
clearInputState(cm);
|
|
return true;
|
|
}
|
|
if (origin != 'mapping') {
|
|
logKey(macroModeState, key);
|
|
}
|
|
}
|
|
}
|
|
function handleEsc() {
|
|
if (key == '<Esc>') {
|
|
clearInputState(cm);
|
|
if (vim.visualMode) {
|
|
exitVisualMode(cm);
|
|
} else if (vim.insertMode) {
|
|
exitInsertMode(cm);
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
function doKeyToKey(keys) {
|
|
var match;
|
|
while (keys) {
|
|
match = (/<\w+-.+?>|<\w+>|./).exec(keys);
|
|
key = match[0];
|
|
keys = keys.substring(match.index + key.length);
|
|
CodeMirror.Vim.handleKey(cm, key, 'mapping');
|
|
}
|
|
}
|
|
|
|
function handleKeyInsertMode() {
|
|
if (handleEsc()) { return true; }
|
|
var keys = vim.inputState.keyBuffer = vim.inputState.keyBuffer + key;
|
|
var keysAreChars = key.length == 1;
|
|
var match = commandDispatcher.matchCommand(keys, defaultKeymap, vim.inputState, 'insert');
|
|
while (keys.length > 1 && match.type != 'full') {
|
|
var keys = vim.inputState.keyBuffer = keys.slice(1);
|
|
var thisMatch = commandDispatcher.matchCommand(keys, defaultKeymap, vim.inputState, 'insert');
|
|
if (thisMatch.type != 'none') { match = thisMatch; }
|
|
}
|
|
if (match.type == 'none') { clearInputState(cm); return false; }
|
|
else if (match.type == 'partial') {
|
|
if (lastInsertModeKeyTimer) { window.clearTimeout(lastInsertModeKeyTimer); }
|
|
lastInsertModeKeyTimer = window.setTimeout(
|
|
function() { if (vim.insertMode && vim.inputState.keyBuffer) { clearInputState(cm); } },
|
|
getOption('insertModeEscKeysTimeout'));
|
|
return !keysAreChars;
|
|
}
|
|
|
|
if (lastInsertModeKeyTimer) { window.clearTimeout(lastInsertModeKeyTimer); }
|
|
if (keysAreChars) {
|
|
var selections = cm.listSelections();
|
|
for (var i = 0; i < selections.length; i++) {
|
|
var here = selections[i].head;
|
|
cm.replaceRange('', offsetCursor(here, 0, -(keys.length - 1)), here, '+input');
|
|
}
|
|
vimGlobalState.macroModeState.lastInsertModeChanges.changes.pop();
|
|
}
|
|
clearInputState(cm);
|
|
return match.command;
|
|
}
|
|
|
|
function handleKeyNonInsertMode() {
|
|
if (handleMacroRecording() || handleEsc()) { return true; }
|
|
|
|
var keys = vim.inputState.keyBuffer = vim.inputState.keyBuffer + key;
|
|
if (/^[1-9]\d*$/.test(keys)) { return true; }
|
|
|
|
var keysMatcher = /^(\d*)(.*)$/.exec(keys);
|
|
if (!keysMatcher) { clearInputState(cm); return false; }
|
|
var context = vim.visualMode ? 'visual' :
|
|
'normal';
|
|
var match = commandDispatcher.matchCommand(keysMatcher[2] || keysMatcher[1], defaultKeymap, vim.inputState, context);
|
|
if (match.type == 'none') { clearInputState(cm); return false; }
|
|
else if (match.type == 'partial') { return true; }
|
|
|
|
vim.inputState.keyBuffer = '';
|
|
var keysMatcher = /^(\d*)(.*)$/.exec(keys);
|
|
if (keysMatcher[1] && keysMatcher[1] != '0') {
|
|
vim.inputState.pushRepeatDigit(keysMatcher[1]);
|
|
}
|
|
return match.command;
|
|
}
|
|
|
|
var command;
|
|
if (vim.insertMode) { command = handleKeyInsertMode(); }
|
|
else { command = handleKeyNonInsertMode(); }
|
|
if (command === false) {
|
|
return undefined;
|
|
} else if (command === true) {
|
|
return function() { return true; };
|
|
} else {
|
|
return function() {
|
|
if ((command.operator || command.isEdit) && cm.getOption('readOnly'))
|
|
return; // ace_patch
|
|
return cm.operation(function() {
|
|
cm.curOp.isVimOp = true;
|
|
try {
|
|
if (command.type == 'keyToKey') {
|
|
doKeyToKey(command.toKeys);
|
|
} else {
|
|
commandDispatcher.processCommand(cm, vim, command);
|
|
}
|
|
} catch (e) {
|
|
cm.state.vim = undefined;
|
|
maybeInitVimState(cm);
|
|
if (!CodeMirror.Vim.suppressErrorLogging) {
|
|
console['log'](e);
|
|
}
|
|
throw e;
|
|
}
|
|
return true;
|
|
});
|
|
};
|
|
}
|
|
},
|
|
handleEx: function(cm, input) {
|
|
exCommandDispatcher.processCommand(cm, input);
|
|
},
|
|
|
|
defineMotion: defineMotion,
|
|
defineAction: defineAction,
|
|
defineOperator: defineOperator,
|
|
mapCommand: mapCommand,
|
|
_mapCommand: _mapCommand,
|
|
|
|
defineRegister: defineRegister,
|
|
|
|
exitVisualMode: exitVisualMode,
|
|
exitInsertMode: exitInsertMode
|
|
};
|
|
function InputState() {
|
|
this.prefixRepeat = [];
|
|
this.motionRepeat = [];
|
|
|
|
this.operator = null;
|
|
this.operatorArgs = null;
|
|
this.motion = null;
|
|
this.motionArgs = null;
|
|
this.keyBuffer = []; // For matching multi-key commands.
|
|
this.registerName = null; // Defaults to the unnamed register.
|
|
}
|
|
InputState.prototype.pushRepeatDigit = function(n) {
|
|
if (!this.operator) {
|
|
this.prefixRepeat = this.prefixRepeat.concat(n);
|
|
} else {
|
|
this.motionRepeat = this.motionRepeat.concat(n);
|
|
}
|
|
};
|
|
InputState.prototype.getRepeat = function() {
|
|
var repeat = 0;
|
|
if (this.prefixRepeat.length > 0 || this.motionRepeat.length > 0) {
|
|
repeat = 1;
|
|
if (this.prefixRepeat.length > 0) {
|
|
repeat *= parseInt(this.prefixRepeat.join(''), 10);
|
|
}
|
|
if (this.motionRepeat.length > 0) {
|
|
repeat *= parseInt(this.motionRepeat.join(''), 10);
|
|
}
|
|
}
|
|
return repeat;
|
|
};
|
|
|
|
function clearInputState(cm, reason) {
|
|
cm.state.vim.inputState = new InputState();
|
|
CodeMirror.signal(cm, 'vim-command-done', reason);
|
|
}
|
|
function Register(text, linewise, blockwise) {
|
|
this.clear();
|
|
this.keyBuffer = [text || ''];
|
|
this.insertModeChanges = [];
|
|
this.searchQueries = [];
|
|
this.linewise = !!linewise;
|
|
this.blockwise = !!blockwise;
|
|
}
|
|
Register.prototype = {
|
|
setText: function(text, linewise, blockwise) {
|
|
this.keyBuffer = [text || ''];
|
|
this.linewise = !!linewise;
|
|
this.blockwise = !!blockwise;
|
|
},
|
|
pushText: function(text, linewise) {
|
|
if (linewise) {
|
|
if (!this.linewise) {
|
|
this.keyBuffer.push('\n');
|
|
}
|
|
this.linewise = true;
|
|
}
|
|
this.keyBuffer.push(text);
|
|
},
|
|
pushInsertModeChanges: function(changes) {
|
|
this.insertModeChanges.push(createInsertModeChanges(changes));
|
|
},
|
|
pushSearchQuery: function(query) {
|
|
this.searchQueries.push(query);
|
|
},
|
|
clear: function() {
|
|
this.keyBuffer = [];
|
|
this.insertModeChanges = [];
|
|
this.searchQueries = [];
|
|
this.linewise = false;
|
|
},
|
|
toString: function() {
|
|
return this.keyBuffer.join('');
|
|
}
|
|
};
|
|
function defineRegister(name, register) {
|
|
var registers = vimGlobalState.registerController.registers[name];
|
|
if (!name || name.length != 1) {
|
|
throw Error('Register name must be 1 character');
|
|
}
|
|
registers[name] = register;
|
|
validRegisters.push(name);
|
|
}
|
|
function RegisterController(registers) {
|
|
this.registers = registers;
|
|
this.unnamedRegister = registers['"'] = new Register();
|
|
registers['.'] = new Register();
|
|
registers[':'] = new Register();
|
|
registers['/'] = new Register();
|
|
}
|
|
RegisterController.prototype = {
|
|
pushText: function(registerName, operator, text, linewise, blockwise) {
|
|
if (linewise && text.charAt(0) == '\n') {
|
|
text = text.slice(1) + '\n';
|
|
}
|
|
if (linewise && text.charAt(text.length - 1) !== '\n'){
|
|
text += '\n';
|
|
}
|
|
var register = this.isValidRegister(registerName) ?
|
|
this.getRegister(registerName) : null;
|
|
if (!register) {
|
|
switch (operator) {
|
|
case 'yank':
|
|
this.registers['0'] = new Register(text, linewise, blockwise);
|
|
break;
|
|
case 'delete':
|
|
case 'change':
|
|
if (text.indexOf('\n') == -1) {
|
|
this.registers['-'] = new Register(text, linewise);
|
|
} else {
|
|
this.shiftNumericRegisters_();
|
|
this.registers['1'] = new Register(text, linewise);
|
|
}
|
|
break;
|
|
}
|
|
this.unnamedRegister.setText(text, linewise, blockwise);
|
|
return;
|
|
}
|
|
var append = isUpperCase(registerName);
|
|
if (append) {
|
|
register.pushText(text, linewise);
|
|
} else {
|
|
register.setText(text, linewise, blockwise);
|
|
}
|
|
this.unnamedRegister.setText(register.toString(), linewise);
|
|
},
|
|
getRegister: function(name) {
|
|
if (!this.isValidRegister(name)) {
|
|
return this.unnamedRegister;
|
|
}
|
|
name = name.toLowerCase();
|
|
if (!this.registers[name]) {
|
|
this.registers[name] = new Register();
|
|
}
|
|
return this.registers[name];
|
|
},
|
|
isValidRegister: function(name) {
|
|
return name && inArray(name, validRegisters);
|
|
},
|
|
shiftNumericRegisters_: function() {
|
|
for (var i = 9; i >= 2; i--) {
|
|
this.registers[i] = this.getRegister('' + (i - 1));
|
|
}
|
|
}
|
|
};
|
|
function HistoryController() {
|
|
this.historyBuffer = [];
|
|
this.iterator;
|
|
this.initialPrefix = null;
|
|
}
|
|
HistoryController.prototype = {
|
|
nextMatch: function (input, up) {
|
|
var historyBuffer = this.historyBuffer;
|
|
var dir = up ? -1 : 1;
|
|
if (this.initialPrefix === null) this.initialPrefix = input;
|
|
for (var i = this.iterator + dir; up ? i >= 0 : i < historyBuffer.length; i+= dir) {
|
|
var element = historyBuffer[i];
|
|
for (var j = 0; j <= element.length; j++) {
|
|
if (this.initialPrefix == element.substring(0, j)) {
|
|
this.iterator = i;
|
|
return element;
|
|
}
|
|
}
|
|
}
|
|
if (i >= historyBuffer.length) {
|
|
this.iterator = historyBuffer.length;
|
|
return this.initialPrefix;
|
|
}
|
|
if (i < 0 ) return input;
|
|
},
|
|
pushInput: function(input) {
|
|
var index = this.historyBuffer.indexOf(input);
|
|
if (index > -1) this.historyBuffer.splice(index, 1);
|
|
if (input.length) this.historyBuffer.push(input);
|
|
},
|
|
reset: function() {
|
|
this.initialPrefix = null;
|
|
this.iterator = this.historyBuffer.length;
|
|
}
|
|
};
|
|
var commandDispatcher = {
|
|
matchCommand: function(keys, keyMap, inputState, context) {
|
|
var matches = commandMatches(keys, keyMap, context, inputState);
|
|
if (!matches.full && !matches.partial) {
|
|
return {type: 'none'};
|
|
} else if (!matches.full && matches.partial) {
|
|
return {type: 'partial'};
|
|
}
|
|
|
|
var bestMatch;
|
|
for (var i = 0; i < matches.full.length; i++) {
|
|
var match = matches.full[i];
|
|
if (!bestMatch) {
|
|
bestMatch = match;
|
|
}
|
|
}
|
|
if (bestMatch.keys.slice(-11) == '<character>') {
|
|
var character = lastChar(keys);
|
|
if (/<C-.>/.test(character)) return {type: 'none'};
|
|
inputState.selectedCharacter = character;
|
|
}
|
|
return {type: 'full', command: bestMatch};
|
|
},
|
|
processCommand: function(cm, vim, command) {
|
|
vim.inputState.repeatOverride = command.repeatOverride;
|
|
switch (command.type) {
|
|
case 'motion':
|
|
this.processMotion(cm, vim, command);
|
|
break;
|
|
case 'operator':
|
|
this.processOperator(cm, vim, command);
|
|
break;
|
|
case 'operatorMotion':
|
|
this.processOperatorMotion(cm, vim, command);
|
|
break;
|
|
case 'action':
|
|
this.processAction(cm, vim, command);
|
|
break;
|
|
case 'search':
|
|
this.processSearch(cm, vim, command);
|
|
break;
|
|
case 'ex':
|
|
case 'keyToEx':
|
|
this.processEx(cm, vim, command);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
},
|
|
processMotion: function(cm, vim, command) {
|
|
vim.inputState.motion = command.motion;
|
|
vim.inputState.motionArgs = copyArgs(command.motionArgs);
|
|
this.evalInput(cm, vim);
|
|
},
|
|
processOperator: function(cm, vim, command) {
|
|
var inputState = vim.inputState;
|
|
if (inputState.operator) {
|
|
if (inputState.operator == command.operator) {
|
|
inputState.motion = 'expandToLine';
|
|
inputState.motionArgs = { linewise: true };
|
|
this.evalInput(cm, vim);
|
|
return;
|
|
} else {
|
|
clearInputState(cm);
|
|
}
|
|
}
|
|
inputState.operator = command.operator;
|
|
inputState.operatorArgs = copyArgs(command.operatorArgs);
|
|
if (vim.visualMode) {
|
|
this.evalInput(cm, vim);
|
|
}
|
|
},
|
|
processOperatorMotion: function(cm, vim, command) {
|
|
var visualMode = vim.visualMode;
|
|
var operatorMotionArgs = copyArgs(command.operatorMotionArgs);
|
|
if (operatorMotionArgs) {
|
|
if (visualMode && operatorMotionArgs.visualLine) {
|
|
vim.visualLine = true;
|
|
}
|
|
}
|
|
this.processOperator(cm, vim, command);
|
|
if (!visualMode) {
|
|
this.processMotion(cm, vim, command);
|
|
}
|
|
},
|
|
processAction: function(cm, vim, command) {
|
|
var inputState = vim.inputState;
|
|
var repeat = inputState.getRepeat();
|
|
var repeatIsExplicit = !!repeat;
|
|
var actionArgs = copyArgs(command.actionArgs) || {};
|
|
if (inputState.selectedCharacter) {
|
|
actionArgs.selectedCharacter = inputState.selectedCharacter;
|
|
}
|
|
if (command.operator) {
|
|
this.processOperator(cm, vim, command);
|
|
}
|
|
if (command.motion) {
|
|
this.processMotion(cm, vim, command);
|
|
}
|
|
if (command.motion || command.operator) {
|
|
this.evalInput(cm, vim);
|
|
}
|
|
actionArgs.repeat = repeat || 1;
|
|
actionArgs.repeatIsExplicit = repeatIsExplicit;
|
|
actionArgs.registerName = inputState.registerName;
|
|
clearInputState(cm);
|
|
vim.lastMotion = null;
|
|
if (command.isEdit) {
|
|
this.recordLastEdit(vim, inputState, command);
|
|
}
|
|
actions[command.action](cm, actionArgs, vim);
|
|
},
|
|
processSearch: function(cm, vim, command) {
|
|
if (!cm.getSearchCursor) {
|
|
return;
|
|
}
|
|
var forward = command.searchArgs.forward;
|
|
var wholeWordOnly = command.searchArgs.wholeWordOnly;
|
|
getSearchState(cm).setReversed(!forward);
|
|
var promptPrefix = (forward) ? '/' : '?';
|
|
var originalQuery = getSearchState(cm).getQuery();
|
|
var originalScrollPos = cm.getScrollInfo();
|
|
function handleQuery(query, ignoreCase, smartCase) {
|
|
vimGlobalState.searchHistoryController.pushInput(query);
|
|
vimGlobalState.searchHistoryController.reset();
|
|
try {
|
|
updateSearchQuery(cm, query, ignoreCase, smartCase);
|
|
} catch (e) {
|
|
showConfirm(cm, 'Invalid regex: ' + query);
|
|
clearInputState(cm);
|
|
return;
|
|
}
|
|
commandDispatcher.processMotion(cm, vim, {
|
|
type: 'motion',
|
|
motion: 'findNext',
|
|
motionArgs: { forward: true, toJumplist: command.searchArgs.toJumplist }
|
|
});
|
|
}
|
|
function onPromptClose(query) {
|
|
cm.scrollTo(originalScrollPos.left, originalScrollPos.top);
|
|
handleQuery(query, true /** ignoreCase */, true /** smartCase */);
|
|
var macroModeState = vimGlobalState.macroModeState;
|
|
if (macroModeState.isRecording) {
|
|
logSearchQuery(macroModeState, query);
|
|
}
|
|
}
|
|
function onPromptKeyUp(e, query, close) {
|
|
var keyName = CodeMirror.keyName(e), up;
|
|
if (keyName == 'Up' || keyName == 'Down') {
|
|
up = keyName == 'Up' ? true : false;
|
|
query = vimGlobalState.searchHistoryController.nextMatch(query, up) || '';
|
|
close(query);
|
|
} else {
|
|
if ( keyName != 'Left' && keyName != 'Right' && keyName != 'Ctrl' && keyName != 'Alt' && keyName != 'Shift')
|
|
vimGlobalState.searchHistoryController.reset();
|
|
}
|
|
var parsedQuery;
|
|
try {
|
|
parsedQuery = updateSearchQuery(cm, query,
|
|
true /** ignoreCase */, true /** smartCase */);
|
|
} catch (e) {
|
|
}
|
|
if (parsedQuery) {
|
|
cm.scrollIntoView(findNext(cm, !forward, parsedQuery), 30);
|
|
} else {
|
|
clearSearchHighlight(cm);
|
|
cm.scrollTo(originalScrollPos.left, originalScrollPos.top);
|
|
}
|
|
}
|
|
function onPromptKeyDown(e, query, close) {
|
|
var keyName = CodeMirror.keyName(e);
|
|
if (keyName == 'Esc' || keyName == 'Ctrl-C' || keyName == 'Ctrl-[' ||
|
|
(keyName == 'Backspace' && query == '')) {
|
|
vimGlobalState.searchHistoryController.pushInput(query);
|
|
vimGlobalState.searchHistoryController.reset();
|
|
updateSearchQuery(cm, originalQuery);
|
|
clearSearchHighlight(cm);
|
|
cm.scrollTo(originalScrollPos.left, originalScrollPos.top);
|
|
CodeMirror.e_stop(e);
|
|
clearInputState(cm);
|
|
close();
|
|
cm.focus();
|
|
} else if (keyName == 'Ctrl-U') {
|
|
CodeMirror.e_stop(e);
|
|
close('');
|
|
}
|
|
}
|
|
switch (command.searchArgs.querySrc) {
|
|
case 'prompt':
|
|
var macroModeState = vimGlobalState.macroModeState;
|
|
if (macroModeState.isPlaying) {
|
|
var query = macroModeState.replaySearchQueries.shift();
|
|
handleQuery(query, true /** ignoreCase */, false /** smartCase */);
|
|
} else {
|
|
showPrompt(cm, {
|
|
onClose: onPromptClose,
|
|
prefix: promptPrefix,
|
|
desc: searchPromptDesc,
|
|
onKeyUp: onPromptKeyUp,
|
|
onKeyDown: onPromptKeyDown
|
|
});
|
|
}
|
|
break;
|
|
case 'wordUnderCursor':
|
|
var word = expandWordUnderCursor(cm, false /** inclusive */,
|
|
true /** forward */, false /** bigWord */,
|
|
true /** noSymbol */);
|
|
var isKeyword = true;
|
|
if (!word) {
|
|
word = expandWordUnderCursor(cm, false /** inclusive */,
|
|
true /** forward */, false /** bigWord */,
|
|
false /** noSymbol */);
|
|
isKeyword = false;
|
|
}
|
|
if (!word) {
|
|
return;
|
|
}
|
|
var query = cm.getLine(word.start.line).substring(word.start.ch,
|
|
word.end.ch);
|
|
if (isKeyword && wholeWordOnly) {
|
|
query = '\\b' + query + '\\b';
|
|
} else {
|
|
query = escapeRegex(query);
|
|
}
|
|
vimGlobalState.jumpList.cachedCursor = cm.getCursor();
|
|
cm.setCursor(word.start);
|
|
|
|
handleQuery(query, true /** ignoreCase */, false /** smartCase */);
|
|
break;
|
|
}
|
|
},
|
|
processEx: function(cm, vim, command) {
|
|
function onPromptClose(input) {
|
|
vimGlobalState.exCommandHistoryController.pushInput(input);
|
|
vimGlobalState.exCommandHistoryController.reset();
|
|
exCommandDispatcher.processCommand(cm, input);
|
|
}
|
|
function onPromptKeyDown(e, input, close) {
|
|
var keyName = CodeMirror.keyName(e), up;
|
|
if (keyName == 'Esc' || keyName == 'Ctrl-C' || keyName == 'Ctrl-[' ||
|
|
(keyName == 'Backspace' && input == '')) {
|
|
vimGlobalState.exCommandHistoryController.pushInput(input);
|
|
vimGlobalState.exCommandHistoryController.reset();
|
|
CodeMirror.e_stop(e);
|
|
clearInputState(cm);
|
|
close();
|
|
cm.focus();
|
|
}
|
|
if (keyName == 'Up' || keyName == 'Down') {
|
|
up = keyName == 'Up' ? true : false;
|
|
input = vimGlobalState.exCommandHistoryController.nextMatch(input, up) || '';
|
|
close(input);
|
|
} else if (keyName == 'Ctrl-U') {
|
|
CodeMirror.e_stop(e);
|
|
close('');
|
|
} else {
|
|
if ( keyName != 'Left' && keyName != 'Right' && keyName != 'Ctrl' && keyName != 'Alt' && keyName != 'Shift')
|
|
vimGlobalState.exCommandHistoryController.reset();
|
|
}
|
|
}
|
|
if (command.type == 'keyToEx') {
|
|
exCommandDispatcher.processCommand(cm, command.exArgs.input);
|
|
} else {
|
|
if (vim.visualMode) {
|
|
showPrompt(cm, { onClose: onPromptClose, prefix: ':', value: '\'<,\'>',
|
|
onKeyDown: onPromptKeyDown});
|
|
} else {
|
|
showPrompt(cm, { onClose: onPromptClose, prefix: ':',
|
|
onKeyDown: onPromptKeyDown});
|
|
}
|
|
}
|
|
},
|
|
evalInput: function(cm, vim) {
|
|
var inputState = vim.inputState;
|
|
var motion = inputState.motion;
|
|
var motionArgs = inputState.motionArgs || {};
|
|
var operator = inputState.operator;
|
|
var operatorArgs = inputState.operatorArgs || {};
|
|
var registerName = inputState.registerName;
|
|
var sel = vim.sel;
|
|
var origHead = copyCursor(vim.visualMode ? clipCursorToContent(cm, sel.head): cm.getCursor('head'));
|
|
var origAnchor = copyCursor(vim.visualMode ? clipCursorToContent(cm, sel.anchor) : cm.getCursor('anchor'));
|
|
var oldHead = copyCursor(origHead);
|
|
var oldAnchor = copyCursor(origAnchor);
|
|
var newHead, newAnchor;
|
|
var repeat;
|
|
if (operator) {
|
|
this.recordLastEdit(vim, inputState);
|
|
}
|
|
if (inputState.repeatOverride !== undefined) {
|
|
repeat = inputState.repeatOverride;
|
|
} else {
|
|
repeat = inputState.getRepeat();
|
|
}
|
|
if (repeat > 0 && motionArgs.explicitRepeat) {
|
|
motionArgs.repeatIsExplicit = true;
|
|
} else if (motionArgs.noRepeat ||
|
|
(!motionArgs.explicitRepeat && repeat === 0)) {
|
|
repeat = 1;
|
|
motionArgs.repeatIsExplicit = false;
|
|
}
|
|
if (inputState.selectedCharacter) {
|
|
motionArgs.selectedCharacter = operatorArgs.selectedCharacter =
|
|
inputState.selectedCharacter;
|
|
}
|
|
motionArgs.repeat = repeat;
|
|
clearInputState(cm);
|
|
if (motion) {
|
|
var motionResult = motions[motion](cm, origHead, motionArgs, vim);
|
|
vim.lastMotion = motions[motion];
|
|
if (!motionResult) {
|
|
return;
|
|
}
|
|
if (motionArgs.toJumplist) {
|
|
if (!operator && cm.ace.curOp != null)
|
|
cm.ace.curOp.command.scrollIntoView = "center-animate"; // ace_patch
|
|
var jumpList = vimGlobalState.jumpList;
|
|
var cachedCursor = jumpList.cachedCursor;
|
|
if (cachedCursor) {
|
|
recordJumpPosition(cm, cachedCursor, motionResult);
|
|
delete jumpList.cachedCursor;
|
|
} else {
|
|
recordJumpPosition(cm, origHead, motionResult);
|
|
}
|
|
}
|
|
if (motionResult instanceof Array) {
|
|
newAnchor = motionResult[0];
|
|
newHead = motionResult[1];
|
|
} else {
|
|
newHead = motionResult;
|
|
}
|
|
if (!newHead) {
|
|
newHead = copyCursor(origHead);
|
|
}
|
|
if (vim.visualMode) {
|
|
if (!(vim.visualBlock && newHead.ch === Infinity)) {
|
|
newHead = clipCursorToContent(cm, newHead, vim.visualBlock);
|
|
}
|
|
if (newAnchor) {
|
|
newAnchor = clipCursorToContent(cm, newAnchor, true);
|
|
}
|
|
newAnchor = newAnchor || oldAnchor;
|
|
sel.anchor = newAnchor;
|
|
sel.head = newHead;
|
|
updateCmSelection(cm);
|
|
updateMark(cm, vim, '<',
|
|
cursorIsBefore(newAnchor, newHead) ? newAnchor
|
|
: newHead);
|
|
updateMark(cm, vim, '>',
|
|
cursorIsBefore(newAnchor, newHead) ? newHead
|
|
: newAnchor);
|
|
} else if (!operator) {
|
|
newHead = clipCursorToContent(cm, newHead);
|
|
cm.setCursor(newHead.line, newHead.ch);
|
|
}
|
|
}
|
|
if (operator) {
|
|
if (operatorArgs.lastSel) {
|
|
newAnchor = oldAnchor;
|
|
var lastSel = operatorArgs.lastSel;
|
|
var lineOffset = Math.abs(lastSel.head.line - lastSel.anchor.line);
|
|
var chOffset = Math.abs(lastSel.head.ch - lastSel.anchor.ch);
|
|
if (lastSel.visualLine) {
|
|
newHead = Pos(oldAnchor.line + lineOffset, oldAnchor.ch);
|
|
} else if (lastSel.visualBlock) {
|
|
newHead = Pos(oldAnchor.line + lineOffset, oldAnchor.ch + chOffset);
|
|
} else if (lastSel.head.line == lastSel.anchor.line) {
|
|
newHead = Pos(oldAnchor.line, oldAnchor.ch + chOffset);
|
|
} else {
|
|
newHead = Pos(oldAnchor.line + lineOffset, oldAnchor.ch);
|
|
}
|
|
vim.visualMode = true;
|
|
vim.visualLine = lastSel.visualLine;
|
|
vim.visualBlock = lastSel.visualBlock;
|
|
sel = vim.sel = {
|
|
anchor: newAnchor,
|
|
head: newHead
|
|
};
|
|
updateCmSelection(cm);
|
|
} else if (vim.visualMode) {
|
|
operatorArgs.lastSel = {
|
|
anchor: copyCursor(sel.anchor),
|
|
head: copyCursor(sel.head),
|
|
visualBlock: vim.visualBlock,
|
|
visualLine: vim.visualLine
|
|
};
|
|
}
|
|
var curStart, curEnd, linewise, mode;
|
|
var cmSel;
|
|
if (vim.visualMode) {
|
|
curStart = cursorMin(sel.head, sel.anchor);
|
|
curEnd = cursorMax(sel.head, sel.anchor);
|
|
linewise = vim.visualLine || operatorArgs.linewise;
|
|
mode = vim.visualBlock ? 'block' :
|
|
linewise ? 'line' :
|
|
'char';
|
|
cmSel = makeCmSelection(cm, {
|
|
anchor: curStart,
|
|
head: curEnd
|
|
}, mode);
|
|
if (linewise) {
|
|
var ranges = cmSel.ranges;
|
|
if (mode == 'block') {
|
|
for (var i = 0; i < ranges.length; i++) {
|
|
ranges[i].head.ch = lineLength(cm, ranges[i].head.line);
|
|
}
|
|
} else if (mode == 'line') {
|
|
ranges[0].head = Pos(ranges[0].head.line + 1, 0);
|
|
}
|
|
}
|
|
} else {
|
|
curStart = copyCursor(newAnchor || oldAnchor);
|
|
curEnd = copyCursor(newHead || oldHead);
|
|
if (cursorIsBefore(curEnd, curStart)) {
|
|
var tmp = curStart;
|
|
curStart = curEnd;
|
|
curEnd = tmp;
|
|
}
|
|
linewise = motionArgs.linewise || operatorArgs.linewise;
|
|
if (linewise) {
|
|
expandSelectionToLine(cm, curStart, curEnd);
|
|
} else if (motionArgs.forward) {
|
|
clipToLine(cm, curStart, curEnd);
|
|
}
|
|
mode = 'char';
|
|
var exclusive = !motionArgs.inclusive || linewise;
|
|
cmSel = makeCmSelection(cm, {
|
|
anchor: curStart,
|
|
head: curEnd
|
|
}, mode, exclusive);
|
|
}
|
|
cm.setSelections(cmSel.ranges, cmSel.primary);
|
|
vim.lastMotion = null;
|
|
operatorArgs.repeat = repeat; // For indent in visual mode.
|
|
operatorArgs.registerName = registerName;
|
|
operatorArgs.linewise = linewise;
|
|
var operatorMoveTo = operators[operator](
|
|
cm, operatorArgs, cmSel.ranges, oldAnchor, newHead);
|
|
if (vim.visualMode) {
|
|
exitVisualMode(cm, operatorMoveTo != null);
|
|
}
|
|
if (operatorMoveTo) {
|
|
cm.setCursor(operatorMoveTo);
|
|
}
|
|
}
|
|
},
|
|
recordLastEdit: function(vim, inputState, actionCommand) {
|
|
var macroModeState = vimGlobalState.macroModeState;
|
|
if (macroModeState.isPlaying) { return; }
|
|
vim.lastEditInputState = inputState;
|
|
vim.lastEditActionCommand = actionCommand;
|
|
macroModeState.lastInsertModeChanges.changes = [];
|
|
macroModeState.lastInsertModeChanges.expectCursorActivityForChange = false;
|
|
}
|
|
};
|
|
var motions = {
|
|
moveToTopLine: function(cm, _head, motionArgs) {
|
|
var line = getUserVisibleLines(cm).top + motionArgs.repeat -1;
|
|
return Pos(line, findFirstNonWhiteSpaceCharacter(cm.getLine(line)));
|
|
},
|
|
moveToMiddleLine: function(cm) {
|
|
var range = getUserVisibleLines(cm);
|
|
var line = Math.floor((range.top + range.bottom) * 0.5);
|
|
return Pos(line, findFirstNonWhiteSpaceCharacter(cm.getLine(line)));
|
|
},
|
|
moveToBottomLine: function(cm, _head, motionArgs) {
|
|
var line = getUserVisibleLines(cm).bottom - motionArgs.repeat +1;
|
|
return Pos(line, findFirstNonWhiteSpaceCharacter(cm.getLine(line)));
|
|
},
|
|
expandToLine: function(_cm, head, motionArgs) {
|
|
var cur = head;
|
|
return Pos(cur.line + motionArgs.repeat - 1, Infinity);
|
|
},
|
|
findNext: function(cm, _head, motionArgs) {
|
|
var state = getSearchState(cm);
|
|
var query = state.getQuery();
|
|
if (!query) {
|
|
return;
|
|
}
|
|
var prev = !motionArgs.forward;
|
|
prev = (state.isReversed()) ? !prev : prev;
|
|
highlightSearchMatches(cm, query);
|
|
return findNext(cm, prev/** prev */, query, motionArgs.repeat);
|
|
},
|
|
goToMark: function(cm, _head, motionArgs, vim) {
|
|
var mark = vim.marks[motionArgs.selectedCharacter];
|
|
if (mark) {
|
|
var pos = mark.find();
|
|
return motionArgs.linewise ? { line: pos.line, ch: findFirstNonWhiteSpaceCharacter(cm.getLine(pos.line)) } : pos;
|
|
}
|
|
return null;
|
|
},
|
|
moveToOtherHighlightedEnd: function(cm, _head, motionArgs, vim) {
|
|
if (vim.visualBlock && motionArgs.sameLine) {
|
|
var sel = vim.sel;
|
|
return [
|
|
clipCursorToContent(cm, Pos(sel.anchor.line, sel.head.ch)),
|
|
clipCursorToContent(cm, Pos(sel.head.line, sel.anchor.ch))
|
|
];
|
|
} else {
|
|
return ([vim.sel.head, vim.sel.anchor]);
|
|
}
|
|
},
|
|
jumpToMark: function(cm, head, motionArgs, vim) {
|
|
var best = head;
|
|
for (var i = 0; i < motionArgs.repeat; i++) {
|
|
var cursor = best;
|
|
for (var key in vim.marks) {
|
|
if (!isLowerCase(key)) {
|
|
continue;
|
|
}
|
|
var mark = vim.marks[key].find();
|
|
var isWrongDirection = (motionArgs.forward) ?
|
|
cursorIsBefore(mark, cursor) : cursorIsBefore(cursor, mark);
|
|
|
|
if (isWrongDirection) {
|
|
continue;
|
|
}
|
|
if (motionArgs.linewise && (mark.line == cursor.line)) {
|
|
continue;
|
|
}
|
|
|
|
var equal = cursorEqual(cursor, best);
|
|
var between = (motionArgs.forward) ?
|
|
cursorIsBetween(cursor, mark, best) :
|
|
cursorIsBetween(best, mark, cursor);
|
|
|
|
if (equal || between) {
|
|
best = mark;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (motionArgs.linewise) {
|
|
best = Pos(best.line, findFirstNonWhiteSpaceCharacter(cm.getLine(best.line)));
|
|
}
|
|
return best;
|
|
},
|
|
moveByCharacters: function(_cm, head, motionArgs) {
|
|
var cur = head;
|
|
var repeat = motionArgs.repeat;
|
|
var ch = motionArgs.forward ? cur.ch + repeat : cur.ch - repeat;
|
|
return Pos(cur.line, ch);
|
|
},
|
|
moveByLines: function(cm, head, motionArgs, vim) {
|
|
var cur = head;
|
|
var endCh = cur.ch;
|
|
switch (vim.lastMotion) {
|
|
case this.moveByLines:
|
|
case this.moveByDisplayLines:
|
|
case this.moveByScroll:
|
|
case this.moveToColumn:
|
|
case this.moveToEol:
|
|
endCh = vim.lastHPos;
|
|
break;
|
|
default:
|
|
vim.lastHPos = endCh;
|
|
}
|
|
var repeat = motionArgs.repeat+(motionArgs.repeatOffset||0);
|
|
var line = motionArgs.forward ? cur.line + repeat : cur.line - repeat;
|
|
var first = cm.firstLine();
|
|
var last = cm.lastLine();
|
|
if ((line < first && cur.line == first) ||
|
|
(line > last && cur.line == last)) {
|
|
return;
|
|
}
|
|
var fold = cm.ace.session.getFoldLine(line);
|
|
if (fold) {
|
|
if (motionArgs.forward) {
|
|
if (line > fold.start.row)
|
|
line = fold.end.row + 1;
|
|
} else {
|
|
line = fold.start.row;
|
|
}
|
|
}
|
|
if (motionArgs.toFirstChar){
|
|
endCh=findFirstNonWhiteSpaceCharacter(cm.getLine(line));
|
|
vim.lastHPos = endCh;
|
|
}
|
|
vim.lastHSPos = cm.charCoords(Pos(line, endCh),'div').left;
|
|
return Pos(line, endCh);
|
|
},
|
|
moveByDisplayLines: function(cm, head, motionArgs, vim) {
|
|
var cur = head;
|
|
switch (vim.lastMotion) {
|
|
case this.moveByDisplayLines:
|
|
case this.moveByScroll:
|
|
case this.moveByLines:
|
|
case this.moveToColumn:
|
|
case this.moveToEol:
|
|
break;
|
|
default:
|
|
vim.lastHSPos = cm.charCoords(cur,'div').left;
|
|
}
|
|
var repeat = motionArgs.repeat;
|
|
var res=cm.findPosV(cur,(motionArgs.forward ? repeat : -repeat),'line',vim.lastHSPos);
|
|
if (res.hitSide) {
|
|
if (motionArgs.forward) {
|
|
var lastCharCoords = cm.charCoords(res, 'div');
|
|
var goalCoords = { top: lastCharCoords.top + 8, left: vim.lastHSPos };
|
|
var res = cm.coordsChar(goalCoords, 'div');
|
|
} else {
|
|
var resCoords = cm.charCoords(Pos(cm.firstLine(), 0), 'div');
|
|
resCoords.left = vim.lastHSPos;
|
|
res = cm.coordsChar(resCoords, 'div');
|
|
}
|
|
}
|
|
vim.lastHPos = res.ch;
|
|
return res;
|
|
},
|
|
moveByPage: function(cm, head, motionArgs) {
|
|
var curStart = head;
|
|
var repeat = motionArgs.repeat;
|
|
return cm.findPosV(curStart, (motionArgs.forward ? repeat : -repeat), 'page');
|
|
},
|
|
moveByParagraph: function(cm, head, motionArgs) {
|
|
var dir = motionArgs.forward ? 1 : -1;
|
|
return findParagraph(cm, head, motionArgs.repeat, dir);
|
|
},
|
|
moveByScroll: function(cm, head, motionArgs, vim) {
|
|
var scrollbox = cm.getScrollInfo();
|
|
var curEnd = null;
|
|
var repeat = motionArgs.repeat;
|
|
if (!repeat) {
|
|
repeat = scrollbox.clientHeight / (2 * cm.defaultTextHeight());
|
|
}
|
|
var orig = cm.charCoords(head, 'local');
|
|
motionArgs.repeat = repeat;
|
|
var curEnd = motions.moveByDisplayLines(cm, head, motionArgs, vim);
|
|
if (!curEnd) {
|
|
return null;
|
|
}
|
|
var dest = cm.charCoords(curEnd, 'local');
|
|
cm.scrollTo(null, scrollbox.top + dest.top - orig.top);
|
|
return curEnd;
|
|
},
|
|
moveByWords: function(cm, head, motionArgs) {
|
|
return moveToWord(cm, head, motionArgs.repeat, !!motionArgs.forward,
|
|
!!motionArgs.wordEnd, !!motionArgs.bigWord);
|
|
},
|
|
moveTillCharacter: function(cm, _head, motionArgs) {
|
|
var repeat = motionArgs.repeat;
|
|
var curEnd = moveToCharacter(cm, repeat, motionArgs.forward,
|
|
motionArgs.selectedCharacter);
|
|
var increment = motionArgs.forward ? -1 : 1;
|
|
recordLastCharacterSearch(increment, motionArgs);
|
|
if (!curEnd) return null;
|
|
curEnd.ch += increment;
|
|
return curEnd;
|
|
},
|
|
moveToCharacter: function(cm, head, motionArgs) {
|
|
var repeat = motionArgs.repeat;
|
|
recordLastCharacterSearch(0, motionArgs);
|
|
return moveToCharacter(cm, repeat, motionArgs.forward,
|
|
motionArgs.selectedCharacter) || head;
|
|
},
|
|
moveToSymbol: function(cm, head, motionArgs) {
|
|
var repeat = motionArgs.repeat;
|
|
return findSymbol(cm, repeat, motionArgs.forward,
|
|
motionArgs.selectedCharacter) || head;
|
|
},
|
|
moveToColumn: function(cm, head, motionArgs, vim) {
|
|
var repeat = motionArgs.repeat;
|
|
vim.lastHPos = repeat - 1;
|
|
vim.lastHSPos = cm.charCoords(head,'div').left;
|
|
return moveToColumn(cm, repeat);
|
|
},
|
|
moveToEol: function(cm, head, motionArgs, vim) {
|
|
var cur = head;
|
|
vim.lastHPos = Infinity;
|
|
var retval= Pos(cur.line + motionArgs.repeat - 1, Infinity);
|
|
var end=cm.clipPos(retval);
|
|
end.ch--;
|
|
vim.lastHSPos = cm.charCoords(end,'div').left;
|
|
return retval;
|
|
},
|
|
moveToFirstNonWhiteSpaceCharacter: function(cm, head) {
|
|
var cursor = head;
|
|
return Pos(cursor.line,
|
|
findFirstNonWhiteSpaceCharacter(cm.getLine(cursor.line)));
|
|
},
|
|
moveToMatchedSymbol: function(cm, head) {
|
|
var cursor = head;
|
|
var line = cursor.line;
|
|
var ch = cursor.ch;
|
|
var lineText = cm.getLine(line);
|
|
var symbol;
|
|
do {
|
|
symbol = lineText.charAt(ch++);
|
|
if (symbol && isMatchableSymbol(symbol)) {
|
|
var style = cm.getTokenTypeAt(Pos(line, ch));
|
|
if (style !== "string" && style !== "comment") {
|
|
break;
|
|
}
|
|
}
|
|
} while (symbol);
|
|
if (symbol) {
|
|
var matched = cm.findMatchingBracket(Pos(line, ch));
|
|
return matched.to;
|
|
} else {
|
|
return cursor;
|
|
}
|
|
},
|
|
moveToStartOfLine: function(_cm, head) {
|
|
return Pos(head.line, 0);
|
|
},
|
|
moveToLineOrEdgeOfDocument: function(cm, _head, motionArgs) {
|
|
var lineNum = motionArgs.forward ? cm.lastLine() : cm.firstLine();
|
|
if (motionArgs.repeatIsExplicit) {
|
|
lineNum = motionArgs.repeat - cm.getOption('firstLineNumber');
|
|
}
|
|
return Pos(lineNum,
|
|
findFirstNonWhiteSpaceCharacter(cm.getLine(lineNum)));
|
|
},
|
|
textObjectManipulation: function(cm, head, motionArgs, vim) {
|
|
var mirroredPairs = {'(': ')', ')': '(',
|
|
'{': '}', '}': '{',
|
|
'[': ']', ']': '['};
|
|
var selfPaired = {'\'': true, '"': true};
|
|
|
|
var character = motionArgs.selectedCharacter;
|
|
if (character == 'b') {
|
|
character = '(';
|
|
} else if (character == 'B') {
|
|
character = '{';
|
|
}
|
|
var inclusive = !motionArgs.textObjectInner;
|
|
|
|
var tmp;
|
|
if (mirroredPairs[character]) {
|
|
tmp = selectCompanionObject(cm, head, character, inclusive);
|
|
} else if (selfPaired[character]) {
|
|
tmp = findBeginningAndEnd(cm, head, character, inclusive);
|
|
} else if (character === 'W') {
|
|
tmp = expandWordUnderCursor(cm, inclusive, true /** forward */,
|
|
true /** bigWord */);
|
|
} else if (character === 'w') {
|
|
tmp = expandWordUnderCursor(cm, inclusive, true /** forward */,
|
|
false /** bigWord */);
|
|
} else if (character === 'p') {
|
|
tmp = findParagraph(cm, head, motionArgs.repeat, 0, inclusive);
|
|
motionArgs.linewise = true;
|
|
if (vim.visualMode) {
|
|
if (!vim.visualLine) { vim.visualLine = true; }
|
|
} else {
|
|
var operatorArgs = vim.inputState.operatorArgs;
|
|
if (operatorArgs) { operatorArgs.linewise = true; }
|
|
tmp.end.line--;
|
|
}
|
|
} else {
|
|
return null;
|
|
}
|
|
|
|
if (!cm.state.vim.visualMode) {
|
|
return [tmp.start, tmp.end];
|
|
} else {
|
|
return expandSelection(cm, tmp.start, tmp.end);
|
|
}
|
|
},
|
|
|
|
repeatLastCharacterSearch: function(cm, head, motionArgs) {
|
|
var lastSearch = vimGlobalState.lastChararacterSearch;
|
|
var repeat = motionArgs.repeat;
|
|
var forward = motionArgs.forward === lastSearch.forward;
|
|
var increment = (lastSearch.increment ? 1 : 0) * (forward ? -1 : 1);
|
|
cm.moveH(-increment, 'char');
|
|
motionArgs.inclusive = forward ? true : false;
|
|
var curEnd = moveToCharacter(cm, repeat, forward, lastSearch.selectedCharacter);
|
|
if (!curEnd) {
|
|
cm.moveH(increment, 'char');
|
|
return head;
|
|
}
|
|
curEnd.ch += increment;
|
|
return curEnd;
|
|
}
|
|
};
|
|
|
|
function defineMotion(name, fn) {
|
|
motions[name] = fn;
|
|
}
|
|
|
|
function fillArray(val, times) {
|
|
var arr = [];
|
|
for (var i = 0; i < times; i++) {
|
|
arr.push(val);
|
|
}
|
|
return arr;
|
|
}
|
|
var operators = {
|
|
change: function(cm, args, ranges) {
|
|
var finalHead, text;
|
|
var vim = cm.state.vim;
|
|
vimGlobalState.macroModeState.lastInsertModeChanges.inVisualBlock = vim.visualBlock;
|
|
if (!vim.visualMode) {
|
|
var anchor = ranges[0].anchor,
|
|
head = ranges[0].head;
|
|
text = cm.getRange(anchor, head);
|
|
var lastState = vim.lastEditInputState || {};
|
|
if (lastState.motion == "moveByWords" && !isWhiteSpaceString(text)) {
|
|
var match = (/\s+$/).exec(text);
|
|
if (match && lastState.motionArgs && lastState.motionArgs.forward) {
|
|
head = offsetCursor(head, 0, - match[0].length);
|
|
text = text.slice(0, - match[0].length);
|
|
}
|
|
}
|
|
var prevLineEnd = new Pos(anchor.line - 1, Number.MAX_VALUE);
|
|
var wasLastLine = cm.firstLine() == cm.lastLine();
|
|
if (head.line > cm.lastLine() && args.linewise && !wasLastLine) {
|
|
cm.replaceRange('', prevLineEnd, head);
|
|
} else {
|
|
cm.replaceRange('', anchor, head);
|
|
}
|
|
if (args.linewise) {
|
|
if (!wasLastLine) {
|
|
cm.setCursor(prevLineEnd);
|
|
CodeMirror.commands.newlineAndIndent(cm);
|
|
}
|
|
anchor.ch = Number.MAX_VALUE;
|
|
}
|
|
finalHead = anchor;
|
|
} else {
|
|
text = cm.getSelection();
|
|
var replacement = fillArray('', ranges.length);
|
|
cm.replaceSelections(replacement);
|
|
finalHead = cursorMin(ranges[0].head, ranges[0].anchor);
|
|
}
|
|
vimGlobalState.registerController.pushText(
|
|
args.registerName, 'change', text,
|
|
args.linewise, ranges.length > 1);
|
|
actions.enterInsertMode(cm, {head: finalHead}, cm.state.vim);
|
|
},
|
|
'delete': function(cm, args, ranges) {
|
|
var finalHead, text;
|
|
var vim = cm.state.vim;
|
|
if (!vim.visualBlock) {
|
|
var anchor = ranges[0].anchor,
|
|
head = ranges[0].head;
|
|
if (args.linewise &&
|
|
head.line != cm.firstLine() &&
|
|
anchor.line == cm.lastLine() &&
|
|
anchor.line == head.line - 1) {
|
|
if (anchor.line == cm.firstLine()) {
|
|
anchor.ch = 0;
|
|
} else {
|
|
anchor = Pos(anchor.line - 1, lineLength(cm, anchor.line - 1));
|
|
}
|
|
}
|
|
text = cm.getRange(anchor, head);
|
|
cm.replaceRange('', anchor, head);
|
|
finalHead = anchor;
|
|
if (args.linewise) {
|
|
finalHead = motions.moveToFirstNonWhiteSpaceCharacter(cm, anchor);
|
|
}
|
|
} else {
|
|
text = cm.getSelection();
|
|
var replacement = fillArray('', ranges.length);
|
|
cm.replaceSelections(replacement);
|
|
finalHead = ranges[0].anchor;
|
|
}
|
|
vimGlobalState.registerController.pushText(
|
|
args.registerName, 'delete', text,
|
|
args.linewise, vim.visualBlock);
|
|
return clipCursorToContent(cm, finalHead);
|
|
},
|
|
indent: function(cm, args, ranges) {
|
|
var vim = cm.state.vim;
|
|
var startLine = ranges[0].anchor.line;
|
|
var endLine = vim.visualBlock ?
|
|
ranges[ranges.length - 1].anchor.line :
|
|
ranges[0].head.line;
|
|
var repeat = (vim.visualMode) ? args.repeat : 1;
|
|
if (args.linewise) {
|
|
endLine--;
|
|
}
|
|
for (var i = startLine; i <= endLine; i++) {
|
|
for (var j = 0; j < repeat; j++) {
|
|
cm.indentLine(i, args.indentRight);
|
|
}
|
|
}
|
|
return motions.moveToFirstNonWhiteSpaceCharacter(cm, ranges[0].anchor);
|
|
},
|
|
changeCase: function(cm, args, ranges, oldAnchor, newHead) {
|
|
var selections = cm.getSelections();
|
|
var swapped = [];
|
|
var toLower = args.toLower;
|
|
for (var j = 0; j < selections.length; j++) {
|
|
var toSwap = selections[j];
|
|
var text = '';
|
|
if (toLower === true) {
|
|
text = toSwap.toLowerCase();
|
|
} else if (toLower === false) {
|
|
text = toSwap.toUpperCase();
|
|
} else {
|
|
for (var i = 0; i < toSwap.length; i++) {
|
|
var character = toSwap.charAt(i);
|
|
text += isUpperCase(character) ? character.toLowerCase() :
|
|
character.toUpperCase();
|
|
}
|
|
}
|
|
swapped.push(text);
|
|
}
|
|
cm.replaceSelections(swapped);
|
|
if (args.shouldMoveCursor){
|
|
return newHead;
|
|
} else if (!cm.state.vim.visualMode && args.linewise && ranges[0].anchor.line + 1 == ranges[0].head.line) {
|
|
return motions.moveToFirstNonWhiteSpaceCharacter(cm, oldAnchor);
|
|
} else if (args.linewise){
|
|
return oldAnchor;
|
|
} else {
|
|
return cursorMin(ranges[0].anchor, ranges[0].head);
|
|
}
|
|
},
|
|
yank: function(cm, args, ranges, oldAnchor) {
|
|
var vim = cm.state.vim;
|
|
var text = cm.getSelection();
|
|
var endPos = vim.visualMode
|
|
? cursorMin(vim.sel.anchor, vim.sel.head, ranges[0].head, ranges[0].anchor)
|
|
: oldAnchor;
|
|
vimGlobalState.registerController.pushText(
|
|
args.registerName, 'yank',
|
|
text, args.linewise, vim.visualBlock);
|
|
return endPos;
|
|
}
|
|
};
|
|
|
|
function defineOperator(name, fn) {
|
|
operators[name] = fn;
|
|
}
|
|
|
|
var actions = {
|
|
jumpListWalk: function(cm, actionArgs, vim) {
|
|
if (vim.visualMode) {
|
|
return;
|
|
}
|
|
var repeat = actionArgs.repeat;
|
|
var forward = actionArgs.forward;
|
|
var jumpList = vimGlobalState.jumpList;
|
|
|
|
var mark = jumpList.move(cm, forward ? repeat : -repeat);
|
|
var markPos = mark ? mark.find() : undefined;
|
|
markPos = markPos ? markPos : cm.getCursor();
|
|
cm.setCursor(markPos);
|
|
cm.ace.curOp.command.scrollIntoView = "center-animate"; // ace_patch
|
|
},
|
|
scroll: function(cm, actionArgs, vim) {
|
|
if (vim.visualMode) {
|
|
return;
|
|
}
|
|
var repeat = actionArgs.repeat || 1;
|
|
var lineHeight = cm.defaultTextHeight();
|
|
var top = cm.getScrollInfo().top;
|
|
var delta = lineHeight * repeat;
|
|
var newPos = actionArgs.forward ? top + delta : top - delta;
|
|
var cursor = copyCursor(cm.getCursor());
|
|
var cursorCoords = cm.charCoords(cursor, 'local');
|
|
if (actionArgs.forward) {
|
|
if (newPos > cursorCoords.top) {
|
|
cursor.line += (newPos - cursorCoords.top) / lineHeight;
|
|
cursor.line = Math.ceil(cursor.line);
|
|
cm.setCursor(cursor);
|
|
cursorCoords = cm.charCoords(cursor, 'local');
|
|
cm.scrollTo(null, cursorCoords.top);
|
|
} else {
|
|
cm.scrollTo(null, newPos);
|
|
}
|
|
} else {
|
|
var newBottom = newPos + cm.getScrollInfo().clientHeight;
|
|
if (newBottom < cursorCoords.bottom) {
|
|
cursor.line -= (cursorCoords.bottom - newBottom) / lineHeight;
|
|
cursor.line = Math.floor(cursor.line);
|
|
cm.setCursor(cursor);
|
|
cursorCoords = cm.charCoords(cursor, 'local');
|
|
cm.scrollTo(
|
|
null, cursorCoords.bottom - cm.getScrollInfo().clientHeight);
|
|
} else {
|
|
cm.scrollTo(null, newPos);
|
|
}
|
|
}
|
|
},
|
|
scrollToCursor: function(cm, actionArgs) {
|
|
var lineNum = cm.getCursor().line;
|
|
var charCoords = cm.charCoords(Pos(lineNum, 0), 'local');
|
|
var height = cm.getScrollInfo().clientHeight;
|
|
var y = charCoords.top;
|
|
var lineHeight = charCoords.bottom - y;
|
|
switch (actionArgs.position) {
|
|
case 'center': y = y - (height / 2) + lineHeight;
|
|
break;
|
|
case 'bottom': y = y - height + lineHeight*1.4;
|
|
break;
|
|
case 'top': y = y + lineHeight*0.4;
|
|
break;
|
|
}
|
|
cm.scrollTo(null, y);
|
|
},
|
|
replayMacro: function(cm, actionArgs, vim) {
|
|
var registerName = actionArgs.selectedCharacter;
|
|
var repeat = actionArgs.repeat;
|
|
var macroModeState = vimGlobalState.macroModeState;
|
|
if (registerName == '@') {
|
|
registerName = macroModeState.latestRegister;
|
|
}
|
|
while(repeat--){
|
|
executeMacroRegister(cm, vim, macroModeState, registerName);
|
|
}
|
|
},
|
|
enterMacroRecordMode: function(cm, actionArgs) {
|
|
var macroModeState = vimGlobalState.macroModeState;
|
|
var registerName = actionArgs.selectedCharacter;
|
|
macroModeState.enterMacroRecordMode(cm, registerName);
|
|
},
|
|
enterInsertMode: function(cm, actionArgs, vim) {
|
|
if (cm.getOption('readOnly')) { return; }
|
|
vim.insertMode = true;
|
|
vim.insertModeRepeat = actionArgs && actionArgs.repeat || 1;
|
|
var insertAt = (actionArgs) ? actionArgs.insertAt : null;
|
|
var sel = vim.sel;
|
|
var head = actionArgs.head || cm.getCursor('head');
|
|
var height = cm.listSelections().length;
|
|
if (insertAt == 'eol') {
|
|
head = Pos(head.line, lineLength(cm, head.line));
|
|
} else if (insertAt == 'charAfter') {
|
|
head = offsetCursor(head, 0, 1);
|
|
} else if (insertAt == 'firstNonBlank') {
|
|
head = motions.moveToFirstNonWhiteSpaceCharacter(cm, head);
|
|
} else if (insertAt == 'startOfSelectedArea') {
|
|
if (!vim.visualBlock) {
|
|
if (sel.head.line < sel.anchor.line) {
|
|
head = sel.head;
|
|
} else {
|
|
head = Pos(sel.anchor.line, 0);
|
|
}
|
|
} else {
|
|
head = Pos(
|
|
Math.min(sel.head.line, sel.anchor.line),
|
|
Math.min(sel.head.ch, sel.anchor.ch));
|
|
height = Math.abs(sel.head.line - sel.anchor.line) + 1;
|
|
}
|
|
} else if (insertAt == 'endOfSelectedArea') {
|
|
if (!vim.visualBlock) {
|
|
if (sel.head.line >= sel.anchor.line) {
|
|
head = offsetCursor(sel.head, 0, 1);
|
|
} else {
|
|
head = Pos(sel.anchor.line, 0);
|
|
}
|
|
} else {
|
|
head = Pos(
|
|
Math.min(sel.head.line, sel.anchor.line),
|
|
Math.max(sel.head.ch + 1, sel.anchor.ch));
|
|
height = Math.abs(sel.head.line - sel.anchor.line) + 1;
|
|
}
|
|
} else if (insertAt == 'inplace') {
|
|
if (vim.visualMode){
|
|
return;
|
|
}
|
|
}
|
|
cm.setOption('keyMap', 'vim-insert');
|
|
cm.setOption('disableInput', false);
|
|
if (actionArgs && actionArgs.replace) {
|
|
cm.toggleOverwrite(true);
|
|
cm.setOption('keyMap', 'vim-replace');
|
|
CodeMirror.signal(cm, "vim-mode-change", {mode: "replace"});
|
|
} else {
|
|
cm.setOption('keyMap', 'vim-insert');
|
|
CodeMirror.signal(cm, "vim-mode-change", {mode: "insert"});
|
|
}
|
|
if (!vimGlobalState.macroModeState.isPlaying) {
|
|
cm.on('change', onChange);
|
|
CodeMirror.on(cm.getInputField(), 'keydown', onKeyEventTargetKeyDown);
|
|
}
|
|
if (vim.visualMode) {
|
|
exitVisualMode(cm);
|
|
}
|
|
selectForInsert(cm, head, height);
|
|
},
|
|
toggleVisualMode: function(cm, actionArgs, vim) {
|
|
var repeat = actionArgs.repeat;
|
|
var anchor = cm.getCursor();
|
|
var head;
|
|
if (!vim.visualMode) {
|
|
vim.visualMode = true;
|
|
vim.visualLine = !!actionArgs.linewise;
|
|
vim.visualBlock = !!actionArgs.blockwise;
|
|
head = clipCursorToContent(
|
|
cm, Pos(anchor.line, anchor.ch + repeat - 1),
|
|
true /** includeLineBreak */);
|
|
vim.sel = {
|
|
anchor: anchor,
|
|
head: head
|
|
};
|
|
CodeMirror.signal(cm, "vim-mode-change", {mode: "visual", subMode: vim.visualLine ? "linewise" : vim.visualBlock ? "blockwise" : ""});
|
|
updateCmSelection(cm);
|
|
updateMark(cm, vim, '<', cursorMin(anchor, head));
|
|
updateMark(cm, vim, '>', cursorMax(anchor, head));
|
|
} else if (vim.visualLine ^ actionArgs.linewise ||
|
|
vim.visualBlock ^ actionArgs.blockwise) {
|
|
vim.visualLine = !!actionArgs.linewise;
|
|
vim.visualBlock = !!actionArgs.blockwise;
|
|
CodeMirror.signal(cm, "vim-mode-change", {mode: "visual", subMode: vim.visualLine ? "linewise" : vim.visualBlock ? "blockwise" : ""});
|
|
updateCmSelection(cm);
|
|
} else {
|
|
exitVisualMode(cm);
|
|
}
|
|
},
|
|
reselectLastSelection: function(cm, _actionArgs, vim) {
|
|
var lastSelection = vim.lastSelection;
|
|
if (vim.visualMode) {
|
|
updateLastSelection(cm, vim);
|
|
}
|
|
if (lastSelection) {
|
|
var anchor = lastSelection.anchorMark.find();
|
|
var head = lastSelection.headMark.find();
|
|
if (!anchor || !head) {
|
|
return;
|
|
}
|
|
vim.sel = {
|
|
anchor: anchor,
|
|
head: head
|
|
};
|
|
vim.visualMode = true;
|
|
vim.visualLine = lastSelection.visualLine;
|
|
vim.visualBlock = lastSelection.visualBlock;
|
|
updateCmSelection(cm);
|
|
updateMark(cm, vim, '<', cursorMin(anchor, head));
|
|
updateMark(cm, vim, '>', cursorMax(anchor, head));
|
|
CodeMirror.signal(cm, 'vim-mode-change', {
|
|
mode: 'visual',
|
|
subMode: vim.visualLine ? 'linewise' :
|
|
vim.visualBlock ? 'blockwise' : ''});
|
|
}
|
|
},
|
|
joinLines: function(cm, actionArgs, vim) {
|
|
var curStart, curEnd;
|
|
if (vim.visualMode) {
|
|
curStart = cm.getCursor('anchor');
|
|
curEnd = cm.getCursor('head');
|
|
if (cursorIsBefore(curEnd, curStart)) {
|
|
var tmp = curEnd;
|
|
curEnd = curStart;
|
|
curStart = tmp;
|
|
}
|
|
curEnd.ch = lineLength(cm, curEnd.line) - 1;
|
|
} else {
|
|
var repeat = Math.max(actionArgs.repeat, 2);
|
|
curStart = cm.getCursor();
|
|
curEnd = clipCursorToContent(cm, Pos(curStart.line + repeat - 1,
|
|
Infinity));
|
|
}
|
|
var finalCh = 0;
|
|
for (var i = curStart.line; i < curEnd.line; i++) {
|
|
finalCh = lineLength(cm, curStart.line);
|
|
var tmp = Pos(curStart.line + 1,
|
|
lineLength(cm, curStart.line + 1));
|
|
var text = cm.getRange(curStart, tmp);
|
|
text = text.replace(/\n\s*/g, ' ');
|
|
cm.replaceRange(text, curStart, tmp);
|
|
}
|
|
var curFinalPos = Pos(curStart.line, finalCh);
|
|
if (vim.visualMode) {
|
|
exitVisualMode(cm, false);
|
|
}
|
|
cm.setCursor(curFinalPos);
|
|
},
|
|
newLineAndEnterInsertMode: function(cm, actionArgs, vim) {
|
|
vim.insertMode = true;
|
|
var insertAt = copyCursor(cm.getCursor());
|
|
if (insertAt.line === cm.firstLine() && !actionArgs.after) {
|
|
cm.replaceRange('\n', Pos(cm.firstLine(), 0));
|
|
cm.setCursor(cm.firstLine(), 0);
|
|
} else {
|
|
insertAt.line = (actionArgs.after) ? insertAt.line :
|
|
insertAt.line - 1;
|
|
insertAt.ch = lineLength(cm, insertAt.line);
|
|
cm.setCursor(insertAt);
|
|
var newlineFn = CodeMirror.commands.newlineAndIndentContinueComment ||
|
|
CodeMirror.commands.newlineAndIndent;
|
|
newlineFn(cm);
|
|
}
|
|
this.enterInsertMode(cm, { repeat: actionArgs.repeat }, vim);
|
|
},
|
|
paste: function(cm, actionArgs, vim) {
|
|
var cur = copyCursor(cm.getCursor());
|
|
var register = vimGlobalState.registerController.getRegister(
|
|
actionArgs.registerName);
|
|
var text = register.toString();
|
|
if (!text) {
|
|
return;
|
|
}
|
|
if (actionArgs.matchIndent) {
|
|
var tabSize = cm.getOption("tabSize");
|
|
var whitespaceLength = function(str) {
|
|
var tabs = (str.split("\t").length - 1);
|
|
var spaces = (str.split(" ").length - 1);
|
|
return tabs * tabSize + spaces * 1;
|
|
};
|
|
var currentLine = cm.getLine(cm.getCursor().line);
|
|
var indent = whitespaceLength(currentLine.match(/^\s*/)[0]);
|
|
var chompedText = text.replace(/\n$/, '');
|
|
var wasChomped = text !== chompedText;
|
|
var firstIndent = whitespaceLength(text.match(/^\s*/)[0]);
|
|
var text = chompedText.replace(/^\s*/gm, function(wspace) {
|
|
var newIndent = indent + (whitespaceLength(wspace) - firstIndent);
|
|
if (newIndent < 0) {
|
|
return "";
|
|
}
|
|
else if (cm.getOption("indentWithTabs")) {
|
|
var quotient = Math.floor(newIndent / tabSize);
|
|
return Array(quotient + 1).join('\t');
|
|
}
|
|
else {
|
|
return Array(newIndent + 1).join(' ');
|
|
}
|
|
});
|
|
text += wasChomped ? "\n" : "";
|
|
}
|
|
if (actionArgs.repeat > 1) {
|
|
var text = Array(actionArgs.repeat + 1).join(text);
|
|
}
|
|
var linewise = register.linewise;
|
|
var blockwise = register.blockwise;
|
|
if (linewise && !blockwise) {
|
|
if(vim.visualMode) {
|
|
text = vim.visualLine ? text.slice(0, -1) : '\n' + text.slice(0, text.length - 1) + '\n';
|
|
} else if (actionArgs.after) {
|
|
text = '\n' + text.slice(0, text.length - 1);
|
|
cur.ch = lineLength(cm, cur.line);
|
|
} else {
|
|
cur.ch = 0;
|
|
}
|
|
} else {
|
|
if (blockwise) {
|
|
text = text.split('\n');
|
|
for (var i = 0; i < text.length; i++) {
|
|
text[i] = (text[i] == '') ? ' ' : text[i];
|
|
}
|
|
}
|
|
cur.ch += actionArgs.after ? 1 : 0;
|
|
}
|
|
var curPosFinal;
|
|
var idx;
|
|
if (vim.visualMode) {
|
|
vim.lastPastedText = text;
|
|
var lastSelectionCurEnd;
|
|
var selectedArea = getSelectedAreaRange(cm, vim);
|
|
var selectionStart = selectedArea[0];
|
|
var selectionEnd = selectedArea[1];
|
|
var selectedText = cm.getSelection();
|
|
var selections = cm.listSelections();
|
|
var emptyStrings = new Array(selections.length).join('1').split('1');
|
|
if (vim.lastSelection) {
|
|
lastSelectionCurEnd = vim.lastSelection.headMark.find();
|
|
}
|
|
vimGlobalState.registerController.unnamedRegister.setText(selectedText);
|
|
if (blockwise) {
|
|
cm.replaceSelections(emptyStrings);
|
|
selectionEnd = Pos(selectionStart.line + text.length-1, selectionStart.ch);
|
|
cm.setCursor(selectionStart);
|
|
selectBlock(cm, selectionEnd);
|
|
cm.replaceSelections(text);
|
|
curPosFinal = selectionStart;
|
|
} else if (vim.visualBlock) {
|
|
cm.replaceSelections(emptyStrings);
|
|
cm.setCursor(selectionStart);
|
|
cm.replaceRange(text, selectionStart, selectionStart);
|
|
curPosFinal = selectionStart;
|
|
} else {
|
|
cm.replaceRange(text, selectionStart, selectionEnd);
|
|
curPosFinal = cm.posFromIndex(cm.indexFromPos(selectionStart) + text.length - 1);
|
|
}
|
|
if(lastSelectionCurEnd) {
|
|
vim.lastSelection.headMark = cm.setBookmark(lastSelectionCurEnd);
|
|
}
|
|
if (linewise) {
|
|
curPosFinal.ch=0;
|
|
}
|
|
} else {
|
|
if (blockwise) {
|
|
cm.setCursor(cur);
|
|
for (var i = 0; i < text.length; i++) {
|
|
var line = cur.line+i;
|
|
if (line > cm.lastLine()) {
|
|
cm.replaceRange('\n', Pos(line, 0));
|
|
}
|
|
var lastCh = lineLength(cm, line);
|
|
if (lastCh < cur.ch) {
|
|
extendLineToColumn(cm, line, cur.ch);
|
|
}
|
|
}
|
|
cm.setCursor(cur);
|
|
selectBlock(cm, Pos(cur.line + text.length-1, cur.ch));
|
|
cm.replaceSelections(text);
|
|
curPosFinal = cur;
|
|
} else {
|
|
cm.replaceRange(text, cur);
|
|
if (linewise && actionArgs.after) {
|
|
curPosFinal = Pos(
|
|
cur.line + 1,
|
|
findFirstNonWhiteSpaceCharacter(cm.getLine(cur.line + 1)));
|
|
} else if (linewise && !actionArgs.after) {
|
|
curPosFinal = Pos(
|
|
cur.line,
|
|
findFirstNonWhiteSpaceCharacter(cm.getLine(cur.line)));
|
|
} else if (!linewise && actionArgs.after) {
|
|
idx = cm.indexFromPos(cur);
|
|
curPosFinal = cm.posFromIndex(idx + text.length - 1);
|
|
} else {
|
|
idx = cm.indexFromPos(cur);
|
|
curPosFinal = cm.posFromIndex(idx + text.length);
|
|
}
|
|
}
|
|
}
|
|
if (vim.visualMode) {
|
|
exitVisualMode(cm, false);
|
|
}
|
|
cm.setCursor(curPosFinal);
|
|
},
|
|
undo: function(cm, actionArgs) {
|
|
cm.operation(function() {
|
|
repeatFn(cm, CodeMirror.commands.undo, actionArgs.repeat)();
|
|
cm.setCursor(cm.getCursor('anchor'));
|
|
});
|
|
},
|
|
redo: function(cm, actionArgs) {
|
|
repeatFn(cm, CodeMirror.commands.redo, actionArgs.repeat)();
|
|
},
|
|
setRegister: function(_cm, actionArgs, vim) {
|
|
vim.inputState.registerName = actionArgs.selectedCharacter;
|
|
},
|
|
setMark: function(cm, actionArgs, vim) {
|
|
var markName = actionArgs.selectedCharacter;
|
|
updateMark(cm, vim, markName, cm.getCursor());
|
|
},
|
|
replace: function(cm, actionArgs, vim) {
|
|
var replaceWith = actionArgs.selectedCharacter;
|
|
var curStart = cm.getCursor();
|
|
var replaceTo;
|
|
var curEnd;
|
|
var selections = cm.listSelections();
|
|
if (vim.visualMode) {
|
|
curStart = cm.getCursor('start');
|
|
curEnd = cm.getCursor('end');
|
|
} else {
|
|
var line = cm.getLine(curStart.line);
|
|
replaceTo = curStart.ch + actionArgs.repeat;
|
|
if (replaceTo > line.length) {
|
|
replaceTo=line.length;
|
|
}
|
|
curEnd = Pos(curStart.line, replaceTo);
|
|
}
|
|
if (replaceWith=='\n') {
|
|
if (!vim.visualMode) cm.replaceRange('', curStart, curEnd);
|
|
(CodeMirror.commands.newlineAndIndentContinueComment || CodeMirror.commands.newlineAndIndent)(cm);
|
|
} else {
|
|
var replaceWithStr = cm.getRange(curStart, curEnd);
|
|
replaceWithStr = replaceWithStr.replace(/[^\n]/g, replaceWith);
|
|
if (vim.visualBlock) {
|
|
var spaces = new Array(cm.getOption("tabSize")+1).join(' ');
|
|
replaceWithStr = cm.getSelection();
|
|
replaceWithStr = replaceWithStr.replace(/\t/g, spaces).replace(/[^\n]/g, replaceWith).split('\n');
|
|
cm.replaceSelections(replaceWithStr);
|
|
} else {
|
|
cm.replaceRange(replaceWithStr, curStart, curEnd);
|
|
}
|
|
if (vim.visualMode) {
|
|
curStart = cursorIsBefore(selections[0].anchor, selections[0].head) ?
|
|
selections[0].anchor : selections[0].head;
|
|
cm.setCursor(curStart);
|
|
exitVisualMode(cm, false);
|
|
} else {
|
|
cm.setCursor(offsetCursor(curEnd, 0, -1));
|
|
}
|
|
}
|
|
},
|
|
incrementNumberToken: function(cm, actionArgs) {
|
|
var cur = cm.getCursor();
|
|
var lineStr = cm.getLine(cur.line);
|
|
var re = /-?\d+/g;
|
|
var match;
|
|
var start;
|
|
var end;
|
|
var numberStr;
|
|
var token;
|
|
while ((match = re.exec(lineStr)) !== null) {
|
|
token = match[0];
|
|
start = match.index;
|
|
end = start + token.length;
|
|
if (cur.ch < end)break;
|
|
}
|
|
if (!actionArgs.backtrack && (end <= cur.ch))return;
|
|
if (token) {
|
|
var increment = actionArgs.increase ? 1 : -1;
|
|
var number = parseInt(token) + (increment * actionArgs.repeat);
|
|
var from = Pos(cur.line, start);
|
|
var to = Pos(cur.line, end);
|
|
numberStr = number.toString();
|
|
cm.replaceRange(numberStr, from, to);
|
|
} else {
|
|
return;
|
|
}
|
|
cm.setCursor(Pos(cur.line, start + numberStr.length - 1));
|
|
},
|
|
repeatLastEdit: function(cm, actionArgs, vim) {
|
|
var lastEditInputState = vim.lastEditInputState;
|
|
if (!lastEditInputState) { return; }
|
|
var repeat = actionArgs.repeat;
|
|
if (repeat && actionArgs.repeatIsExplicit) {
|
|
vim.lastEditInputState.repeatOverride = repeat;
|
|
} else {
|
|
repeat = vim.lastEditInputState.repeatOverride || repeat;
|
|
}
|
|
repeatLastEdit(cm, vim, repeat, false /** repeatForInsert */);
|
|
},
|
|
exitInsertMode: exitInsertMode
|
|
};
|
|
|
|
function defineAction(name, fn) {
|
|
actions[name] = fn;
|
|
}
|
|
function clipCursorToContent(cm, cur, includeLineBreak) {
|
|
var line = Math.min(Math.max(cm.firstLine(), cur.line), cm.lastLine() );
|
|
var maxCh = lineLength(cm, line) - 1;
|
|
maxCh = (includeLineBreak) ? maxCh + 1 : maxCh;
|
|
var ch = Math.min(Math.max(0, cur.ch), maxCh);
|
|
return Pos(line, ch);
|
|
}
|
|
function copyArgs(args) {
|
|
var ret = {};
|
|
for (var prop in args) {
|
|
if (args.hasOwnProperty(prop)) {
|
|
ret[prop] = args[prop];
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
function offsetCursor(cur, offsetLine, offsetCh) {
|
|
if (typeof offsetLine === 'object') {
|
|
offsetCh = offsetLine.ch;
|
|
offsetLine = offsetLine.line;
|
|
}
|
|
return Pos(cur.line + offsetLine, cur.ch + offsetCh);
|
|
}
|
|
function getOffset(anchor, head) {
|
|
return {
|
|
line: head.line - anchor.line,
|
|
ch: head.line - anchor.line
|
|
};
|
|
}
|
|
function commandMatches(keys, keyMap, context, inputState) {
|
|
var match, partial = [], full = [];
|
|
for (var i = 0; i < keyMap.length; i++) {
|
|
var command = keyMap[i];
|
|
if (context == 'insert' && command.context != 'insert' ||
|
|
command.context && command.context != context ||
|
|
inputState.operator && command.type == 'action' ||
|
|
!(match = commandMatch(keys, command.keys))) { continue; }
|
|
if (match == 'partial') { partial.push(command); }
|
|
if (match == 'full') { full.push(command); }
|
|
}
|
|
return {
|
|
partial: partial.length && partial,
|
|
full: full.length && full
|
|
};
|
|
}
|
|
function commandMatch(pressed, mapped) {
|
|
if (mapped.slice(-11) == '<character>') {
|
|
var prefixLen = mapped.length - 11;
|
|
var pressedPrefix = pressed.slice(0, prefixLen);
|
|
var mappedPrefix = mapped.slice(0, prefixLen);
|
|
return pressedPrefix == mappedPrefix && pressed.length > prefixLen ? 'full' :
|
|
mappedPrefix.indexOf(pressedPrefix) == 0 ? 'partial' : false;
|
|
} else {
|
|
return pressed == mapped ? 'full' :
|
|
mapped.indexOf(pressed) == 0 ? 'partial' : false;
|
|
}
|
|
}
|
|
function lastChar(keys) {
|
|
var match = /^.*(<[\w\-]+>)$/.exec(keys);
|
|
var selectedCharacter = match ? match[1] : keys.slice(-1);
|
|
if (selectedCharacter.length > 1){
|
|
switch(selectedCharacter){
|
|
case '<CR>':
|
|
selectedCharacter='\n';
|
|
break;
|
|
case '<Space>':
|
|
selectedCharacter=' ';
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
return selectedCharacter;
|
|
}
|
|
function repeatFn(cm, fn, repeat) {
|
|
return function() {
|
|
for (var i = 0; i < repeat; i++) {
|
|
fn(cm);
|
|
}
|
|
};
|
|
}
|
|
function copyCursor(cur) {
|
|
return Pos(cur.line, cur.ch);
|
|
}
|
|
function cursorEqual(cur1, cur2) {
|
|
return cur1.ch == cur2.ch && cur1.line == cur2.line;
|
|
}
|
|
function cursorIsBefore(cur1, cur2) {
|
|
if (cur1.line < cur2.line) {
|
|
return true;
|
|
}
|
|
if (cur1.line == cur2.line && cur1.ch < cur2.ch) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
function cursorMin(cur1, cur2) {
|
|
if (arguments.length > 2) {
|
|
cur2 = cursorMin.apply(undefined, Array.prototype.slice.call(arguments, 1));
|
|
}
|
|
return cursorIsBefore(cur1, cur2) ? cur1 : cur2;
|
|
}
|
|
function cursorMax(cur1, cur2) {
|
|
if (arguments.length > 2) {
|
|
cur2 = cursorMax.apply(undefined, Array.prototype.slice.call(arguments, 1));
|
|
}
|
|
return cursorIsBefore(cur1, cur2) ? cur2 : cur1;
|
|
}
|
|
function cursorIsBetween(cur1, cur2, cur3) {
|
|
var cur1before2 = cursorIsBefore(cur1, cur2);
|
|
var cur2before3 = cursorIsBefore(cur2, cur3);
|
|
return cur1before2 && cur2before3;
|
|
}
|
|
function lineLength(cm, lineNum) {
|
|
return cm.getLine(lineNum).length;
|
|
}
|
|
function trim(s) {
|
|
if (s.trim) {
|
|
return s.trim();
|
|
}
|
|
return s.replace(/^\s+|\s+$/g, '');
|
|
}
|
|
function escapeRegex(s) {
|
|
return s.replace(/([.?*+$\[\]\/\\(){}|\-])/g, '\\$1');
|
|
}
|
|
function extendLineToColumn(cm, lineNum, column) {
|
|
var endCh = lineLength(cm, lineNum);
|
|
var spaces = new Array(column-endCh+1).join(' ');
|
|
cm.setCursor(Pos(lineNum, endCh));
|
|
cm.replaceRange(spaces, cm.getCursor());
|
|
}
|
|
function selectBlock(cm, selectionEnd) {
|
|
var selections = [], ranges = cm.listSelections();
|
|
var head = copyCursor(cm.clipPos(selectionEnd));
|
|
var isClipped = !cursorEqual(selectionEnd, head);
|
|
var curHead = cm.getCursor('head');
|
|
var primIndex = getIndex(ranges, curHead);
|
|
var wasClipped = cursorEqual(ranges[primIndex].head, ranges[primIndex].anchor);
|
|
var max = ranges.length - 1;
|
|
var index = max - primIndex > primIndex ? max : 0;
|
|
var base = ranges[index].anchor;
|
|
|
|
var firstLine = Math.min(base.line, head.line);
|
|
var lastLine = Math.max(base.line, head.line);
|
|
var baseCh = base.ch, headCh = head.ch;
|
|
|
|
var dir = ranges[index].head.ch - baseCh;
|
|
var newDir = headCh - baseCh;
|
|
if (dir > 0 && newDir <= 0) {
|
|
baseCh++;
|
|
if (!isClipped) { headCh--; }
|
|
} else if (dir < 0 && newDir >= 0) {
|
|
baseCh--;
|
|
if (!wasClipped) { headCh++; }
|
|
} else if (dir < 0 && newDir == -1) {
|
|
baseCh--;
|
|
headCh++;
|
|
}
|
|
for (var line = firstLine; line <= lastLine; line++) {
|
|
var range = {anchor: new Pos(line, baseCh), head: new Pos(line, headCh)};
|
|
selections.push(range);
|
|
}
|
|
primIndex = head.line == lastLine ? selections.length - 1 : 0;
|
|
cm.setSelections(selections);
|
|
selectionEnd.ch = headCh;
|
|
base.ch = baseCh;
|
|
return base;
|
|
}
|
|
function selectForInsert(cm, head, height) {
|
|
var sel = [];
|
|
for (var i = 0; i < height; i++) {
|
|
var lineHead = offsetCursor(head, i, 0);
|
|
sel.push({anchor: lineHead, head: lineHead});
|
|
}
|
|
cm.setSelections(sel, 0);
|
|
}
|
|
function getIndex(ranges, cursor, end) {
|
|
for (var i = 0; i < ranges.length; i++) {
|
|
var atAnchor = end != 'head' && cursorEqual(ranges[i].anchor, cursor);
|
|
var atHead = end != 'anchor' && cursorEqual(ranges[i].head, cursor);
|
|
if (atAnchor || atHead) {
|
|
return i;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
function getSelectedAreaRange(cm, vim) {
|
|
var lastSelection = vim.lastSelection;
|
|
var getCurrentSelectedAreaRange = function() {
|
|
var selections = cm.listSelections();
|
|
var start = selections[0];
|
|
var end = selections[selections.length-1];
|
|
var selectionStart = cursorIsBefore(start.anchor, start.head) ? start.anchor : start.head;
|
|
var selectionEnd = cursorIsBefore(end.anchor, end.head) ? end.head : end.anchor;
|
|
return [selectionStart, selectionEnd];
|
|
};
|
|
var getLastSelectedAreaRange = function() {
|
|
var selectionStart = cm.getCursor();
|
|
var selectionEnd = cm.getCursor();
|
|
var block = lastSelection.visualBlock;
|
|
if (block) {
|
|
var width = block.width;
|
|
var height = block.height;
|
|
selectionEnd = Pos(selectionStart.line + height, selectionStart.ch + width);
|
|
var selections = [];
|
|
for (var i = selectionStart.line; i < selectionEnd.line; i++) {
|
|
var anchor = Pos(i, selectionStart.ch);
|
|
var head = Pos(i, selectionEnd.ch);
|
|
var range = {anchor: anchor, head: head};
|
|
selections.push(range);
|
|
}
|
|
cm.setSelections(selections);
|
|
} else {
|
|
var start = lastSelection.anchorMark.find();
|
|
var end = lastSelection.headMark.find();
|
|
var line = end.line - start.line;
|
|
var ch = end.ch - start.ch;
|
|
selectionEnd = {line: selectionEnd.line + line, ch: line ? selectionEnd.ch : ch + selectionEnd.ch};
|
|
if (lastSelection.visualLine) {
|
|
selectionStart = Pos(selectionStart.line, 0);
|
|
selectionEnd = Pos(selectionEnd.line, lineLength(cm, selectionEnd.line));
|
|
}
|
|
cm.setSelection(selectionStart, selectionEnd);
|
|
}
|
|
return [selectionStart, selectionEnd];
|
|
};
|
|
if (!vim.visualMode) {
|
|
return getLastSelectedAreaRange();
|
|
} else {
|
|
return getCurrentSelectedAreaRange();
|
|
}
|
|
}
|
|
function updateLastSelection(cm, vim) {
|
|
var anchor = vim.sel.anchor;
|
|
var head = vim.sel.head;
|
|
if (vim.lastPastedText) {
|
|
head = cm.posFromIndex(cm.indexFromPos(anchor) + vim.lastPastedText.length);
|
|
vim.lastPastedText = null;
|
|
}
|
|
vim.lastSelection = {'anchorMark': cm.setBookmark(anchor),
|
|
'headMark': cm.setBookmark(head),
|
|
'anchor': copyCursor(anchor),
|
|
'head': copyCursor(head),
|
|
'visualMode': vim.visualMode,
|
|
'visualLine': vim.visualLine,
|
|
'visualBlock': vim.visualBlock};
|
|
}
|
|
function expandSelection(cm, start, end) {
|
|
var sel = cm.state.vim.sel;
|
|
var head = sel.head;
|
|
var anchor = sel.anchor;
|
|
var tmp;
|
|
if (cursorIsBefore(end, start)) {
|
|
tmp = end;
|
|
end = start;
|
|
start = tmp;
|
|
}
|
|
if (cursorIsBefore(head, anchor)) {
|
|
head = cursorMin(start, head);
|
|
anchor = cursorMax(anchor, end);
|
|
} else {
|
|
anchor = cursorMin(start, anchor);
|
|
head = cursorMax(head, end);
|
|
head = offsetCursor(head, 0, -1);
|
|
if (head.ch == -1 && head.line != cm.firstLine()) {
|
|
head = Pos(head.line - 1, lineLength(cm, head.line - 1));
|
|
}
|
|
}
|
|
return [anchor, head];
|
|
}
|
|
function updateCmSelection(cm, sel, mode) {
|
|
var vim = cm.state.vim;
|
|
sel = sel || vim.sel;
|
|
var mode = mode ||
|
|
vim.visualLine ? 'line' : vim.visualBlock ? 'block' : 'char';
|
|
var cmSel = makeCmSelection(cm, sel, mode);
|
|
cm.setSelections(cmSel.ranges, cmSel.primary);
|
|
updateFakeCursor(cm);
|
|
}
|
|
function makeCmSelection(cm, sel, mode, exclusive) {
|
|
var head = copyCursor(sel.head);
|
|
var anchor = copyCursor(sel.anchor);
|
|
if (mode == 'char') {
|
|
var headOffset = !exclusive && !cursorIsBefore(sel.head, sel.anchor) ? 1 : 0;
|
|
var anchorOffset = cursorIsBefore(sel.head, sel.anchor) ? 1 : 0;
|
|
head = offsetCursor(sel.head, 0, headOffset);
|
|
anchor = offsetCursor(sel.anchor, 0, anchorOffset);
|
|
return {
|
|
ranges: [{anchor: anchor, head: head}],
|
|
primary: 0
|
|
};
|
|
} else if (mode == 'line') {
|
|
if (!cursorIsBefore(sel.head, sel.anchor)) {
|
|
anchor.ch = 0;
|
|
|
|
var lastLine = cm.lastLine();
|
|
if (head.line > lastLine) {
|
|
head.line = lastLine;
|
|
}
|
|
head.ch = lineLength(cm, head.line);
|
|
} else {
|
|
head.ch = 0;
|
|
anchor.ch = lineLength(cm, anchor.line);
|
|
}
|
|
return {
|
|
ranges: [{anchor: anchor, head: head}],
|
|
primary: 0
|
|
};
|
|
} else if (mode == 'block') {
|
|
var top = Math.min(anchor.line, head.line),
|
|
left = Math.min(anchor.ch, head.ch),
|
|
bottom = Math.max(anchor.line, head.line),
|
|
right = Math.max(anchor.ch, head.ch) + 1;
|
|
var height = bottom - top + 1;
|
|
var primary = head.line == top ? 0 : height - 1;
|
|
var ranges = [];
|
|
for (var i = 0; i < height; i++) {
|
|
ranges.push({
|
|
anchor: Pos(top + i, left),
|
|
head: Pos(top + i, right)
|
|
});
|
|
}
|
|
return {
|
|
ranges: ranges,
|
|
primary: primary
|
|
};
|
|
}
|
|
}
|
|
function getHead(cm) {
|
|
var cur = cm.getCursor('head');
|
|
if (cm.getSelection().length == 1) {
|
|
cur = cursorMin(cur, cm.getCursor('anchor'));
|
|
}
|
|
return cur;
|
|
}
|
|
function exitVisualMode(cm, moveHead) {
|
|
var vim = cm.state.vim;
|
|
if (moveHead !== false) {
|
|
cm.setCursor(clipCursorToContent(cm, vim.sel.head));
|
|
}
|
|
updateLastSelection(cm, vim);
|
|
vim.visualMode = false;
|
|
vim.visualLine = false;
|
|
vim.visualBlock = false;
|
|
CodeMirror.signal(cm, "vim-mode-change", {mode: "normal"});
|
|
if (vim.fakeCursor) {
|
|
vim.fakeCursor.clear();
|
|
}
|
|
}
|
|
function clipToLine(cm, curStart, curEnd) {
|
|
var selection = cm.getRange(curStart, curEnd);
|
|
if (/\n\s*$/.test(selection)) {
|
|
var lines = selection.split('\n');
|
|
lines.pop();
|
|
var line;
|
|
for (var line = lines.pop(); lines.length > 0 && line && isWhiteSpaceString(line); line = lines.pop()) {
|
|
curEnd.line--;
|
|
curEnd.ch = 0;
|
|
}
|
|
if (line) {
|
|
curEnd.line--;
|
|
curEnd.ch = lineLength(cm, curEnd.line);
|
|
} else {
|
|
curEnd.ch = 0;
|
|
}
|
|
}
|
|
}
|
|
function expandSelectionToLine(_cm, curStart, curEnd) {
|
|
curStart.ch = 0;
|
|
curEnd.ch = 0;
|
|
curEnd.line++;
|
|
}
|
|
|
|
function findFirstNonWhiteSpaceCharacter(text) {
|
|
if (!text) {
|
|
return 0;
|
|
}
|
|
var firstNonWS = text.search(/\S/);
|
|
return firstNonWS == -1 ? text.length : firstNonWS;
|
|
}
|
|
|
|
function expandWordUnderCursor(cm, inclusive, _forward, bigWord, noSymbol) {
|
|
var cur = getHead(cm);
|
|
var line = cm.getLine(cur.line);
|
|
var idx = cur.ch;
|
|
var test = noSymbol ? wordCharTest[0] : bigWordCharTest [0];
|
|
while (!test(line.charAt(idx))) {
|
|
idx++;
|
|
if (idx >= line.length) { return null; }
|
|
}
|
|
|
|
if (bigWord) {
|
|
test = bigWordCharTest[0];
|
|
} else {
|
|
test = wordCharTest[0];
|
|
if (!test(line.charAt(idx))) {
|
|
test = wordCharTest[1];
|
|
}
|
|
}
|
|
|
|
var end = idx, start = idx;
|
|
while (test(line.charAt(end)) && end < line.length) { end++; }
|
|
while (test(line.charAt(start)) && start >= 0) { start--; }
|
|
start++;
|
|
|
|
if (inclusive) {
|
|
var wordEnd = end;
|
|
while (/\s/.test(line.charAt(end)) && end < line.length) { end++; }
|
|
if (wordEnd == end) {
|
|
var wordStart = start;
|
|
while (/\s/.test(line.charAt(start - 1)) && start > 0) { start--; }
|
|
if (!start) { start = wordStart; }
|
|
}
|
|
}
|
|
return { start: Pos(cur.line, start), end: Pos(cur.line, end) };
|
|
}
|
|
|
|
function recordJumpPosition(cm, oldCur, newCur) {
|
|
if (!cursorEqual(oldCur, newCur)) {
|
|
vimGlobalState.jumpList.add(cm, oldCur, newCur);
|
|
}
|
|
}
|
|
|
|
function recordLastCharacterSearch(increment, args) {
|
|
vimGlobalState.lastChararacterSearch.increment = increment;
|
|
vimGlobalState.lastChararacterSearch.forward = args.forward;
|
|
vimGlobalState.lastChararacterSearch.selectedCharacter = args.selectedCharacter;
|
|
}
|
|
|
|
var symbolToMode = {
|
|
'(': 'bracket', ')': 'bracket', '{': 'bracket', '}': 'bracket',
|
|
'[': 'section', ']': 'section',
|
|
'*': 'comment', '/': 'comment',
|
|
'm': 'method', 'M': 'method',
|
|
'#': 'preprocess'
|
|
};
|
|
var findSymbolModes = {
|
|
bracket: {
|
|
isComplete: function(state) {
|
|
if (state.nextCh === state.symb) {
|
|
state.depth++;
|
|
if (state.depth >= 1)return true;
|
|
} else if (state.nextCh === state.reverseSymb) {
|
|
state.depth--;
|
|
}
|
|
return false;
|
|
}
|
|
},
|
|
section: {
|
|
init: function(state) {
|
|
state.curMoveThrough = true;
|
|
state.symb = (state.forward ? ']' : '[') === state.symb ? '{' : '}';
|
|
},
|
|
isComplete: function(state) {
|
|
return state.index === 0 && state.nextCh === state.symb;
|
|
}
|
|
},
|
|
comment: {
|
|
isComplete: function(state) {
|
|
var found = state.lastCh === '*' && state.nextCh === '/';
|
|
state.lastCh = state.nextCh;
|
|
return found;
|
|
}
|
|
},
|
|
method: {
|
|
init: function(state) {
|
|
state.symb = (state.symb === 'm' ? '{' : '}');
|
|
state.reverseSymb = state.symb === '{' ? '}' : '{';
|
|
},
|
|
isComplete: function(state) {
|
|
if (state.nextCh === state.symb)return true;
|
|
return false;
|
|
}
|
|
},
|
|
preprocess: {
|
|
init: function(state) {
|
|
state.index = 0;
|
|
},
|
|
isComplete: function(state) {
|
|
if (state.nextCh === '#') {
|
|
var token = state.lineText.match(/#(\w+)/)[1];
|
|
if (token === 'endif') {
|
|
if (state.forward && state.depth === 0) {
|
|
return true;
|
|
}
|
|
state.depth++;
|
|
} else if (token === 'if') {
|
|
if (!state.forward && state.depth === 0) {
|
|
return true;
|
|
}
|
|
state.depth--;
|
|
}
|
|
if (token === 'else' && state.depth === 0)return true;
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
};
|
|
function findSymbol(cm, repeat, forward, symb) {
|
|
var cur = copyCursor(cm.getCursor());
|
|
var increment = forward ? 1 : -1;
|
|
var endLine = forward ? cm.lineCount() : -1;
|
|
var curCh = cur.ch;
|
|
var line = cur.line;
|
|
var lineText = cm.getLine(line);
|
|
var state = {
|
|
lineText: lineText,
|
|
nextCh: lineText.charAt(curCh),
|
|
lastCh: null,
|
|
index: curCh,
|
|
symb: symb,
|
|
reverseSymb: (forward ? { ')': '(', '}': '{' } : { '(': ')', '{': '}' })[symb],
|
|
forward: forward,
|
|
depth: 0,
|
|
curMoveThrough: false
|
|
};
|
|
var mode = symbolToMode[symb];
|
|
if (!mode)return cur;
|
|
var init = findSymbolModes[mode].init;
|
|
var isComplete = findSymbolModes[mode].isComplete;
|
|
if (init) { init(state); }
|
|
while (line !== endLine && repeat) {
|
|
state.index += increment;
|
|
state.nextCh = state.lineText.charAt(state.index);
|
|
if (!state.nextCh) {
|
|
line += increment;
|
|
state.lineText = cm.getLine(line) || '';
|
|
if (increment > 0) {
|
|
state.index = 0;
|
|
} else {
|
|
var lineLen = state.lineText.length;
|
|
state.index = (lineLen > 0) ? (lineLen-1) : 0;
|
|
}
|
|
state.nextCh = state.lineText.charAt(state.index);
|
|
}
|
|
if (isComplete(state)) {
|
|
cur.line = line;
|
|
cur.ch = state.index;
|
|
repeat--;
|
|
}
|
|
}
|
|
if (state.nextCh || state.curMoveThrough) {
|
|
return Pos(line, state.index);
|
|
}
|
|
return cur;
|
|
}
|
|
function findWord(cm, cur, forward, bigWord, emptyLineIsWord) {
|
|
var lineNum = cur.line;
|
|
var pos = cur.ch;
|
|
var line = cm.getLine(lineNum);
|
|
var dir = forward ? 1 : -1;
|
|
var charTests = bigWord ? bigWordCharTest: wordCharTest;
|
|
|
|
if (emptyLineIsWord && line == '') {
|
|
lineNum += dir;
|
|
line = cm.getLine(lineNum);
|
|
if (!isLine(cm, lineNum)) {
|
|
return null;
|
|
}
|
|
pos = (forward) ? 0 : line.length;
|
|
}
|
|
|
|
while (true) {
|
|
if (emptyLineIsWord && line == '') {
|
|
return { from: 0, to: 0, line: lineNum };
|
|
}
|
|
var stop = (dir > 0) ? line.length : -1;
|
|
var wordStart = stop, wordEnd = stop;
|
|
while (pos != stop) {
|
|
var foundWord = false;
|
|
for (var i = 0; i < charTests.length && !foundWord; ++i) {
|
|
if (charTests[i](line.charAt(pos))) {
|
|
wordStart = pos;
|
|
while (pos != stop && charTests[i](line.charAt(pos))) {
|
|
pos += dir;
|
|
}
|
|
wordEnd = pos;
|
|
foundWord = wordStart != wordEnd;
|
|
if (wordStart == cur.ch && lineNum == cur.line &&
|
|
wordEnd == wordStart + dir) {
|
|
continue;
|
|
} else {
|
|
return {
|
|
from: Math.min(wordStart, wordEnd + 1),
|
|
to: Math.max(wordStart, wordEnd),
|
|
line: lineNum };
|
|
}
|
|
}
|
|
}
|
|
if (!foundWord) {
|
|
pos += dir;
|
|
}
|
|
}
|
|
lineNum += dir;
|
|
if (!isLine(cm, lineNum)) {
|
|
return null;
|
|
}
|
|
line = cm.getLine(lineNum);
|
|
pos = (dir > 0) ? 0 : line.length;
|
|
}
|
|
throw new Error('The impossible happened.');
|
|
}
|
|
function moveToWord(cm, cur, repeat, forward, wordEnd, bigWord) {
|
|
var curStart = copyCursor(cur);
|
|
var words = [];
|
|
if (forward && !wordEnd || !forward && wordEnd) {
|
|
repeat++;
|
|
}
|
|
var emptyLineIsWord = !(forward && wordEnd);
|
|
for (var i = 0; i < repeat; i++) {
|
|
var word = findWord(cm, cur, forward, bigWord, emptyLineIsWord);
|
|
if (!word) {
|
|
var eodCh = lineLength(cm, cm.lastLine());
|
|
words.push(forward
|
|
? {line: cm.lastLine(), from: eodCh, to: eodCh}
|
|
: {line: 0, from: 0, to: 0});
|
|
break;
|
|
}
|
|
words.push(word);
|
|
cur = Pos(word.line, forward ? (word.to - 1) : word.from);
|
|
}
|
|
var shortCircuit = words.length != repeat;
|
|
var firstWord = words[0];
|
|
var lastWord = words.pop();
|
|
if (forward && !wordEnd) {
|
|
if (!shortCircuit && (firstWord.from != curStart.ch || firstWord.line != curStart.line)) {
|
|
lastWord = words.pop();
|
|
}
|
|
return Pos(lastWord.line, lastWord.from);
|
|
} else if (forward && wordEnd) {
|
|
return Pos(lastWord.line, lastWord.to - 1);
|
|
} else if (!forward && wordEnd) {
|
|
if (!shortCircuit && (firstWord.to != curStart.ch || firstWord.line != curStart.line)) {
|
|
lastWord = words.pop();
|
|
}
|
|
return Pos(lastWord.line, lastWord.to);
|
|
} else {
|
|
return Pos(lastWord.line, lastWord.from);
|
|
}
|
|
}
|
|
|
|
function moveToCharacter(cm, repeat, forward, character) {
|
|
var cur = cm.getCursor();
|
|
var start = cur.ch;
|
|
var idx;
|
|
for (var i = 0; i < repeat; i ++) {
|
|
var line = cm.getLine(cur.line);
|
|
idx = charIdxInLine(start, line, character, forward, true);
|
|
if (idx == -1) {
|
|
return null;
|
|
}
|
|
start = idx;
|
|
}
|
|
return Pos(cm.getCursor().line, idx);
|
|
}
|
|
|
|
function moveToColumn(cm, repeat) {
|
|
var line = cm.getCursor().line;
|
|
return clipCursorToContent(cm, Pos(line, repeat - 1));
|
|
}
|
|
|
|
function updateMark(cm, vim, markName, pos) {
|
|
if (!inArray(markName, validMarks)) {
|
|
return;
|
|
}
|
|
if (vim.marks[markName]) {
|
|
vim.marks[markName].clear();
|
|
}
|
|
vim.marks[markName] = cm.setBookmark(pos);
|
|
}
|
|
|
|
function charIdxInLine(start, line, character, forward, includeChar) {
|
|
var idx;
|
|
if (forward) {
|
|
idx = line.indexOf(character, start + 1);
|
|
if (idx != -1 && !includeChar) {
|
|
idx -= 1;
|
|
}
|
|
} else {
|
|
idx = line.lastIndexOf(character, start - 1);
|
|
if (idx != -1 && !includeChar) {
|
|
idx += 1;
|
|
}
|
|
}
|
|
return idx;
|
|
}
|
|
|
|
function findParagraph(cm, head, repeat, dir, inclusive) {
|
|
var line = head.line;
|
|
var min = cm.firstLine();
|
|
var max = cm.lastLine();
|
|
var start, end, i = line;
|
|
function isEmpty(i) { return !/\S/.test(cm.getLine(i)); } // ace_patch
|
|
function isBoundary(i, dir, any) {
|
|
if (any) { return isEmpty(i) != isEmpty(i + dir); }
|
|
return !isEmpty(i) && isEmpty(i + dir);
|
|
}
|
|
function skipFold(i) {
|
|
dir = dir > 0 ? 1 : -1;
|
|
var foldLine = cm.ace.session.getFoldLine(i);
|
|
if (foldLine) {
|
|
if (i + dir > foldLine.start.row && i + dir < foldLine.end.row)
|
|
dir = (dir > 0 ? foldLine.end.row : foldLine.start.row) - i;
|
|
}
|
|
}
|
|
if (dir) {
|
|
while (min <= i && i <= max && repeat > 0) {
|
|
skipFold(i);
|
|
if (isBoundary(i, dir)) { repeat--; }
|
|
i += dir;
|
|
}
|
|
return new Pos(i, 0);
|
|
}
|
|
|
|
var vim = cm.state.vim;
|
|
if (vim.visualLine && isBoundary(line, 1, true)) {
|
|
var anchor = vim.sel.anchor;
|
|
if (isBoundary(anchor.line, -1, true)) {
|
|
if (!inclusive || anchor.line != line) {
|
|
line += 1;
|
|
}
|
|
}
|
|
}
|
|
var startState = isEmpty(line);
|
|
for (i = line; i <= max && repeat; i++) {
|
|
if (isBoundary(i, 1, true)) {
|
|
if (!inclusive || isEmpty(i) != startState) {
|
|
repeat--;
|
|
}
|
|
}
|
|
}
|
|
end = new Pos(i, 0);
|
|
if (i > max && !startState) { startState = true; }
|
|
else { inclusive = false; }
|
|
for (i = line; i > min; i--) {
|
|
if (!inclusive || isEmpty(i) == startState || i == line) {
|
|
if (isBoundary(i, -1, true)) { break; }
|
|
}
|
|
}
|
|
start = new Pos(i, 0);
|
|
return { start: start, end: end };
|
|
}
|
|
function selectCompanionObject(cm, head, symb, inclusive) {
|
|
var cur = head, start, end;
|
|
|
|
var bracketRegexp = ({
|
|
'(': /[()]/, ')': /[()]/,
|
|
'[': /[[\]]/, ']': /[[\]]/,
|
|
'{': /[{}]/, '}': /[{}]/})[symb];
|
|
var openSym = ({
|
|
'(': '(', ')': '(',
|
|
'[': '[', ']': '[',
|
|
'{': '{', '}': '{'})[symb];
|
|
var curChar = cm.getLine(cur.line).charAt(cur.ch);
|
|
var offset = curChar === openSym ? 1 : 0;
|
|
|
|
start = cm.scanForBracket(Pos(cur.line, cur.ch + offset), -1, null, {'bracketRegex': bracketRegexp});
|
|
end = cm.scanForBracket(Pos(cur.line, cur.ch + offset), 1, null, {'bracketRegex': bracketRegexp});
|
|
|
|
if (!start || !end) {
|
|
return { start: cur, end: cur };
|
|
}
|
|
|
|
start = start.pos;
|
|
end = end.pos;
|
|
|
|
if ((start.line == end.line && start.ch > end.ch)
|
|
|| (start.line > end.line)) {
|
|
var tmp = start;
|
|
start = end;
|
|
end = tmp;
|
|
}
|
|
|
|
if (inclusive) {
|
|
end.ch += 1;
|
|
} else {
|
|
start.ch += 1;
|
|
}
|
|
|
|
return { start: start, end: end };
|
|
}
|
|
function findBeginningAndEnd(cm, head, symb, inclusive) {
|
|
var cur = copyCursor(head);
|
|
var line = cm.getLine(cur.line);
|
|
var chars = line.split('');
|
|
var start, end, i, len;
|
|
var firstIndex = chars.indexOf(symb);
|
|
if (cur.ch < firstIndex) {
|
|
cur.ch = firstIndex;
|
|
}
|
|
else if (firstIndex < cur.ch && chars[cur.ch] == symb) {
|
|
end = cur.ch; // assign end to the current cursor
|
|
--cur.ch; // make sure to look backwards
|
|
}
|
|
if (chars[cur.ch] == symb && !end) {
|
|
start = cur.ch + 1; // assign start to ahead of the cursor
|
|
} else {
|
|
for (i = cur.ch; i > -1 && !start; i--) {
|
|
if (chars[i] == symb) {
|
|
start = i + 1;
|
|
}
|
|
}
|
|
}
|
|
if (start && !end) {
|
|
for (i = start, len = chars.length; i < len && !end; i++) {
|
|
if (chars[i] == symb) {
|
|
end = i;
|
|
}
|
|
}
|
|
}
|
|
if (!start || !end) {
|
|
return { start: cur, end: cur };
|
|
}
|
|
if (inclusive) {
|
|
--start; ++end;
|
|
}
|
|
|
|
return {
|
|
start: Pos(cur.line, start),
|
|
end: Pos(cur.line, end)
|
|
};
|
|
}
|
|
defineOption('pcre', true, 'boolean');
|
|
function SearchState() {}
|
|
SearchState.prototype = {
|
|
getQuery: function() {
|
|
return vimGlobalState.query;
|
|
},
|
|
setQuery: function(query) {
|
|
vimGlobalState.query = query;
|
|
},
|
|
getOverlay: function() {
|
|
return this.searchOverlay;
|
|
},
|
|
setOverlay: function(overlay) {
|
|
this.searchOverlay = overlay;
|
|
},
|
|
isReversed: function() {
|
|
return vimGlobalState.isReversed;
|
|
},
|
|
setReversed: function(reversed) {
|
|
vimGlobalState.isReversed = reversed;
|
|
},
|
|
getScrollbarAnnotate: function() {
|
|
return this.annotate;
|
|
},
|
|
setScrollbarAnnotate: function(annotate) {
|
|
this.annotate = annotate;
|
|
}
|
|
};
|
|
function getSearchState(cm) {
|
|
var vim = cm.state.vim;
|
|
return vim.searchState_ || (vim.searchState_ = new SearchState());
|
|
}
|
|
function dialog(cm, template, shortText, onClose, options) {
|
|
if (cm.openDialog) {
|
|
cm.openDialog(template, onClose, { bottom: true, value: options.value,
|
|
onKeyDown: options.onKeyDown, onKeyUp: options.onKeyUp,
|
|
selectValueOnOpen: false});
|
|
}
|
|
else {
|
|
onClose(prompt(shortText, ''));
|
|
}
|
|
}
|
|
function splitBySlash(argString) {
|
|
var slashes = findUnescapedSlashes(argString) || [];
|
|
if (!slashes.length) return [];
|
|
var tokens = [];
|
|
if (slashes[0] !== 0) return;
|
|
for (var i = 0; i < slashes.length; i++) {
|
|
if (typeof slashes[i] == 'number')
|
|
tokens.push(argString.substring(slashes[i] + 1, slashes[i+1]));
|
|
}
|
|
return tokens;
|
|
}
|
|
|
|
function findUnescapedSlashes(str) {
|
|
var escapeNextChar = false;
|
|
var slashes = [];
|
|
for (var i = 0; i < str.length; i++) {
|
|
var c = str.charAt(i);
|
|
if (!escapeNextChar && c == '/') {
|
|
slashes.push(i);
|
|
}
|
|
escapeNextChar = !escapeNextChar && (c == '\\');
|
|
}
|
|
return slashes;
|
|
}
|
|
function translateRegex(str) {
|
|
var specials = '|(){';
|
|
var unescape = '}';
|
|
var escapeNextChar = false;
|
|
var out = [];
|
|
for (var i = -1; i < str.length; i++) {
|
|
var c = str.charAt(i) || '';
|
|
var n = str.charAt(i+1) || '';
|
|
var specialComesNext = (n && specials.indexOf(n) != -1);
|
|
if (escapeNextChar) {
|
|
if (c !== '\\' || !specialComesNext) {
|
|
out.push(c);
|
|
}
|
|
escapeNextChar = false;
|
|
} else {
|
|
if (c === '\\') {
|
|
escapeNextChar = true;
|
|
if (n && unescape.indexOf(n) != -1) {
|
|
specialComesNext = true;
|
|
}
|
|
if (!specialComesNext || n === '\\') {
|
|
out.push(c);
|
|
}
|
|
} else {
|
|
out.push(c);
|
|
if (specialComesNext && n !== '\\') {
|
|
out.push('\\');
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return out.join('');
|
|
}
|
|
var charUnescapes = {'\\n': '\n', '\\r': '\r', '\\t': '\t'};
|
|
function translateRegexReplace(str) {
|
|
var escapeNextChar = false;
|
|
var out = [];
|
|
for (var i = -1; i < str.length; i++) {
|
|
var c = str.charAt(i) || '';
|
|
var n = str.charAt(i+1) || '';
|
|
if (charUnescapes[c + n]) {
|
|
out.push(charUnescapes[c+n]);
|
|
i++;
|
|
} else if (escapeNextChar) {
|
|
out.push(c);
|
|
escapeNextChar = false;
|
|
} else {
|
|
if (c === '\\') {
|
|
escapeNextChar = true;
|
|
if ((isNumber(n) || n === '$')) {
|
|
out.push('$');
|
|
} else if (n !== '/' && n !== '\\') {
|
|
out.push('\\');
|
|
}
|
|
} else {
|
|
if (c === '$') {
|
|
out.push('$');
|
|
}
|
|
out.push(c);
|
|
if (n === '/') {
|
|
out.push('\\');
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return out.join('');
|
|
}
|
|
var unescapes = {'\\/': '/', '\\\\': '\\', '\\n': '\n', '\\r': '\r', '\\t': '\t'};
|
|
function unescapeRegexReplace(str) {
|
|
var stream = new CodeMirror.StringStream(str);
|
|
var output = [];
|
|
while (!stream.eol()) {
|
|
while (stream.peek() && stream.peek() != '\\') {
|
|
output.push(stream.next());
|
|
}
|
|
var matched = false;
|
|
for (var matcher in unescapes) {
|
|
if (stream.match(matcher, true)) {
|
|
matched = true;
|
|
output.push(unescapes[matcher]);
|
|
break;
|
|
}
|
|
}
|
|
if (!matched) {
|
|
output.push(stream.next());
|
|
}
|
|
}
|
|
return output.join('');
|
|
}
|
|
function parseQuery(query, ignoreCase, smartCase) {
|
|
var lastSearchRegister = vimGlobalState.registerController.getRegister('/');
|
|
lastSearchRegister.setText(query);
|
|
if (query instanceof RegExp) { return query; }
|
|
var slashes = findUnescapedSlashes(query);
|
|
var regexPart;
|
|
var forceIgnoreCase;
|
|
if (!slashes.length) {
|
|
regexPart = query;
|
|
} else {
|
|
regexPart = query.substring(0, slashes[0]);
|
|
var flagsPart = query.substring(slashes[0]);
|
|
forceIgnoreCase = (flagsPart.indexOf('i') != -1);
|
|
}
|
|
if (!regexPart) {
|
|
return null;
|
|
}
|
|
if (!getOption('pcre')) {
|
|
regexPart = translateRegex(regexPart);
|
|
}
|
|
if (smartCase) {
|
|
ignoreCase = (/^[^A-Z]*$/).test(regexPart);
|
|
}
|
|
var regexp = new RegExp(regexPart,
|
|
(ignoreCase || forceIgnoreCase) ? 'i' : undefined);
|
|
return regexp;
|
|
}
|
|
function showConfirm(cm, text) {
|
|
if (cm.openNotification) {
|
|
cm.openNotification('<span style="color: red">' + text + '</span>',
|
|
{bottom: true, duration: 5000});
|
|
} else {
|
|
alert(text);
|
|
}
|
|
}
|
|
function makePrompt(prefix, desc) {
|
|
var raw = '';
|
|
if (prefix) {
|
|
raw += '<span style="font-family: monospace">' + prefix + '</span>';
|
|
}
|
|
raw += '<input type="text"/> ' +
|
|
'<span style="color: #888">';
|
|
if (desc) {
|
|
raw += '<span style="color: #888">';
|
|
raw += desc;
|
|
raw += '</span>';
|
|
}
|
|
return raw;
|
|
}
|
|
var searchPromptDesc = '(Javascript regexp)';
|
|
function showPrompt(cm, options) {
|
|
var shortText = (options.prefix || '') + ' ' + (options.desc || '');
|
|
var prompt = makePrompt(options.prefix, options.desc);
|
|
dialog(cm, prompt, shortText, options.onClose, options);
|
|
}
|
|
function regexEqual(r1, r2) {
|
|
if (r1 instanceof RegExp && r2 instanceof RegExp) {
|
|
var props = ['global', 'multiline', 'ignoreCase', 'source'];
|
|
for (var i = 0; i < props.length; i++) {
|
|
var prop = props[i];
|
|
if (r1[prop] !== r2[prop]) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
function updateSearchQuery(cm, rawQuery, ignoreCase, smartCase) {
|
|
if (!rawQuery) {
|
|
return;
|
|
}
|
|
var state = getSearchState(cm);
|
|
var query = parseQuery(rawQuery, !!ignoreCase, !!smartCase);
|
|
if (!query) {
|
|
return;
|
|
}
|
|
highlightSearchMatches(cm, query);
|
|
if (regexEqual(query, state.getQuery())) {
|
|
return query;
|
|
}
|
|
state.setQuery(query);
|
|
return query;
|
|
}
|
|
function searchOverlay(query) {
|
|
if (query.source.charAt(0) == '^') {
|
|
var matchSol = true;
|
|
}
|
|
return {
|
|
token: function(stream) {
|
|
if (matchSol && !stream.sol()) {
|
|
stream.skipToEnd();
|
|
return;
|
|
}
|
|
var match = stream.match(query, false);
|
|
if (match) {
|
|
if (match[0].length == 0) {
|
|
stream.next();
|
|
return 'searching';
|
|
}
|
|
if (!stream.sol()) {
|
|
stream.backUp(1);
|
|
if (!query.exec(stream.next() + match[0])) {
|
|
stream.next();
|
|
return null;
|
|
}
|
|
}
|
|
stream.match(query);
|
|
return 'searching';
|
|
}
|
|
while (!stream.eol()) {
|
|
stream.next();
|
|
if (stream.match(query, false)) break;
|
|
}
|
|
},
|
|
query: query
|
|
};
|
|
}
|
|
function highlightSearchMatches(cm, query) {
|
|
var searchState = getSearchState(cm);
|
|
var overlay = searchState.getOverlay();
|
|
if (!overlay || query != overlay.query) {
|
|
if (overlay) {
|
|
cm.removeOverlay(overlay);
|
|
}
|
|
overlay = searchOverlay(query);
|
|
cm.addOverlay(overlay);
|
|
if (cm.showMatchesOnScrollbar) {
|
|
if (searchState.getScrollbarAnnotate()) {
|
|
searchState.getScrollbarAnnotate().clear();
|
|
}
|
|
searchState.setScrollbarAnnotate(cm.showMatchesOnScrollbar(query));
|
|
}
|
|
searchState.setOverlay(overlay);
|
|
}
|
|
}
|
|
function findNext(cm, prev, query, repeat) {
|
|
if (repeat === undefined) { repeat = 1; }
|
|
return cm.operation(function() {
|
|
var pos = cm.getCursor();
|
|
var cursor = cm.getSearchCursor(query, pos);
|
|
for (var i = 0; i < repeat; i++) {
|
|
var found = cursor.find(prev);
|
|
if (i == 0 && found && cursorEqual(cursor.from(), pos)) { found = cursor.find(prev); }
|
|
if (!found) {
|
|
cursor = cm.getSearchCursor(query,
|
|
(prev) ? Pos(cm.lastLine()) : Pos(cm.firstLine(), 0) );
|
|
if (!cursor.find(prev)) {
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
return cursor.from();
|
|
});
|
|
}
|
|
function clearSearchHighlight(cm) {
|
|
var state = getSearchState(cm);
|
|
cm.removeOverlay(getSearchState(cm).getOverlay());
|
|
state.setOverlay(null);
|
|
if (state.getScrollbarAnnotate()) {
|
|
state.getScrollbarAnnotate().clear();
|
|
state.setScrollbarAnnotate(null);
|
|
}
|
|
}
|
|
function isInRange(pos, start, end) {
|
|
if (typeof pos != 'number') {
|
|
pos = pos.line;
|
|
}
|
|
if (start instanceof Array) {
|
|
return inArray(pos, start);
|
|
} else {
|
|
if (end) {
|
|
return (pos >= start && pos <= end);
|
|
} else {
|
|
return pos == start;
|
|
}
|
|
}
|
|
}
|
|
function getUserVisibleLines(cm) {
|
|
var renderer = cm.ace.renderer;
|
|
return {
|
|
top: renderer.getFirstFullyVisibleRow(),
|
|
bottom: renderer.getLastFullyVisibleRow()
|
|
}
|
|
}
|
|
|
|
var ExCommandDispatcher = function() {
|
|
this.buildCommandMap_();
|
|
};
|
|
ExCommandDispatcher.prototype = {
|
|
processCommand: function(cm, input, opt_params) {
|
|
var that = this;
|
|
cm.operation(function () {
|
|
cm.curOp.isVimOp = true;
|
|
that._processCommand(cm, input, opt_params);
|
|
});
|
|
},
|
|
_processCommand: function(cm, input, opt_params) {
|
|
var vim = cm.state.vim;
|
|
var commandHistoryRegister = vimGlobalState.registerController.getRegister(':');
|
|
var previousCommand = commandHistoryRegister.toString();
|
|
if (vim.visualMode) {
|
|
exitVisualMode(cm);
|
|
}
|
|
var inputStream = new CodeMirror.StringStream(input);
|
|
commandHistoryRegister.setText(input);
|
|
var params = opt_params || {};
|
|
params.input = input;
|
|
try {
|
|
this.parseInput_(cm, inputStream, params);
|
|
} catch(e) {
|
|
showConfirm(cm, e);
|
|
throw e;
|
|
}
|
|
var command;
|
|
var commandName;
|
|
if (!params.commandName) {
|
|
if (params.line !== undefined) {
|
|
commandName = 'move';
|
|
}
|
|
} else {
|
|
command = this.matchCommand_(params.commandName);
|
|
if (command) {
|
|
commandName = command.name;
|
|
if (command.excludeFromCommandHistory) {
|
|
commandHistoryRegister.setText(previousCommand);
|
|
}
|
|
this.parseCommandArgs_(inputStream, params, command);
|
|
if (command.type == 'exToKey') {
|
|
for (var i = 0; i < command.toKeys.length; i++) {
|
|
CodeMirror.Vim.handleKey(cm, command.toKeys[i], 'mapping');
|
|
}
|
|
return;
|
|
} else if (command.type == 'exToEx') {
|
|
this.processCommand(cm, command.toInput);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
if (!commandName) {
|
|
showConfirm(cm, 'Not an editor command ":' + input + '"');
|
|
return;
|
|
}
|
|
try {
|
|
exCommands[commandName](cm, params);
|
|
if ((!command || !command.possiblyAsync) && params.callback) {
|
|
params.callback();
|
|
}
|
|
} catch(e) {
|
|
showConfirm(cm, e);
|
|
throw e;
|
|
}
|
|
},
|
|
parseInput_: function(cm, inputStream, result) {
|
|
inputStream.eatWhile(':');
|
|
if (inputStream.eat('%')) {
|
|
result.line = cm.firstLine();
|
|
result.lineEnd = cm.lastLine();
|
|
} else {
|
|
result.line = this.parseLineSpec_(cm, inputStream);
|
|
if (result.line !== undefined && inputStream.eat(',')) {
|
|
result.lineEnd = this.parseLineSpec_(cm, inputStream);
|
|
}
|
|
}
|
|
var commandMatch = inputStream.match(/^(\w+)/);
|
|
if (commandMatch) {
|
|
result.commandName = commandMatch[1];
|
|
} else {
|
|
result.commandName = inputStream.match(/.*/)[0];
|
|
}
|
|
|
|
return result;
|
|
},
|
|
parseLineSpec_: function(cm, inputStream) {
|
|
var numberMatch = inputStream.match(/^(\d+)/);
|
|
if (numberMatch) {
|
|
return parseInt(numberMatch[1], 10) - 1;
|
|
}
|
|
switch (inputStream.next()) {
|
|
case '.':
|
|
return cm.getCursor().line;
|
|
case '$':
|
|
return cm.lastLine();
|
|
case '\'':
|
|
var mark = cm.state.vim.marks[inputStream.next()];
|
|
if (mark && mark.find()) {
|
|
return mark.find().line;
|
|
}
|
|
throw new Error('Mark not set');
|
|
default:
|
|
inputStream.backUp(1);
|
|
return undefined;
|
|
}
|
|
},
|
|
parseCommandArgs_: function(inputStream, params, command) {
|
|
if (inputStream.eol()) {
|
|
return;
|
|
}
|
|
params.argString = inputStream.match(/.*/)[0];
|
|
var delim = command.argDelimiter || /\s+/;
|
|
var args = trim(params.argString).split(delim);
|
|
if (args.length && args[0]) {
|
|
params.args = args;
|
|
}
|
|
},
|
|
matchCommand_: function(commandName) {
|
|
for (var i = commandName.length; i > 0; i--) {
|
|
var prefix = commandName.substring(0, i);
|
|
if (this.commandMap_[prefix]) {
|
|
var command = this.commandMap_[prefix];
|
|
if (command.name.indexOf(commandName) === 0) {
|
|
return command;
|
|
}
|
|
}
|
|
}
|
|
return null;
|
|
},
|
|
buildCommandMap_: function() {
|
|
this.commandMap_ = {};
|
|
for (var i = 0; i < defaultExCommandMap.length; i++) {
|
|
var command = defaultExCommandMap[i];
|
|
var key = command.shortName || command.name;
|
|
this.commandMap_[key] = command;
|
|
}
|
|
},
|
|
map: function(lhs, rhs, ctx) {
|
|
if (lhs != ':' && lhs.charAt(0) == ':') {
|
|
if (ctx) { throw Error('Mode not supported for ex mappings'); }
|
|
var commandName = lhs.substring(1);
|
|
if (rhs != ':' && rhs.charAt(0) == ':') {
|
|
this.commandMap_[commandName] = {
|
|
name: commandName,
|
|
type: 'exToEx',
|
|
toInput: rhs.substring(1),
|
|
user: true
|
|
};
|
|
} else {
|
|
this.commandMap_[commandName] = {
|
|
name: commandName,
|
|
type: 'exToKey',
|
|
toKeys: rhs,
|
|
user: true
|
|
};
|
|
}
|
|
} else {
|
|
if (rhs != ':' && rhs.charAt(0) == ':') {
|
|
var mapping = {
|
|
keys: lhs,
|
|
type: 'keyToEx',
|
|
exArgs: { input: rhs.substring(1) },
|
|
user: true};
|
|
if (ctx) { mapping.context = ctx; }
|
|
defaultKeymap.unshift(mapping);
|
|
} else {
|
|
var mapping = {
|
|
keys: lhs,
|
|
type: 'keyToKey',
|
|
toKeys: rhs,
|
|
user: true
|
|
};
|
|
if (ctx) { mapping.context = ctx; }
|
|
defaultKeymap.unshift(mapping);
|
|
}
|
|
}
|
|
},
|
|
unmap: function(lhs, ctx) {
|
|
if (lhs != ':' && lhs.charAt(0) == ':') {
|
|
if (ctx) { throw Error('Mode not supported for ex mappings'); }
|
|
var commandName = lhs.substring(1);
|
|
if (this.commandMap_[commandName] && this.commandMap_[commandName].user) {
|
|
delete this.commandMap_[commandName];
|
|
return;
|
|
}
|
|
} else {
|
|
var keys = lhs;
|
|
for (var i = 0; i < defaultKeymap.length; i++) {
|
|
if (keys == defaultKeymap[i].keys
|
|
&& defaultKeymap[i].context === ctx
|
|
&& defaultKeymap[i].user) {
|
|
defaultKeymap.splice(i, 1);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
var exCommands = {
|
|
colorscheme: function(cm, params) {
|
|
if (!params.args || params.args.length < 1) {
|
|
showConfirm(cm, cm.getOption('theme'));
|
|
return;
|
|
}
|
|
cm.setOption('theme', params.args[0]);
|
|
},
|
|
map: function(cm, params, ctx) {
|
|
var mapArgs = params.args;
|
|
if (!mapArgs || mapArgs.length < 2) {
|
|
if (cm) {
|
|
showConfirm(cm, 'Invalid mapping: ' + params.input);
|
|
}
|
|
return;
|
|
}
|
|
exCommandDispatcher.map(mapArgs[0], mapArgs[1], ctx);
|
|
},
|
|
imap: function(cm, params) { this.map(cm, params, 'insert'); },
|
|
nmap: function(cm, params) { this.map(cm, params, 'normal'); },
|
|
vmap: function(cm, params) { this.map(cm, params, 'visual'); },
|
|
unmap: function(cm, params, ctx) {
|
|
var mapArgs = params.args;
|
|
if (!mapArgs || mapArgs.length < 1) {
|
|
if (cm) {
|
|
showConfirm(cm, 'No such mapping: ' + params.input);
|
|
}
|
|
return;
|
|
}
|
|
exCommandDispatcher.unmap(mapArgs[0], ctx);
|
|
},
|
|
move: function(cm, params) {
|
|
commandDispatcher.processCommand(cm, cm.state.vim, {
|
|
type: 'motion',
|
|
motion: 'moveToLineOrEdgeOfDocument',
|
|
motionArgs: { forward: false, explicitRepeat: true,
|
|
linewise: true },
|
|
repeatOverride: params.line+1});
|
|
},
|
|
set: function(cm, params) {
|
|
var setArgs = params.args;
|
|
var setCfg = params.setCfg || {};
|
|
if (!setArgs || setArgs.length < 1) {
|
|
if (cm) {
|
|
showConfirm(cm, 'Invalid mapping: ' + params.input);
|
|
}
|
|
return;
|
|
}
|
|
var expr = setArgs[0].split('=');
|
|
var optionName = expr[0];
|
|
var value = expr[1];
|
|
var forceGet = false;
|
|
|
|
if (optionName.charAt(optionName.length - 1) == '?') {
|
|
if (value) { throw Error('Trailing characters: ' + params.argString); }
|
|
optionName = optionName.substring(0, optionName.length - 1);
|
|
forceGet = true;
|
|
}
|
|
if (value === undefined && optionName.substring(0, 2) == 'no') {
|
|
optionName = optionName.substring(2);
|
|
value = false;
|
|
}
|
|
|
|
var optionIsBoolean = options[optionName] && options[optionName].type == 'boolean';
|
|
if (optionIsBoolean && value == undefined) {
|
|
value = true;
|
|
}
|
|
if (!optionIsBoolean && value === undefined || forceGet) {
|
|
var oldValue = getOption(optionName, cm, setCfg);
|
|
if (oldValue === true || oldValue === false) {
|
|
showConfirm(cm, ' ' + (oldValue ? '' : 'no') + optionName);
|
|
} else {
|
|
showConfirm(cm, ' ' + optionName + '=' + oldValue);
|
|
}
|
|
} else {
|
|
setOption(optionName, value, cm, setCfg);
|
|
}
|
|
},
|
|
setlocal: function (cm, params) {
|
|
params.setCfg = {scope: 'local'};
|
|
this.set(cm, params);
|
|
},
|
|
setglobal: function (cm, params) {
|
|
params.setCfg = {scope: 'global'};
|
|
this.set(cm, params);
|
|
},
|
|
registers: function(cm, params) {
|
|
var regArgs = params.args;
|
|
var registers = vimGlobalState.registerController.registers;
|
|
var regInfo = '----------Registers----------<br><br>';
|
|
if (!regArgs) {
|
|
for (var registerName in registers) {
|
|
var text = registers[registerName].toString();
|
|
if (text.length) {
|
|
regInfo += '"' + registerName + ' ' + text + '<br>';
|
|
}
|
|
}
|
|
} else {
|
|
var registerName;
|
|
regArgs = regArgs.join('');
|
|
for (var i = 0; i < regArgs.length; i++) {
|
|
registerName = regArgs.charAt(i);
|
|
if (!vimGlobalState.registerController.isValidRegister(registerName)) {
|
|
continue;
|
|
}
|
|
var register = registers[registerName] || new Register();
|
|
regInfo += '"' + registerName + ' ' + register.toString() + '<br>';
|
|
}
|
|
}
|
|
showConfirm(cm, regInfo);
|
|
},
|
|
sort: function(cm, params) {
|
|
var reverse, ignoreCase, unique, number;
|
|
function parseArgs() {
|
|
if (params.argString) {
|
|
var args = new CodeMirror.StringStream(params.argString);
|
|
if (args.eat('!')) { reverse = true; }
|
|
if (args.eol()) { return; }
|
|
if (!args.eatSpace()) { return 'Invalid arguments'; }
|
|
var opts = args.match(/[a-z]+/);
|
|
if (opts) {
|
|
opts = opts[0];
|
|
ignoreCase = opts.indexOf('i') != -1;
|
|
unique = opts.indexOf('u') != -1;
|
|
var decimal = opts.indexOf('d') != -1 && 1;
|
|
var hex = opts.indexOf('x') != -1 && 1;
|
|
var octal = opts.indexOf('o') != -1 && 1;
|
|
if (decimal + hex + octal > 1) { return 'Invalid arguments'; }
|
|
number = decimal && 'decimal' || hex && 'hex' || octal && 'octal';
|
|
}
|
|
if (args.match(/\/.*\//)) { return 'patterns not supported'; }
|
|
}
|
|
}
|
|
var err = parseArgs();
|
|
if (err) {
|
|
showConfirm(cm, err + ': ' + params.argString);
|
|
return;
|
|
}
|
|
var lineStart = params.line || cm.firstLine();
|
|
var lineEnd = params.lineEnd || params.line || cm.lastLine();
|
|
if (lineStart == lineEnd) { return; }
|
|
var curStart = Pos(lineStart, 0);
|
|
var curEnd = Pos(lineEnd, lineLength(cm, lineEnd));
|
|
var text = cm.getRange(curStart, curEnd).split('\n');
|
|
var numberRegex = (number == 'decimal') ? /(-?)([\d]+)/ :
|
|
(number == 'hex') ? /(-?)(?:0x)?([0-9a-f]+)/i :
|
|
(number == 'octal') ? /([0-7]+)/ : null;
|
|
var radix = (number == 'decimal') ? 10 : (number == 'hex') ? 16 : (number == 'octal') ? 8 : null;
|
|
var numPart = [], textPart = [];
|
|
if (number) {
|
|
for (var i = 0; i < text.length; i++) {
|
|
if (numberRegex.exec(text[i])) {
|
|
numPart.push(text[i]);
|
|
} else {
|
|
textPart.push(text[i]);
|
|
}
|
|
}
|
|
} else {
|
|
textPart = text;
|
|
}
|
|
function compareFn(a, b) {
|
|
if (reverse) { var tmp; tmp = a; a = b; b = tmp; }
|
|
if (ignoreCase) { a = a.toLowerCase(); b = b.toLowerCase(); }
|
|
var anum = number && numberRegex.exec(a);
|
|
var bnum = number && numberRegex.exec(b);
|
|
if (!anum) { return a < b ? -1 : 1; }
|
|
anum = parseInt((anum[1] + anum[2]).toLowerCase(), radix);
|
|
bnum = parseInt((bnum[1] + bnum[2]).toLowerCase(), radix);
|
|
return anum - bnum;
|
|
}
|
|
numPart.sort(compareFn);
|
|
textPart.sort(compareFn);
|
|
text = (!reverse) ? textPart.concat(numPart) : numPart.concat(textPart);
|
|
if (unique) { // Remove duplicate lines
|
|
var textOld = text;
|
|
var lastLine;
|
|
text = [];
|
|
for (var i = 0; i < textOld.length; i++) {
|
|
if (textOld[i] != lastLine) {
|
|
text.push(textOld[i]);
|
|
}
|
|
lastLine = textOld[i];
|
|
}
|
|
}
|
|
cm.replaceRange(text.join('\n'), curStart, curEnd);
|
|
},
|
|
global: function(cm, params) {
|
|
var argString = params.argString;
|
|
if (!argString) {
|
|
showConfirm(cm, 'Regular Expression missing from global');
|
|
return;
|
|
}
|
|
var lineStart = (params.line !== undefined) ? params.line : cm.firstLine();
|
|
var lineEnd = params.lineEnd || params.line || cm.lastLine();
|
|
var tokens = splitBySlash(argString);
|
|
var regexPart = argString, cmd;
|
|
if (tokens.length) {
|
|
regexPart = tokens[0];
|
|
cmd = tokens.slice(1, tokens.length).join('/');
|
|
}
|
|
if (regexPart) {
|
|
try {
|
|
updateSearchQuery(cm, regexPart, true /** ignoreCase */,
|
|
true /** smartCase */);
|
|
} catch (e) {
|
|
showConfirm(cm, 'Invalid regex: ' + regexPart);
|
|
return;
|
|
}
|
|
}
|
|
var query = getSearchState(cm).getQuery();
|
|
var matchedLines = [], content = '';
|
|
for (var i = lineStart; i <= lineEnd; i++) {
|
|
var matched = query.test(cm.getLine(i));
|
|
if (matched) {
|
|
matchedLines.push(i+1);
|
|
content+= cm.getLine(i) + '<br>';
|
|
}
|
|
}
|
|
if (!cmd) {
|
|
showConfirm(cm, content);
|
|
return;
|
|
}
|
|
var index = 0;
|
|
var nextCommand = function() {
|
|
if (index < matchedLines.length) {
|
|
var command = matchedLines[index] + cmd;
|
|
exCommandDispatcher.processCommand(cm, command, {
|
|
callback: nextCommand
|
|
});
|
|
}
|
|
index++;
|
|
};
|
|
nextCommand();
|
|
},
|
|
substitute: function(cm, params) {
|
|
if (!cm.getSearchCursor) {
|
|
throw new Error('Search feature not available. Requires searchcursor.js or ' +
|
|
'any other getSearchCursor implementation.');
|
|
}
|
|
var argString = params.argString;
|
|
var tokens = argString ? splitBySlash(argString) : [];
|
|
var regexPart, replacePart = '', trailing, flagsPart, count;
|
|
var confirm = false; // Whether to confirm each replace.
|
|
var global = false; // True to replace all instances on a line, false to replace only 1.
|
|
if (tokens.length) {
|
|
regexPart = tokens[0];
|
|
replacePart = tokens[1];
|
|
if (replacePart !== undefined) {
|
|
if (getOption('pcre')) {
|
|
replacePart = unescapeRegexReplace(replacePart);
|
|
} else {
|
|
replacePart = translateRegexReplace(replacePart);
|
|
}
|
|
vimGlobalState.lastSubstituteReplacePart = replacePart;
|
|
}
|
|
trailing = tokens[2] ? tokens[2].split(' ') : [];
|
|
} else {
|
|
if (argString && argString.length) {
|
|
showConfirm(cm, 'Substitutions should be of the form ' +
|
|
':s/pattern/replace/');
|
|
return;
|
|
}
|
|
}
|
|
if (trailing) {
|
|
flagsPart = trailing[0];
|
|
count = parseInt(trailing[1]);
|
|
if (flagsPart) {
|
|
if (flagsPart.indexOf('c') != -1) {
|
|
confirm = true;
|
|
flagsPart.replace('c', '');
|
|
}
|
|
if (flagsPart.indexOf('g') != -1) {
|
|
global = true;
|
|
flagsPart.replace('g', '');
|
|
}
|
|
regexPart = regexPart + '/' + flagsPart;
|
|
}
|
|
}
|
|
if (regexPart) {
|
|
try {
|
|
updateSearchQuery(cm, regexPart, true /** ignoreCase */,
|
|
true /** smartCase */);
|
|
} catch (e) {
|
|
showConfirm(cm, 'Invalid regex: ' + regexPart);
|
|
return;
|
|
}
|
|
}
|
|
replacePart = replacePart || vimGlobalState.lastSubstituteReplacePart;
|
|
if (replacePart === undefined) {
|
|
showConfirm(cm, 'No previous substitute regular expression');
|
|
return;
|
|
}
|
|
var state = getSearchState(cm);
|
|
var query = state.getQuery();
|
|
var lineStart = (params.line !== undefined) ? params.line : cm.getCursor().line;
|
|
var lineEnd = params.lineEnd || lineStart;
|
|
if (lineStart == cm.firstLine() && lineEnd == cm.lastLine()) {
|
|
lineEnd = Infinity;
|
|
}
|
|
if (count) {
|
|
lineStart = lineEnd;
|
|
lineEnd = lineStart + count - 1;
|
|
}
|
|
var startPos = clipCursorToContent(cm, Pos(lineStart, 0));
|
|
var cursor = cm.getSearchCursor(query, startPos);
|
|
doReplace(cm, confirm, global, lineStart, lineEnd, cursor, query, replacePart, params.callback);
|
|
},
|
|
redo: CodeMirror.commands.redo,
|
|
undo: CodeMirror.commands.undo,
|
|
write: function(cm) {
|
|
if (CodeMirror.commands.save) {
|
|
CodeMirror.commands.save(cm);
|
|
} else {
|
|
cm.save();
|
|
}
|
|
},
|
|
nohlsearch: function(cm) {
|
|
clearSearchHighlight(cm);
|
|
},
|
|
delmarks: function(cm, params) {
|
|
if (!params.argString || !trim(params.argString)) {
|
|
showConfirm(cm, 'Argument required');
|
|
return;
|
|
}
|
|
|
|
var state = cm.state.vim;
|
|
var stream = new CodeMirror.StringStream(trim(params.argString));
|
|
while (!stream.eol()) {
|
|
stream.eatSpace();
|
|
var count = stream.pos;
|
|
|
|
if (!stream.match(/[a-zA-Z]/, false)) {
|
|
showConfirm(cm, 'Invalid argument: ' + params.argString.substring(count));
|
|
return;
|
|
}
|
|
|
|
var sym = stream.next();
|
|
if (stream.match('-', true)) {
|
|
if (!stream.match(/[a-zA-Z]/, false)) {
|
|
showConfirm(cm, 'Invalid argument: ' + params.argString.substring(count));
|
|
return;
|
|
}
|
|
|
|
var startMark = sym;
|
|
var finishMark = stream.next();
|
|
if (isLowerCase(startMark) && isLowerCase(finishMark) ||
|
|
isUpperCase(startMark) && isUpperCase(finishMark)) {
|
|
var start = startMark.charCodeAt(0);
|
|
var finish = finishMark.charCodeAt(0);
|
|
if (start >= finish) {
|
|
showConfirm(cm, 'Invalid argument: ' + params.argString.substring(count));
|
|
return;
|
|
}
|
|
for (var j = 0; j <= finish - start; j++) {
|
|
var mark = String.fromCharCode(start + j);
|
|
delete state.marks[mark];
|
|
}
|
|
} else {
|
|
showConfirm(cm, 'Invalid argument: ' + startMark + '-');
|
|
return;
|
|
}
|
|
} else {
|
|
delete state.marks[sym];
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
var exCommandDispatcher = new ExCommandDispatcher();
|
|
function doReplace(cm, confirm, global, lineStart, lineEnd, searchCursor, query,
|
|
replaceWith, callback) {
|
|
cm.state.vim.exMode = true;
|
|
var done = false;
|
|
var lastPos = searchCursor.from();
|
|
function replaceAll() {
|
|
cm.operation(function() {
|
|
while (!done) {
|
|
replace();
|
|
next();
|
|
}
|
|
stop();
|
|
});
|
|
}
|
|
function replace() {
|
|
var text = cm.getRange(searchCursor.from(), searchCursor.to());
|
|
var newText = text.replace(query, replaceWith);
|
|
searchCursor.replace(newText);
|
|
}
|
|
function next() {
|
|
while(searchCursor.findNext() &&
|
|
isInRange(searchCursor.from(), lineStart, lineEnd)) {
|
|
if (!global && lastPos && searchCursor.from().line == lastPos.line) {
|
|
continue;
|
|
}
|
|
cm.scrollIntoView(searchCursor.from(), 30);
|
|
cm.setSelection(searchCursor.from(), searchCursor.to());
|
|
lastPos = searchCursor.from();
|
|
done = false;
|
|
return;
|
|
}
|
|
done = true;
|
|
}
|
|
function stop(close) {
|
|
if (close) { close(); }
|
|
cm.focus();
|
|
if (lastPos) {
|
|
cm.setCursor(lastPos);
|
|
var vim = cm.state.vim;
|
|
vim.exMode = false;
|
|
vim.lastHPos = vim.lastHSPos = lastPos.ch;
|
|
}
|
|
if (callback) { callback(); }
|
|
}
|
|
function onPromptKeyDown(e, _value, close) {
|
|
CodeMirror.e_stop(e);
|
|
var keyName = CodeMirror.keyName(e);
|
|
switch (keyName) {
|
|
case 'Y':
|
|
replace(); next(); break;
|
|
case 'N':
|
|
next(); break;
|
|
case 'A':
|
|
var savedCallback = callback;
|
|
callback = undefined;
|
|
cm.operation(replaceAll);
|
|
callback = savedCallback;
|
|
break;
|
|
case 'L':
|
|
replace();
|
|
case 'Q':
|
|
case 'Esc':
|
|
case 'Ctrl-C':
|
|
case 'Ctrl-[':
|
|
stop(close);
|
|
break;
|
|
}
|
|
if (done) { stop(close); }
|
|
return true;
|
|
}
|
|
next();
|
|
if (done) {
|
|
showConfirm(cm, 'No matches for ' + query.source);
|
|
return;
|
|
}
|
|
if (!confirm) {
|
|
replaceAll();
|
|
if (callback) { callback(); }
|
|
return;
|
|
}
|
|
showPrompt(cm, {
|
|
prefix: 'replace with <strong>' + replaceWith + '</strong> (y/n/a/q/l)',
|
|
onKeyDown: onPromptKeyDown
|
|
});
|
|
}
|
|
|
|
CodeMirror.keyMap.vim = {
|
|
attach: attachVimMap,
|
|
detach: detachVimMap,
|
|
call: cmKey
|
|
};
|
|
|
|
function exitInsertMode(cm) {
|
|
var vim = cm.state.vim;
|
|
var macroModeState = vimGlobalState.macroModeState;
|
|
var insertModeChangeRegister = vimGlobalState.registerController.getRegister('.');
|
|
var isPlaying = macroModeState.isPlaying;
|
|
var lastChange = macroModeState.lastInsertModeChanges;
|
|
var text = [];
|
|
if (!isPlaying) {
|
|
var selLength = lastChange.inVisualBlock ? vim.lastSelection.visualBlock.height : 1;
|
|
var changes = lastChange.changes;
|
|
var text = [];
|
|
var i = 0;
|
|
while (i < changes.length) {
|
|
text.push(changes[i]);
|
|
if (changes[i] instanceof InsertModeKey) {
|
|
i++;
|
|
} else {
|
|
i+= selLength;
|
|
}
|
|
}
|
|
lastChange.changes = text;
|
|
cm.off('change', onChange);
|
|
CodeMirror.off(cm.getInputField(), 'keydown', onKeyEventTargetKeyDown);
|
|
}
|
|
if (!isPlaying && vim.insertModeRepeat > 1) {
|
|
repeatLastEdit(cm, vim, vim.insertModeRepeat - 1,
|
|
true /** repeatForInsert */);
|
|
vim.lastEditInputState.repeatOverride = vim.insertModeRepeat;
|
|
}
|
|
delete vim.insertModeRepeat;
|
|
vim.insertMode = false;
|
|
cm.setCursor(cm.getCursor().line, cm.getCursor().ch-1);
|
|
cm.setOption('keyMap', 'vim');
|
|
cm.setOption('disableInput', true);
|
|
|
|
lastChange.overwrite = cm.state.overwrite;
|
|
cm.toggleOverwrite(false); // exit replace mode if we were in it.
|
|
insertModeChangeRegister.setText(lastChange.changes.join(''));
|
|
CodeMirror.signal(cm, "vim-mode-change", {mode: "normal"});
|
|
if (macroModeState.isRecording) {
|
|
logInsertModeChange(macroModeState);
|
|
}
|
|
}
|
|
|
|
function _mapCommand(command) {
|
|
defaultKeymap.unshift(command);
|
|
}
|
|
|
|
function mapCommand(keys, type, name, args, extra) {
|
|
var command = {keys: keys, type: type};
|
|
command[type] = name;
|
|
command[type + "Args"] = args;
|
|
for (var key in extra)
|
|
command[key] = extra[key];
|
|
_mapCommand(command);
|
|
}
|
|
defineOption('insertModeEscKeysTimeout', 200, 'number');
|
|
|
|
CodeMirror.keyMap['vim-insert'] = {
|
|
'Ctrl-N': 'autocomplete',
|
|
'Ctrl-P': 'autocomplete',
|
|
'Enter': function(cm) {
|
|
var fn = CodeMirror.commands.newlineAndIndentContinueComment ||
|
|
CodeMirror.commands.newlineAndIndent;
|
|
fn(cm);
|
|
},
|
|
fallthrough: ['default'],
|
|
attach: attachVimMap,
|
|
detach: detachVimMap,
|
|
call: cmKey
|
|
};
|
|
|
|
CodeMirror.keyMap['vim-replace'] = {
|
|
'Backspace': 'goCharLeft',
|
|
fallthrough: ['vim-insert'],
|
|
attach: attachVimMap,
|
|
detach: detachVimMap,
|
|
call: cmKey
|
|
};
|
|
|
|
function executeMacroRegister(cm, vim, macroModeState, registerName) {
|
|
var register = vimGlobalState.registerController.getRegister(registerName);
|
|
if (registerName == ':') {
|
|
if (register.keyBuffer[0]) {
|
|
exCommandDispatcher.processCommand(cm, register.keyBuffer[0]);
|
|
}
|
|
macroModeState.isPlaying = false;
|
|
return;
|
|
}
|
|
var keyBuffer = register.keyBuffer;
|
|
var imc = 0;
|
|
macroModeState.isPlaying = true;
|
|
macroModeState.replaySearchQueries = register.searchQueries.slice(0);
|
|
for (var i = 0; i < keyBuffer.length; i++) {
|
|
var text = keyBuffer[i];
|
|
var match, key;
|
|
while (text) {
|
|
match = (/<\w+-.+?>|<\w+>|./).exec(text);
|
|
key = match[0];
|
|
text = text.substring(match.index + key.length);
|
|
CodeMirror.Vim.handleKey(cm, key, 'macro');
|
|
if (vim.insertMode) {
|
|
var changes = register.insertModeChanges[imc++].changes;
|
|
vimGlobalState.macroModeState.lastInsertModeChanges.changes =
|
|
changes;
|
|
repeatInsertModeChanges(cm, changes, 1);
|
|
exitInsertMode(cm);
|
|
}
|
|
}
|
|
}
|
|
macroModeState.isPlaying = false;
|
|
}
|
|
|
|
function logKey(macroModeState, key) {
|
|
if (macroModeState.isPlaying) { return; }
|
|
var registerName = macroModeState.latestRegister;
|
|
var register = vimGlobalState.registerController.getRegister(registerName);
|
|
if (register) {
|
|
register.pushText(key);
|
|
}
|
|
}
|
|
|
|
function logInsertModeChange(macroModeState) {
|
|
if (macroModeState.isPlaying) { return; }
|
|
var registerName = macroModeState.latestRegister;
|
|
var register = vimGlobalState.registerController.getRegister(registerName);
|
|
if (register && register.pushInsertModeChanges) {
|
|
register.pushInsertModeChanges(macroModeState.lastInsertModeChanges);
|
|
}
|
|
}
|
|
|
|
function logSearchQuery(macroModeState, query) {
|
|
if (macroModeState.isPlaying) { return; }
|
|
var registerName = macroModeState.latestRegister;
|
|
var register = vimGlobalState.registerController.getRegister(registerName);
|
|
if (register && register.pushSearchQuery) {
|
|
register.pushSearchQuery(query);
|
|
}
|
|
}
|
|
function onChange(_cm, changeObj) {
|
|
var macroModeState = vimGlobalState.macroModeState;
|
|
var lastChange = macroModeState.lastInsertModeChanges;
|
|
if (!macroModeState.isPlaying) {
|
|
while(changeObj) {
|
|
lastChange.expectCursorActivityForChange = true;
|
|
if (changeObj.origin == '+input' || changeObj.origin == 'paste'
|
|
|| changeObj.origin === undefined /* only in testing */) {
|
|
var text = changeObj.text.join('\n');
|
|
if (lastChange.maybeReset) {
|
|
lastChange.changes = [];
|
|
lastChange.maybeReset = false;
|
|
}
|
|
lastChange.changes.push(text);
|
|
}
|
|
changeObj = changeObj.next;
|
|
}
|
|
}
|
|
}
|
|
function onCursorActivity(cm) {
|
|
var vim = cm.state.vim;
|
|
if (vim.insertMode) {
|
|
var macroModeState = vimGlobalState.macroModeState;
|
|
if (macroModeState.isPlaying) { return; }
|
|
var lastChange = macroModeState.lastInsertModeChanges;
|
|
if (lastChange.expectCursorActivityForChange) {
|
|
lastChange.expectCursorActivityForChange = false;
|
|
} else {
|
|
lastChange.maybeReset = true;
|
|
}
|
|
} else if (!cm.curOp.isVimOp) {
|
|
handleExternalSelection(cm, vim);
|
|
}
|
|
if (vim.visualMode) {
|
|
updateFakeCursor(cm);
|
|
}
|
|
}
|
|
function updateFakeCursor(cm) {
|
|
var vim = cm.state.vim;
|
|
var from = clipCursorToContent(cm, copyCursor(vim.sel.head));
|
|
var to = offsetCursor(from, 0, 1);
|
|
if (vim.fakeCursor) {
|
|
vim.fakeCursor.clear();
|
|
}
|
|
vim.fakeCursor = cm.markText(from, to, {className: 'cm-animate-fat-cursor'});
|
|
}
|
|
function handleExternalSelection(cm, vim) {
|
|
var anchor = cm.getCursor('anchor');
|
|
var head = cm.getCursor('head');
|
|
if (vim.visualMode && !cm.somethingSelected()) {
|
|
exitVisualMode(cm, false);
|
|
} else if (!vim.visualMode && !vim.insertMode && cm.somethingSelected()) {
|
|
vim.visualMode = true;
|
|
vim.visualLine = false;
|
|
CodeMirror.signal(cm, "vim-mode-change", {mode: "visual"});
|
|
}
|
|
if (vim.visualMode) {
|
|
var headOffset = !cursorIsBefore(head, anchor) ? -1 : 0;
|
|
var anchorOffset = cursorIsBefore(head, anchor) ? -1 : 0;
|
|
head = offsetCursor(head, 0, headOffset);
|
|
anchor = offsetCursor(anchor, 0, anchorOffset);
|
|
vim.sel = {
|
|
anchor: anchor,
|
|
head: head
|
|
};
|
|
updateMark(cm, vim, '<', cursorMin(head, anchor));
|
|
updateMark(cm, vim, '>', cursorMax(head, anchor));
|
|
} else if (!vim.insertMode) {
|
|
vim.lastHPos = cm.getCursor().ch;
|
|
}
|
|
}
|
|
function InsertModeKey(keyName) {
|
|
this.keyName = keyName;
|
|
}
|
|
function onKeyEventTargetKeyDown(e) {
|
|
var macroModeState = vimGlobalState.macroModeState;
|
|
var lastChange = macroModeState.lastInsertModeChanges;
|
|
var keyName = CodeMirror.keyName(e);
|
|
if (!keyName) { return; }
|
|
function onKeyFound() {
|
|
if (lastChange.maybeReset) {
|
|
lastChange.changes = [];
|
|
lastChange.maybeReset = false;
|
|
}
|
|
lastChange.changes.push(new InsertModeKey(keyName));
|
|
return true;
|
|
}
|
|
if (keyName.indexOf('Delete') != -1 || keyName.indexOf('Backspace') != -1) {
|
|
CodeMirror.lookupKey(keyName, 'vim-insert', onKeyFound);
|
|
}
|
|
}
|
|
function repeatLastEdit(cm, vim, repeat, repeatForInsert) {
|
|
var macroModeState = vimGlobalState.macroModeState;
|
|
macroModeState.isPlaying = true;
|
|
var isAction = !!vim.lastEditActionCommand;
|
|
var cachedInputState = vim.inputState;
|
|
function repeatCommand() {
|
|
if (isAction) {
|
|
commandDispatcher.processAction(cm, vim, vim.lastEditActionCommand);
|
|
} else {
|
|
commandDispatcher.evalInput(cm, vim);
|
|
}
|
|
}
|
|
function repeatInsert(repeat) {
|
|
if (macroModeState.lastInsertModeChanges.changes.length > 0) {
|
|
repeat = !vim.lastEditActionCommand ? 1 : repeat;
|
|
var changeObject = macroModeState.lastInsertModeChanges;
|
|
repeatInsertModeChanges(cm, changeObject.changes, repeat, changeObject.overwrite);
|
|
}
|
|
}
|
|
vim.inputState = vim.lastEditInputState;
|
|
if (isAction && vim.lastEditActionCommand.interlaceInsertRepeat) {
|
|
for (var i = 0; i < repeat; i++) {
|
|
repeatCommand();
|
|
repeatInsert(1);
|
|
}
|
|
} else {
|
|
if (!repeatForInsert) {
|
|
repeatCommand();
|
|
}
|
|
repeatInsert(repeat);
|
|
}
|
|
vim.inputState = cachedInputState;
|
|
if (vim.insertMode && !repeatForInsert) {
|
|
exitInsertMode(cm);
|
|
}
|
|
macroModeState.isPlaying = false;
|
|
}
|
|
|
|
function repeatInsertModeChanges(cm, changes, repeat, overwrite) {
|
|
function keyHandler(binding) {
|
|
if (typeof binding == 'string') {
|
|
CodeMirror.commands[binding](cm);
|
|
} else {
|
|
binding(cm);
|
|
}
|
|
return true;
|
|
}
|
|
var head = cm.getCursor('head');
|
|
var inVisualBlock = vimGlobalState.macroModeState.lastInsertModeChanges.inVisualBlock;
|
|
if (inVisualBlock) {
|
|
var vim = cm.state.vim;
|
|
var lastSel = vim.lastSelection;
|
|
var offset = getOffset(lastSel.anchor, lastSel.head);
|
|
selectForInsert(cm, head, offset.line + 1);
|
|
repeat = cm.listSelections().length;
|
|
cm.setCursor(head);
|
|
}
|
|
for (var i = 0; i < repeat; i++) {
|
|
if (inVisualBlock) {
|
|
cm.setCursor(offsetCursor(head, i, 0));
|
|
}
|
|
for (var j = 0; j < changes.length; j++) {
|
|
var change = changes[j];
|
|
if (change instanceof InsertModeKey) {
|
|
CodeMirror.lookupKey(change.keyName, 'vim-insert', keyHandler);
|
|
} else {
|
|
var cur = cm.getCursor();
|
|
var end = cur;
|
|
if (overwrite && !/\n/.test(change)) {
|
|
end = offsetCursor(cur, 0, change.length);
|
|
}
|
|
cm.replaceRange(change, cur, end);
|
|
}
|
|
}
|
|
}
|
|
if (inVisualBlock) {
|
|
cm.setCursor(offsetCursor(head, 0, 1));
|
|
}
|
|
}
|
|
|
|
resetVimGlobalState();
|
|
CodeMirror.Vim = Vim();
|
|
|
|
Vim = CodeMirror.Vim;
|
|
|
|
var specialKey = {'return':'CR',backspace:'BS','delete':'Del',esc:'Esc',
|
|
left:'Left',right:'Right',up:'Up',down:'Down',space: 'Space',
|
|
home:'Home',end:'End',pageup:'PageUp',pagedown:'PageDown', enter: 'CR'
|
|
};
|
|
function lookupKey(hashId, key, e) {
|
|
if (key.length > 1 && key[0] == "n") {
|
|
key = key.replace("numpad", "");
|
|
}
|
|
key = specialKey[key] || key;
|
|
var name = '';
|
|
if (e.ctrlKey) { name += 'C-'; }
|
|
if (e.altKey) { name += 'A-'; }
|
|
if (e.shiftKey) { name += 'S-'; }
|
|
|
|
name += key;
|
|
if (name.length > 1) { name = '<' + name + '>'; }
|
|
return name;
|
|
}
|
|
var handleKey = Vim.handleKey.bind(Vim);
|
|
Vim.handleKey = function(cm, key, origin) {
|
|
return cm.operation(function() {
|
|
return handleKey(cm, key, origin);
|
|
}, true);
|
|
}
|
|
function cloneVimState(state) {
|
|
var n = new state.constructor();
|
|
Object.keys(state).forEach(function(key) {
|
|
var o = state[key];
|
|
if (Array.isArray(o))
|
|
o = o.slice();
|
|
else if (o && typeof o == "object" && o.constructor != Object)
|
|
o = cloneVimState(o);
|
|
n[key] = o;
|
|
});
|
|
if (state.sel) {
|
|
n.sel = {
|
|
head: state.sel.head && copyCursor(state.sel.head),
|
|
anchor: state.sel.anchor && copyCursor(state.sel.anchor)
|
|
};
|
|
}
|
|
return n;
|
|
}
|
|
function multiSelectHandleKey(cm, key, origin) {
|
|
var isHandled = false;
|
|
var vim = Vim.maybeInitVimState_(cm);
|
|
var visualBlock = vim.visualBlock || vim.wasInVisualBlock;
|
|
if (vim.wasInVisualBlock && !cm.ace.inMultiSelectMode) {
|
|
vim.wasInVisualBlock = false;
|
|
} else if (cm.ace.inMultiSelectMode && vim.visualBlock) {
|
|
vim.wasInVisualBlock = true;
|
|
}
|
|
|
|
if (key == '<Esc>' && !vim.insertMode && !vim.visualMode && cm.ace.inMultiSelectMode) {
|
|
cm.ace.exitMultiSelectMode();
|
|
} else if (visualBlock || !cm.ace.inMultiSelectMode || cm.ace.inVirtualSelectionMode) {
|
|
isHandled = Vim.handleKey(cm, key, origin);
|
|
} else {
|
|
var old = cloneVimState(vim);
|
|
cm.operation(function() {
|
|
cm.ace.forEachSelection(function() {
|
|
var sel = cm.ace.selection;
|
|
cm.state.vim.lastHPos = sel.$desiredColumn == null ? sel.lead.column : sel.$desiredColumn;
|
|
var head = cm.getCursor("head");
|
|
var anchor = cm.getCursor("anchor");
|
|
var headOffset = !cursorIsBefore(head, anchor) ? -1 : 0;
|
|
var anchorOffset = cursorIsBefore(head, anchor) ? -1 : 0;
|
|
head = offsetCursor(head, 0, headOffset);
|
|
anchor = offsetCursor(anchor, 0, anchorOffset);
|
|
cm.state.vim.sel.head = head;
|
|
cm.state.vim.sel.anchor = anchor;
|
|
|
|
isHandled = handleKey(cm, key, origin);
|
|
sel.$desiredColumn = cm.state.vim.lastHPos == -1 ? null : cm.state.vim.lastHPos;
|
|
if (cm.virtualSelectionMode()) {
|
|
cm.state.vim = cloneVimState(old);
|
|
}
|
|
});
|
|
if (cm.curOp.cursorActivity && !isHandled)
|
|
cm.curOp.cursorActivity = false;
|
|
}, true);
|
|
}
|
|
return isHandled;
|
|
}
|
|
exports.CodeMirror = CodeMirror;
|
|
var getVim = Vim.maybeInitVimState_;
|
|
exports.handler = {
|
|
$id: "ace/keyboard/vim",
|
|
drawCursor: function(style, pixelPos, config, sel, session) {
|
|
var vim = this.state.vim || {};
|
|
var w = config.characterWidth;
|
|
var h = config.lineHeight;
|
|
var top = pixelPos.top;
|
|
var left = pixelPos.left;
|
|
if (!vim.insertMode) {
|
|
var isbackwards = !sel.cursor
|
|
? session.selection.isBackwards() || session.selection.isEmpty()
|
|
: Range.comparePoints(sel.cursor, sel.start) <= 0;
|
|
if (!isbackwards && left > w)
|
|
left -= w;
|
|
}
|
|
if (!vim.insertMode && vim.status) {
|
|
h = h / 2;
|
|
top += h;
|
|
}
|
|
style.left = left + "px";
|
|
style.top = top + "px";
|
|
style.width = w + "px";
|
|
style.height = h + "px";
|
|
},
|
|
handleKeyboard: function(data, hashId, key, keyCode, e) {
|
|
var editor = data.editor;
|
|
var cm = editor.state.cm;
|
|
var vim = getVim(cm);
|
|
if (keyCode == -1) return;
|
|
|
|
if (key == "c" && hashId == 1) { // key == "ctrl-c"
|
|
if (!useragent.isMac && editor.getCopyText()) {
|
|
editor.once("copy", function() {
|
|
editor.selection.clearSelection();
|
|
});
|
|
return {command: "null", passEvent: true};
|
|
}
|
|
} else if (!vim.insertMode) {
|
|
if (useragent.isMac && this.handleMacRepeat(data, hashId, key)) {
|
|
hashId = -1;
|
|
key = data.inputChar;
|
|
}
|
|
}
|
|
|
|
if (hashId == -1 || hashId & 1 || hashId === 0 && key.length > 1) {
|
|
var insertMode = vim.insertMode;
|
|
var name = lookupKey(hashId, key, e || {});
|
|
if (vim.status == null)
|
|
vim.status = "";
|
|
var isHandled = multiSelectHandleKey(cm, name, 'user');
|
|
vim = getVim(cm); // may be changed by multiSelectHandleKey
|
|
if (isHandled && vim.status != null)
|
|
vim.status += name;
|
|
else if (vim.status == null)
|
|
vim.status = "";
|
|
cm._signal("changeStatus");
|
|
if (!isHandled && (hashId != -1 || insertMode))
|
|
return;
|
|
return {command: "null", passEvent: !isHandled};
|
|
}
|
|
},
|
|
attach: function(editor) {
|
|
if (!editor.state) editor.state = {};
|
|
var cm = new CodeMirror(editor);
|
|
editor.state.cm = cm;
|
|
editor.$vimModeHandler = this;
|
|
CodeMirror.keyMap.vim.attach(cm);
|
|
getVim(cm).status = null;
|
|
cm.on('vim-command-done', function() {
|
|
if (cm.virtualSelectionMode()) return;
|
|
getVim(cm).status = null;
|
|
cm.ace._signal("changeStatus");
|
|
cm.ace.session.markUndoGroup();
|
|
});
|
|
cm.on("changeStatus", function() {
|
|
cm.ace.renderer.updateCursor();
|
|
cm.ace._signal("changeStatus");
|
|
});
|
|
cm.on("vim-mode-change", function() {
|
|
if (cm.virtualSelectionMode()) return;
|
|
cm.ace.renderer.setStyle("normal-mode", !getVim(cm).insertMode);
|
|
cm._signal("changeStatus");
|
|
});
|
|
cm.ace.renderer.setStyle("normal-mode", !getVim(cm).insertMode);
|
|
editor.renderer.$cursorLayer.drawCursor = this.drawCursor.bind(cm);
|
|
this.updateMacCompositionHandlers(editor, true);
|
|
},
|
|
detach: function(editor) {
|
|
var cm = editor.state.cm;
|
|
CodeMirror.keyMap.vim.detach(cm);
|
|
cm.destroy();
|
|
editor.state.cm = null;
|
|
editor.$vimModeHandler = null;
|
|
editor.renderer.$cursorLayer.drawCursor = null;
|
|
editor.renderer.setStyle("normal-mode", false);
|
|
this.updateMacCompositionHandlers(editor, false);
|
|
},
|
|
getStatusText: function(editor) {
|
|
var cm = editor.state.cm;
|
|
var vim = getVim(cm);
|
|
if (vim.insertMode)
|
|
return "INSERT";
|
|
var status = "";
|
|
if (vim.visualMode) {
|
|
status += "VISUAL";
|
|
if (vim.visualLine)
|
|
status += " LINE";
|
|
if (vim.visualBlock)
|
|
status += " BLOCK";
|
|
}
|
|
if (vim.status)
|
|
status += (status ? " " : "") + vim.status;
|
|
return status;
|
|
},
|
|
handleMacRepeat: function(data, hashId, key) {
|
|
if (hashId == -1) {
|
|
data.inputChar = key;
|
|
data.lastEvent = "input";
|
|
} else if (data.inputChar && data.$lastHash == hashId && data.$lastKey == key) {
|
|
if (data.lastEvent == "input") {
|
|
data.lastEvent = "input1";
|
|
} else if (data.lastEvent == "input1") {
|
|
return true;
|
|
}
|
|
} else {
|
|
data.$lastHash = hashId;
|
|
data.$lastKey = key;
|
|
data.lastEvent = "keypress";
|
|
}
|
|
},
|
|
updateMacCompositionHandlers: function(editor, enable) {
|
|
var onCompositionUpdateOverride = function(text) {
|
|
var cm = editor.state.cm;
|
|
var vim = getVim(cm);
|
|
if (!vim.insertMode) {
|
|
var el = this.textInput.getElement();
|
|
el.blur();
|
|
el.focus();
|
|
el.value = text;
|
|
} else {
|
|
this.onCompositionUpdateOrig(text);
|
|
}
|
|
};
|
|
var onCompositionStartOverride = function(text) {
|
|
var cm = editor.state.cm;
|
|
var vim = getVim(cm);
|
|
if (!vim.insertMode) {
|
|
this.onCompositionStartOrig(text);
|
|
}
|
|
};
|
|
if (enable) {
|
|
if (!editor.onCompositionUpdateOrig) {
|
|
editor.onCompositionUpdateOrig = editor.onCompositionUpdate;
|
|
editor.onCompositionUpdate = onCompositionUpdateOverride;
|
|
editor.onCompositionStartOrig = editor.onCompositionStart;
|
|
editor.onCompositionStart = onCompositionStartOverride;
|
|
}
|
|
} else {
|
|
if (editor.onCompositionUpdateOrig) {
|
|
editor.onCompositionUpdate = editor.onCompositionUpdateOrig;
|
|
editor.onCompositionUpdateOrig = null;
|
|
editor.onCompositionStart = editor.onCompositionStartOrig;
|
|
editor.onCompositionStartOrig = null;
|
|
}
|
|
}
|
|
}
|
|
};
|
|
var renderVirtualNumbers = {
|
|
getText: function(session, row) {
|
|
return (Math.abs(session.selection.lead.row - row) || (row + 1 + (row < 9? "\xb7" : "" ))) + "";
|
|
},
|
|
getWidth: function(session, lastLineNumber, config) {
|
|
return session.getLength().toString().length * config.characterWidth;
|
|
},
|
|
update: function(e, editor) {
|
|
editor.renderer.$loop.schedule(editor.renderer.CHANGE_GUTTER);
|
|
},
|
|
attach: function(editor) {
|
|
editor.renderer.$gutterLayer.$renderer = this;
|
|
editor.on("changeSelection", this.update);
|
|
},
|
|
detach: function(editor) {
|
|
editor.renderer.$gutterLayer.$renderer = null;
|
|
editor.off("changeSelection", this.update);
|
|
}
|
|
};
|
|
Vim.defineOption({
|
|
name: "wrap",
|
|
set: function(value, cm) {
|
|
if (cm) {cm.ace.setOption("wrap", value)}
|
|
},
|
|
type: "boolean"
|
|
}, false);
|
|
Vim.defineEx('write', 'w', function() {
|
|
console.log(':write is not implemented')
|
|
});
|
|
defaultKeymap.push(
|
|
{ keys: 'zc', type: 'action', action: 'fold', actionArgs: { open: false } },
|
|
{ keys: 'zC', type: 'action', action: 'fold', actionArgs: { open: false, all: true } },
|
|
{ keys: 'zo', type: 'action', action: 'fold', actionArgs: { open: true } },
|
|
{ keys: 'zO', type: 'action', action: 'fold', actionArgs: { open: true, all: true } },
|
|
{ keys: 'za', type: 'action', action: 'fold', actionArgs: { toggle: true } },
|
|
{ keys: 'zA', type: 'action', action: 'fold', actionArgs: { toggle: true, all: true } },
|
|
{ keys: 'zf', type: 'action', action: 'fold', actionArgs: { open: true, all: true } },
|
|
{ keys: 'zd', type: 'action', action: 'fold', actionArgs: { open: true, all: true } },
|
|
|
|
{ keys: '<C-A-k>', type: 'action', action: 'aceCommand', actionArgs: { name: "addCursorAbove" } },
|
|
{ keys: '<C-A-j>', type: 'action', action: 'aceCommand', actionArgs: { name: "addCursorBelow" } },
|
|
{ keys: '<C-A-S-k>', type: 'action', action: 'aceCommand', actionArgs: { name: "addCursorAboveSkipCurrent" } },
|
|
{ keys: '<C-A-S-j>', type: 'action', action: 'aceCommand', actionArgs: { name: "addCursorBelowSkipCurrent" } },
|
|
{ keys: '<C-A-h>', type: 'action', action: 'aceCommand', actionArgs: { name: "selectMoreBefore" } },
|
|
{ keys: '<C-A-l>', type: 'action', action: 'aceCommand', actionArgs: { name: "selectMoreAfter" } },
|
|
{ keys: '<C-A-S-h>', type: 'action', action: 'aceCommand', actionArgs: { name: "selectNextBefore" } },
|
|
{ keys: '<C-A-S-l>', type: 'action', action: 'aceCommand', actionArgs: { name: "selectNextAfter" } }
|
|
);
|
|
actions.aceCommand = function(cm, actionArgs, vim) {
|
|
cm.vimCmd = actionArgs;
|
|
if (cm.ace.inVirtualSelectionMode)
|
|
cm.ace.on("beforeEndOperation", delayedExecAceCommand);
|
|
else
|
|
delayedExecAceCommand(null, cm.ace);
|
|
};
|
|
function delayedExecAceCommand(op, ace) {
|
|
ace.off("beforeEndOperation", delayedExecAceCommand);
|
|
var cmd = ace.state.cm.vimCmd;
|
|
if (cmd) {
|
|
ace.execCommand(cmd.exec ? cmd : cmd.name, cmd.args);
|
|
}
|
|
ace.curOp = ace.prevOp;
|
|
}
|
|
actions.fold = function(cm, actionArgs, vim) {
|
|
cm.ace.execCommand(['toggleFoldWidget', 'toggleFoldWidget', 'foldOther', 'unfoldall'
|
|
][(actionArgs.all ? 2 : 0) + (actionArgs.open ? 1 : 0)]);
|
|
};
|
|
|
|
exports.handler.defaultKeymap = defaultKeymap;
|
|
exports.handler.actions = actions;
|
|
exports.Vim = Vim;
|
|
|
|
Vim.map("Y", "yy", "normal");
|
|
});
|
|
|
|
define("ace/ext/statusbar",["require","exports","module","ace/lib/dom","ace/lib/lang"], function(require, exports, module) {
|
|
"use strict";
|
|
var dom = require("ace/lib/dom");
|
|
var lang = require("ace/lib/lang");
|
|
|
|
var StatusBar = function(editor, parentNode) {
|
|
this.element = dom.createElement("div");
|
|
this.element.className = "ace_status-indicator";
|
|
this.element.style.cssText = "display: inline-block;";
|
|
parentNode.appendChild(this.element);
|
|
|
|
var statusUpdate = lang.delayedCall(function(){
|
|
this.updateStatus(editor)
|
|
}.bind(this)).schedule.bind(null, 100);
|
|
|
|
editor.on("changeStatus", statusUpdate);
|
|
editor.on("changeSelection", statusUpdate);
|
|
editor.on("keyboardActivity", statusUpdate);
|
|
};
|
|
|
|
(function(){
|
|
this.updateStatus = function(editor) {
|
|
var status = [];
|
|
function add(str, separator) {
|
|
str && status.push(str, separator || "|");
|
|
}
|
|
|
|
add(editor.keyBinding.getStatusText(editor));
|
|
if (editor.commands.recording)
|
|
add("REC");
|
|
|
|
var sel = editor.selection;
|
|
var c = sel.lead;
|
|
|
|
if (!sel.isEmpty()) {
|
|
var r = editor.getSelectionRange();
|
|
add("(" + (r.end.row - r.start.row) + ":" +(r.end.column - r.start.column) + ")", " ");
|
|
}
|
|
add(c.row + ":" + c.column, " ");
|
|
if (sel.rangeCount)
|
|
add("[" + sel.rangeCount + "]", " ");
|
|
status.pop();
|
|
this.element.textContent = status.join("");
|
|
};
|
|
}).call(StatusBar.prototype);
|
|
|
|
exports.StatusBar = StatusBar;
|
|
|
|
});
|
|
|
|
define("ace/snippets",["require","exports","module","ace/lib/oop","ace/lib/event_emitter","ace/lib/lang","ace/range","ace/anchor","ace/keyboard/hash_handler","ace/tokenizer","ace/lib/dom","ace/editor"], function(require, exports, module) {
|
|
"use strict";
|
|
var oop = require("./lib/oop");
|
|
var EventEmitter = require("./lib/event_emitter").EventEmitter;
|
|
var lang = require("./lib/lang");
|
|
var Range = require("./range").Range;
|
|
var Anchor = require("./anchor").Anchor;
|
|
var HashHandler = require("./keyboard/hash_handler").HashHandler;
|
|
var Tokenizer = require("./tokenizer").Tokenizer;
|
|
var comparePoints = Range.comparePoints;
|
|
|
|
var SnippetManager = function() {
|
|
this.snippetMap = {};
|
|
this.snippetNameMap = {};
|
|
};
|
|
|
|
(function() {
|
|
oop.implement(this, EventEmitter);
|
|
|
|
this.getTokenizer = function() {
|
|
function TabstopToken(str, _, stack) {
|
|
str = str.substr(1);
|
|
if (/^\d+$/.test(str) && !stack.inFormatString)
|
|
return [{tabstopId: parseInt(str, 10)}];
|
|
return [{text: str}];
|
|
}
|
|
function escape(ch) {
|
|
return "(?:[^\\\\" + ch + "]|\\\\.)";
|
|
}
|
|
SnippetManager.$tokenizer = new Tokenizer({
|
|
start: [
|
|
{regex: /:/, onMatch: function(val, state, stack) {
|
|
if (stack.length && stack[0].expectIf) {
|
|
stack[0].expectIf = false;
|
|
stack[0].elseBranch = stack[0];
|
|
return [stack[0]];
|
|
}
|
|
return ":";
|
|
}},
|
|
{regex: /\\./, onMatch: function(val, state, stack) {
|
|
var ch = val[1];
|
|
if (ch == "}" && stack.length) {
|
|
val = ch;
|
|
}else if ("`$\\".indexOf(ch) != -1) {
|
|
val = ch;
|
|
} else if (stack.inFormatString) {
|
|
if (ch == "n")
|
|
val = "\n";
|
|
else if (ch == "t")
|
|
val = "\n";
|
|
else if ("ulULE".indexOf(ch) != -1) {
|
|
val = {changeCase: ch, local: ch > "a"};
|
|
}
|
|
}
|
|
|
|
return [val];
|
|
}},
|
|
{regex: /}/, onMatch: function(val, state, stack) {
|
|
return [stack.length ? stack.shift() : val];
|
|
}},
|
|
{regex: /\$(?:\d+|\w+)/, onMatch: TabstopToken},
|
|
{regex: /\$\{[\dA-Z_a-z]+/, onMatch: function(str, state, stack) {
|
|
var t = TabstopToken(str.substr(1), state, stack);
|
|
stack.unshift(t[0]);
|
|
return t;
|
|
}, next: "snippetVar"},
|
|
{regex: /\n/, token: "newline", merge: false}
|
|
],
|
|
snippetVar: [
|
|
{regex: "\\|" + escape("\\|") + "*\\|", onMatch: function(val, state, stack) {
|
|
stack[0].choices = val.slice(1, -1).split(",");
|
|
}, next: "start"},
|
|
{regex: "/(" + escape("/") + "+)/(?:(" + escape("/") + "*)/)(\\w*):?",
|
|
onMatch: function(val, state, stack) {
|
|
var ts = stack[0];
|
|
ts.fmtString = val;
|
|
|
|
val = this.splitRegex.exec(val);
|
|
ts.guard = val[1];
|
|
ts.fmt = val[2];
|
|
ts.flag = val[3];
|
|
return "";
|
|
}, next: "start"},
|
|
{regex: "`" + escape("`") + "*`", onMatch: function(val, state, stack) {
|
|
stack[0].code = val.splice(1, -1);
|
|
return "";
|
|
}, next: "start"},
|
|
{regex: "\\?", onMatch: function(val, state, stack) {
|
|
if (stack[0])
|
|
stack[0].expectIf = true;
|
|
}, next: "start"},
|
|
{regex: "([^:}\\\\]|\\\\.)*:?", token: "", next: "start"}
|
|
],
|
|
formatString: [
|
|
{regex: "/(" + escape("/") + "+)/", token: "regex"},
|
|
{regex: "", onMatch: function(val, state, stack) {
|
|
stack.inFormatString = true;
|
|
}, next: "start"}
|
|
]
|
|
});
|
|
SnippetManager.prototype.getTokenizer = function() {
|
|
return SnippetManager.$tokenizer;
|
|
};
|
|
return SnippetManager.$tokenizer;
|
|
};
|
|
|
|
this.tokenizeTmSnippet = function(str, startState) {
|
|
return this.getTokenizer().getLineTokens(str, startState).tokens.map(function(x) {
|
|
return x.value || x;
|
|
});
|
|
};
|
|
|
|
this.$getDefaultValue = function(editor, name) {
|
|
if (/^[A-Z]\d+$/.test(name)) {
|
|
var i = name.substr(1);
|
|
return (this.variables[name[0] + "__"] || {})[i];
|
|
}
|
|
if (/^\d+$/.test(name)) {
|
|
return (this.variables.__ || {})[name];
|
|
}
|
|
name = name.replace(/^TM_/, "");
|
|
|
|
if (!editor)
|
|
return;
|
|
var s = editor.session;
|
|
switch(name) {
|
|
case "CURRENT_WORD":
|
|
var r = s.getWordRange();
|
|
case "SELECTION":
|
|
case "SELECTED_TEXT":
|
|
return s.getTextRange(r);
|
|
case "CURRENT_LINE":
|
|
return s.getLine(editor.getCursorPosition().row);
|
|
case "PREV_LINE": // not possible in textmate
|
|
return s.getLine(editor.getCursorPosition().row - 1);
|
|
case "LINE_INDEX":
|
|
return editor.getCursorPosition().column;
|
|
case "LINE_NUMBER":
|
|
return editor.getCursorPosition().row + 1;
|
|
case "SOFT_TABS":
|
|
return s.getUseSoftTabs() ? "YES" : "NO";
|
|
case "TAB_SIZE":
|
|
return s.getTabSize();
|
|
case "FILENAME":
|
|
case "FILEPATH":
|
|
return "";
|
|
case "FULLNAME":
|
|
return "Ace";
|
|
}
|
|
};
|
|
this.variables = {};
|
|
this.getVariableValue = function(editor, varName) {
|
|
if (this.variables.hasOwnProperty(varName))
|
|
return this.variables[varName](editor, varName) || "";
|
|
return this.$getDefaultValue(editor, varName) || "";
|
|
};
|
|
this.tmStrFormat = function(str, ch, editor) {
|
|
var flag = ch.flag || "";
|
|
var re = ch.guard;
|
|
re = new RegExp(re, flag.replace(/[^gi]/, ""));
|
|
var fmtTokens = this.tokenizeTmSnippet(ch.fmt, "formatString");
|
|
var _self = this;
|
|
var formatted = str.replace(re, function() {
|
|
_self.variables.__ = arguments;
|
|
var fmtParts = _self.resolveVariables(fmtTokens, editor);
|
|
var gChangeCase = "E";
|
|
for (var i = 0; i < fmtParts.length; i++) {
|
|
var ch = fmtParts[i];
|
|
if (typeof ch == "object") {
|
|
fmtParts[i] = "";
|
|
if (ch.changeCase && ch.local) {
|
|
var next = fmtParts[i + 1];
|
|
if (next && typeof next == "string") {
|
|
if (ch.changeCase == "u")
|
|
fmtParts[i] = next[0].toUpperCase();
|
|
else
|
|
fmtParts[i] = next[0].toLowerCase();
|
|
fmtParts[i + 1] = next.substr(1);
|
|
}
|
|
} else if (ch.changeCase) {
|
|
gChangeCase = ch.changeCase;
|
|
}
|
|
} else if (gChangeCase == "U") {
|
|
fmtParts[i] = ch.toUpperCase();
|
|
} else if (gChangeCase == "L") {
|
|
fmtParts[i] = ch.toLowerCase();
|
|
}
|
|
}
|
|
return fmtParts.join("");
|
|
});
|
|
this.variables.__ = null;
|
|
return formatted;
|
|
};
|
|
|
|
this.resolveVariables = function(snippet, editor) {
|
|
var result = [];
|
|
for (var i = 0; i < snippet.length; i++) {
|
|
var ch = snippet[i];
|
|
if (typeof ch == "string") {
|
|
result.push(ch);
|
|
} else if (typeof ch != "object") {
|
|
continue;
|
|
} else if (ch.skip) {
|
|
gotoNext(ch);
|
|
} else if (ch.processed < i) {
|
|
continue;
|
|
} else if (ch.text) {
|
|
var value = this.getVariableValue(editor, ch.text);
|
|
if (value && ch.fmtString)
|
|
value = this.tmStrFormat(value, ch);
|
|
ch.processed = i;
|
|
if (ch.expectIf == null) {
|
|
if (value) {
|
|
result.push(value);
|
|
gotoNext(ch);
|
|
}
|
|
} else {
|
|
if (value) {
|
|
ch.skip = ch.elseBranch;
|
|
} else
|
|
gotoNext(ch);
|
|
}
|
|
} else if (ch.tabstopId != null) {
|
|
result.push(ch);
|
|
} else if (ch.changeCase != null) {
|
|
result.push(ch);
|
|
}
|
|
}
|
|
function gotoNext(ch) {
|
|
var i1 = snippet.indexOf(ch, i + 1);
|
|
if (i1 != -1)
|
|
i = i1;
|
|
}
|
|
return result;
|
|
};
|
|
|
|
this.insertSnippetForSelection = function(editor, snippetText) {
|
|
var cursor = editor.getCursorPosition();
|
|
var line = editor.session.getLine(cursor.row);
|
|
var tabString = editor.session.getTabString();
|
|
var indentString = line.match(/^\s*/)[0];
|
|
|
|
if (cursor.column < indentString.length)
|
|
indentString = indentString.slice(0, cursor.column);
|
|
|
|
snippetText = snippetText.replace(/\r/g, "");
|
|
var tokens = this.tokenizeTmSnippet(snippetText);
|
|
tokens = this.resolveVariables(tokens, editor);
|
|
tokens = tokens.map(function(x) {
|
|
if (x == "\n")
|
|
return x + indentString;
|
|
if (typeof x == "string")
|
|
return x.replace(/\t/g, tabString);
|
|
return x;
|
|
});
|
|
var tabstops = [];
|
|
tokens.forEach(function(p, i) {
|
|
if (typeof p != "object")
|
|
return;
|
|
var id = p.tabstopId;
|
|
var ts = tabstops[id];
|
|
if (!ts) {
|
|
ts = tabstops[id] = [];
|
|
ts.index = id;
|
|
ts.value = "";
|
|
}
|
|
if (ts.indexOf(p) !== -1)
|
|
return;
|
|
ts.push(p);
|
|
var i1 = tokens.indexOf(p, i + 1);
|
|
if (i1 === -1)
|
|
return;
|
|
|
|
var value = tokens.slice(i + 1, i1);
|
|
var isNested = value.some(function(t) {return typeof t === "object"});
|
|
if (isNested && !ts.value) {
|
|
ts.value = value;
|
|
} else if (value.length && (!ts.value || typeof ts.value !== "string")) {
|
|
ts.value = value.join("");
|
|
}
|
|
});
|
|
tabstops.forEach(function(ts) {ts.length = 0});
|
|
var expanding = {};
|
|
function copyValue(val) {
|
|
var copy = [];
|
|
for (var i = 0; i < val.length; i++) {
|
|
var p = val[i];
|
|
if (typeof p == "object") {
|
|
if (expanding[p.tabstopId])
|
|
continue;
|
|
var j = val.lastIndexOf(p, i - 1);
|
|
p = copy[j] || {tabstopId: p.tabstopId};
|
|
}
|
|
copy[i] = p;
|
|
}
|
|
return copy;
|
|
}
|
|
for (var i = 0; i < tokens.length; i++) {
|
|
var p = tokens[i];
|
|
if (typeof p != "object")
|
|
continue;
|
|
var id = p.tabstopId;
|
|
var i1 = tokens.indexOf(p, i + 1);
|
|
if (expanding[id]) {
|
|
if (expanding[id] === p)
|
|
expanding[id] = null;
|
|
continue;
|
|
}
|
|
|
|
var ts = tabstops[id];
|
|
var arg = typeof ts.value == "string" ? [ts.value] : copyValue(ts.value);
|
|
arg.unshift(i + 1, Math.max(0, i1 - i));
|
|
arg.push(p);
|
|
expanding[id] = p;
|
|
tokens.splice.apply(tokens, arg);
|
|
|
|
if (ts.indexOf(p) === -1)
|
|
ts.push(p);
|
|
}
|
|
var row = 0, column = 0;
|
|
var text = "";
|
|
tokens.forEach(function(t) {
|
|
if (typeof t === "string") {
|
|
var lines = t.split("\n");
|
|
if (lines.length > 1){
|
|
column = lines[lines.length - 1].length;
|
|
row += lines.length - 1;
|
|
} else
|
|
column += t.length;
|
|
text += t;
|
|
} else {
|
|
if (!t.start)
|
|
t.start = {row: row, column: column};
|
|
else
|
|
t.end = {row: row, column: column};
|
|
}
|
|
});
|
|
var range = editor.getSelectionRange();
|
|
var end = editor.session.replace(range, text);
|
|
|
|
var tabstopManager = new TabstopManager(editor);
|
|
var selectionId = editor.inVirtualSelectionMode && editor.selection.index;
|
|
tabstopManager.addTabstops(tabstops, range.start, end, selectionId);
|
|
};
|
|
|
|
this.insertSnippet = function(editor, snippetText) {
|
|
var self = this;
|
|
if (editor.inVirtualSelectionMode)
|
|
return self.insertSnippetForSelection(editor, snippetText);
|
|
|
|
editor.forEachSelection(function() {
|
|
self.insertSnippetForSelection(editor, snippetText);
|
|
}, null, {keepOrder: true});
|
|
|
|
if (editor.tabstopManager)
|
|
editor.tabstopManager.tabNext();
|
|
};
|
|
|
|
this.$getScope = function(editor) {
|
|
var scope = editor.session.$mode.$id || "";
|
|
scope = scope.split("/").pop();
|
|
if (scope === "html" || scope === "php") {
|
|
if (scope === "php" && !editor.session.$mode.inlinePhp)
|
|
scope = "html";
|
|
var c = editor.getCursorPosition();
|
|
var state = editor.session.getState(c.row);
|
|
if (typeof state === "object") {
|
|
state = state[0];
|
|
}
|
|
if (state.substring) {
|
|
if (state.substring(0, 3) == "js-")
|
|
scope = "javascript";
|
|
else if (state.substring(0, 4) == "css-")
|
|
scope = "css";
|
|
else if (state.substring(0, 4) == "php-")
|
|
scope = "php";
|
|
}
|
|
}
|
|
|
|
return scope;
|
|
};
|
|
|
|
this.getActiveScopes = function(editor) {
|
|
var scope = this.$getScope(editor);
|
|
var scopes = [scope];
|
|
var snippetMap = this.snippetMap;
|
|
if (snippetMap[scope] && snippetMap[scope].includeScopes) {
|
|
scopes.push.apply(scopes, snippetMap[scope].includeScopes);
|
|
}
|
|
scopes.push("_");
|
|
return scopes;
|
|
};
|
|
|
|
this.expandWithTab = function(editor, options) {
|
|
var self = this;
|
|
var result = editor.forEachSelection(function() {
|
|
return self.expandSnippetForSelection(editor, options);
|
|
}, null, {keepOrder: true});
|
|
if (result && editor.tabstopManager)
|
|
editor.tabstopManager.tabNext();
|
|
return result;
|
|
};
|
|
|
|
this.expandSnippetForSelection = function(editor, options) {
|
|
var cursor = editor.getCursorPosition();
|
|
var line = editor.session.getLine(cursor.row);
|
|
var before = line.substring(0, cursor.column);
|
|
var after = line.substr(cursor.column);
|
|
|
|
var snippetMap = this.snippetMap;
|
|
var snippet;
|
|
this.getActiveScopes(editor).some(function(scope) {
|
|
var snippets = snippetMap[scope];
|
|
if (snippets)
|
|
snippet = this.findMatchingSnippet(snippets, before, after);
|
|
return !!snippet;
|
|
}, this);
|
|
if (!snippet)
|
|
return false;
|
|
if (options && options.dryRun)
|
|
return true;
|
|
editor.session.doc.removeInLine(cursor.row,
|
|
cursor.column - snippet.replaceBefore.length,
|
|
cursor.column + snippet.replaceAfter.length
|
|
);
|
|
|
|
this.variables.M__ = snippet.matchBefore;
|
|
this.variables.T__ = snippet.matchAfter;
|
|
this.insertSnippetForSelection(editor, snippet.content);
|
|
|
|
this.variables.M__ = this.variables.T__ = null;
|
|
return true;
|
|
};
|
|
|
|
this.findMatchingSnippet = function(snippetList, before, after) {
|
|
for (var i = snippetList.length; i--;) {
|
|
var s = snippetList[i];
|
|
if (s.startRe && !s.startRe.test(before))
|
|
continue;
|
|
if (s.endRe && !s.endRe.test(after))
|
|
continue;
|
|
if (!s.startRe && !s.endRe)
|
|
continue;
|
|
|
|
s.matchBefore = s.startRe ? s.startRe.exec(before) : [""];
|
|
s.matchAfter = s.endRe ? s.endRe.exec(after) : [""];
|
|
s.replaceBefore = s.triggerRe ? s.triggerRe.exec(before)[0] : "";
|
|
s.replaceAfter = s.endTriggerRe ? s.endTriggerRe.exec(after)[0] : "";
|
|
return s;
|
|
}
|
|
};
|
|
|
|
this.snippetMap = {};
|
|
this.snippetNameMap = {};
|
|
this.register = function(snippets, scope) {
|
|
var snippetMap = this.snippetMap;
|
|
var snippetNameMap = this.snippetNameMap;
|
|
var self = this;
|
|
|
|
if (!snippets)
|
|
snippets = [];
|
|
|
|
function wrapRegexp(src) {
|
|
if (src && !/^\^?\(.*\)\$?$|^\\b$/.test(src))
|
|
src = "(?:" + src + ")";
|
|
|
|
return src || "";
|
|
}
|
|
function guardedRegexp(re, guard, opening) {
|
|
re = wrapRegexp(re);
|
|
guard = wrapRegexp(guard);
|
|
if (opening) {
|
|
re = guard + re;
|
|
if (re && re[re.length - 1] != "$")
|
|
re = re + "$";
|
|
} else {
|
|
re = re + guard;
|
|
if (re && re[0] != "^")
|
|
re = "^" + re;
|
|
}
|
|
return new RegExp(re);
|
|
}
|
|
|
|
function addSnippet(s) {
|
|
if (!s.scope)
|
|
s.scope = scope || "_";
|
|
scope = s.scope;
|
|
if (!snippetMap[scope]) {
|
|
snippetMap[scope] = [];
|
|
snippetNameMap[scope] = {};
|
|
}
|
|
|
|
var map = snippetNameMap[scope];
|
|
if (s.name) {
|
|
var old = map[s.name];
|
|
if (old)
|
|
self.unregister(old);
|
|
map[s.name] = s;
|
|
}
|
|
snippetMap[scope].push(s);
|
|
|
|
if (s.tabTrigger && !s.trigger) {
|
|
if (!s.guard && /^\w/.test(s.tabTrigger))
|
|
s.guard = "\\b";
|
|
s.trigger = lang.escapeRegExp(s.tabTrigger);
|
|
}
|
|
|
|
if (!s.trigger && !s.guard && !s.endTrigger && !s.endGuard)
|
|
return;
|
|
|
|
s.startRe = guardedRegexp(s.trigger, s.guard, true);
|
|
s.triggerRe = new RegExp(s.trigger, "", true);
|
|
|
|
s.endRe = guardedRegexp(s.endTrigger, s.endGuard, true);
|
|
s.endTriggerRe = new RegExp(s.endTrigger, "", true);
|
|
}
|
|
|
|
if (snippets && snippets.content)
|
|
addSnippet(snippets);
|
|
else if (Array.isArray(snippets))
|
|
snippets.forEach(addSnippet);
|
|
|
|
this._signal("registerSnippets", {scope: scope});
|
|
};
|
|
this.unregister = function(snippets, scope) {
|
|
var snippetMap = this.snippetMap;
|
|
var snippetNameMap = this.snippetNameMap;
|
|
|
|
function removeSnippet(s) {
|
|
var nameMap = snippetNameMap[s.scope||scope];
|
|
if (nameMap && nameMap[s.name]) {
|
|
delete nameMap[s.name];
|
|
var map = snippetMap[s.scope||scope];
|
|
var i = map && map.indexOf(s);
|
|
if (i >= 0)
|
|
map.splice(i, 1);
|
|
}
|
|
}
|
|
if (snippets.content)
|
|
removeSnippet(snippets);
|
|
else if (Array.isArray(snippets))
|
|
snippets.forEach(removeSnippet);
|
|
};
|
|
this.parseSnippetFile = function(str) {
|
|
str = str.replace(/\r/g, "");
|
|
var list = [], snippet = {};
|
|
var re = /^#.*|^({[\s\S]*})\s*$|^(\S+) (.*)$|^((?:\n*\t.*)+)/gm;
|
|
var m;
|
|
while (m = re.exec(str)) {
|
|
if (m[1]) {
|
|
try {
|
|
snippet = JSON.parse(m[1]);
|
|
list.push(snippet);
|
|
} catch (e) {}
|
|
} if (m[4]) {
|
|
snippet.content = m[4].replace(/^\t/gm, "");
|
|
list.push(snippet);
|
|
snippet = {};
|
|
} else {
|
|
var key = m[2], val = m[3];
|
|
if (key == "regex") {
|
|
var guardRe = /\/((?:[^\/\\]|\\.)*)|$/g;
|
|
snippet.guard = guardRe.exec(val)[1];
|
|
snippet.trigger = guardRe.exec(val)[1];
|
|
snippet.endTrigger = guardRe.exec(val)[1];
|
|
snippet.endGuard = guardRe.exec(val)[1];
|
|
} else if (key == "snippet") {
|
|
snippet.tabTrigger = val.match(/^\S*/)[0];
|
|
if (!snippet.name)
|
|
snippet.name = val;
|
|
} else {
|
|
snippet[key] = val;
|
|
}
|
|
}
|
|
}
|
|
return list;
|
|
};
|
|
this.getSnippetByName = function(name, editor) {
|
|
var snippetMap = this.snippetNameMap;
|
|
var snippet;
|
|
this.getActiveScopes(editor).some(function(scope) {
|
|
var snippets = snippetMap[scope];
|
|
if (snippets)
|
|
snippet = snippets[name];
|
|
return !!snippet;
|
|
}, this);
|
|
return snippet;
|
|
};
|
|
|
|
}).call(SnippetManager.prototype);
|
|
|
|
|
|
var TabstopManager = function(editor) {
|
|
if (editor.tabstopManager)
|
|
return editor.tabstopManager;
|
|
editor.tabstopManager = this;
|
|
this.$onChange = this.onChange.bind(this);
|
|
this.$onChangeSelection = lang.delayedCall(this.onChangeSelection.bind(this)).schedule;
|
|
this.$onChangeSession = this.onChangeSession.bind(this);
|
|
this.$onAfterExec = this.onAfterExec.bind(this);
|
|
this.attach(editor);
|
|
};
|
|
(function() {
|
|
this.attach = function(editor) {
|
|
this.index = 0;
|
|
this.ranges = [];
|
|
this.tabstops = [];
|
|
this.$openTabstops = null;
|
|
this.selectedTabstop = null;
|
|
|
|
this.editor = editor;
|
|
this.editor.on("change", this.$onChange);
|
|
this.editor.on("changeSelection", this.$onChangeSelection);
|
|
this.editor.on("changeSession", this.$onChangeSession);
|
|
this.editor.commands.on("afterExec", this.$onAfterExec);
|
|
this.editor.keyBinding.addKeyboardHandler(this.keyboardHandler);
|
|
};
|
|
this.detach = function() {
|
|
this.tabstops.forEach(this.removeTabstopMarkers, this);
|
|
this.ranges = null;
|
|
this.tabstops = null;
|
|
this.selectedTabstop = null;
|
|
this.editor.removeListener("change", this.$onChange);
|
|
this.editor.removeListener("changeSelection", this.$onChangeSelection);
|
|
this.editor.removeListener("changeSession", this.$onChangeSession);
|
|
this.editor.commands.removeListener("afterExec", this.$onAfterExec);
|
|
this.editor.keyBinding.removeKeyboardHandler(this.keyboardHandler);
|
|
this.editor.tabstopManager = null;
|
|
this.editor = null;
|
|
};
|
|
|
|
this.onChange = function(delta) {
|
|
var changeRange = delta;
|
|
var isRemove = delta.action[0] == "r";
|
|
var start = delta.start;
|
|
var end = delta.end;
|
|
var startRow = start.row;
|
|
var endRow = end.row;
|
|
var lineDif = endRow - startRow;
|
|
var colDiff = end.column - start.column;
|
|
|
|
if (isRemove) {
|
|
lineDif = -lineDif;
|
|
colDiff = -colDiff;
|
|
}
|
|
if (!this.$inChange && isRemove) {
|
|
var ts = this.selectedTabstop;
|
|
var changedOutside = ts && !ts.some(function(r) {
|
|
return comparePoints(r.start, start) <= 0 && comparePoints(r.end, end) >= 0;
|
|
});
|
|
if (changedOutside)
|
|
return this.detach();
|
|
}
|
|
var ranges = this.ranges;
|
|
for (var i = 0; i < ranges.length; i++) {
|
|
var r = ranges[i];
|
|
if (r.end.row < start.row)
|
|
continue;
|
|
|
|
if (isRemove && comparePoints(start, r.start) < 0 && comparePoints(end, r.end) > 0) {
|
|
this.removeRange(r);
|
|
i--;
|
|
continue;
|
|
}
|
|
|
|
if (r.start.row == startRow && r.start.column > start.column)
|
|
r.start.column += colDiff;
|
|
if (r.end.row == startRow && r.end.column >= start.column)
|
|
r.end.column += colDiff;
|
|
if (r.start.row >= startRow)
|
|
r.start.row += lineDif;
|
|
if (r.end.row >= startRow)
|
|
r.end.row += lineDif;
|
|
|
|
if (comparePoints(r.start, r.end) > 0)
|
|
this.removeRange(r);
|
|
}
|
|
if (!ranges.length)
|
|
this.detach();
|
|
};
|
|
this.updateLinkedFields = function() {
|
|
var ts = this.selectedTabstop;
|
|
if (!ts || !ts.hasLinkedRanges)
|
|
return;
|
|
this.$inChange = true;
|
|
var session = this.editor.session;
|
|
var text = session.getTextRange(ts.firstNonLinked);
|
|
for (var i = ts.length; i--;) {
|
|
var range = ts[i];
|
|
if (!range.linked)
|
|
continue;
|
|
var fmt = exports.snippetManager.tmStrFormat(text, range.original);
|
|
session.replace(range, fmt);
|
|
}
|
|
this.$inChange = false;
|
|
};
|
|
this.onAfterExec = function(e) {
|
|
if (e.command && !e.command.readOnly)
|
|
this.updateLinkedFields();
|
|
};
|
|
this.onChangeSelection = function() {
|
|
if (!this.editor)
|
|
return;
|
|
var lead = this.editor.selection.lead;
|
|
var anchor = this.editor.selection.anchor;
|
|
var isEmpty = this.editor.selection.isEmpty();
|
|
for (var i = this.ranges.length; i--;) {
|
|
if (this.ranges[i].linked)
|
|
continue;
|
|
var containsLead = this.ranges[i].contains(lead.row, lead.column);
|
|
var containsAnchor = isEmpty || this.ranges[i].contains(anchor.row, anchor.column);
|
|
if (containsLead && containsAnchor)
|
|
return;
|
|
}
|
|
this.detach();
|
|
};
|
|
this.onChangeSession = function() {
|
|
this.detach();
|
|
};
|
|
this.tabNext = function(dir) {
|
|
var max = this.tabstops.length;
|
|
var index = this.index + (dir || 1);
|
|
index = Math.min(Math.max(index, 1), max);
|
|
if (index == max)
|
|
index = 0;
|
|
this.selectTabstop(index);
|
|
if (index === 0)
|
|
this.detach();
|
|
};
|
|
this.selectTabstop = function(index) {
|
|
this.$openTabstops = null;
|
|
var ts = this.tabstops[this.index];
|
|
if (ts)
|
|
this.addTabstopMarkers(ts);
|
|
this.index = index;
|
|
ts = this.tabstops[this.index];
|
|
if (!ts || !ts.length)
|
|
return;
|
|
|
|
this.selectedTabstop = ts;
|
|
if (!this.editor.inVirtualSelectionMode) {
|
|
var sel = this.editor.multiSelect;
|
|
sel.toSingleRange(ts.firstNonLinked.clone());
|
|
for (var i = ts.length; i--;) {
|
|
if (ts.hasLinkedRanges && ts[i].linked)
|
|
continue;
|
|
sel.addRange(ts[i].clone(), true);
|
|
}
|
|
if (sel.ranges[0])
|
|
sel.addRange(sel.ranges[0].clone());
|
|
} else {
|
|
this.editor.selection.setRange(ts.firstNonLinked);
|
|
}
|
|
|
|
this.editor.keyBinding.addKeyboardHandler(this.keyboardHandler);
|
|
};
|
|
this.addTabstops = function(tabstops, start, end) {
|
|
if (!this.$openTabstops)
|
|
this.$openTabstops = [];
|
|
if (!tabstops[0]) {
|
|
var p = Range.fromPoints(end, end);
|
|
moveRelative(p.start, start);
|
|
moveRelative(p.end, start);
|
|
tabstops[0] = [p];
|
|
tabstops[0].index = 0;
|
|
}
|
|
|
|
var i = this.index;
|
|
var arg = [i + 1, 0];
|
|
var ranges = this.ranges;
|
|
tabstops.forEach(function(ts, index) {
|
|
var dest = this.$openTabstops[index] || ts;
|
|
|
|
for (var i = ts.length; i--;) {
|
|
var p = ts[i];
|
|
var range = Range.fromPoints(p.start, p.end || p.start);
|
|
movePoint(range.start, start);
|
|
movePoint(range.end, start);
|
|
range.original = p;
|
|
range.tabstop = dest;
|
|
ranges.push(range);
|
|
if (dest != ts)
|
|
dest.unshift(range);
|
|
else
|
|
dest[i] = range;
|
|
if (p.fmtString) {
|
|
range.linked = true;
|
|
dest.hasLinkedRanges = true;
|
|
} else if (!dest.firstNonLinked)
|
|
dest.firstNonLinked = range;
|
|
}
|
|
if (!dest.firstNonLinked)
|
|
dest.hasLinkedRanges = false;
|
|
if (dest === ts) {
|
|
arg.push(dest);
|
|
this.$openTabstops[index] = dest;
|
|
}
|
|
this.addTabstopMarkers(dest);
|
|
}, this);
|
|
|
|
if (arg.length > 2) {
|
|
if (this.tabstops.length)
|
|
arg.push(arg.splice(2, 1)[0]);
|
|
this.tabstops.splice.apply(this.tabstops, arg);
|
|
}
|
|
};
|
|
|
|
this.addTabstopMarkers = function(ts) {
|
|
var session = this.editor.session;
|
|
ts.forEach(function(range) {
|
|
if (!range.markerId)
|
|
range.markerId = session.addMarker(range, "ace_snippet-marker", "text");
|
|
});
|
|
};
|
|
this.removeTabstopMarkers = function(ts) {
|
|
var session = this.editor.session;
|
|
ts.forEach(function(range) {
|
|
session.removeMarker(range.markerId);
|
|
range.markerId = null;
|
|
});
|
|
};
|
|
this.removeRange = function(range) {
|
|
var i = range.tabstop.indexOf(range);
|
|
range.tabstop.splice(i, 1);
|
|
i = this.ranges.indexOf(range);
|
|
this.ranges.splice(i, 1);
|
|
this.editor.session.removeMarker(range.markerId);
|
|
if (!range.tabstop.length) {
|
|
i = this.tabstops.indexOf(range.tabstop);
|
|
if (i != -1)
|
|
this.tabstops.splice(i, 1);
|
|
if (!this.tabstops.length)
|
|
this.detach();
|
|
}
|
|
};
|
|
|
|
this.keyboardHandler = new HashHandler();
|
|
this.keyboardHandler.bindKeys({
|
|
"Tab": function(ed) {
|
|
if (exports.snippetManager && exports.snippetManager.expandWithTab(ed)) {
|
|
return;
|
|
}
|
|
|
|
ed.tabstopManager.tabNext(1);
|
|
},
|
|
"Shift-Tab": function(ed) {
|
|
ed.tabstopManager.tabNext(-1);
|
|
},
|
|
"Esc": function(ed) {
|
|
ed.tabstopManager.detach();
|
|
},
|
|
"Return": function(ed) {
|
|
return false;
|
|
}
|
|
});
|
|
}).call(TabstopManager.prototype);
|
|
|
|
|
|
|
|
var changeTracker = {};
|
|
changeTracker.onChange = Anchor.prototype.onChange;
|
|
changeTracker.setPosition = function(row, column) {
|
|
this.pos.row = row;
|
|
this.pos.column = column;
|
|
};
|
|
changeTracker.update = function(pos, delta, $insertRight) {
|
|
this.$insertRight = $insertRight;
|
|
this.pos = pos;
|
|
this.onChange(delta);
|
|
};
|
|
|
|
var movePoint = function(point, diff) {
|
|
if (point.row == 0)
|
|
point.column += diff.column;
|
|
point.row += diff.row;
|
|
};
|
|
|
|
var moveRelative = function(point, start) {
|
|
if (point.row == start.row)
|
|
point.column -= start.column;
|
|
point.row -= start.row;
|
|
};
|
|
|
|
|
|
require("./lib/dom").importCssString("\
|
|
.ace_snippet-marker {\
|
|
-moz-box-sizing: border-box;\
|
|
box-sizing: border-box;\
|
|
background: rgba(194, 193, 208, 0.09);\
|
|
border: 1px dotted rgba(211, 208, 235, 0.62);\
|
|
position: absolute;\
|
|
}");
|
|
|
|
exports.snippetManager = new SnippetManager();
|
|
|
|
|
|
var Editor = require("./editor").Editor;
|
|
(function() {
|
|
this.insertSnippet = function(content, options) {
|
|
return exports.snippetManager.insertSnippet(this, content, options);
|
|
};
|
|
this.expandSnippet = function(options) {
|
|
return exports.snippetManager.expandWithTab(this, options);
|
|
};
|
|
}).call(Editor.prototype);
|
|
|
|
});
|
|
|
|
define("ace/ext/emmet",["require","exports","module","ace/keyboard/hash_handler","ace/editor","ace/snippets","ace/range","resources","resources","tabStops","resources","utils","actions","ace/config","ace/config"], function(require, exports, module) {
|
|
"use strict";
|
|
var HashHandler = require("ace/keyboard/hash_handler").HashHandler;
|
|
var Editor = require("ace/editor").Editor;
|
|
var snippetManager = require("ace/snippets").snippetManager;
|
|
var Range = require("ace/range").Range;
|
|
var emmet, emmetPath;
|
|
function AceEmmetEditor() {}
|
|
|
|
AceEmmetEditor.prototype = {
|
|
setupContext: function(editor) {
|
|
this.ace = editor;
|
|
this.indentation = editor.session.getTabString();
|
|
if (!emmet)
|
|
emmet = window.emmet;
|
|
var resources = emmet.resources || emmet.require("resources");
|
|
resources.setVariable("indentation", this.indentation);
|
|
this.$syntax = null;
|
|
this.$syntax = this.getSyntax();
|
|
},
|
|
getSelectionRange: function() {
|
|
var range = this.ace.getSelectionRange();
|
|
var doc = this.ace.session.doc;
|
|
return {
|
|
start: doc.positionToIndex(range.start),
|
|
end: doc.positionToIndex(range.end)
|
|
};
|
|
},
|
|
createSelection: function(start, end) {
|
|
var doc = this.ace.session.doc;
|
|
this.ace.selection.setRange({
|
|
start: doc.indexToPosition(start),
|
|
end: doc.indexToPosition(end)
|
|
});
|
|
},
|
|
getCurrentLineRange: function() {
|
|
var ace = this.ace;
|
|
var row = ace.getCursorPosition().row;
|
|
var lineLength = ace.session.getLine(row).length;
|
|
var index = ace.session.doc.positionToIndex({row: row, column: 0});
|
|
return {
|
|
start: index,
|
|
end: index + lineLength
|
|
};
|
|
},
|
|
getCaretPos: function(){
|
|
var pos = this.ace.getCursorPosition();
|
|
return this.ace.session.doc.positionToIndex(pos);
|
|
},
|
|
setCaretPos: function(index){
|
|
var pos = this.ace.session.doc.indexToPosition(index);
|
|
this.ace.selection.moveToPosition(pos);
|
|
},
|
|
getCurrentLine: function() {
|
|
var row = this.ace.getCursorPosition().row;
|
|
return this.ace.session.getLine(row);
|
|
},
|
|
replaceContent: function(value, start, end, noIndent) {
|
|
if (end == null)
|
|
end = start == null ? this.getContent().length : start;
|
|
if (start == null)
|
|
start = 0;
|
|
|
|
var editor = this.ace;
|
|
var doc = editor.session.doc;
|
|
var range = Range.fromPoints(doc.indexToPosition(start), doc.indexToPosition(end));
|
|
editor.session.remove(range);
|
|
|
|
range.end = range.start;
|
|
|
|
value = this.$updateTabstops(value);
|
|
snippetManager.insertSnippet(editor, value);
|
|
},
|
|
getContent: function(){
|
|
return this.ace.getValue();
|
|
},
|
|
getSyntax: function() {
|
|
if (this.$syntax)
|
|
return this.$syntax;
|
|
var syntax = this.ace.session.$modeId.split("/").pop();
|
|
if (syntax == "html" || syntax == "php") {
|
|
var cursor = this.ace.getCursorPosition();
|
|
var state = this.ace.session.getState(cursor.row);
|
|
if (typeof state != "string")
|
|
state = state[0];
|
|
if (state) {
|
|
state = state.split("-");
|
|
if (state.length > 1)
|
|
syntax = state[0];
|
|
else if (syntax == "php")
|
|
syntax = "html";
|
|
}
|
|
}
|
|
return syntax;
|
|
},
|
|
getProfileName: function() {
|
|
var resources = emmet.resources || emmet.require("resources");
|
|
switch (this.getSyntax()) {
|
|
case "css": return "css";
|
|
case "xml":
|
|
case "xsl":
|
|
return "xml";
|
|
case "html":
|
|
var profile = resources.getVariable("profile");
|
|
if (!profile)
|
|
profile = this.ace.session.getLines(0,2).join("").search(/<!DOCTYPE[^>]+XHTML/i) != -1 ? "xhtml": "html";
|
|
return profile;
|
|
default:
|
|
var mode = this.ace.session.$mode;
|
|
return mode.emmetConfig && mode.emmetConfig.profile || "xhtml";
|
|
}
|
|
},
|
|
prompt: function(title) {
|
|
return prompt(title);
|
|
},
|
|
getSelection: function() {
|
|
return this.ace.session.getTextRange();
|
|
},
|
|
getFilePath: function() {
|
|
return "";
|
|
},
|
|
$updateTabstops: function(value) {
|
|
var base = 1000;
|
|
var zeroBase = 0;
|
|
var lastZero = null;
|
|
var ts = emmet.tabStops || emmet.require('tabStops');
|
|
var resources = emmet.resources || emmet.require("resources");
|
|
var settings = resources.getVocabulary("user");
|
|
var tabstopOptions = {
|
|
tabstop: function(data) {
|
|
var group = parseInt(data.group, 10);
|
|
var isZero = group === 0;
|
|
if (isZero)
|
|
group = ++zeroBase;
|
|
else
|
|
group += base;
|
|
|
|
var placeholder = data.placeholder;
|
|
if (placeholder) {
|
|
placeholder = ts.processText(placeholder, tabstopOptions);
|
|
}
|
|
|
|
var result = '${' + group + (placeholder ? ':' + placeholder : '') + '}';
|
|
|
|
if (isZero) {
|
|
lastZero = [data.start, result];
|
|
}
|
|
|
|
return result;
|
|
},
|
|
escape: function(ch) {
|
|
if (ch == '$') return '\\$';
|
|
if (ch == '\\') return '\\\\';
|
|
return ch;
|
|
}
|
|
};
|
|
|
|
value = ts.processText(value, tabstopOptions);
|
|
|
|
if (settings.variables['insert_final_tabstop'] && !/\$\{0\}$/.test(value)) {
|
|
value += '${0}';
|
|
} else if (lastZero) {
|
|
var common = emmet.utils ? emmet.utils.common : emmet.require('utils');
|
|
value = common.replaceSubstring(value, '${0}', lastZero[0], lastZero[1]);
|
|
}
|
|
|
|
return value;
|
|
}
|
|
};
|
|
|
|
|
|
var keymap = {
|
|
expand_abbreviation: {"mac": "ctrl+alt+e", "win": "alt+e"},
|
|
match_pair_outward: {"mac": "ctrl+d", "win": "ctrl+,"},
|
|
match_pair_inward: {"mac": "ctrl+j", "win": "ctrl+shift+0"},
|
|
matching_pair: {"mac": "ctrl+alt+j", "win": "alt+j"},
|
|
next_edit_point: "alt+right",
|
|
prev_edit_point: "alt+left",
|
|
toggle_comment: {"mac": "command+/", "win": "ctrl+/"},
|
|
split_join_tag: {"mac": "shift+command+'", "win": "shift+ctrl+`"},
|
|
remove_tag: {"mac": "command+'", "win": "shift+ctrl+;"},
|
|
evaluate_math_expression: {"mac": "shift+command+y", "win": "shift+ctrl+y"},
|
|
increment_number_by_1: "ctrl+up",
|
|
decrement_number_by_1: "ctrl+down",
|
|
increment_number_by_01: "alt+up",
|
|
decrement_number_by_01: "alt+down",
|
|
increment_number_by_10: {"mac": "alt+command+up", "win": "shift+alt+up"},
|
|
decrement_number_by_10: {"mac": "alt+command+down", "win": "shift+alt+down"},
|
|
select_next_item: {"mac": "shift+command+.", "win": "shift+ctrl+."},
|
|
select_previous_item: {"mac": "shift+command+,", "win": "shift+ctrl+,"},
|
|
reflect_css_value: {"mac": "shift+command+r", "win": "shift+ctrl+r"},
|
|
|
|
encode_decode_data_url: {"mac": "shift+ctrl+d", "win": "ctrl+'"},
|
|
expand_abbreviation_with_tab: "Tab",
|
|
wrap_with_abbreviation: {"mac": "shift+ctrl+a", "win": "shift+ctrl+a"}
|
|
};
|
|
|
|
var editorProxy = new AceEmmetEditor();
|
|
exports.commands = new HashHandler();
|
|
exports.runEmmetCommand = function runEmmetCommand(editor) {
|
|
try {
|
|
editorProxy.setupContext(editor);
|
|
var actions = emmet.actions || emmet.require("actions");
|
|
|
|
if (this.action == "expand_abbreviation_with_tab") {
|
|
if (!editor.selection.isEmpty())
|
|
return false;
|
|
var pos = editor.selection.lead;
|
|
var token = editor.session.getTokenAt(pos.row, pos.column);
|
|
if (token && /\btag\b/.test(token.type))
|
|
return false;
|
|
}
|
|
|
|
if (this.action == "wrap_with_abbreviation") {
|
|
return setTimeout(function() {
|
|
actions.run("wrap_with_abbreviation", editorProxy);
|
|
}, 0);
|
|
}
|
|
|
|
var result = actions.run(this.action, editorProxy);
|
|
} catch(e) {
|
|
if (!emmet) {
|
|
load(runEmmetCommand.bind(this, editor));
|
|
return true;
|
|
}
|
|
editor._signal("changeStatus", typeof e == "string" ? e : e.message);
|
|
console.log(e);
|
|
result = false;
|
|
}
|
|
return result;
|
|
};
|
|
|
|
for (var command in keymap) {
|
|
exports.commands.addCommand({
|
|
name: "emmet:" + command,
|
|
action: command,
|
|
bindKey: keymap[command],
|
|
exec: exports.runEmmetCommand,
|
|
multiSelectAction: "forEach"
|
|
});
|
|
}
|
|
|
|
exports.updateCommands = function(editor, enabled) {
|
|
if (enabled) {
|
|
editor.keyBinding.addKeyboardHandler(exports.commands);
|
|
} else {
|
|
editor.keyBinding.removeKeyboardHandler(exports.commands);
|
|
}
|
|
};
|
|
|
|
exports.isSupportedMode = function(mode) {
|
|
if (!mode) return false;
|
|
if (mode.emmetConfig) return true;
|
|
var id = mode.$id || mode;
|
|
return /css|less|scss|sass|stylus|html|php|twig|ejs|handlebars/.test(id);
|
|
};
|
|
|
|
exports.isAvailable = function(editor, command) {
|
|
if (/(evaluate_math_expression|expand_abbreviation)$/.test(command))
|
|
return true;
|
|
var mode = editor.session.$mode;
|
|
var isSupported = exports.isSupportedMode(mode);
|
|
if (isSupported && mode.$modes) {
|
|
try {
|
|
editorProxy.setupContext(editor);
|
|
if (/js|php/.test(editorProxy.getSyntax()))
|
|
isSupported = false;
|
|
} catch(e) {}
|
|
}
|
|
return isSupported;
|
|
}
|
|
|
|
var onChangeMode = function(e, target) {
|
|
var editor = target;
|
|
if (!editor)
|
|
return;
|
|
var enabled = exports.isSupportedMode(editor.session.$mode);
|
|
if (e.enableEmmet === false)
|
|
enabled = false;
|
|
if (enabled)
|
|
load();
|
|
exports.updateCommands(editor, enabled);
|
|
};
|
|
|
|
var load = function(cb) {
|
|
if (typeof emmetPath == "string") {
|
|
require("ace/config").loadModule(emmetPath, function() {
|
|
emmetPath = null;
|
|
cb && cb();
|
|
});
|
|
}
|
|
};
|
|
|
|
exports.AceEmmetEditor = AceEmmetEditor;
|
|
require("ace/config").defineOptions(Editor.prototype, "editor", {
|
|
enableEmmet: {
|
|
set: function(val) {
|
|
this[val ? "on" : "removeListener"]("changeMode", onChangeMode);
|
|
onChangeMode({enableEmmet: !!val}, this);
|
|
},
|
|
value: true
|
|
}
|
|
});
|
|
|
|
exports.setCore = function(e) {
|
|
if (typeof e == "string")
|
|
emmetPath = e;
|
|
else
|
|
emmet = e;
|
|
};
|
|
});
|
|
|
|
define("ace/autocomplete/popup",["require","exports","module","ace/virtual_renderer","ace/editor","ace/range","ace/lib/event","ace/lib/lang","ace/lib/dom"], function(require, exports, module) {
|
|
"use strict";
|
|
|
|
var Renderer = require("../virtual_renderer").VirtualRenderer;
|
|
var Editor = require("../editor").Editor;
|
|
var Range = require("../range").Range;
|
|
var event = require("../lib/event");
|
|
var lang = require("../lib/lang");
|
|
var dom = require("../lib/dom");
|
|
|
|
var $singleLineEditor = function(el) {
|
|
var renderer = new Renderer(el);
|
|
|
|
renderer.$maxLines = 4;
|
|
|
|
var editor = new Editor(renderer);
|
|
|
|
editor.setHighlightActiveLine(false);
|
|
editor.setShowPrintMargin(false);
|
|
editor.renderer.setShowGutter(false);
|
|
editor.renderer.setHighlightGutterLine(false);
|
|
|
|
editor.$mouseHandler.$focusWaitTimout = 0;
|
|
editor.$highlightTagPending = true;
|
|
|
|
return editor;
|
|
};
|
|
|
|
var AcePopup = function(parentNode) {
|
|
var el = dom.createElement("div");
|
|
var popup = new $singleLineEditor(el);
|
|
|
|
if (parentNode)
|
|
parentNode.appendChild(el);
|
|
el.style.display = "none";
|
|
popup.renderer.content.style.cursor = "default";
|
|
popup.renderer.setStyle("ace_autocomplete");
|
|
|
|
popup.setOption("displayIndentGuides", false);
|
|
popup.setOption("dragDelay", 150);
|
|
|
|
var noop = function(){};
|
|
|
|
popup.focus = noop;
|
|
popup.$isFocused = true;
|
|
|
|
popup.renderer.$cursorLayer.restartTimer = noop;
|
|
popup.renderer.$cursorLayer.element.style.opacity = 0;
|
|
|
|
popup.renderer.$maxLines = 8;
|
|
popup.renderer.$keepTextAreaAtCursor = false;
|
|
|
|
popup.setHighlightActiveLine(false);
|
|
popup.session.highlight("");
|
|
popup.session.$searchHighlight.clazz = "ace_highlight-marker";
|
|
|
|
popup.on("mousedown", function(e) {
|
|
var pos = e.getDocumentPosition();
|
|
popup.selection.moveToPosition(pos);
|
|
selectionMarker.start.row = selectionMarker.end.row = pos.row;
|
|
e.stop();
|
|
});
|
|
|
|
var lastMouseEvent;
|
|
var hoverMarker = new Range(-1,0,-1,Infinity);
|
|
var selectionMarker = new Range(-1,0,-1,Infinity);
|
|
selectionMarker.id = popup.session.addMarker(selectionMarker, "ace_active-line", "fullLine");
|
|
popup.setSelectOnHover = function(val) {
|
|
if (!val) {
|
|
hoverMarker.id = popup.session.addMarker(hoverMarker, "ace_line-hover", "fullLine");
|
|
} else if (hoverMarker.id) {
|
|
popup.session.removeMarker(hoverMarker.id);
|
|
hoverMarker.id = null;
|
|
}
|
|
};
|
|
popup.setSelectOnHover(false);
|
|
popup.on("mousemove", function(e) {
|
|
if (!lastMouseEvent) {
|
|
lastMouseEvent = e;
|
|
return;
|
|
}
|
|
if (lastMouseEvent.x == e.x && lastMouseEvent.y == e.y) {
|
|
return;
|
|
}
|
|
lastMouseEvent = e;
|
|
lastMouseEvent.scrollTop = popup.renderer.scrollTop;
|
|
var row = lastMouseEvent.getDocumentPosition().row;
|
|
if (hoverMarker.start.row != row) {
|
|
if (!hoverMarker.id)
|
|
popup.setRow(row);
|
|
setHoverMarker(row);
|
|
}
|
|
});
|
|
popup.renderer.on("beforeRender", function() {
|
|
if (lastMouseEvent && hoverMarker.start.row != -1) {
|
|
lastMouseEvent.$pos = null;
|
|
var row = lastMouseEvent.getDocumentPosition().row;
|
|
if (!hoverMarker.id)
|
|
popup.setRow(row);
|
|
setHoverMarker(row, true);
|
|
}
|
|
});
|
|
popup.renderer.on("afterRender", function() {
|
|
var row = popup.getRow();
|
|
var t = popup.renderer.$textLayer;
|
|
var selected = t.element.childNodes[row - t.config.firstRow];
|
|
if (selected == t.selectedNode)
|
|
return;
|
|
if (t.selectedNode)
|
|
dom.removeCssClass(t.selectedNode, "ace_selected");
|
|
t.selectedNode = selected;
|
|
if (selected)
|
|
dom.addCssClass(selected, "ace_selected");
|
|
});
|
|
var hideHoverMarker = function() { setHoverMarker(-1) };
|
|
var setHoverMarker = function(row, suppressRedraw) {
|
|
if (row !== hoverMarker.start.row) {
|
|
hoverMarker.start.row = hoverMarker.end.row = row;
|
|
if (!suppressRedraw)
|
|
popup.session._emit("changeBackMarker");
|
|
popup._emit("changeHoverMarker");
|
|
}
|
|
};
|
|
popup.getHoveredRow = function() {
|
|
return hoverMarker.start.row;
|
|
};
|
|
|
|
event.addListener(popup.container, "mouseout", hideHoverMarker);
|
|
popup.on("hide", hideHoverMarker);
|
|
popup.on("changeSelection", hideHoverMarker);
|
|
|
|
popup.session.doc.getLength = function() {
|
|
return popup.data.length;
|
|
};
|
|
popup.session.doc.getLine = function(i) {
|
|
var data = popup.data[i];
|
|
if (typeof data == "string")
|
|
return data;
|
|
return (data && data.value) || "";
|
|
};
|
|
|
|
var bgTokenizer = popup.session.bgTokenizer;
|
|
bgTokenizer.$tokenizeRow = function(row) {
|
|
var data = popup.data[row];
|
|
var tokens = [];
|
|
if (!data)
|
|
return tokens;
|
|
if (typeof data == "string")
|
|
data = {value: data};
|
|
if (!data.caption)
|
|
data.caption = data.value || data.name;
|
|
|
|
var last = -1;
|
|
var flag, c;
|
|
for (var i = 0; i < data.caption.length; i++) {
|
|
c = data.caption[i];
|
|
flag = data.matchMask & (1 << i) ? 1 : 0;
|
|
if (last !== flag) {
|
|
tokens.push({type: data.className || "" + ( flag ? "completion-highlight" : ""), value: c});
|
|
last = flag;
|
|
} else {
|
|
tokens[tokens.length - 1].value += c;
|
|
}
|
|
}
|
|
|
|
if (data.meta) {
|
|
var maxW = popup.renderer.$size.scrollerWidth / popup.renderer.layerConfig.characterWidth;
|
|
var metaData = data.meta;
|
|
if (metaData.length + data.caption.length > maxW - 2) {
|
|
metaData = metaData.substr(0, maxW - data.caption.length - 3) + "\u2026"
|
|
}
|
|
tokens.push({type: "rightAlignedText", value: metaData});
|
|
}
|
|
return tokens;
|
|
};
|
|
bgTokenizer.$updateOnChange = noop;
|
|
bgTokenizer.start = noop;
|
|
|
|
popup.session.$computeWidth = function() {
|
|
return this.screenWidth = 0;
|
|
};
|
|
|
|
popup.$blockScrolling = Infinity;
|
|
popup.isOpen = false;
|
|
popup.isTopdown = false;
|
|
popup.autoSelect = true;
|
|
|
|
popup.data = [];
|
|
popup.setData = function(list) {
|
|
popup.setValue(lang.stringRepeat("\n", list.length), -1);
|
|
popup.data = list || [];
|
|
popup.setRow(0);
|
|
};
|
|
popup.getData = function(row) {
|
|
return popup.data[row];
|
|
};
|
|
|
|
popup.getRow = function() {
|
|
return selectionMarker.start.row;
|
|
};
|
|
popup.setRow = function(line) {
|
|
line = Math.max(this.autoSelect ? 0 : -1, Math.min(this.data.length, line));
|
|
if (selectionMarker.start.row != line) {
|
|
popup.selection.clearSelection();
|
|
selectionMarker.start.row = selectionMarker.end.row = line || 0;
|
|
popup.session._emit("changeBackMarker");
|
|
popup.moveCursorTo(line || 0, 0);
|
|
if (popup.isOpen)
|
|
popup._signal("select");
|
|
}
|
|
};
|
|
|
|
popup.on("changeSelection", function() {
|
|
if (popup.isOpen)
|
|
popup.setRow(popup.selection.lead.row);
|
|
popup.renderer.scrollCursorIntoView();
|
|
});
|
|
|
|
popup.hide = function() {
|
|
this.container.style.display = "none";
|
|
this._signal("hide");
|
|
popup.isOpen = false;
|
|
};
|
|
popup.show = function(pos, lineHeight, topdownOnly) {
|
|
var el = this.container;
|
|
var screenHeight = window.innerHeight;
|
|
var screenWidth = window.innerWidth;
|
|
var renderer = this.renderer;
|
|
var maxH = renderer.$maxLines * lineHeight * 1.4;
|
|
var top = pos.top + this.$borderSize;
|
|
var allowTopdown = top > screenHeight / 2 && !topdownOnly;
|
|
if (allowTopdown && top + lineHeight + maxH > screenHeight) {
|
|
renderer.$maxPixelHeight = top - 2 * this.$borderSize;
|
|
el.style.top = "";
|
|
el.style.bottom = screenHeight - top + "px";
|
|
popup.isTopdown = false;
|
|
} else {
|
|
top += lineHeight;
|
|
renderer.$maxPixelHeight = screenHeight - top - 0.2 * lineHeight;
|
|
el.style.top = top + "px";
|
|
el.style.bottom = "";
|
|
popup.isTopdown = true;
|
|
}
|
|
|
|
el.style.display = "";
|
|
this.renderer.$textLayer.checkForSizeChanges();
|
|
|
|
var left = pos.left;
|
|
if (left + el.offsetWidth > screenWidth)
|
|
left = screenWidth - el.offsetWidth;
|
|
|
|
el.style.left = left + "px";
|
|
|
|
this._signal("show");
|
|
lastMouseEvent = null;
|
|
popup.isOpen = true;
|
|
};
|
|
|
|
popup.getTextLeftOffset = function() {
|
|
return this.$borderSize + this.renderer.$padding + this.$imageSize;
|
|
};
|
|
|
|
popup.$imageSize = 0;
|
|
popup.$borderSize = 1;
|
|
|
|
return popup;
|
|
};
|
|
|
|
dom.importCssString("\
|
|
.ace_editor.ace_autocomplete .ace_marker-layer .ace_active-line {\
|
|
background-color: #CAD6FA;\
|
|
z-index: 1;\
|
|
}\
|
|
.ace_editor.ace_autocomplete .ace_line-hover {\
|
|
border: 1px solid #abbffe;\
|
|
margin-top: -1px;\
|
|
background: rgba(233,233,253,0.4);\
|
|
}\
|
|
.ace_editor.ace_autocomplete .ace_line-hover {\
|
|
position: absolute;\
|
|
z-index: 2;\
|
|
}\
|
|
.ace_editor.ace_autocomplete .ace_scroller {\
|
|
background: none;\
|
|
border: none;\
|
|
box-shadow: none;\
|
|
}\
|
|
.ace_rightAlignedText {\
|
|
color: gray;\
|
|
display: inline-block;\
|
|
position: absolute;\
|
|
right: 4px;\
|
|
text-align: right;\
|
|
z-index: -1;\
|
|
}\
|
|
.ace_editor.ace_autocomplete .ace_completion-highlight{\
|
|
color: #000;\
|
|
text-shadow: 0 0 0.01em;\
|
|
}\
|
|
.ace_editor.ace_autocomplete {\
|
|
width: 280px;\
|
|
z-index: 200000;\
|
|
background: #fbfbfb;\
|
|
color: #444;\
|
|
border: 1px lightgray solid;\
|
|
position: fixed;\
|
|
box-shadow: 2px 3px 5px rgba(0,0,0,.2);\
|
|
line-height: 1.4;\
|
|
}");
|
|
|
|
exports.AcePopup = AcePopup;
|
|
|
|
});
|
|
|
|
define("ace/autocomplete/util",["require","exports","module"], function(require, exports, module) {
|
|
"use strict";
|
|
|
|
exports.parForEach = function(array, fn, callback) {
|
|
var completed = 0;
|
|
var arLength = array.length;
|
|
if (arLength === 0)
|
|
callback();
|
|
for (var i = 0; i < arLength; i++) {
|
|
fn(array[i], function(result, err) {
|
|
completed++;
|
|
if (completed === arLength)
|
|
callback(result, err);
|
|
});
|
|
}
|
|
};
|
|
|
|
var ID_REGEX = /[a-zA-Z_0-9\$\-\u00A2-\uFFFF]/;
|
|
|
|
exports.retrievePrecedingIdentifier = function(text, pos, regex) {
|
|
regex = regex || ID_REGEX;
|
|
var buf = [];
|
|
for (var i = pos-1; i >= 0; i--) {
|
|
if (regex.test(text[i]))
|
|
buf.push(text[i]);
|
|
else
|
|
break;
|
|
}
|
|
return buf.reverse().join("");
|
|
};
|
|
|
|
exports.retrieveFollowingIdentifier = function(text, pos, regex) {
|
|
regex = regex || ID_REGEX;
|
|
var buf = [];
|
|
for (var i = pos; i < text.length; i++) {
|
|
if (regex.test(text[i]))
|
|
buf.push(text[i]);
|
|
else
|
|
break;
|
|
}
|
|
return buf;
|
|
};
|
|
|
|
exports.getCompletionPrefix = function (editor) {
|
|
var pos = editor.getCursorPosition();
|
|
var line = editor.session.getLine(pos.row);
|
|
var prefix;
|
|
editor.completers.forEach(function(completer) {
|
|
if (completer.identifierRegexps) {
|
|
completer.identifierRegexps.forEach(function(identifierRegex) {
|
|
if (!prefix && identifierRegex)
|
|
prefix = this.retrievePrecedingIdentifier(line, pos.column, identifierRegex);
|
|
}.bind(this));
|
|
}
|
|
}.bind(this));
|
|
return prefix || this.retrievePrecedingIdentifier(line, pos.column);
|
|
};
|
|
|
|
});
|
|
|
|
define("ace/autocomplete",["require","exports","module","ace/keyboard/hash_handler","ace/autocomplete/popup","ace/autocomplete/util","ace/lib/event","ace/lib/lang","ace/lib/dom","ace/snippets"], function(require, exports, module) {
|
|
"use strict";
|
|
|
|
var HashHandler = require("./keyboard/hash_handler").HashHandler;
|
|
var AcePopup = require("./autocomplete/popup").AcePopup;
|
|
var util = require("./autocomplete/util");
|
|
var event = require("./lib/event");
|
|
var lang = require("./lib/lang");
|
|
var dom = require("./lib/dom");
|
|
var snippetManager = require("./snippets").snippetManager;
|
|
|
|
var Autocomplete = function() {
|
|
this.autoInsert = false;
|
|
this.autoSelect = true;
|
|
this.exactMatch = false;
|
|
this.gatherCompletionsId = 0;
|
|
this.keyboardHandler = new HashHandler();
|
|
this.keyboardHandler.bindKeys(this.commands);
|
|
|
|
this.blurListener = this.blurListener.bind(this);
|
|
this.changeListener = this.changeListener.bind(this);
|
|
this.mousedownListener = this.mousedownListener.bind(this);
|
|
this.mousewheelListener = this.mousewheelListener.bind(this);
|
|
|
|
this.changeTimer = lang.delayedCall(function() {
|
|
this.updateCompletions(true);
|
|
}.bind(this));
|
|
|
|
this.tooltipTimer = lang.delayedCall(this.updateDocTooltip.bind(this), 50);
|
|
};
|
|
|
|
(function() {
|
|
|
|
this.$init = function() {
|
|
this.popup = new AcePopup(document.body || document.documentElement);
|
|
this.popup.on("click", function(e) {
|
|
this.insertMatch();
|
|
e.stop();
|
|
}.bind(this));
|
|
this.popup.focus = this.editor.focus.bind(this.editor);
|
|
this.popup.on("show", this.tooltipTimer.bind(null, null));
|
|
this.popup.on("select", this.tooltipTimer.bind(null, null));
|
|
this.popup.on("changeHoverMarker", this.tooltipTimer.bind(null, null));
|
|
return this.popup;
|
|
};
|
|
|
|
this.getPopup = function() {
|
|
return this.popup || this.$init();
|
|
};
|
|
|
|
this.openPopup = function(editor, prefix, keepPopupPosition) {
|
|
if (!this.popup)
|
|
this.$init();
|
|
|
|
this.popup.autoSelect = this.autoSelect;
|
|
|
|
this.popup.setData(this.completions.filtered);
|
|
|
|
editor.keyBinding.addKeyboardHandler(this.keyboardHandler);
|
|
|
|
var renderer = editor.renderer;
|
|
this.popup.setRow(this.autoSelect ? 0 : -1);
|
|
if (!keepPopupPosition) {
|
|
this.popup.setTheme(editor.getTheme());
|
|
this.popup.setFontSize(editor.getFontSize());
|
|
|
|
var lineHeight = renderer.layerConfig.lineHeight;
|
|
|
|
var pos = renderer.$cursorLayer.getPixelPosition(this.base, true);
|
|
pos.left -= this.popup.getTextLeftOffset();
|
|
|
|
var rect = editor.container.getBoundingClientRect();
|
|
pos.top += rect.top - renderer.layerConfig.offset;
|
|
pos.left += rect.left - editor.renderer.scrollLeft;
|
|
pos.left += renderer.gutterWidth;
|
|
|
|
this.popup.show(pos, lineHeight);
|
|
} else if (keepPopupPosition && !prefix) {
|
|
this.detach();
|
|
}
|
|
};
|
|
|
|
this.detach = function() {
|
|
this.editor.keyBinding.removeKeyboardHandler(this.keyboardHandler);
|
|
this.editor.off("changeSelection", this.changeListener);
|
|
this.editor.off("blur", this.blurListener);
|
|
this.editor.off("mousedown", this.mousedownListener);
|
|
this.editor.off("mousewheel", this.mousewheelListener);
|
|
this.changeTimer.cancel();
|
|
this.hideDocTooltip();
|
|
|
|
this.gatherCompletionsId += 1;
|
|
if (this.popup && this.popup.isOpen)
|
|
this.popup.hide();
|
|
|
|
if (this.base)
|
|
this.base.detach();
|
|
this.activated = false;
|
|
this.completions = this.base = null;
|
|
};
|
|
|
|
this.changeListener = function(e) {
|
|
var cursor = this.editor.selection.lead;
|
|
if (cursor.row != this.base.row || cursor.column < this.base.column) {
|
|
this.detach();
|
|
}
|
|
if (this.activated)
|
|
this.changeTimer.schedule();
|
|
else
|
|
this.detach();
|
|
};
|
|
|
|
this.blurListener = function(e) {
|
|
var el = document.activeElement;
|
|
var text = this.editor.textInput.getElement();
|
|
var fromTooltip = e.relatedTarget && this.tooltipNode && this.tooltipNode.contains(e.relatedTarget);
|
|
var container = this.popup && this.popup.container;
|
|
if (el != text && el.parentNode != container && !fromTooltip
|
|
&& el != this.tooltipNode && e.relatedTarget != text
|
|
) {
|
|
this.detach();
|
|
}
|
|
};
|
|
|
|
this.mousedownListener = function(e) {
|
|
this.detach();
|
|
};
|
|
|
|
this.mousewheelListener = function(e) {
|
|
this.detach();
|
|
};
|
|
|
|
this.goTo = function(where) {
|
|
var row = this.popup.getRow();
|
|
var max = this.popup.session.getLength() - 1;
|
|
|
|
switch(where) {
|
|
case "up": row = row <= 0 ? max : row - 1; break;
|
|
case "down": row = row >= max ? -1 : row + 1; break;
|
|
case "start": row = 0; break;
|
|
case "end": row = max; break;
|
|
}
|
|
|
|
this.popup.setRow(row);
|
|
};
|
|
|
|
this.insertMatch = function(data, options) {
|
|
if (!data)
|
|
data = this.popup.getData(this.popup.getRow());
|
|
if (!data)
|
|
return false;
|
|
|
|
if (data.completer && data.completer.insertMatch) {
|
|
data.completer.insertMatch(this.editor, data);
|
|
} else {
|
|
if (this.completions.filterText) {
|
|
var ranges = this.editor.selection.getAllRanges();
|
|
for (var i = 0, range; range = ranges[i]; i++) {
|
|
range.start.column -= this.completions.filterText.length;
|
|
this.editor.session.remove(range);
|
|
}
|
|
}
|
|
if (data.snippet)
|
|
snippetManager.insertSnippet(this.editor, data.snippet);
|
|
else
|
|
this.editor.execCommand("insertstring", data.value || data);
|
|
}
|
|
this.detach();
|
|
};
|
|
|
|
|
|
this.commands = {
|
|
"Up": function(editor) { editor.completer.goTo("up"); },
|
|
"Down": function(editor) { editor.completer.goTo("down"); },
|
|
"Ctrl-Up|Ctrl-Home": function(editor) { editor.completer.goTo("start"); },
|
|
"Ctrl-Down|Ctrl-End": function(editor) { editor.completer.goTo("end"); },
|
|
|
|
"Esc": function(editor) { editor.completer.detach(); },
|
|
"Return": function(editor) { return editor.completer.insertMatch(); },
|
|
"Shift-Return": function(editor) { editor.completer.insertMatch(null, {deleteSuffix: true}); },
|
|
"Tab": function(editor) {
|
|
var result = editor.completer.insertMatch();
|
|
if (!result && !editor.tabstopManager)
|
|
editor.completer.goTo("down");
|
|
else
|
|
return result;
|
|
},
|
|
|
|
"PageUp": function(editor) { editor.completer.popup.gotoPageUp(); },
|
|
"PageDown": function(editor) { editor.completer.popup.gotoPageDown(); }
|
|
};
|
|
|
|
this.gatherCompletions = function(editor, callback) {
|
|
var session = editor.getSession();
|
|
var pos = editor.getCursorPosition();
|
|
|
|
var prefix = util.getCompletionPrefix(editor);
|
|
|
|
this.base = session.doc.createAnchor(pos.row, pos.column - prefix.length);
|
|
this.base.$insertRight = true;
|
|
|
|
var matches = [];
|
|
var total = editor.completers.length;
|
|
editor.completers.forEach(function(completer, i) {
|
|
completer.getCompletions(editor, session, pos, prefix, function(err, results) {
|
|
if (!err && results)
|
|
matches = matches.concat(results);
|
|
callback(null, {
|
|
prefix: util.getCompletionPrefix(editor),
|
|
matches: matches,
|
|
finished: (--total === 0)
|
|
});
|
|
});
|
|
});
|
|
return true;
|
|
};
|
|
|
|
this.showPopup = function(editor) {
|
|
if (this.editor)
|
|
this.detach();
|
|
|
|
this.activated = true;
|
|
|
|
this.editor = editor;
|
|
if (editor.completer != this) {
|
|
if (editor.completer)
|
|
editor.completer.detach();
|
|
editor.completer = this;
|
|
}
|
|
|
|
editor.on("changeSelection", this.changeListener);
|
|
editor.on("blur", this.blurListener);
|
|
editor.on("mousedown", this.mousedownListener);
|
|
editor.on("mousewheel", this.mousewheelListener);
|
|
|
|
this.updateCompletions();
|
|
};
|
|
|
|
this.updateCompletions = function(keepPopupPosition) {
|
|
if (keepPopupPosition && this.base && this.completions) {
|
|
var pos = this.editor.getCursorPosition();
|
|
var prefix = this.editor.session.getTextRange({start: this.base, end: pos});
|
|
if (prefix == this.completions.filterText)
|
|
return;
|
|
this.completions.setFilter(prefix);
|
|
if (!this.completions.filtered.length)
|
|
return this.detach();
|
|
if (this.completions.filtered.length == 1
|
|
&& this.completions.filtered[0].value == prefix
|
|
&& !this.completions.filtered[0].snippet)
|
|
return this.detach();
|
|
this.openPopup(this.editor, prefix, keepPopupPosition);
|
|
return;
|
|
}
|
|
var _id = this.gatherCompletionsId;
|
|
this.gatherCompletions(this.editor, function(err, results) {
|
|
var detachIfFinished = function() {
|
|
if (!results.finished) return;
|
|
return this.detach();
|
|
}.bind(this);
|
|
|
|
var prefix = results.prefix;
|
|
var matches = results && results.matches;
|
|
|
|
if (!matches || !matches.length)
|
|
return detachIfFinished();
|
|
if (prefix.indexOf(results.prefix) !== 0 || _id != this.gatherCompletionsId)
|
|
return;
|
|
|
|
this.completions = new FilteredList(matches);
|
|
|
|
if (this.exactMatch)
|
|
this.completions.exactMatch = true;
|
|
|
|
this.completions.setFilter(prefix);
|
|
var filtered = this.completions.filtered;
|
|
if (!filtered.length)
|
|
return detachIfFinished();
|
|
if (filtered.length == 1 && filtered[0].value == prefix && !filtered[0].snippet)
|
|
return detachIfFinished();
|
|
if (this.autoInsert && filtered.length == 1 && results.finished)
|
|
return this.insertMatch(filtered[0]);
|
|
|
|
this.openPopup(this.editor, prefix, keepPopupPosition);
|
|
}.bind(this));
|
|
};
|
|
|
|
this.cancelContextMenu = function() {
|
|
this.editor.$mouseHandler.cancelContextMenu();
|
|
};
|
|
|
|
this.updateDocTooltip = function() {
|
|
var popup = this.popup;
|
|
var all = popup.data;
|
|
var selected = all && (all[popup.getHoveredRow()] || all[popup.getRow()]);
|
|
var doc = null;
|
|
if (!selected || !this.editor || !this.popup.isOpen)
|
|
return this.hideDocTooltip();
|
|
this.editor.completers.some(function(completer) {
|
|
if (completer.getDocTooltip)
|
|
doc = completer.getDocTooltip(selected);
|
|
return doc;
|
|
});
|
|
if (!doc)
|
|
doc = selected;
|
|
|
|
if (typeof doc == "string")
|
|
doc = {docText: doc};
|
|
if (!doc || !(doc.docHTML || doc.docText))
|
|
return this.hideDocTooltip();
|
|
this.showDocTooltip(doc);
|
|
};
|
|
|
|
this.showDocTooltip = function(item) {
|
|
if (!this.tooltipNode) {
|
|
this.tooltipNode = dom.createElement("div");
|
|
this.tooltipNode.className = "ace_tooltip ace_doc-tooltip";
|
|
this.tooltipNode.style.margin = 0;
|
|
this.tooltipNode.style.pointerEvents = "auto";
|
|
this.tooltipNode.tabIndex = -1;
|
|
this.tooltipNode.onblur = this.blurListener.bind(this);
|
|
this.tooltipNode.onclick = this.onTooltipClick.bind(this);
|
|
}
|
|
|
|
var tooltipNode = this.tooltipNode;
|
|
if (item.docHTML) {
|
|
tooltipNode.innerHTML = item.docHTML;
|
|
} else if (item.docText) {
|
|
tooltipNode.textContent = item.docText;
|
|
}
|
|
|
|
if (!tooltipNode.parentNode)
|
|
document.body.appendChild(tooltipNode);
|
|
var popup = this.popup;
|
|
var rect = popup.container.getBoundingClientRect();
|
|
tooltipNode.style.top = popup.container.style.top;
|
|
tooltipNode.style.bottom = popup.container.style.bottom;
|
|
|
|
if (window.innerWidth - rect.right < 320) {
|
|
tooltipNode.style.right = window.innerWidth - rect.left + "px";
|
|
tooltipNode.style.left = "";
|
|
} else {
|
|
tooltipNode.style.left = (rect.right + 1) + "px";
|
|
tooltipNode.style.right = "";
|
|
}
|
|
tooltipNode.style.display = "block";
|
|
};
|
|
|
|
this.hideDocTooltip = function() {
|
|
this.tooltipTimer.cancel();
|
|
if (!this.tooltipNode) return;
|
|
var el = this.tooltipNode;
|
|
if (!this.editor.isFocused() && document.activeElement == el)
|
|
this.editor.focus();
|
|
this.tooltipNode = null;
|
|
if (el.parentNode)
|
|
el.parentNode.removeChild(el);
|
|
};
|
|
|
|
this.onTooltipClick = function(e) {
|
|
var a = e.target;
|
|
while (a && a != this.tooltipNode) {
|
|
if (a.nodeName == "A" && a.href) {
|
|
a.rel = "noreferrer";
|
|
a.target = "_blank";
|
|
break;
|
|
}
|
|
a = a.parentNode;
|
|
}
|
|
}
|
|
|
|
}).call(Autocomplete.prototype);
|
|
|
|
Autocomplete.startCommand = {
|
|
name: "startAutocomplete",
|
|
exec: function(editor) {
|
|
if (!editor.completer)
|
|
editor.completer = new Autocomplete();
|
|
editor.completer.autoInsert = false;
|
|
editor.completer.autoSelect = true;
|
|
editor.completer.showPopup(editor);
|
|
editor.completer.cancelContextMenu();
|
|
},
|
|
bindKey: "Ctrl-Space|Ctrl-Shift-Space|Alt-Space"
|
|
};
|
|
|
|
var FilteredList = function(array, filterText) {
|
|
this.all = array;
|
|
this.filtered = array;
|
|
this.filterText = filterText || "";
|
|
this.exactMatch = false;
|
|
};
|
|
(function(){
|
|
this.setFilter = function(str) {
|
|
if (str.length > this.filterText && str.lastIndexOf(this.filterText, 0) === 0)
|
|
var matches = this.filtered;
|
|
else
|
|
var matches = this.all;
|
|
|
|
this.filterText = str;
|
|
matches = this.filterCompletions(matches, this.filterText);
|
|
matches = matches.sort(function(a, b) {
|
|
return b.exactMatch - a.exactMatch || b.score - a.score;
|
|
});
|
|
var prev = null;
|
|
matches = matches.filter(function(item){
|
|
var caption = item.snippet || item.caption || item.value;
|
|
if (caption === prev) return false;
|
|
prev = caption;
|
|
return true;
|
|
});
|
|
|
|
this.filtered = matches;
|
|
};
|
|
this.filterCompletions = function(items, needle) {
|
|
var results = [];
|
|
var upper = needle.toUpperCase();
|
|
var lower = needle.toLowerCase();
|
|
loop: for (var i = 0, item; item = items[i]; i++) {
|
|
var caption = item.value || item.caption || item.snippet;
|
|
if (!caption) continue;
|
|
var lastIndex = -1;
|
|
var matchMask = 0;
|
|
var penalty = 0;
|
|
var index, distance;
|
|
|
|
if (this.exactMatch) {
|
|
if (needle !== caption.substr(0, needle.length))
|
|
continue loop;
|
|
}else{
|
|
for (var j = 0; j < needle.length; j++) {
|
|
var i1 = caption.indexOf(lower[j], lastIndex + 1);
|
|
var i2 = caption.indexOf(upper[j], lastIndex + 1);
|
|
index = (i1 >= 0) ? ((i2 < 0 || i1 < i2) ? i1 : i2) : i2;
|
|
if (index < 0)
|
|
continue loop;
|
|
distance = index - lastIndex - 1;
|
|
if (distance > 0) {
|
|
if (lastIndex === -1)
|
|
penalty += 10;
|
|
penalty += distance;
|
|
}
|
|
matchMask = matchMask | (1 << index);
|
|
lastIndex = index;
|
|
}
|
|
}
|
|
item.matchMask = matchMask;
|
|
item.exactMatch = penalty ? 0 : 1;
|
|
item.score = (item.score || 0) - penalty;
|
|
results.push(item);
|
|
}
|
|
return results;
|
|
};
|
|
}).call(FilteredList.prototype);
|
|
|
|
exports.Autocomplete = Autocomplete;
|
|
exports.FilteredList = FilteredList;
|
|
|
|
});
|
|
|
|
define("ace/autocomplete/text_completer",["require","exports","module","ace/range"], function(require, exports, module) {
|
|
var Range = require("../range").Range;
|
|
|
|
var splitRegex = /[^a-zA-Z_0-9\$\-\u00C0-\u1FFF\u2C00-\uD7FF\w]+/;
|
|
|
|
function getWordIndex(doc, pos) {
|
|
var textBefore = doc.getTextRange(Range.fromPoints({row: 0, column:0}, pos));
|
|
return textBefore.split(splitRegex).length - 1;
|
|
}
|
|
function wordDistance(doc, pos) {
|
|
var prefixPos = getWordIndex(doc, pos);
|
|
var words = doc.getValue().split(splitRegex);
|
|
var wordScores = Object.create(null);
|
|
|
|
var currentWord = words[prefixPos];
|
|
|
|
words.forEach(function(word, idx) {
|
|
if (!word || word === currentWord) return;
|
|
|
|
var distance = Math.abs(prefixPos - idx);
|
|
var score = words.length - distance;
|
|
if (wordScores[word]) {
|
|
wordScores[word] = Math.max(score, wordScores[word]);
|
|
} else {
|
|
wordScores[word] = score;
|
|
}
|
|
});
|
|
return wordScores;
|
|
}
|
|
|
|
exports.getCompletions = function(editor, session, pos, prefix, callback) {
|
|
var wordScore = wordDistance(session, pos, prefix);
|
|
var wordList = Object.keys(wordScore);
|
|
callback(null, wordList.map(function(word) {
|
|
return {
|
|
caption: word,
|
|
value: word,
|
|
score: wordScore[word],
|
|
meta: "local"
|
|
};
|
|
}));
|
|
};
|
|
});
|
|
|
|
define("ace/ext/language_tools",["require","exports","module","ace/snippets","ace/autocomplete","ace/config","ace/lib/lang","ace/autocomplete/util","ace/autocomplete/text_completer","ace/editor","ace/config"], function(require, exports, module) {
|
|
"use strict";
|
|
|
|
var snippetManager = require("../snippets").snippetManager;
|
|
var Autocomplete = require("../autocomplete").Autocomplete;
|
|
var config = require("../config");
|
|
var lang = require("../lib/lang");
|
|
var util = require("../autocomplete/util");
|
|
|
|
var textCompleter = require("../autocomplete/text_completer");
|
|
var keyWordCompleter = {
|
|
getCompletions: function(editor, session, pos, prefix, callback) {
|
|
if (session.$mode.completer) {
|
|
return session.$mode.completer.getCompletions(editor, session, pos, prefix, callback);
|
|
}
|
|
var state = editor.session.getState(pos.row);
|
|
var completions = session.$mode.getCompletions(state, session, pos, prefix);
|
|
callback(null, completions);
|
|
}
|
|
};
|
|
|
|
var snippetCompleter = {
|
|
getCompletions: function(editor, session, pos, prefix, callback) {
|
|
var snippetMap = snippetManager.snippetMap;
|
|
var completions = [];
|
|
snippetManager.getActiveScopes(editor).forEach(function(scope) {
|
|
var snippets = snippetMap[scope] || [];
|
|
for (var i = snippets.length; i--;) {
|
|
var s = snippets[i];
|
|
var caption = s.name || s.tabTrigger;
|
|
if (!caption)
|
|
continue;
|
|
completions.push({
|
|
caption: caption,
|
|
snippet: s.content,
|
|
meta: s.tabTrigger && !s.name ? s.tabTrigger + "\u21E5 " : "snippet",
|
|
type: "snippet"
|
|
});
|
|
}
|
|
}, this);
|
|
callback(null, completions);
|
|
},
|
|
getDocTooltip: function(item) {
|
|
if (item.type == "snippet" && !item.docHTML) {
|
|
item.docHTML = [
|
|
"<b>", lang.escapeHTML(item.caption), "</b>", "<hr></hr>",
|
|
lang.escapeHTML(item.snippet)
|
|
].join("");
|
|
}
|
|
}
|
|
};
|
|
|
|
var completers = [snippetCompleter, textCompleter, keyWordCompleter];
|
|
exports.setCompleters = function(val) {
|
|
completers.length = 0;
|
|
if (val) completers.push.apply(completers, val);
|
|
};
|
|
exports.addCompleter = function(completer) {
|
|
completers.push(completer);
|
|
};
|
|
exports.textCompleter = textCompleter;
|
|
exports.keyWordCompleter = keyWordCompleter;
|
|
exports.snippetCompleter = snippetCompleter;
|
|
|
|
var expandSnippet = {
|
|
name: "expandSnippet",
|
|
exec: function(editor) {
|
|
return snippetManager.expandWithTab(editor);
|
|
},
|
|
bindKey: "Tab"
|
|
};
|
|
|
|
var onChangeMode = function(e, editor) {
|
|
loadSnippetsForMode(editor.session.$mode);
|
|
};
|
|
|
|
var loadSnippetsForMode = function(mode) {
|
|
var id = mode.$id;
|
|
if (!snippetManager.files)
|
|
snippetManager.files = {};
|
|
loadSnippetFile(id);
|
|
if (mode.modes)
|
|
mode.modes.forEach(loadSnippetsForMode);
|
|
};
|
|
|
|
var loadSnippetFile = function(id) {
|
|
if (!id || snippetManager.files[id])
|
|
return;
|
|
var snippetFilePath = id.replace("mode", "snippets");
|
|
snippetManager.files[id] = {};
|
|
config.loadModule(snippetFilePath, function(m) {
|
|
if (m) {
|
|
snippetManager.files[id] = m;
|
|
if (!m.snippets && m.snippetText)
|
|
m.snippets = snippetManager.parseSnippetFile(m.snippetText);
|
|
snippetManager.register(m.snippets || [], m.scope);
|
|
if (m.includeScopes) {
|
|
snippetManager.snippetMap[m.scope].includeScopes = m.includeScopes;
|
|
m.includeScopes.forEach(function(x) {
|
|
loadSnippetFile("ace/mode/" + x);
|
|
});
|
|
}
|
|
}
|
|
});
|
|
};
|
|
|
|
var doLiveAutocomplete = function(e) {
|
|
var editor = e.editor;
|
|
var hasCompleter = editor.completer && editor.completer.activated;
|
|
if (e.command.name === "backspace") {
|
|
if (hasCompleter && !util.getCompletionPrefix(editor))
|
|
editor.completer.detach();
|
|
}
|
|
else if (e.command.name === "insertstring") {
|
|
var prefix = util.getCompletionPrefix(editor);
|
|
if (prefix && !hasCompleter) {
|
|
if (!editor.completer) {
|
|
editor.completer = new Autocomplete();
|
|
}
|
|
editor.completer.autoInsert = false;
|
|
editor.completer.showPopup(editor);
|
|
}
|
|
}
|
|
};
|
|
|
|
var Editor = require("../editor").Editor;
|
|
require("../config").defineOptions(Editor.prototype, "editor", {
|
|
enableBasicAutocompletion: {
|
|
set: function(val) {
|
|
if (val) {
|
|
if (!this.completers)
|
|
this.completers = Array.isArray(val)? val: completers;
|
|
this.commands.addCommand(Autocomplete.startCommand);
|
|
} else {
|
|
this.commands.removeCommand(Autocomplete.startCommand);
|
|
}
|
|
},
|
|
value: false
|
|
},
|
|
enableLiveAutocompletion: {
|
|
set: function(val) {
|
|
if (val) {
|
|
if (!this.completers)
|
|
this.completers = Array.isArray(val)? val: completers;
|
|
this.commands.on('afterExec', doLiveAutocomplete);
|
|
} else {
|
|
this.commands.removeListener('afterExec', doLiveAutocomplete);
|
|
}
|
|
},
|
|
value: false
|
|
},
|
|
enableSnippets: {
|
|
set: function(val) {
|
|
if (val) {
|
|
this.commands.addCommand(expandSnippet);
|
|
this.on("changeMode", onChangeMode);
|
|
onChangeMode(null, this);
|
|
} else {
|
|
this.commands.removeCommand(expandSnippet);
|
|
this.off("changeMode", onChangeMode);
|
|
}
|
|
},
|
|
value: false
|
|
}
|
|
});
|
|
});
|
|
|
|
define("ace/ext/beautify/php_rules",["require","exports","module","ace/token_iterator"], function(require, exports, module) {
|
|
"use strict";
|
|
var TokenIterator = require("ace/token_iterator").TokenIterator;
|
|
exports.newLines = [{
|
|
type: 'support.php_tag',
|
|
value: '<?php'
|
|
}, {
|
|
type: 'support.php_tag',
|
|
value: '<?'
|
|
}, {
|
|
type: 'support.php_tag',
|
|
value: '?>'
|
|
}, {
|
|
type: 'paren.lparen',
|
|
value: '{',
|
|
indent: true
|
|
}, {
|
|
type: 'paren.rparen',
|
|
breakBefore: true,
|
|
value: '}',
|
|
indent: false
|
|
}, {
|
|
type: 'paren.rparen',
|
|
breakBefore: true,
|
|
value: '})',
|
|
indent: false,
|
|
dontBreak: true
|
|
}, {
|
|
type: 'comment'
|
|
}, {
|
|
type: 'text',
|
|
value: ';'
|
|
}, {
|
|
type: 'text',
|
|
value: ':',
|
|
context: 'php'
|
|
}, {
|
|
type: 'keyword',
|
|
value: 'case',
|
|
indent: true,
|
|
dontBreak: true
|
|
}, {
|
|
type: 'keyword',
|
|
value: 'default',
|
|
indent: true,
|
|
dontBreak: true
|
|
}, {
|
|
type: 'keyword',
|
|
value: 'break',
|
|
indent: false,
|
|
dontBreak: true
|
|
}, {
|
|
type: 'punctuation.doctype.end',
|
|
value: '>'
|
|
}, {
|
|
type: 'meta.tag.punctuation.end',
|
|
value: '>'
|
|
}, {
|
|
type: 'meta.tag.punctuation.begin',
|
|
value: '<',
|
|
blockTag: true,
|
|
indent: true,
|
|
dontBreak: true
|
|
}, {
|
|
type: 'meta.tag.punctuation.begin',
|
|
value: '</',
|
|
indent: false,
|
|
breakBefore: true,
|
|
dontBreak: true
|
|
}, {
|
|
type: 'punctuation.operator',
|
|
value: ';'
|
|
}];
|
|
|
|
exports.spaces = [{
|
|
type: 'xml-pe',
|
|
prepend: true
|
|
},{
|
|
type: 'entity.other.attribute-name',
|
|
prepend: true
|
|
}, {
|
|
type: 'storage.type',
|
|
value: 'var',
|
|
append: true
|
|
}, {
|
|
type: 'storage.type',
|
|
value: 'function',
|
|
append: true
|
|
}, {
|
|
type: 'keyword.operator',
|
|
value: '='
|
|
}, {
|
|
type: 'keyword',
|
|
value: 'as',
|
|
prepend: true,
|
|
append: true
|
|
}, {
|
|
type: 'keyword',
|
|
value: 'function',
|
|
append: true
|
|
}, {
|
|
type: 'support.function',
|
|
next: /[^\(]/,
|
|
append: true
|
|
}, {
|
|
type: 'keyword',
|
|
value: 'or',
|
|
append: true,
|
|
prepend: true
|
|
}, {
|
|
type: 'keyword',
|
|
value: 'and',
|
|
append: true,
|
|
prepend: true
|
|
}, {
|
|
type: 'keyword',
|
|
value: 'case',
|
|
append: true
|
|
}, {
|
|
type: 'keyword.operator',
|
|
value: '||',
|
|
append: true,
|
|
prepend: true
|
|
}, {
|
|
type: 'keyword.operator',
|
|
value: '&&',
|
|
append: true,
|
|
prepend: true
|
|
}];
|
|
exports.singleTags = ['!doctype','area','base','br','hr','input','img','link','meta'];
|
|
|
|
exports.transform = function(iterator, maxPos, context) {
|
|
var token = iterator.getCurrentToken();
|
|
|
|
var newLines = exports.newLines;
|
|
var spaces = exports.spaces;
|
|
var singleTags = exports.singleTags;
|
|
|
|
var code = '';
|
|
|
|
var indentation = 0;
|
|
var dontBreak = false;
|
|
var tag;
|
|
var lastTag;
|
|
var lastToken = {};
|
|
var nextTag;
|
|
var nextToken = {};
|
|
var breakAdded = false;
|
|
var value = '';
|
|
|
|
while (token!==null) {
|
|
console.log(token);
|
|
|
|
if( !token ){
|
|
token = iterator.stepForward();
|
|
continue;
|
|
}
|
|
if( token.type == 'support.php_tag' && token.value != '?>' ){
|
|
context = 'php';
|
|
}
|
|
else if( token.type == 'support.php_tag' && token.value == '?>' ){
|
|
context = 'html';
|
|
}
|
|
else if( token.type == 'meta.tag.name.style' && context != 'css' ){
|
|
context = 'css';
|
|
}
|
|
else if( token.type == 'meta.tag.name.style' && context == 'css' ){
|
|
context = 'html';
|
|
}
|
|
else if( token.type == 'meta.tag.name.script' && context != 'js' ){
|
|
context = 'js';
|
|
}
|
|
else if( token.type == 'meta.tag.name.script' && context == 'js' ){
|
|
context = 'html';
|
|
}
|
|
|
|
nextToken = iterator.stepForward();
|
|
if (nextToken && nextToken.type.indexOf('meta.tag.name') == 0) {
|
|
nextTag = nextToken.value;
|
|
}
|
|
if ( lastToken.type == 'support.php_tag' && lastToken.value == '<?=') {
|
|
dontBreak = true;
|
|
}
|
|
if (token.type == 'meta.tag.name') {
|
|
token.value = token.value.toLowerCase();
|
|
}
|
|
if (token.type == 'text') {
|
|
token.value = token.value.trim();
|
|
}
|
|
if (!token.value) {
|
|
token = nextToken;
|
|
continue;
|
|
}
|
|
value = token.value;
|
|
for (var i in spaces) {
|
|
if (
|
|
token.type == spaces[i].type &&
|
|
(!spaces[i].value || token.value == spaces[i].value) &&
|
|
(
|
|
nextToken &&
|
|
(!spaces[i].next || spaces[i].next.test(nextToken.value))
|
|
)
|
|
) {
|
|
if (spaces[i].prepend) {
|
|
value = ' ' + token.value;
|
|
}
|
|
|
|
if (spaces[i].append) {
|
|
value += ' ';
|
|
}
|
|
}
|
|
}
|
|
if (token.type.indexOf('meta.tag.name') == 0) {
|
|
tag = token.value;
|
|
}
|
|
breakAdded = false;
|
|
for (i in newLines) {
|
|
if (
|
|
token.type == newLines[i].type &&
|
|
(
|
|
!newLines[i].value ||
|
|
token.value == newLines[i].value
|
|
) &&
|
|
(
|
|
!newLines[i].blockTag ||
|
|
singleTags.indexOf(nextTag) === -1
|
|
) &&
|
|
(
|
|
!newLines[i].context ||
|
|
newLines[i].context === context
|
|
)
|
|
) {
|
|
if (newLines[i].indent === false) {
|
|
indentation--;
|
|
}
|
|
|
|
if (
|
|
newLines[i].breakBefore &&
|
|
( !newLines[i].prev || newLines[i].prev.test(lastToken.value) )
|
|
) {
|
|
code += "\n";
|
|
breakAdded = true;
|
|
for (i = 0; i < indentation; i++) {
|
|
code += "\t";
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (dontBreak===false) {
|
|
for (i in newLines) {
|
|
if (
|
|
lastToken.type == newLines[i].type &&
|
|
(
|
|
!newLines[i].value || lastToken.value == newLines[i].value
|
|
) &&
|
|
(
|
|
!newLines[i].blockTag ||
|
|
singleTags.indexOf(tag) === -1
|
|
) &&
|
|
(
|
|
!newLines[i].context ||
|
|
newLines[i].context === context
|
|
)
|
|
) {
|
|
if (newLines[i].indent === true) {
|
|
indentation++;
|
|
}
|
|
|
|
if (!newLines[i].dontBreak && !breakAdded) {
|
|
code += "\n";
|
|
for (i = 0; i < indentation; i++) {
|
|
code += "\t";
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
code += value;
|
|
if ( lastToken.type == 'support.php_tag' && lastToken.value == '?>' ) {
|
|
dontBreak = false;
|
|
}
|
|
lastTag = tag;
|
|
|
|
lastToken = token;
|
|
|
|
token = nextToken;
|
|
|
|
if (token===null) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
return code;
|
|
};
|
|
|
|
|
|
|
|
});
|
|
|
|
define("ace/ext/beautify",["require","exports","module","ace/token_iterator","ace/ext/beautify/php_rules"], function(require, exports, module) {
|
|
"use strict";
|
|
var TokenIterator = require("ace/token_iterator").TokenIterator;
|
|
|
|
var phpTransform = require("./beautify/php_rules").transform;
|
|
|
|
exports.beautify = function(session) {
|
|
var iterator = new TokenIterator(session, 0, 0);
|
|
var token = iterator.getCurrentToken();
|
|
|
|
var context = session.$modeId.split("/").pop();
|
|
|
|
var code = phpTransform(iterator, context);
|
|
session.doc.setValue(code);
|
|
};
|
|
|
|
exports.commands = [{
|
|
name: "beautify",
|
|
exec: function(editor) {
|
|
exports.beautify(editor.session);
|
|
},
|
|
bindKey: "Ctrl-Shift-B"
|
|
}]
|
|
|
|
});
|
|
|
|
define("kitchen-sink/demo",["require","exports","module","ace/lib/fixoldbrowsers","ace/multi_select","ace/ext/spellcheck","kitchen-sink/inline_editor","kitchen-sink/dev_util","kitchen-sink/file_drop","ace/config","ace/lib/dom","ace/lib/net","ace/lib/lang","ace/lib/useragent","ace/lib/event","ace/theme/textmate","ace/edit_session","ace/undomanager","ace/keyboard/hash_handler","ace/virtual_renderer","ace/editor","ace/ext/whitespace","kitchen-sink/doclist","ace/ext/modelist","ace/ext/themelist","kitchen-sink/layout","kitchen-sink/token_tooltip","kitchen-sink/util","ace/ext/elastic_tabstops_lite","ace/incremental_search","ace/worker/worker_client","ace/split","ace/keyboard/vim","ace/ext/statusbar","ace/ext/emmet","ace/snippets","ace/ext/language_tools","ace/ext/beautify","ace/keyboard/keybinding","ace/commands/command_manager"], function(require, exports, module) {
|
|
"use strict";
|
|
|
|
require("ace/lib/fixoldbrowsers");
|
|
|
|
require("ace/multi_select");
|
|
require("ace/ext/spellcheck");
|
|
require("./inline_editor");
|
|
require("./dev_util");
|
|
require("./file_drop");
|
|
|
|
var config = require("ace/config");
|
|
config.init();
|
|
var env = {};
|
|
|
|
var dom = require("ace/lib/dom");
|
|
var net = require("ace/lib/net");
|
|
var lang = require("ace/lib/lang");
|
|
var useragent = require("ace/lib/useragent");
|
|
|
|
var event = require("ace/lib/event");
|
|
var theme = require("ace/theme/textmate");
|
|
var EditSession = require("ace/edit_session").EditSession;
|
|
var UndoManager = require("ace/undomanager").UndoManager;
|
|
|
|
var HashHandler = require("ace/keyboard/hash_handler").HashHandler;
|
|
|
|
var Renderer = require("ace/virtual_renderer").VirtualRenderer;
|
|
var Editor = require("ace/editor").Editor;
|
|
|
|
var whitespace = require("ace/ext/whitespace");
|
|
|
|
|
|
|
|
var doclist = require("./doclist");
|
|
var modelist = require("ace/ext/modelist");
|
|
var themelist = require("ace/ext/themelist");
|
|
var layout = require("./layout");
|
|
var TokenTooltip = require("./token_tooltip").TokenTooltip;
|
|
var util = require("./util");
|
|
var saveOption = util.saveOption;
|
|
var fillDropdown = util.fillDropdown;
|
|
var bindCheckbox = util.bindCheckbox;
|
|
var bindDropdown = util.bindDropdown;
|
|
|
|
var ElasticTabstopsLite = require("ace/ext/elastic_tabstops_lite").ElasticTabstopsLite;
|
|
|
|
var IncrementalSearch = require("ace/incremental_search").IncrementalSearch;
|
|
|
|
|
|
var workerModule = require("ace/worker/worker_client");
|
|
if (location.href.indexOf("noworker") !== -1) {
|
|
workerModule.WorkerClient = workerModule.UIWorkerClient;
|
|
}
|
|
var container = document.getElementById("editor-container");
|
|
var Split = require("ace/split").Split;
|
|
var split = new Split(container, theme, 1);
|
|
env.editor = split.getEditor(0);
|
|
split.on("focus", function(editor) {
|
|
env.editor = editor;
|
|
updateUIEditorOptions();
|
|
});
|
|
env.split = split;
|
|
window.env = env;
|
|
|
|
|
|
var consoleEl = dom.createElement("div");
|
|
container.parentNode.appendChild(consoleEl);
|
|
consoleEl.style.cssText = "position:fixed; bottom:1px; right:0;\
|
|
border:1px solid #baf; z-index:100";
|
|
|
|
var cmdLine = new layout.singleLineEditor(consoleEl);
|
|
cmdLine.editor = env.editor;
|
|
env.editor.cmdLine = cmdLine;
|
|
|
|
env.editor.showCommandLine = function(val) {
|
|
this.cmdLine.focus();
|
|
if (typeof val == "string")
|
|
this.cmdLine.setValue(val, 1);
|
|
};
|
|
env.editor.commands.addCommands([{
|
|
name: "gotoline",
|
|
bindKey: {win: "Ctrl-L", mac: "Command-L"},
|
|
exec: function(editor, line) {
|
|
if (typeof line == "object") {
|
|
var arg = this.name + " " + editor.getCursorPosition().row;
|
|
editor.cmdLine.setValue(arg, 1);
|
|
editor.cmdLine.focus();
|
|
return;
|
|
}
|
|
line = parseInt(line, 10);
|
|
if (!isNaN(line))
|
|
editor.gotoLine(line);
|
|
},
|
|
readOnly: true
|
|
}, {
|
|
name: "snippet",
|
|
bindKey: {win: "Alt-C", mac: "Command-Alt-C"},
|
|
exec: function(editor, needle) {
|
|
if (typeof needle == "object") {
|
|
editor.cmdLine.setValue("snippet ", 1);
|
|
editor.cmdLine.focus();
|
|
return;
|
|
}
|
|
var s = snippetManager.getSnippetByName(needle, editor);
|
|
if (s)
|
|
snippetManager.insertSnippet(editor, s.content);
|
|
},
|
|
readOnly: true
|
|
}, {
|
|
name: "focusCommandLine",
|
|
bindKey: "shift-esc|ctrl-`",
|
|
exec: function(editor, needle) { editor.cmdLine.focus(); },
|
|
readOnly: true
|
|
}, {
|
|
name: "nextFile",
|
|
bindKey: "Ctrl-tab",
|
|
exec: function(editor) { doclist.cycleOpen(editor, 1); },
|
|
readOnly: true
|
|
}, {
|
|
name: "previousFile",
|
|
bindKey: "Ctrl-shift-tab",
|
|
exec: function(editor) { doclist.cycleOpen(editor, -1); },
|
|
readOnly: true
|
|
}, {
|
|
name: "execute",
|
|
bindKey: "ctrl+enter",
|
|
exec: function(editor) {
|
|
try {
|
|
var r = window.eval(editor.getCopyText() || editor.getValue());
|
|
} catch(e) {
|
|
r = e;
|
|
}
|
|
editor.cmdLine.setValue(r + "");
|
|
},
|
|
readOnly: true
|
|
}, {
|
|
name: "showKeyboardShortcuts",
|
|
bindKey: {win: "Ctrl-Alt-h", mac: "Command-Alt-h"},
|
|
exec: function(editor) {
|
|
config.loadModule("ace/ext/keybinding_menu", function(module) {
|
|
module.init(editor);
|
|
editor.showKeyboardShortcuts();
|
|
});
|
|
}
|
|
}, {
|
|
name: "increaseFontSize",
|
|
bindKey: "Ctrl-=|Ctrl-+",
|
|
exec: function(editor) {
|
|
var size = parseInt(editor.getFontSize(), 10) || 12;
|
|
editor.setFontSize(size + 1);
|
|
}
|
|
}, {
|
|
name: "decreaseFontSize",
|
|
bindKey: "Ctrl+-|Ctrl-_",
|
|
exec: function(editor) {
|
|
var size = parseInt(editor.getFontSize(), 10) || 12;
|
|
editor.setFontSize(Math.max(size - 1 || 1));
|
|
}
|
|
}, {
|
|
name: "resetFontSize",
|
|
bindKey: "Ctrl+0|Ctrl-Numpad0",
|
|
exec: function(editor) {
|
|
editor.setFontSize(12);
|
|
}
|
|
}]);
|
|
|
|
|
|
env.editor.commands.addCommands(whitespace.commands);
|
|
|
|
cmdLine.commands.bindKeys({
|
|
"Shift-Return|Ctrl-Return|Alt-Return": function(cmdLine) { cmdLine.insert("\n"); },
|
|
"Esc|Shift-Esc": function(cmdLine){ cmdLine.editor.focus(); },
|
|
"Return": function(cmdLine){
|
|
var command = cmdLine.getValue().split(/\s+/);
|
|
var editor = cmdLine.editor;
|
|
editor.commands.exec(command[0], editor, command[1]);
|
|
editor.focus();
|
|
}
|
|
});
|
|
|
|
cmdLine.commands.removeCommands(["find", "gotoline", "findall", "replace", "replaceall"]);
|
|
|
|
var commands = env.editor.commands;
|
|
commands.addCommand({
|
|
name: "save",
|
|
bindKey: {win: "Ctrl-S", mac: "Command-S"},
|
|
exec: function(arg) {
|
|
var session = env.editor.session;
|
|
var name = session.name.match(/[^\/]+$/);
|
|
localStorage.setItem(
|
|
"saved_file:" + name,
|
|
session.getValue()
|
|
);
|
|
env.editor.cmdLine.setValue("saved "+ name);
|
|
}
|
|
});
|
|
|
|
commands.addCommand({
|
|
name: "load",
|
|
bindKey: {win: "Ctrl-O", mac: "Command-O"},
|
|
exec: function(arg) {
|
|
var session = env.editor.session;
|
|
var name = session.name.match(/[^\/]+$/);
|
|
var value = localStorage.getItem("saved_file:" + name);
|
|
if (typeof value == "string") {
|
|
session.setValue(value);
|
|
env.editor.cmdLine.setValue("loaded "+ name);
|
|
} else {
|
|
env.editor.cmdLine.setValue("no previuos value saved for "+ name);
|
|
}
|
|
}
|
|
});
|
|
|
|
var keybindings = {
|
|
ace: null, // Null = use "default" keymapping
|
|
vim: require("ace/keyboard/vim").handler,
|
|
emacs: "ace/keyboard/emacs",
|
|
custom: new HashHandler({
|
|
"gotoright": "Tab",
|
|
"indent": "]",
|
|
"outdent": "[",
|
|
"gotolinestart": "^",
|
|
"gotolineend": "$"
|
|
})
|
|
};
|
|
var consoleHeight = 20;
|
|
function onResize() {
|
|
var left = env.split.$container.offsetLeft;
|
|
var width = document.documentElement.clientWidth - left;
|
|
container.style.width = width + "px";
|
|
container.style.height = document.documentElement.clientHeight - consoleHeight + "px";
|
|
env.split.resize();
|
|
|
|
consoleEl.style.width = width + "px";
|
|
cmdLine.resize();
|
|
}
|
|
|
|
window.onresize = onResize;
|
|
onResize();
|
|
var docEl = document.getElementById("doc");
|
|
var modeEl = document.getElementById("mode");
|
|
var wrapModeEl = document.getElementById("soft_wrap");
|
|
var themeEl = document.getElementById("theme");
|
|
var foldingEl = document.getElementById("folding");
|
|
var selectStyleEl = document.getElementById("select_style");
|
|
var highlightActiveEl = document.getElementById("highlight_active");
|
|
var showHiddenEl = document.getElementById("show_hidden");
|
|
var showGutterEl = document.getElementById("show_gutter");
|
|
var showPrintMarginEl = document.getElementById("show_print_margin");
|
|
var highlightSelectedWordE = document.getElementById("highlight_selected_word");
|
|
var showHScrollEl = document.getElementById("show_hscroll");
|
|
var showVScrollEl = document.getElementById("show_vscroll");
|
|
var animateScrollEl = document.getElementById("animate_scroll");
|
|
var softTabEl = document.getElementById("soft_tab");
|
|
var navigateWithinSoftTabEl = document.getElementById("navigate_within_soft_tab");
|
|
var behavioursEl = document.getElementById("enable_behaviours");
|
|
|
|
fillDropdown(docEl, doclist.all);
|
|
|
|
fillDropdown(modeEl, modelist.modes);
|
|
var modesByName = modelist.modesByName;
|
|
bindDropdown("mode", function(value) {
|
|
env.editor.session.setMode(modesByName[value].mode || modesByName.text.mode);
|
|
env.editor.session.modeName = value;
|
|
});
|
|
|
|
doclist.history = doclist.docs.map(function(doc) {
|
|
return doc.name;
|
|
});
|
|
doclist.history.index = 0;
|
|
doclist.cycleOpen = function(editor, dir) {
|
|
var h = this.history;
|
|
h.index += dir;
|
|
if (h.index >= h.length)
|
|
h.index = 0;
|
|
else if (h.index <= 0)
|
|
h.index = h.length - 1;
|
|
var s = h[h.index];
|
|
docEl.value = s;
|
|
docEl.onchange();
|
|
};
|
|
doclist.addToHistory = function(name) {
|
|
var h = this.history;
|
|
var i = h.indexOf(name);
|
|
if (i != h.index) {
|
|
if (i != -1)
|
|
h.splice(i, 1);
|
|
h.index = h.push(name);
|
|
}
|
|
};
|
|
|
|
bindDropdown("doc", function(name) {
|
|
doclist.loadDoc(name, function(session) {
|
|
if (!session)
|
|
return;
|
|
doclist.addToHistory(session.name);
|
|
session = env.split.setSession(session);
|
|
whitespace.detectIndentation(session);
|
|
updateUIEditorOptions();
|
|
env.editor.focus();
|
|
});
|
|
});
|
|
|
|
|
|
function updateUIEditorOptions() {
|
|
var editor = env.editor;
|
|
var session = editor.session;
|
|
|
|
session.setFoldStyle(foldingEl.value);
|
|
|
|
saveOption(docEl, session.name);
|
|
saveOption(modeEl, session.modeName || "text");
|
|
saveOption(wrapModeEl, session.getUseWrapMode() ? session.getWrapLimitRange().min || "free" : "off");
|
|
|
|
saveOption(selectStyleEl, editor.getSelectionStyle() == "line");
|
|
saveOption(themeEl, editor.getTheme());
|
|
saveOption(highlightActiveEl, editor.getHighlightActiveLine());
|
|
saveOption(showHiddenEl, editor.getShowInvisibles());
|
|
saveOption(showGutterEl, editor.renderer.getShowGutter());
|
|
saveOption(showPrintMarginEl, editor.renderer.getShowPrintMargin());
|
|
saveOption(highlightSelectedWordE, editor.getHighlightSelectedWord());
|
|
saveOption(showHScrollEl, editor.renderer.getHScrollBarAlwaysVisible());
|
|
saveOption(animateScrollEl, editor.getAnimatedScroll());
|
|
saveOption(softTabEl, session.getUseSoftTabs());
|
|
saveOption(navigateWithinSoftTabEl, session.getNavigateWithinSoftTabs());
|
|
saveOption(behavioursEl, editor.getBehavioursEnabled());
|
|
}
|
|
|
|
themelist.themes.forEach(function(x){ x.value = x.theme });
|
|
fillDropdown(themeEl, {
|
|
Bright: themelist.themes.filter(function(x){return !x.isDark}),
|
|
Dark: themelist.themes.filter(function(x){return x.isDark})
|
|
});
|
|
|
|
event.addListener(themeEl, "mouseover", function(e){
|
|
themeEl.desiredValue = e.target.value;
|
|
if (!themeEl.$timer)
|
|
themeEl.$timer = setTimeout(themeEl.updateTheme);
|
|
});
|
|
|
|
event.addListener(themeEl, "mouseout", function(e){
|
|
themeEl.desiredValue = null;
|
|
if (!themeEl.$timer)
|
|
themeEl.$timer = setTimeout(themeEl.updateTheme, 20);
|
|
});
|
|
|
|
themeEl.updateTheme = function(){
|
|
env.split.setTheme((themeEl.desiredValue || themeEl.selectedValue));
|
|
themeEl.$timer = null;
|
|
};
|
|
|
|
bindDropdown("theme", function(value) {
|
|
if (!value)
|
|
return;
|
|
env.editor.setTheme(value);
|
|
themeEl.selectedValue = value;
|
|
});
|
|
|
|
bindDropdown("keybinding", function(value) {
|
|
env.editor.setKeyboardHandler(keybindings[value]);
|
|
});
|
|
|
|
bindDropdown("fontsize", function(value) {
|
|
env.split.setFontSize(value);
|
|
});
|
|
|
|
bindDropdown("folding", function(value) {
|
|
env.editor.session.setFoldStyle(value);
|
|
env.editor.setShowFoldWidgets(value !== "manual");
|
|
});
|
|
|
|
bindDropdown("soft_wrap", function(value) {
|
|
env.editor.setOption("wrap", value);
|
|
});
|
|
|
|
bindCheckbox("select_style", function(checked) {
|
|
env.editor.setOption("selectionStyle", checked ? "line" : "text");
|
|
});
|
|
|
|
bindCheckbox("highlight_active", function(checked) {
|
|
env.editor.setHighlightActiveLine(checked);
|
|
});
|
|
|
|
bindCheckbox("show_hidden", function(checked) {
|
|
env.editor.setShowInvisibles(checked);
|
|
});
|
|
|
|
bindCheckbox("display_indent_guides", function(checked) {
|
|
env.editor.setDisplayIndentGuides(checked);
|
|
});
|
|
|
|
bindCheckbox("show_gutter", function(checked) {
|
|
env.editor.renderer.setShowGutter(checked);
|
|
});
|
|
|
|
bindCheckbox("show_print_margin", function(checked) {
|
|
env.editor.renderer.setShowPrintMargin(checked);
|
|
});
|
|
|
|
bindCheckbox("highlight_selected_word", function(checked) {
|
|
env.editor.setHighlightSelectedWord(checked);
|
|
});
|
|
|
|
bindCheckbox("show_hscroll", function(checked) {
|
|
env.editor.setOption("hScrollBarAlwaysVisible", checked);
|
|
});
|
|
|
|
bindCheckbox("show_vscroll", function(checked) {
|
|
env.editor.setOption("vScrollBarAlwaysVisible", checked);
|
|
});
|
|
|
|
bindCheckbox("animate_scroll", function(checked) {
|
|
env.editor.setAnimatedScroll(checked);
|
|
});
|
|
|
|
bindCheckbox("soft_tab", function(checked) {
|
|
env.editor.session.setUseSoftTabs(checked);
|
|
});
|
|
|
|
bindCheckbox("navigate_within_soft_tab", function(checked) {
|
|
env.editor.session.setNavigateWithinSoftTabs(checked);
|
|
});
|
|
|
|
bindCheckbox("enable_behaviours", function(checked) {
|
|
env.editor.setBehavioursEnabled(checked);
|
|
});
|
|
|
|
bindCheckbox("fade_fold_widgets", function(checked) {
|
|
env.editor.setFadeFoldWidgets(checked);
|
|
});
|
|
bindCheckbox("read_only", function(checked) {
|
|
env.editor.setReadOnly(checked);
|
|
});
|
|
bindCheckbox("scrollPastEnd", function(checked) {
|
|
env.editor.setOption("scrollPastEnd", checked);
|
|
});
|
|
|
|
bindDropdown("split", function(value) {
|
|
var sp = env.split;
|
|
if (value == "none") {
|
|
sp.setSplits(1);
|
|
} else {
|
|
var newEditor = (sp.getSplits() == 1);
|
|
sp.setOrientation(value == "below" ? sp.BELOW : sp.BESIDE);
|
|
sp.setSplits(2);
|
|
|
|
if (newEditor) {
|
|
var session = sp.getEditor(0).session;
|
|
var newSession = sp.setSession(session, 1);
|
|
newSession.name = session.name;
|
|
}
|
|
}
|
|
});
|
|
|
|
|
|
bindCheckbox("elastic_tabstops", function(checked) {
|
|
env.editor.setOption("useElasticTabstops", checked);
|
|
});
|
|
|
|
var iSearchCheckbox = bindCheckbox("isearch", function(checked) {
|
|
env.editor.setOption("useIncrementalSearch", checked);
|
|
});
|
|
|
|
env.editor.addEventListener('incrementalSearchSettingChanged', function(event) {
|
|
iSearchCheckbox.checked = event.isEnabled;
|
|
});
|
|
|
|
|
|
function synchroniseScrolling() {
|
|
var s1 = env.split.$editors[0].session;
|
|
var s2 = env.split.$editors[1].session;
|
|
s1.on('changeScrollTop', function(pos) {s2.setScrollTop(pos)});
|
|
s2.on('changeScrollTop', function(pos) {s1.setScrollTop(pos)});
|
|
s1.on('changeScrollLeft', function(pos) {s2.setScrollLeft(pos)});
|
|
s2.on('changeScrollLeft', function(pos) {s1.setScrollLeft(pos)});
|
|
}
|
|
|
|
bindCheckbox("highlight_token", function(checked) {
|
|
var editor = env.editor;
|
|
if (editor.tokenTooltip && !checked) {
|
|
editor.tokenTooltip.destroy();
|
|
delete editor.tokenTooltip;
|
|
} else if (checked) {
|
|
editor.tokenTooltip = new TokenTooltip(editor);
|
|
}
|
|
});
|
|
|
|
var StatusBar = require("ace/ext/statusbar").StatusBar;
|
|
new StatusBar(env.editor, cmdLine.container);
|
|
|
|
|
|
var Emmet = require("ace/ext/emmet");
|
|
net.loadScript("https://cloud9ide.github.io/emmet-core/emmet.js", function() {
|
|
Emmet.setCore(window.emmet);
|
|
env.editor.setOption("enableEmmet", true);
|
|
});
|
|
|
|
var snippetManager = require("ace/snippets").snippetManager;
|
|
|
|
env.editSnippets = function() {
|
|
var sp = env.split;
|
|
if (sp.getSplits() == 2) {
|
|
sp.setSplits(1);
|
|
return;
|
|
}
|
|
sp.setSplits(1);
|
|
sp.setSplits(2);
|
|
sp.setOrientation(sp.BESIDE);
|
|
var editor = sp.$editors[1];
|
|
var id = sp.$editors[0].session.$mode.$id || "";
|
|
var m = snippetManager.files[id];
|
|
if (!doclist["snippets/" + id]) {
|
|
var text = m.snippetText;
|
|
var s = doclist.initDoc(text, "", {});
|
|
s.setMode("ace/mode/snippets");
|
|
doclist["snippets/" + id] = s;
|
|
}
|
|
editor.on("blur", function() {
|
|
m.snippetText = editor.getValue();
|
|
snippetManager.unregister(m.snippets);
|
|
m.snippets = snippetManager.parseSnippetFile(m.snippetText, m.scope);
|
|
snippetManager.register(m.snippets);
|
|
});
|
|
sp.$editors[0].once("changeMode", function() {
|
|
sp.setSplits(1);
|
|
});
|
|
editor.setSession(doclist["snippets/" + id], 1);
|
|
editor.focus();
|
|
};
|
|
|
|
require("ace/ext/language_tools");
|
|
env.editor.setOptions({
|
|
enableBasicAutocompletion: true,
|
|
enableLiveAutocompletion: false,
|
|
enableSnippets: true
|
|
});
|
|
|
|
var beautify = require("ace/ext/beautify");
|
|
env.editor.commands.addCommands(beautify.commands);
|
|
|
|
var KeyBinding = require("ace/keyboard/keybinding").KeyBinding;
|
|
var CommandManager = require("ace/commands/command_manager").CommandManager;
|
|
var commandManager = new CommandManager();
|
|
var kb = new KeyBinding({
|
|
commands: commandManager,
|
|
fake: true
|
|
});
|
|
event.addCommandKeyListener(document.documentElement, kb.onCommandKey.bind(kb));
|
|
event.addListener(document.documentElement, "keyup", function(e) {
|
|
if (e.keyCode === 18) // do not trigger browser menu on windows
|
|
e.preventDefault();
|
|
});
|
|
commandManager.addCommands([{
|
|
name: "window-left",
|
|
bindKey: {win: "cmd-alt-left", mac: "ctrl-cmd-left"},
|
|
exec: function() {
|
|
moveFocus("left");
|
|
}
|
|
}, {
|
|
name: "window-right",
|
|
bindKey: {win: "cmd-alt-right", mac: "ctrl-cmd-right"},
|
|
exec: function() {
|
|
moveFocus("right");
|
|
}
|
|
}, {
|
|
name: "window-up",
|
|
bindKey: {win: "cmd-alt-up", mac: "ctrl-cmd-up"},
|
|
exec: function() {
|
|
moveFocus("up");
|
|
}
|
|
}, {
|
|
name: "window-down",
|
|
bindKey: {win: "cmd-alt-down", mac: "ctrl-cmd-down"},
|
|
exec: function() {
|
|
moveFocus("down");
|
|
}
|
|
}]);
|
|
|
|
function moveFocus() {
|
|
var el = document.activeElement;
|
|
if (el == env.editor.textInput.getElement())
|
|
env.editor.cmdLine.focus();
|
|
else
|
|
env.editor.focus();
|
|
}
|
|
|
|
});
|