include all plugins in c9/core
|
@ -0,0 +1,21 @@
|
|||
|
||||
Copyright (c) 2015, Ajax.org B.V.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
|
@ -0,0 +1 @@
|
|||
# c9.ide.automate
|
|
@ -0,0 +1,289 @@
|
|||
define(function(require, exports, module) {
|
||||
main.consumes = ["Plugin", "c9"];
|
||||
main.provides = ["automate"];
|
||||
return main;
|
||||
|
||||
function main(options, imports, register) {
|
||||
var Plugin = imports.Plugin;
|
||||
var c9 = imports.c9;
|
||||
|
||||
var async = require("async");
|
||||
|
||||
/***** Initialization *****/
|
||||
|
||||
var plugin = new Plugin("Ajax.org", main.consumes);
|
||||
// var emit = plugin.getEmitter();
|
||||
|
||||
var namespaces = {};
|
||||
|
||||
/***** Methods *****/
|
||||
|
||||
function addCommand(ns, name, implementation) {
|
||||
if (!namespaces[ns]) namespaces[ns] = { commands: {}, alias: {}};
|
||||
|
||||
namespaces[ns].commands[name] = implementation;
|
||||
}
|
||||
|
||||
function removeCommand(ns, name) {
|
||||
if (!namespaces[ns]) namespaces[ns] = { commands: {}, alias: {}};
|
||||
|
||||
delete namespaces[ns].commands[name];
|
||||
}
|
||||
|
||||
function addCommandAlias(ns, name) {
|
||||
if (!namespaces[ns]) namespaces[ns] = { commands: {}, alias: {}};
|
||||
|
||||
for (var i = 1; i < arguments.length; i++) {
|
||||
namespaces[ns].alias[arguments[i]] = name;
|
||||
}
|
||||
}
|
||||
|
||||
function getCommand(ns, name) {
|
||||
if (!namespaces[ns]) throw new Error("Unknown namespace: " + ns);
|
||||
|
||||
var cmd = namespaces[ns].commands;
|
||||
return cmd[name] || cmd[namespaces[ns].alias[name]];
|
||||
}
|
||||
|
||||
function createSession(ns) {
|
||||
var session = new Plugin("Ajax.org", main.consumes);
|
||||
var emit = session.getEmitter();
|
||||
|
||||
var tasks = [];
|
||||
var output = "";
|
||||
var lastProcess;
|
||||
var lastTask;
|
||||
var executing = false;
|
||||
var aborting = false;
|
||||
|
||||
function task(task, options, validate) {
|
||||
if (executing) throw new Error("Adding tasks while executing");
|
||||
|
||||
if (typeof options == "function" || options === undefined) {
|
||||
if (!validate) validate = options;
|
||||
options = {};
|
||||
}
|
||||
|
||||
Object.defineProperty(task, "$options", {
|
||||
enumerable: false,
|
||||
configurable: false,
|
||||
writable: false,
|
||||
value: options
|
||||
});
|
||||
|
||||
tasks.push(task);
|
||||
}
|
||||
|
||||
function execute(tasks, callback, options) {
|
||||
if (tasks.$options)
|
||||
options = tasks.$options;
|
||||
|
||||
// Loop over all tasks or sub-tasks when called recursively
|
||||
async.eachSeries(tasks, function(task, next) {
|
||||
options = task.$options || options || {};
|
||||
|
||||
if (options.ignore)
|
||||
return next();
|
||||
|
||||
// The task consists of multiple tasks
|
||||
if (Array.isArray(task))
|
||||
return execute(task, next, options);
|
||||
|
||||
// Loop over all competing tasks
|
||||
async.eachSeries(Object.keys(task), function(type, next) {
|
||||
var s = type.split(":");
|
||||
if (s.length > 1)
|
||||
s = { ns: s[0], type: s[1] };
|
||||
|
||||
if (type == "install") {
|
||||
return execute(task[type], function(err) {
|
||||
next(err || 1);
|
||||
}, options);
|
||||
}
|
||||
|
||||
var command = getCommand(s.ns || ns, s.type || type);
|
||||
command.isAvailable(function(available) {
|
||||
if (!available) return next();
|
||||
|
||||
var items = Array.isArray(task[type])
|
||||
? task[type] : [task[type]];
|
||||
|
||||
// Loop over each of the tasks for this command
|
||||
async.eachSeries(items, function(item, next) {
|
||||
if (aborting)
|
||||
return next(new Error("Aborted"));
|
||||
|
||||
emit("each", {
|
||||
session: session,
|
||||
task: task,
|
||||
options: options,
|
||||
type: type,
|
||||
item: item
|
||||
});
|
||||
|
||||
lastTask = task;
|
||||
|
||||
var onData = function(chunk, process) {
|
||||
if (aborting) return process && process.end();
|
||||
|
||||
output += chunk;
|
||||
lastProcess = process;
|
||||
emit("data", { data: chunk, process: process });
|
||||
};
|
||||
|
||||
var onFinish = function(err) {
|
||||
if (err && err.code == "EDISCONNECT") {
|
||||
c9.once("connect", function() {
|
||||
command.execute(item, options, onData, onFinish);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
next(err);
|
||||
};
|
||||
|
||||
command.execute(item, options, onData, onFinish);
|
||||
}, function(err) {
|
||||
next(err || 1);
|
||||
});
|
||||
});
|
||||
}, function(err) {
|
||||
// Success
|
||||
if (err === 1) return next();
|
||||
|
||||
// Failure
|
||||
if (err) return next(err);
|
||||
|
||||
// No command avialable
|
||||
err = new Error("None of the available commands are available: "
|
||||
+ JSON.stringify(task, 4, " "));
|
||||
err.code = "ENOTAVAILABLE";
|
||||
return next(err);
|
||||
});
|
||||
|
||||
}, function(err) {
|
||||
callback(err);
|
||||
});
|
||||
}
|
||||
|
||||
function run(callback) {
|
||||
if (executing) return;
|
||||
|
||||
emit("run");
|
||||
|
||||
aborting = false;
|
||||
executing = true;
|
||||
execute(tasks, function(err) {
|
||||
executing = false;
|
||||
lastProcess = null;
|
||||
|
||||
callback && callback.apply(this, arguments);
|
||||
session.unload();
|
||||
|
||||
emit("stop", err);
|
||||
});
|
||||
}
|
||||
|
||||
function abort(callback) {
|
||||
aborting = true;
|
||||
|
||||
if (!executing) {
|
||||
lastProcess = null;
|
||||
emit("stop", new Error("Aborted"));
|
||||
}
|
||||
|
||||
callback && callback();
|
||||
}
|
||||
|
||||
// Make session a baseclass to allow others to extend
|
||||
session.baseclass();
|
||||
|
||||
/**
|
||||
*
|
||||
**/
|
||||
session.freezePublicAPI({
|
||||
/**
|
||||
*
|
||||
*/
|
||||
get tasks() { return tasks; },
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
get executing() { return executing; },
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
get output() { return output; },
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
get process() { return lastProcess || null; },
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
get lastTask() { return lastTask || null; },
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
task: task,
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
run: run,
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
abort: abort
|
||||
});
|
||||
|
||||
return session;
|
||||
}
|
||||
|
||||
/***** Lifecycle *****/
|
||||
|
||||
plugin.on("load", function() {
|
||||
|
||||
});
|
||||
plugin.on("unload", function() {
|
||||
|
||||
});
|
||||
|
||||
/***** Register and define API *****/
|
||||
|
||||
/**
|
||||
*
|
||||
**/
|
||||
plugin.freezePublicAPI({
|
||||
/**
|
||||
*
|
||||
*/
|
||||
createSession: createSession,
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
addCommand: addCommand,
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
removeCommand: removeCommand,
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
addCommandAlias: addCommandAlias
|
||||
});
|
||||
|
||||
register(null, {
|
||||
automate: plugin
|
||||
});
|
||||
}
|
||||
});
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"name": "c9.automate",
|
||||
"description": "The repository for c9.automate",
|
||||
"version": "3.0.0",
|
||||
"author": "Cloud9",
|
||||
"contributors": [],
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "http://github.com/c9/c9.automate.git"
|
||||
},
|
||||
"plugins": {
|
||||
"automate": {}
|
||||
},
|
||||
"categories": [
|
||||
"core"
|
||||
],
|
||||
"licenses": [
|
||||
"MIT"
|
||||
]
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
|
||||
Copyright (c) 2015, Ajax.org B.V.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
|
@ -0,0 +1 @@
|
|||
# c9.ide.ace.emmet
|
|
@ -0,0 +1,127 @@
|
|||
define(function(require, exports, module) {
|
||||
"use strict";
|
||||
main.consumes = [
|
||||
"Plugin", "commands", "menus", "ace"
|
||||
];
|
||||
main.provides = ["emmet"];
|
||||
return main;
|
||||
|
||||
function main(options, imports, register) {
|
||||
var Plugin = imports.Plugin;
|
||||
var commands = imports.commands;
|
||||
var menus = imports.menus;
|
||||
|
||||
var emmetExt = require("ace/ext/emmet");
|
||||
emmetExt.setCore("lib/emmet/emmet");
|
||||
emmetExt.updateCommands = function() {};
|
||||
|
||||
/***** Initialization *****/
|
||||
|
||||
var plugin = new Plugin("Ajax.org", main.consumes);
|
||||
var emit = plugin.getEmitter();
|
||||
|
||||
var loaded = false;
|
||||
function load() {
|
||||
if (loaded) return false;
|
||||
loaded = true;
|
||||
|
||||
var keymap = {
|
||||
expand_abbreviation: { mac: "ctrl+alt+e", win: "ctrl+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+'"},
|
||||
// update_image_size: {mac: "shift+ctrl+i", win: "ctrl+u"},
|
||||
// expand_as_you_type: "ctrl+alt+enter",
|
||||
// wrap_as_you_type: {mac: "shift+ctrl+g", win: "shift+ctrl+g"},
|
||||
expand_abbreviation_with_tab: { mac: "Tab", win: "Tab" },
|
||||
wrap_with_abbreviation: { mac: "shift+ctrl+a", win: "shift+ctrl+a" }
|
||||
};
|
||||
|
||||
for (var i in keymap) {
|
||||
commands.addCommand({
|
||||
name: "emmet_" + i,
|
||||
action: i,
|
||||
group: "emmet",
|
||||
bindKey: keymap[i],
|
||||
exec: emmetExt.runEmmetCommand,
|
||||
isAvailable: isAvailable,
|
||||
findEditor: findEditor
|
||||
}, plugin);
|
||||
}
|
||||
}
|
||||
|
||||
var drawn = false;
|
||||
function draw() {
|
||||
if (drawn) return;
|
||||
drawn = true;
|
||||
|
||||
// TODO add menu items?
|
||||
|
||||
emit("draw");
|
||||
}
|
||||
|
||||
/***** Methods *****/
|
||||
|
||||
function isAvailable(editor, args, event) {
|
||||
if (!editor || !editor.ace) return false;
|
||||
|
||||
// using this instead of editor.type == "ace" to make
|
||||
// commands avaliable in editors inheriting from ace
|
||||
if (event instanceof KeyboardEvent && (!editor.ace.isFocused()))
|
||||
return false;
|
||||
|
||||
return emmetExt.isAvailable(editor.ace, this.name);
|
||||
}
|
||||
|
||||
function findEditor(editor) {
|
||||
if (editor && editor.ace)
|
||||
return editor.ace;
|
||||
return editor;
|
||||
}
|
||||
|
||||
/***** Lifecycle *****/
|
||||
|
||||
plugin.on("load", function() {
|
||||
load();
|
||||
});
|
||||
plugin.on("enable", function() {
|
||||
|
||||
});
|
||||
plugin.on("disable", function() {
|
||||
|
||||
});
|
||||
plugin.on("unload", function() {
|
||||
loaded = false;
|
||||
drawn = false;
|
||||
});
|
||||
|
||||
/***** Register and define API *****/
|
||||
|
||||
/**
|
||||
*
|
||||
**/
|
||||
plugin.freezePublicAPI({
|
||||
});
|
||||
|
||||
register(null, {
|
||||
emmet: plugin
|
||||
});
|
||||
}
|
||||
});
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"name": "c9.ide.ace.emmet",
|
||||
"description": "The repository for c9.ide.ace.emmet, a Cloud9 core plugin",
|
||||
"version": "3.0.0",
|
||||
"author": "Cloud9",
|
||||
"contributors": [],
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "http://github.com/c9/c9.ide.ace.emmet.git"
|
||||
},
|
||||
"plugins": {
|
||||
"emmet": {}
|
||||
},
|
||||
"categories": [
|
||||
"core"
|
||||
],
|
||||
"licenses": [
|
||||
"MIT"
|
||||
]
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
|
||||
Copyright (c) 2015, Ajax.org B.V.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
|
@ -0,0 +1 @@
|
|||
# c9.ide.ace.gotoline
|
|
@ -0,0 +1,403 @@
|
|||
define(function(require, exports, module) {
|
||||
main.consumes = [
|
||||
"Plugin", "c9", "settings", "ui",
|
||||
"anims", "menus", "commands", "util", "tabManager"
|
||||
];
|
||||
main.provides = ["ace.gotoline"];
|
||||
return main;
|
||||
|
||||
// @todo add commands for list navigation and bookmarking
|
||||
// @todo fix pasting of line numbers
|
||||
|
||||
function main(options, imports, register) {
|
||||
var c9 = imports.c9;
|
||||
var Plugin = imports.Plugin;
|
||||
var settings = imports.settings;
|
||||
var ui = imports.ui;
|
||||
var anims = imports.anims;
|
||||
var util = imports.util;
|
||||
var menus = imports.menus;
|
||||
var commands = imports.commands;
|
||||
var tabs = imports.tabManager;
|
||||
|
||||
var skin = require("text!./skin.xml");
|
||||
var markup = require("text!./gotoline.xml");
|
||||
|
||||
/***** Initialization *****/
|
||||
|
||||
var plugin = new Plugin("Ajax.org", main.consumes);
|
||||
var emit = plugin.getEmitter();
|
||||
|
||||
var originalLine, originalColumn, control, lastLine, lineControl;
|
||||
var nohide, originalPath;
|
||||
var win, input, list, model; // ui elements
|
||||
|
||||
var loaded = false, changed = false;
|
||||
function load() {
|
||||
if (loaded) return false;
|
||||
loaded = true;
|
||||
|
||||
model = new ui.model();
|
||||
|
||||
menus.addItemByPath("Goto/Goto Line...", new apf.item({
|
||||
caption: "Goto Line...",
|
||||
hint: "enter a line number and jump to it in the active document",
|
||||
command: "gotoline"
|
||||
}), 200, plugin);
|
||||
|
||||
commands.addCommand({
|
||||
name: "gotoline",
|
||||
bindKey: { mac: "Command-L", win: "Ctrl-G" },
|
||||
isAvailable: function(editor) {
|
||||
return editor && editor.type == "ace";
|
||||
},
|
||||
exec: function() {
|
||||
gotoline();
|
||||
}
|
||||
}, plugin);
|
||||
|
||||
commands.addCommand({
|
||||
name: "hideGotoLine",
|
||||
group: "ignore",
|
||||
bindKey: { mac: "ESC", win: "ESC" },
|
||||
isAvailable: function(editor) { return win && win.visible; },
|
||||
exec: function() {
|
||||
hide();
|
||||
var tab = tabs.focussedTab;
|
||||
tab && tabs.focusTab(tab);
|
||||
|
||||
if (originalLine) {
|
||||
execGotoLine(originalLine, originalColumn, true);
|
||||
originalPath = originalColumn = originalLine = undefined;
|
||||
}
|
||||
}
|
||||
}, plugin);
|
||||
|
||||
settings.on("read", function() {
|
||||
var lines = settings.getJson("state/gotoline") || [];
|
||||
var xml = "";
|
||||
for (var i = 0, l = lines.length; i < l; i += 2) {
|
||||
xml += "<line nr='" + lines[i] + "' />";
|
||||
}
|
||||
model.load("<lines>" + xml + "</lines>");
|
||||
}, plugin);
|
||||
|
||||
settings.on("write", function() {
|
||||
if (changed) {
|
||||
var nodes = model.data.childNodes;
|
||||
var lines = [];
|
||||
for (var i = 0, l = Math.min(20, nodes.length); i < l; i++) {
|
||||
lines.push(nodes[i].getAttribute("nr"));
|
||||
}
|
||||
settings.setJson("state/gotoline", lines);
|
||||
}
|
||||
}, plugin);
|
||||
}
|
||||
|
||||
var drawn = false;
|
||||
function draw() {
|
||||
if (drawn) return;
|
||||
drawn = true;
|
||||
|
||||
// Import Skin
|
||||
ui.insertSkin({
|
||||
name: "gotoline",
|
||||
data: skin,
|
||||
"media-path": options.staticPrefix + "/images/"
|
||||
}, plugin);
|
||||
|
||||
// Create UI elements
|
||||
ui.insertMarkup(null, markup, plugin);
|
||||
|
||||
win = plugin.getElement("window");
|
||||
input = plugin.getElement("input");
|
||||
list = plugin.getElement("list");
|
||||
|
||||
list.setAttribute("model", model);
|
||||
|
||||
list.addEventListener("afterchoose", function() {
|
||||
if (list.selected) {
|
||||
execGotoLine(parseInt(list.selected.getAttribute("nr"), 10));
|
||||
}
|
||||
else {
|
||||
execGotoLine();
|
||||
}
|
||||
});
|
||||
list.addEventListener("afterselect", function() {
|
||||
if (!list.selected)
|
||||
return;
|
||||
|
||||
var line = list.selected.getAttribute("nr");
|
||||
input.setValue(line);
|
||||
|
||||
// Focus the list
|
||||
list.focus();
|
||||
|
||||
// Go to the right line
|
||||
execGotoLine(null, null, true);
|
||||
});
|
||||
|
||||
var restricted = [38, 40, 36, 35];
|
||||
list.addEventListener("keydown", function(e) {
|
||||
if (e.keyCode == 13 && list.selected) {
|
||||
return false;
|
||||
}
|
||||
else if (e.keyCode == 38) {
|
||||
if (list.selected == list.getFirstTraverseNode()) {
|
||||
input.focus();
|
||||
list.clearSelection();
|
||||
}
|
||||
}
|
||||
else if (restricted.indexOf(e.keyCode) == -1)
|
||||
input.focus();
|
||||
}, true);
|
||||
|
||||
input.addEventListener("keydown", function(e) {
|
||||
var NotANumber = (e.keyCode > 57 || e.keyCode == 32)
|
||||
&& (e.keyCode < 96 || e.keyCode > 105);
|
||||
|
||||
if (e.keyCode == 13) {
|
||||
execGotoLine();
|
||||
return false;
|
||||
}
|
||||
else if (e.keyCode == 40) {
|
||||
var first = list.getFirstTraverseNode();
|
||||
if (first) {
|
||||
list.select(first);
|
||||
list.$container.scrollTop = 0;
|
||||
list.focus();
|
||||
}
|
||||
}
|
||||
else if (NotANumber && !e.metaKey && !e.ctrlKey && !e.altKey) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Numbers & Cmd-V / Cmd-C
|
||||
if (!NotANumber || (e.metaKey || e.ctrlKey)
|
||||
&& (e.keyCode == 86 || e.keyCode == 88)) {
|
||||
setTimeout(function() {
|
||||
execGotoLine(null, null, true);
|
||||
}, 10);
|
||||
}
|
||||
});
|
||||
|
||||
win.addEventListener("blur", function(e) {
|
||||
if (!ui.isChildOf(win, e.toElement))
|
||||
hide();
|
||||
});
|
||||
|
||||
input.addEventListener("blur", function(e) {
|
||||
if (!ui.isChildOf(win, e.toElement))
|
||||
hide();
|
||||
});
|
||||
|
||||
emit("draw");
|
||||
}
|
||||
|
||||
/***** Methods *****/
|
||||
|
||||
function show(noanim) {
|
||||
var tab = tabs.focussedTab;
|
||||
var editor = tab && tab.editor;
|
||||
if (!editor || editor.type != "ace") return;
|
||||
|
||||
var ace = editor.ace;
|
||||
var aceHtml = ace.container;
|
||||
var cursor = ace.getCursorPosition();
|
||||
|
||||
originalLine = cursor.row + 1;
|
||||
originalColumn = cursor.column;
|
||||
originalPath = tab.path;
|
||||
|
||||
//Set the current line
|
||||
input.setValue(input.getValue() || cursor.row + 1);
|
||||
|
||||
//Determine the position of the window
|
||||
var pos = ace.renderer.textToScreenCoordinates(cursor.row, cursor.column);
|
||||
var epos = ui.getAbsolutePosition(aceHtml.parentNode);
|
||||
var maxTop = aceHtml.offsetHeight - 100;
|
||||
var top = Math.max(0, Math.min(maxTop, pos.pageY - epos[1] - 5));
|
||||
var left = 0;
|
||||
|
||||
ace.container.parentNode.appendChild(win.$ext);
|
||||
|
||||
win.show();
|
||||
win.$ext.style.top = top + "px";
|
||||
|
||||
//Animate
|
||||
if (!noanim && settings.getBool('user/general/@animateui')) {
|
||||
win.setWidth(0);
|
||||
|
||||
anims.animate(win, {
|
||||
width: "60px",
|
||||
duration: 0.15,
|
||||
timingFunction: "cubic-bezier(.11, .93, .84, 1)"
|
||||
}, function() {
|
||||
win.$ext.style.left = left + "px";
|
||||
});
|
||||
}
|
||||
else {
|
||||
win.setWidth(60);
|
||||
}
|
||||
input.focus();
|
||||
}
|
||||
|
||||
function hide() {
|
||||
if (nohide) return;
|
||||
|
||||
if (settings.getBool('user/general/@animateui')) {
|
||||
anims.animate(win, {
|
||||
width: "0px",
|
||||
duration: 0.15,
|
||||
timingFunction: "cubic-bezier(.10, .10, .25, .90)"
|
||||
}, function() {
|
||||
win.hide();
|
||||
});
|
||||
}
|
||||
else {
|
||||
win.hide();
|
||||
}
|
||||
}
|
||||
|
||||
function gotoline(force) {
|
||||
draw();
|
||||
|
||||
if (control && control.stop)
|
||||
control.stop();
|
||||
|
||||
var tab = tabs.focussedTab;
|
||||
var editor = tab && tab.editor;
|
||||
if (!editor || editor.type != "ace")
|
||||
return;
|
||||
|
||||
if (force != 2 && !win.visible || force == 1)
|
||||
show();
|
||||
else
|
||||
hide();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function execGotoLine(line, column, preview) {
|
||||
var tab = tabs.focussedTab && tabs.focussedTab;
|
||||
var editor = tab && tab.editor;
|
||||
if (!editor || editor.type != "ace") return;
|
||||
|
||||
var ace = editor.ace;
|
||||
var aceHtml = ace.container;
|
||||
|
||||
if (typeof line != "number")
|
||||
line = parseInt(input.getValue(), 10) || 0;
|
||||
|
||||
// I don't know why this if was here. It caused a bug where if the
|
||||
// line target was already in view, it wouldn't jump to it.
|
||||
// if (!lastLine || lastLine != line || !ace.isRowFullyVisible(line)) {
|
||||
ace.gotoLine(line, column);
|
||||
lastLine = line;
|
||||
// }
|
||||
|
||||
if (typeof preview != "undefined") {
|
||||
var animate = settings.getBool("user/ace/@animatedScroll");
|
||||
if (!animate)
|
||||
return;
|
||||
|
||||
var cursor = ace.getCursorPosition();
|
||||
var renderer = ace.renderer;
|
||||
var pos = renderer.textToScreenCoordinates(cursor.row, cursor.column);
|
||||
var maxTop = renderer.$size.height - win.getHeight() - 10;
|
||||
var epos = ui.getAbsolutePosition(aceHtml);
|
||||
var sm = renderer.scrollMargin;
|
||||
var scrollTop = ace.session.getScrollTop();
|
||||
scrollTop = Math.max(-sm.top, Math.min(scrollTop,
|
||||
renderer.layerConfig.maxHeight - renderer.$size.scrollerHeight + sm.v));
|
||||
var top = Math.min(pos.pageY - epos[1] - 2
|
||||
+ renderer.scrollTop - scrollTop, maxTop);
|
||||
|
||||
if (lineControl)
|
||||
lineControl.stop();
|
||||
|
||||
//Animate
|
||||
anims.animate(win, {
|
||||
top: top + "px",
|
||||
duration: 0.25,
|
||||
timingFunction: "cubic-bezier(.11, .93, .84, 1)"
|
||||
}, function() {
|
||||
win.$ext.style.left = "0px";
|
||||
});
|
||||
}
|
||||
else {
|
||||
//win.hide();
|
||||
hide();
|
||||
|
||||
var lineNode = model.queryNode("line[@nr='" + line + "']");
|
||||
|
||||
if (!lineNode) {
|
||||
lineNode = ui.n("<line />")
|
||||
.attr("nr", line)
|
||||
.node();
|
||||
}
|
||||
|
||||
var pNode = model.data;
|
||||
if (lineNode != pNode.firstChild) {
|
||||
apf.xmldb.appendChild(pNode, lineNode, pNode.firstChild);
|
||||
changed = true;
|
||||
settings.save();
|
||||
}
|
||||
|
||||
tabs.focusTab(tab);
|
||||
}
|
||||
}
|
||||
|
||||
/***** Lifecycle *****/
|
||||
|
||||
plugin.on("load", function() {
|
||||
load();
|
||||
});
|
||||
plugin.on("enable", function() {
|
||||
|
||||
});
|
||||
plugin.on("disable", function() {
|
||||
|
||||
});
|
||||
plugin.on("unload", function() {
|
||||
loaded = false;
|
||||
});
|
||||
|
||||
/***** Register and define API *****/
|
||||
|
||||
/**
|
||||
* The goto line dialog for ace editors. The goto line dialog allows
|
||||
* users to jump to a line in a file. It has a history of all lines
|
||||
* that were jumped to before. Users can navigate this list and press
|
||||
* ESC to return to their original position.
|
||||
*
|
||||
* @singleton
|
||||
**/
|
||||
plugin.freezePublicAPI({
|
||||
/**
|
||||
* Jump to a line and column in the focussed tab.
|
||||
* @param {Number} line The line to jump to.
|
||||
* @param {Number} column The column to jump to.
|
||||
* @param {Boolean} preview Whether to keep the original location in memory.
|
||||
*/
|
||||
gotoline: function(line, column, preview) {
|
||||
gotoline(1);
|
||||
return execGotoLine(line, column, preview);
|
||||
},
|
||||
|
||||
/**
|
||||
* Show the goto line dialog
|
||||
*/
|
||||
show: function() { gotoline(1); },
|
||||
|
||||
/**
|
||||
* Hide the goto line dialog
|
||||
*/
|
||||
hide: function() { gotoline(2); }
|
||||
});
|
||||
|
||||
register(null, {
|
||||
"ace.gotoline": plugin
|
||||
});
|
||||
}
|
||||
});
|
|
@ -0,0 +1,22 @@
|
|||
<?xml version='1.0'?>
|
||||
<a:application xmlns:a="http://ajax.org/2005/aml">
|
||||
<a:bar id="window"
|
||||
skinset = "gotoline"
|
||||
visible = "false"
|
||||
width = "60"
|
||||
height = "90"
|
||||
left = "0"
|
||||
zindex = "100000"
|
||||
style = "overflow:hidden">
|
||||
<a:vbox top="0" right="0" bottom="0" width="61" padding="0" edge="5">
|
||||
<a:textbox id="input" focusselect="true" />
|
||||
<a:list id="list"
|
||||
flex = "1"
|
||||
caption = "[@nr]"
|
||||
each = "[line]"
|
||||
autoselect = "false"
|
||||
multiselect = "false"
|
||||
empty-message = "empty" />
|
||||
</a:vbox>
|
||||
</a:bar>
|
||||
</a:application>
|
|
@ -0,0 +1,191 @@
|
|||
/*global describe it before after apf bar */
|
||||
|
||||
"use client";
|
||||
|
||||
require(["lib/architect/architect", "lib/chai/chai"], function (architect, chai) {
|
||||
var expect = chai.expect;
|
||||
|
||||
expect.setupArchitectTest([
|
||||
{
|
||||
packagePath: "plugins/c9.core/c9",
|
||||
workspaceId: "ubuntu/ip-10-35-77-180",
|
||||
startdate: new Date(),
|
||||
debug: true,
|
||||
hosted: true,
|
||||
local: false,
|
||||
davPrefix: "/"
|
||||
},
|
||||
|
||||
"plugins/c9.core/ext",
|
||||
"plugins/c9.core/http-xhr",
|
||||
"plugins/c9.core/util",
|
||||
{
|
||||
packagePath: "plugins/c9.core/settings",
|
||||
settings: { user: { general: { animateui: true }}}
|
||||
},
|
||||
"plugins/c9.core/api.js",
|
||||
"plugins/c9.ide.ui/lib_apf",
|
||||
"plugins/c9.ide.ui/anims",
|
||||
"plugins/c9.ide.ui/menus",
|
||||
{
|
||||
packagePath: "plugins/c9.ide.ui/ui",
|
||||
staticPrefix: "plugins/c9.ide.ui"
|
||||
},
|
||||
"plugins/c9.ide.editors/document",
|
||||
"plugins/c9.ide.editors/undomanager",
|
||||
{
|
||||
packagePath: "plugins/c9.ide.editors/editors",
|
||||
defaultEditor: "ace"
|
||||
},
|
||||
"plugins/c9.ide.editors/editor",
|
||||
"plugins/c9.ide.editors/tabmanager",
|
||||
"plugins/c9.ide.ui/focus",
|
||||
"plugins/c9.ide.editors/pane",
|
||||
"plugins/c9.ide.editors/tab",
|
||||
"plugins/c9.ide.ace/ace",
|
||||
{
|
||||
packagePath: "plugins/c9.ide.ace.gotoline/gotoline",
|
||||
staticPrefix: "plugins/c9.ide.ace.gotoline"
|
||||
},
|
||||
"plugins/c9.ide.keys/commands",
|
||||
"plugins/c9.fs/proc",
|
||||
"plugins/c9.vfs.client/vfs_client",
|
||||
"plugins/c9.vfs.client/endpoint",
|
||||
"plugins/c9.ide.auth/auth",
|
||||
"plugins/c9.fs/fs",
|
||||
|
||||
// Mock plugins
|
||||
{
|
||||
consumes: ["tabManager", "ace", "settings"],
|
||||
provides: [],
|
||||
setup: main
|
||||
}
|
||||
], architect);
|
||||
|
||||
function main(options, imports, register) {
|
||||
var tabs = imports.tabManager;
|
||||
var ace = imports.ace;
|
||||
|
||||
function getTabHtml(tab) {
|
||||
return tab.pane.aml.getPage("editor::" + tab.editorType).$ext;
|
||||
}
|
||||
|
||||
expect.html.setConstructor(function(tab) {
|
||||
if (typeof tab == "object")
|
||||
return tab.$ext;
|
||||
});
|
||||
|
||||
describe('gotoline', function() {
|
||||
before(function(done) {
|
||||
apf.config.setProperty("allow-select", false);
|
||||
apf.config.setProperty("allow-blur", false);
|
||||
|
||||
bar.$ext.style.background = "rgba(220, 220, 220, 0.93)";
|
||||
bar.$ext.style.position = "fixed";
|
||||
bar.$ext.style.left = "20px";
|
||||
bar.$ext.style.right = "20px";
|
||||
bar.$ext.style.bottom = "20px";
|
||||
bar.$ext.style.height = "33%";
|
||||
|
||||
imports.settings.set("user/ace/@animatedscroll", "true");
|
||||
|
||||
document.body.style.marginBottom = "33%";
|
||||
tabs.once("ready", function() {
|
||||
tabs.getPanes()[0].focus();
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
describe("open", function() {
|
||||
this.timeout(10000);
|
||||
|
||||
it('should open a pane with just an editor', function(done) {
|
||||
tabs.openFile("/file.js", function(err, tab) {
|
||||
setTimeout(function() {
|
||||
expect(tabs.getTabs()).length(1);
|
||||
|
||||
// var sb = tab.document.getSession().statusBar;
|
||||
// var bar = sb.getElement("bar");
|
||||
// expect.html(bar, "rowcol").text("1:1");
|
||||
//
|
||||
// tab.document.editor.ace.selectAll();
|
||||
// setTimeout(function(){
|
||||
// expect.html(bar, "rowcol sel").text("2:1");
|
||||
// expect.html(bar, "sel").text("23 Bytes");
|
||||
//
|
||||
// done();
|
||||
// }, 100);
|
||||
done();
|
||||
}, 50);
|
||||
});
|
||||
});
|
||||
it('should handle multiple documents in the same pane', function(done) {
|
||||
tabs.openFile("/listing.json", function(err, tab) {
|
||||
setTimeout(function() {
|
||||
expect(tabs.getTabs()).length(2);
|
||||
|
||||
tab.activate();
|
||||
|
||||
// setTimeout(function(){
|
||||
// var sb = tab.document.getSession().statusBar;
|
||||
// expect.html(sb.getElement("bar"), "caption").text("1:1");
|
||||
|
||||
done();
|
||||
// }, 100);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
describe("split(), pane.unload()", function() {
|
||||
it('should split a pane horizontally, making the existing pane the left one', function(done) {
|
||||
var pane = tabs.focussedTab.pane;
|
||||
var righttab = pane.hsplit(true);
|
||||
tabs.focussedTab.attachTo(righttab);
|
||||
done();
|
||||
// setTimeout(function(){
|
||||
// expect.html(pane.aml, "pane").text("2:1");
|
||||
// expect.html(righttab.aml, "righttab").text("1:1");
|
||||
|
||||
//done();
|
||||
// }, 100);
|
||||
});
|
||||
// it('should remove the left pane from a horizontal split', function(done) {
|
||||
// var pane = tabs.getPanes()[0];
|
||||
// var tab = tabs.getPanes()[1].getTab();
|
||||
// pane.unload();
|
||||
// expect(tabs.getPanes()).length(1);
|
||||
// expect(tabs.getTabs()).length(2);
|
||||
// tabs.focusTab(tab);
|
||||
// done();
|
||||
// });
|
||||
});
|
||||
// describe("Change Theme", function(){
|
||||
// this.timeout(10000);
|
||||
//
|
||||
// it('should change a theme', function(done) {
|
||||
// var editor = tabs.focussedTab.editor;
|
||||
// ace.on("themeInit", function setTheme(){
|
||||
// ace.off("theme.init", setTheme);
|
||||
// expect.html(getTabHtml(tabs.focussedTab).childNodes[1]).className("ace-monokai");
|
||||
// editor.setOption("theme", "ace/theme/textmate");
|
||||
// done();
|
||||
// });
|
||||
// editor.setOption("theme", "ace/theme/monokai");
|
||||
// });
|
||||
// });
|
||||
|
||||
// @todo test split api and menu
|
||||
|
||||
if (!onload.remain) {
|
||||
after(function(done) {
|
||||
tabs.unload();
|
||||
|
||||
document.body.style.marginBottom = "";
|
||||
done();
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
onload && onload();
|
||||
}
|
||||
});
|
Po Szerokość: | Wysokość: | Rozmiar: 990 B |
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"name": "c9.ide.ace.gotoline",
|
||||
"description": "The repository for c9.ide.ace.gotoline, a Cloud9 core plugin",
|
||||
"version": "3.0.0",
|
||||
"author": "Cloud9",
|
||||
"contributors": [],
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "http://github.com/c9/c9.ide.ace.gotoline.git"
|
||||
},
|
||||
"plugins": {
|
||||
"gotoline": {}
|
||||
},
|
||||
"categories": [
|
||||
"core"
|
||||
],
|
||||
"licenses": [
|
||||
"MIT"
|
||||
]
|
||||
}
|
|
@ -0,0 +1,186 @@
|
|||
<?xml version='1.0'?>
|
||||
<a:skin xmlns:a="http://ajax.org/2005/aml" xmlns="http://www.w3.org/1999/xhtml">
|
||||
<a:list name="list">
|
||||
<a:style><![CDATA[
|
||||
.gotolinelist{
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
border-style: solid;
|
||||
.gradient(~"@{gotoline-list-background}");
|
||||
color: @gotoline-list-color;
|
||||
border-color: @gotoline-list-border-color;
|
||||
border-width: @gotoline-list-border-width;
|
||||
border-radius: @gotoline-list-border-radius;
|
||||
margin: 0;
|
||||
outline: none;
|
||||
font-family: Tahoma;
|
||||
font-size: 12px;
|
||||
text-overflow: ellipsis;
|
||||
cursor: default;
|
||||
text-align: right;
|
||||
}
|
||||
.dark .gotolinelist{
|
||||
.gradient(~"@{gotoline-list-background-dark}");
|
||||
color: @gotoline-list-color-dark;
|
||||
border-color: @gotoline-list-border-color-dark;
|
||||
border-width: @gotoline-list-border-width-dark;
|
||||
border-radius: @gotoline-list-border-radius-dark;
|
||||
|
||||
}
|
||||
.gotolinelist>.selected{
|
||||
background-color: @gotoline-list-selected-background;
|
||||
color: #e7e7e7;
|
||||
}
|
||||
.dark .gotolinelist>.selected{
|
||||
background-color: @gotoline-list-selected-background-dark;
|
||||
}
|
||||
.gotolinelistFocus>.selected{
|
||||
color : #e7e7e7;
|
||||
}
|
||||
.gotolinelist .empty, .gotolinelist .offline, .gotolinelist .loading{
|
||||
text-align: center;
|
||||
padding: 8px 0 0 0;
|
||||
color: #777;
|
||||
font-size : 8pt;
|
||||
font-weight : normal;
|
||||
}
|
||||
.gotolinelist>DIV{
|
||||
padding: 2px 6px;
|
||||
}
|
||||
|
||||
]]></a:style>
|
||||
|
||||
<a:presentation>
|
||||
<a:main container=".">
|
||||
<div class='gotolinelist'>
|
||||
</div>
|
||||
</a:main>
|
||||
<a:item
|
||||
class = "."
|
||||
container = "."
|
||||
caption = "."
|
||||
select = "."
|
||||
>
|
||||
<div> </div>
|
||||
</a:item>
|
||||
<a:empty caption=".">
|
||||
<div class="message">-</div>
|
||||
</a:empty>
|
||||
</a:presentation>
|
||||
</a:list>
|
||||
<a:bar name="bar">
|
||||
<a:style><![CDATA[
|
||||
.barGotoline{
|
||||
position : absolute;
|
||||
background-color: @gotoline-background;
|
||||
border-radius: @gotoline-border-radius;
|
||||
box-shadow: @gotoline-box-shadow;
|
||||
border: @gotoline-border;
|
||||
border-left: 0;
|
||||
width : 20px;
|
||||
left : -100px;
|
||||
}
|
||||
.dark .barGotoline{
|
||||
background-color: @gotoline-background-dark;
|
||||
box-shadow: @gotoline-box-shadow-dark;
|
||||
border: @gotoline-border-dark;
|
||||
border-left: 0;
|
||||
}
|
||||
.brGtlContent {
|
||||
}
|
||||
.barGotoline .brGtlTop {
|
||||
background: url(images/barGotoLineCorners.png) no-repeat 0 0;
|
||||
height: 3px;
|
||||
width: 4px;
|
||||
position: absolute;
|
||||
top: -3px;
|
||||
left:0;
|
||||
}
|
||||
.barGotoline .brGtlBottom {
|
||||
background: url(images/barGotoLineCorners.png) no-repeat 0 -3px;
|
||||
height: 3px;
|
||||
width: 4px;
|
||||
position: absolute;
|
||||
bottom: -3px;
|
||||
left:0;
|
||||
}
|
||||
]]></a:style>
|
||||
<a:presentation>
|
||||
<a:main container=".">
|
||||
<div class="barGotoline">
|
||||
<div class="brGtlTop"></div>
|
||||
<div class="brGtlBottom"></div>
|
||||
</div>
|
||||
</a:main>
|
||||
</a:presentation>
|
||||
</a:bar>
|
||||
<a:textbox name="textbox">
|
||||
<a:style><![CDATA[
|
||||
.tbGotoline {
|
||||
position : relative;
|
||||
height : 25px;
|
||||
}
|
||||
|
||||
.tbGotoline .sbtb_middle {
|
||||
height : 15px;
|
||||
padding : 2px 5px 2px 5px;
|
||||
|
||||
background-color: #ffffff;
|
||||
.gradient(~"linear-gradient(center bottom,rgb(255,255,255) 50%,rgb(235,235,235) 100%)");
|
||||
color: #0471cf;
|
||||
margin: 0;
|
||||
outline: none;
|
||||
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', 'Droid Sans Mono', 'Consolas', monospace;
|
||||
font-size: 12px;
|
||||
text-overflow: ellipsis;
|
||||
|
||||
border-style: solid;
|
||||
border-color: @gotoline-input-border-color;
|
||||
border-width: @gotoline-input-border-width;
|
||||
border-radius: @gotoline-input-border-radius;
|
||||
box-shadow: @gotoline-input-box-shadow;
|
||||
}
|
||||
.dark .tbGotoline .sbtb_middle{
|
||||
border-color: @gotoline-input-border-color-dark;
|
||||
border-width: @gotoline-input-border-width-dark;
|
||||
border-radius: @gotoline-input-border-radius-dark;
|
||||
box-shadow: @gotoline-input-box-shadow-dark;
|
||||
}
|
||||
|
||||
.tbGotoline .sbtb_middle INPUT {
|
||||
border : 0;
|
||||
height : 14px;
|
||||
font-size : 12px;
|
||||
color : #0471cf;
|
||||
font-family : 'Monaco', 'Menlo', 'Ubuntu Mono', 'Droid Sans Mono', 'Consolas', monospace;
|
||||
outline : none;
|
||||
background-color : transparent;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tbGotolineInitial .sbtb_middle INPUT {
|
||||
color : #0471cf;
|
||||
}
|
||||
|
||||
.tbGotolineDisabled .sbtb_middle INPUT {
|
||||
color : #0471cf;
|
||||
}
|
||||
|
||||
.tbGotolineDisabled .sbtb_middle{
|
||||
background: linear-gradient(center bottom,rgb(235,235,235) 0%,rgb(255,255,255) 40%);
|
||||
cursor : default;
|
||||
}
|
||||
]]></a:style>
|
||||
|
||||
<a:presentation>
|
||||
<a:main input="div[1]/input">
|
||||
<div class="tbGotoline">
|
||||
<div class="sbtb_middle">
|
||||
<input type="text" />
|
||||
</div>
|
||||
</div>
|
||||
</a:main>
|
||||
</a:presentation>
|
||||
</a:textbox>
|
||||
</a:skin>
|
|
@ -0,0 +1,21 @@
|
|||
|
||||
Copyright (c) 2015, Ajax.org B.V.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
|
@ -0,0 +1 @@
|
|||
# c9.ide.ace.keymaps
|
|
@ -0,0 +1,803 @@
|
|||
define(function(require, exports, module) {
|
||||
main.consumes = [
|
||||
"Plugin", "ui", "layout", "commands", "fs", "navigate", "save",
|
||||
"tabbehavior", "ace", "commands", "tabManager"
|
||||
];
|
||||
main.provides = ["vim.cli"];
|
||||
return main;
|
||||
|
||||
function main(options, imports, register) {
|
||||
var Plugin = imports.Plugin;
|
||||
var ui = imports.ui;
|
||||
var commands = imports.commands;
|
||||
var layout = imports.layout;
|
||||
var fs = imports.fs;
|
||||
var navigate = imports.navigate;
|
||||
var save = imports.save;
|
||||
var tabbehavior = imports.tabbehavior;
|
||||
var tabManager = imports.tabManager;
|
||||
var ace = imports.ace;
|
||||
|
||||
var Editor = require("ace/editor").Editor;
|
||||
var lang = require("ace/lib/lang");
|
||||
var pathLib = require("path");
|
||||
|
||||
/***** Initialization *****/
|
||||
|
||||
var plugin = new Plugin("Ajax.org", main.consumes);
|
||||
var emit = plugin.getEmitter();
|
||||
|
||||
var cmdLine;
|
||||
|
||||
var searchStore = {
|
||||
current: "",
|
||||
options: {
|
||||
needle: "",
|
||||
backwards: false,
|
||||
wrap: true,
|
||||
caseSensitive: false,
|
||||
wholeWord: false,
|
||||
regExp: false
|
||||
}
|
||||
};
|
||||
|
||||
var loaded = false;
|
||||
function load() {
|
||||
if (loaded) return false;
|
||||
loaded = true;
|
||||
|
||||
}
|
||||
|
||||
var drawn = false;
|
||||
function draw() {
|
||||
if (drawn) return;
|
||||
drawn = true;
|
||||
|
||||
cmdLine = new apf.codebox();
|
||||
layout.findParent(plugin).appendChild(cmdLine);
|
||||
cmdLine.setHeight(23);
|
||||
cmdLine.$ext.className = "searchbox tb_console vimInput";
|
||||
|
||||
updateTheme();
|
||||
initCmdLine(cmdLine.ace);
|
||||
|
||||
ace.on("themeChange", updateTheme, plugin);
|
||||
|
||||
emit("draw");
|
||||
}
|
||||
|
||||
function updateTheme() {
|
||||
if (!cmdLine)
|
||||
return;
|
||||
|
||||
var activeTheme = ace.theme; // themes.getActiveTheme();
|
||||
if (!activeTheme)
|
||||
return;
|
||||
|
||||
cmdLine.ace.setTheme(activeTheme);
|
||||
|
||||
var htmlNode = cmdLine.ace.container.parentNode;
|
||||
var style = htmlNode.style;
|
||||
style.background = activeTheme.bg;
|
||||
|
||||
activeTheme.isDark
|
||||
? ui.setStyleClass(htmlNode, "dark")
|
||||
: ui.setStyleClass(htmlNode, "", ["dark"]);
|
||||
}
|
||||
|
||||
function show() {
|
||||
draw();
|
||||
layout.setFindArea(cmdLine, { isDefault: true });
|
||||
}
|
||||
|
||||
function hide() {
|
||||
if (cmdLine) {
|
||||
layout.setFindArea(null, { isDefault: true });
|
||||
}
|
||||
}
|
||||
|
||||
function toggle(force, a, b, callback) {
|
||||
if (force == -1)
|
||||
hide();
|
||||
else
|
||||
show();
|
||||
|
||||
callback && callback();
|
||||
}
|
||||
|
||||
/***** Methods *****/
|
||||
|
||||
var cliCmds = {};
|
||||
|
||||
cliCmds.ascii = {
|
||||
name: "ascii",
|
||||
description: "",
|
||||
exec: function(editor) {
|
||||
var onSelectionChange = lang.delayedCall(function(e) {
|
||||
var c = editor.getCursorPosition();
|
||||
var ch = editor.session.getLine(c.row)[c.column - 1] || "\n";
|
||||
var code = ch.charCodeAt(0);
|
||||
var msg = JSON.stringify(ch).slice(1, -1) + "=" + " ";
|
||||
var str = code.toString(16);
|
||||
str = [, "\\x0", "\\x", "\\u0", "\\u"][str.length] + str;
|
||||
msg += str + " ";
|
||||
msg += "\\" + code.toString(8) + " ";
|
||||
msg += "&#" + code + ";";
|
||||
editor.cmdLine.setMessage(msg);
|
||||
clear.delay(2000);
|
||||
});
|
||||
|
||||
var clear = lang.delayedCall(function(e) {
|
||||
editor.removeListener(editor.asciiMessageListener);
|
||||
editor.asciiMessageListener = null;
|
||||
editor.cmdLine.setMessage("");
|
||||
});
|
||||
|
||||
if (editor.asciiMessageListener) {
|
||||
return clear.call();
|
||||
}
|
||||
editor.on("changeSelection", editor.asciiMessageListener = function() {
|
||||
onSelectionChange.schedule(200);
|
||||
});
|
||||
onSelectionChange.call();
|
||||
}
|
||||
};
|
||||
|
||||
cliCmds.set = {
|
||||
name: "set",
|
||||
description: "set editor option",
|
||||
exec: function(editor, args) {
|
||||
var cmd = args.text.split(" ");
|
||||
var optName = cmd[1];
|
||||
var optValue = parseOption(cmd[2]);
|
||||
editor.setOption(optName, optValue);
|
||||
},
|
||||
getCompletions: function() {
|
||||
return Object.keys(Editor.prototype.$options);
|
||||
}
|
||||
};
|
||||
|
||||
function parseOption(optValue) {
|
||||
if (/^\d+$/.test(optValue))
|
||||
optValue = parseInt(optValue, 10);
|
||||
if (/^[\d.\^e]+$/.test(optValue))
|
||||
optValue = parseFloat(optValue);
|
||||
else if (/^(true|false)$/i.test(optValue))
|
||||
optValue = optValue.length = 4;
|
||||
return optValue;
|
||||
}
|
||||
|
||||
cliCmds["/"] = {
|
||||
name: "/",
|
||||
history: [],
|
||||
cliExec: function(ed, data) { }
|
||||
};
|
||||
|
||||
cliCmds["?"] = {
|
||||
name: "?",
|
||||
history: cliCmds["/"].history,
|
||||
cliExec: cliCmds["/"].cliExec
|
||||
};
|
||||
|
||||
cliCmds[":"] = {
|
||||
name: ":",
|
||||
history: [],
|
||||
getCompletions: function() {
|
||||
return Object.keys(this.commands).concat(Object.keys(commands.commands));
|
||||
}
|
||||
};
|
||||
|
||||
cliCmds[":"].commands = {
|
||||
w: function(editor, data, callback) {
|
||||
var tab = tabManager.focussedTab;
|
||||
if (!tab || !tab.path)
|
||||
return;
|
||||
var lines = editor.session.getLength();
|
||||
if (data.argv.length === 2 && data.argv[1]) {
|
||||
var path = pathLib.join(pathLib.dirname(tab.path), data.argv[1]);
|
||||
|
||||
save.save(tab, { path: path }, function(err) {
|
||||
if (!err)
|
||||
editor.cmdLine.setTimedMessage(path + " [New] " + lines + "L, ##C written to ");
|
||||
callback && callback();
|
||||
});
|
||||
}
|
||||
else {
|
||||
save.save(tab, {}, function(err) {
|
||||
if (!err)
|
||||
editor.cmdLine.setTimedMessage(tab.path + " " + lines + "L, ##C written");
|
||||
callback && callback();
|
||||
});
|
||||
}
|
||||
},
|
||||
e: function(editor, data) {
|
||||
var path = data.argv[1];
|
||||
if (!path) {
|
||||
navigate.show();
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
var currentPath = tabManager.focussedTab && tabManager.focussedTab.path || "/";
|
||||
path = pathLib.join(pathLib.dirname(currentPath), data.argv[1]);
|
||||
fs.exists(path, function(exists) {
|
||||
if (exists) {
|
||||
tabManager.openFile(path, { focus: true }, function() {});
|
||||
}
|
||||
else {
|
||||
tabManager.open({
|
||||
path: path,
|
||||
focus: true,
|
||||
document: {
|
||||
meta: {
|
||||
newfile: true,
|
||||
cli: true
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
x: function(editor, data) {
|
||||
var page = tabManager.focussedTab;
|
||||
if (!page)
|
||||
return;
|
||||
|
||||
if (page.document.changed) {
|
||||
cliCmds[":"].commands.wq(editor, data);
|
||||
return;
|
||||
}
|
||||
else {
|
||||
cliCmds[":"].commands.q();
|
||||
}
|
||||
},
|
||||
wq: function(editor, data) {
|
||||
cliCmds[":"].commands.w(editor, data, function() {
|
||||
cliCmds[":"].commands.q();
|
||||
});
|
||||
},
|
||||
wa: function(editor, data) {
|
||||
commands.exec("saveall");
|
||||
},
|
||||
q: function(editor, data) {
|
||||
var page = tabManager.focussedTab;
|
||||
if (!page) return;
|
||||
|
||||
if (data && data.force)
|
||||
page.document.undoManager.bookmark();
|
||||
|
||||
page.close();
|
||||
},
|
||||
"q!": function() {
|
||||
cliCmds[":"].commands.q(null, { force: true });
|
||||
},
|
||||
tabn: "gototabright",
|
||||
tabp: "gototableft",
|
||||
tabfirst: function() {
|
||||
tabbehavior.cycleTab("first", { editorType: "ace" });
|
||||
},
|
||||
tablast: function() {
|
||||
tabbehavior.cycleTab("last", { editorType: "ace" });
|
||||
},
|
||||
tabnew: function(editor, data) {
|
||||
var path = data.argv[1];
|
||||
if (!path) {
|
||||
tabManager.open({
|
||||
path: "",
|
||||
document: {
|
||||
meta: {
|
||||
newfile: true
|
||||
}
|
||||
},
|
||||
focus: true
|
||||
});
|
||||
}
|
||||
else {
|
||||
cliCmds[":"].commands.e(editor, data);
|
||||
}
|
||||
},
|
||||
tabclose: "closetab",
|
||||
tabmove: function(editor, data) {
|
||||
// todo
|
||||
},
|
||||
ascii: cliCmds.ascii,
|
||||
sort: function(editor, data) {
|
||||
commands.exec("sortlines");
|
||||
},
|
||||
};
|
||||
|
||||
// aliases
|
||||
cliCmds[":"].commands["tab drop"] = cliCmds[":"].commands.e;
|
||||
cliCmds[":"].commands.write = cliCmds[":"].commands.w;
|
||||
cliCmds[":"].commands.tabNext = cliCmds[":"].commands.tabn;
|
||||
cliCmds[":"].commands.tabPrevious = cliCmds[":"].commands.tabp;
|
||||
cliCmds[":"].commands.tabc = cliCmds[":"].commands.tabclose;
|
||||
|
||||
cliCmds[":"].commands.set = {
|
||||
vimOpts: [
|
||||
"cursorline", "cul", "nocursorline", "nocul", //, "highlightActiveLine",
|
||||
"expandtab", "et", "noexpandtab", "noet", //"useSoftTabs",
|
||||
"number", "nu", "nonumber", "nonu" // "showGutter"
|
||||
// ["relativenumber", "rnu", "norelativenumber", "nornu"]
|
||||
// 'softtabstop' 'sts' number
|
||||
// 'tabstop' 'ts' number
|
||||
],
|
||||
exec: function(ed, args) {
|
||||
var optName = args.argv[1];
|
||||
var optval = optName.slice(0, 2) != "no";
|
||||
if (optName[optName.length - 1] == "!") {
|
||||
var toggle = true;
|
||||
optName = optName.slice(0, -1);
|
||||
}
|
||||
var i = this.vimOpts.indexOf(optName);
|
||||
if (i == -1) {
|
||||
ed.cmdLine.setTimedMessage("Unrecognised option '" + optName + "'.", 1500);
|
||||
return;
|
||||
} else if (i < 4) {
|
||||
optName = "highlightActiveLine";
|
||||
} else if (i < 8) {
|
||||
optName = "useSoftTabs";
|
||||
} else if (i < 12) {
|
||||
optName = "showGutter";
|
||||
}
|
||||
if (toggle)
|
||||
optval = !ed.getOption(optName);
|
||||
ed.setOption(optName, optval);
|
||||
},
|
||||
getCompletions: function(e) {
|
||||
return this.vimOpts; //.concat(Object.keys(Editor.prototype.$options));
|
||||
}
|
||||
};
|
||||
|
||||
cliCmds[":"].commands.syntax = commands.commands.syntax;
|
||||
|
||||
cliCmds[":"].cliExec = function(ed, cmd, tokens) {
|
||||
var last = tokens[tokens.length - 1];
|
||||
if (last && last.type == "invisible")
|
||||
cmd += last.value;
|
||||
cmd = cmd.substr(1).trim();
|
||||
var args = cmd.split(/\s+/);
|
||||
cmd = args[0];
|
||||
|
||||
if (this.commands[cmd]) {
|
||||
cmd = this.commands[cmd];
|
||||
if (typeof cmd == "string")
|
||||
return commands.exec(cmd, null, { argv: args });
|
||||
else if (typeof cmd == "function")
|
||||
return cmd(ed, { argv: args });
|
||||
else if (cmd.exec)
|
||||
return cmd.exec(ed, { argv: args });
|
||||
}
|
||||
else if (commands.commands[cmd]) {
|
||||
commands.exec(cmd, null, { argv: args });
|
||||
}
|
||||
else if (/^\d+$/.test(cmd)) {
|
||||
ed.gotoLine(cmd, 0, true);
|
||||
}
|
||||
else {
|
||||
ed.cmdLine.setTimedMessage("Vim command '"
|
||||
+ cmd + "' not implemented.", 1500);
|
||||
}
|
||||
};
|
||||
|
||||
var allCommands;
|
||||
function getCompletions(command) {
|
||||
if (command) {
|
||||
if (command.getCompletions)
|
||||
return command.getCompletions() || [];
|
||||
if (command.commands)
|
||||
return Object.keys(command.commands);
|
||||
return [];
|
||||
}
|
||||
|
||||
if (!allCommands) {
|
||||
allCommands = Object.keys(commands.commands)
|
||||
.concat(Object.keys(cliCmds));
|
||||
}
|
||||
return allCommands;
|
||||
}
|
||||
|
||||
function getCommand(name, root) {
|
||||
if (root && root.commands && root.commands[name])
|
||||
return root.commands[name];
|
||||
if (root)
|
||||
return root;
|
||||
return cliCmds[name] || commands.commands[name];
|
||||
}
|
||||
|
||||
function getActiveEditor() {
|
||||
var tab = tabManager.focussedTab;
|
||||
if (tab && tab.editorType == "ace")
|
||||
return tab.editor.ace;
|
||||
}
|
||||
|
||||
function processCommandParts(ed, tokens, text) {
|
||||
for (var i = 0; i < tokens.length; i++) {
|
||||
var tok = tokens[i];
|
||||
var cmd = tok.command;
|
||||
if (!cmd)
|
||||
continue;
|
||||
|
||||
if (cmd.name !== tok.value) {
|
||||
var next = tokens[i + 1];
|
||||
if (!next || next.type !== "invisible")
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cmd.cliExec)
|
||||
return cmd.cliExec(ed, text, tokens);
|
||||
else if (cmd.exec)
|
||||
return cmd.exec(ed, {
|
||||
argv: text.split(/\s+/),
|
||||
text: text,
|
||||
tokens: tokens
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function endCommandInput(cmdLine) {
|
||||
cmdLine.addToHistory();
|
||||
cmdLine.setValue("");
|
||||
var editor = getActiveEditor();
|
||||
if (editor) editor.focus();
|
||||
}
|
||||
|
||||
function initCmdLine(cmdLine) {
|
||||
cmdLine.commands.addCommands([{
|
||||
bindKey: "Shift-Return|Ctrl-Return|Alt-Return",
|
||||
name: "insertNewLine",
|
||||
exec: function(cmdLine) {
|
||||
cmdLine.insert("\n");
|
||||
},
|
||||
}, {
|
||||
bindKey: "Esc|Shift-Esc|Ctrl-[",
|
||||
name: "cancel",
|
||||
exec: function(cmdLine) {
|
||||
endCommandInput(cmdLine);
|
||||
}
|
||||
}, {
|
||||
bindKey: "Return",
|
||||
name: "run",
|
||||
exec: function run(cmdLine) {
|
||||
var editor = cmdLine.editor || getActiveEditor();
|
||||
var tokens = cmdLine.session.getTokens(0);
|
||||
if (editor) editor.cmdLine = cmdLine;
|
||||
processCommandParts(editor, tokens, cmdLine.getValue());
|
||||
endCommandInput(cmdLine);
|
||||
},
|
||||
}, {
|
||||
bindKey: "Tab",
|
||||
name: "tabNext",
|
||||
exec: function tabNext(ed) { tabCycle(ed, 1); },
|
||||
}, {
|
||||
bindKey: "Shift-Tab",
|
||||
name: "tabPrevious",
|
||||
exec: function tabPrevious(ed) { tabCycle(ed, -1); },
|
||||
}, {
|
||||
bindKey: "Right",
|
||||
name: "arrowCompleteRight",
|
||||
exec: function arrowCompleteRight(ed) {
|
||||
var session = ed.session;
|
||||
var col = ed.selection.isEmpty() ? ed.selection.lead.column : -1;
|
||||
ed.navigateRight();
|
||||
var tok = session.getTokenAt(0, col + 1);
|
||||
if (col == ed.selection.lead.column && tok && tok.type == "invisible")
|
||||
session.doc.insertInLine({ row: 0, column: col }, tok.value);
|
||||
},
|
||||
}, {
|
||||
bindKey: "Up",
|
||||
name: "Up",
|
||||
exec: function(cmdLine) {cmdLine.navigateHistory(-1);},
|
||||
}, {
|
||||
bindKey: "Down",
|
||||
name: "Down",
|
||||
exec: function(cmdLine) {cmdLine.navigateHistory(1);},
|
||||
}, {
|
||||
bindKey: "Ctrl-Home|PageUp",
|
||||
name: "firstInHistory",
|
||||
exec: function(cmdLine) {cmdLine.navigateHistory(0);},
|
||||
}, {
|
||||
bindKey: "Ctrl-End|PageDown",
|
||||
name: "lastInHistory",
|
||||
exec: function(cmdLine) {cmdLine.navigateHistory();}
|
||||
}]);
|
||||
|
||||
function tabCycle(ed, dir) {
|
||||
var session = ed.session;
|
||||
var range = ed.getSelectionRange();
|
||||
var line = session.getLine(0);
|
||||
var len = line.length;
|
||||
|
||||
if (range.end.column != len || !range.isEmpty()) {
|
||||
ed.navigateLineEnd();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ed.$tabCycle) {
|
||||
var tok = session.getTokenAt(0, len) || { value: "", type: "" };
|
||||
var matches = session.getState(0);
|
||||
|
||||
if (matches == "start")
|
||||
matches = getCompletions();
|
||||
if (!matches)
|
||||
return;
|
||||
|
||||
if (matches.length == 1 && tok.value == matches[0]) {
|
||||
if (tok.command) {
|
||||
matches = getCompletions(tok.command);
|
||||
tok = { value: "", type: "" };
|
||||
}
|
||||
if (!matches)
|
||||
return;
|
||||
}
|
||||
|
||||
ed.$tabCycle = {
|
||||
matches: matches,
|
||||
index: tok.value == matches[0] ? 0 : -1,
|
||||
start: len - tok.value.length
|
||||
};
|
||||
ed.commands.on("exec", function onExec(e) {
|
||||
var name = e.command && e.command.name;
|
||||
if (name !== "tabNext" && name !== "tabPrevious") {
|
||||
ed.$tabCycle = null;
|
||||
ed.commands.removeListener("exec", onExec);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
var matches = ed.$tabCycle.matches;
|
||||
var index = ed.$tabCycle.index;
|
||||
var start = ed.$tabCycle.start;
|
||||
|
||||
index += dir;
|
||||
index %= matches.length;
|
||||
if (index < 0)
|
||||
index = matches.length + index;
|
||||
ed.$tabCycle.index = index;
|
||||
|
||||
var match = matches[index];
|
||||
if (!match)
|
||||
return;
|
||||
|
||||
var i = 0;
|
||||
while (match[i] && match[i] == line[start + i])
|
||||
i++;
|
||||
start += i;
|
||||
match = match.substr(i);
|
||||
|
||||
if (i === 0 && (/\w/.test(match[0]) && /\w/.test(line[start - 1])))
|
||||
match = " " + match;
|
||||
if (/\w$/.test(match))
|
||||
match += " ";
|
||||
|
||||
range.start.column = start;
|
||||
range.end.column = len;
|
||||
session.replace(range, match);
|
||||
|
||||
if (ed.$tabCycle.matches.length == 1)
|
||||
ed.$tabCycle = null;
|
||||
}
|
||||
|
||||
cmdLine.history = [];
|
||||
cmdLine.navigateHistory = function(dir) {
|
||||
var cliCmd = this.getCurrentCommandWithHistory() || {};
|
||||
var history = cliCmd.history || this.history;
|
||||
var index = history.index;
|
||||
var cmd = history[index] || "";
|
||||
|
||||
if (dir === 0) {
|
||||
index = 0;
|
||||
} else if (dir === null) {
|
||||
index = history.length;
|
||||
} else if (typeof dir == "number") {
|
||||
index += dir;
|
||||
if (index < 0)
|
||||
index = 0;
|
||||
if (index > history.length)
|
||||
index = history.length;
|
||||
}
|
||||
|
||||
cmd = history[index] || "";
|
||||
if (cliCmd.history && cliCmd.name)
|
||||
cmd = cliCmd.name + cmd;
|
||||
// TODO keep history.lastTyped
|
||||
this.setValue(cmd, 1);
|
||||
history.index = index;
|
||||
};
|
||||
|
||||
cmdLine.addToHistory = function(val) {
|
||||
var cliCmd = this.getCurrentCommandWithHistory() || {};
|
||||
var history = cliCmd.history || this.history;
|
||||
val = val || this.getValue();
|
||||
if (cliCmd.name && cliCmd.history)
|
||||
val = val.substr(cliCmd.name.length);
|
||||
|
||||
if (val && val != history[history.index]) {
|
||||
history.push(val);
|
||||
history.index = history.length;
|
||||
}
|
||||
};
|
||||
|
||||
cmdLine.getCurrentCommand = function() {
|
||||
var tokens = this.session.getTokens(0);
|
||||
var tok = tokens[tokens.length - 1];
|
||||
return tok && tok.command;
|
||||
};
|
||||
|
||||
cmdLine.getCurrentCommandWithHistory = function() {
|
||||
var tokens = this.session.getTokens(0);
|
||||
for (var i = tokens.length; i--;) {
|
||||
var tok = tokens[i];
|
||||
if (tok && tok.command && tok.command.history)
|
||||
return tok.command;
|
||||
}
|
||||
};
|
||||
|
||||
cmdLine.on("blur", function() {
|
||||
cmdLine.renderer.$cursorLayer.element.style.opacity = 0;
|
||||
if (!cmdLine.getValue()) {
|
||||
cmdLine.renderer.content.style.visibility = "hidden";
|
||||
cmdLine.$messageNode.style.display = "";
|
||||
cmdLine.$inMessageMode = true;
|
||||
cmdLine.$messageNode.textContent = cmdLine.$message || "";
|
||||
}
|
||||
});
|
||||
|
||||
cmdLine.on("focus", function() {
|
||||
cmdLine.renderer.$cursorLayer.element.style.opacity = "";
|
||||
cmdLine.renderer.content.style.visibility = "";
|
||||
cmdLine.$messageNode.style.display = "none";
|
||||
cmdLine.$inMessageMode = false;
|
||||
});
|
||||
|
||||
cmdLine.commands.on("exec", function(e) {
|
||||
if (!e.command)
|
||||
return;
|
||||
if (e.command.name == "insertstring") {
|
||||
|
||||
}
|
||||
cmdLine.commands.lastCommandName = e.command.name;
|
||||
});
|
||||
|
||||
cmdLine.session.bgTokenizer.$tokenizeRow = function(row) {
|
||||
var line = this.doc.getLine(row);
|
||||
var command = null;
|
||||
var tokens = [];
|
||||
function add(type, value) {
|
||||
tokens.push({ type: type, value: value, command: command });
|
||||
}
|
||||
|
||||
while (line.length) {
|
||||
var names = getCompletions(command);
|
||||
var matches = matchCommand(line, names);
|
||||
|
||||
if (!matches.length) {
|
||||
add("text", line);
|
||||
break;
|
||||
}
|
||||
var cur = matches[0];
|
||||
command = getCommand(cur, command);
|
||||
if (cur.length >= line.length) {
|
||||
add("keyword", line);
|
||||
add("invisible", cur.substring(line.length));
|
||||
} else {
|
||||
add("keyword", cur);
|
||||
}
|
||||
line = line.substr(cur.length);
|
||||
var i = line.search(/\S|$/);
|
||||
if (i > 0) {
|
||||
add("text", line.substring(0, i));
|
||||
line = line.substr(i);
|
||||
if (!line.length)
|
||||
matches = getCompletions(command);
|
||||
}
|
||||
}
|
||||
|
||||
this.lines[row] = tokens;
|
||||
this.states[row] = matches;
|
||||
return tokens;
|
||||
};
|
||||
|
||||
function matchCommand(line, names) {
|
||||
var matches = [];
|
||||
names.forEach(function(name) {
|
||||
if (name.length < line.length) {
|
||||
var isMatch = line.lastIndexOf(name, 0) === 0;
|
||||
if (isMatch && /\w/.test(name[name.length - 1]))
|
||||
isMatch = !/\w/.test(line[name.length]);
|
||||
} else {
|
||||
var isMatch = name.lastIndexOf(line, 0) === 0;
|
||||
}
|
||||
if (isMatch)
|
||||
matches.push(name);
|
||||
});
|
||||
return matches;
|
||||
}
|
||||
|
||||
cmdLine.$messageNode = document.createElement("div");
|
||||
cmdLine.$messageNode.style.cssText = "position:absolute;"
|
||||
+ "opacity:0.8;padding:0 5px;top:0;font-size:11px";
|
||||
cmdLine.container.appendChild(cmdLine.$messageNode);
|
||||
|
||||
cmdLine.$clearMessageDelayed = lang.delayedCall(function() {
|
||||
cmdLine.setMessage("");
|
||||
});
|
||||
cmdLine.setTimedMessage = function(text, timeout) {
|
||||
this.setMessage(text);
|
||||
this.once("setMessage", function() {
|
||||
cmdLine.$clearMessageDelayed.cancel();
|
||||
});
|
||||
cmdLine.$clearMessageDelayed.schedule(timeout || 2000);
|
||||
};
|
||||
|
||||
cmdLine.setMessage = function(text, from) {
|
||||
this._signal("setMessage", text);
|
||||
this.$message = text;
|
||||
if (this.$inMessageMode)
|
||||
this.$messageNode.textContent = text;
|
||||
};
|
||||
|
||||
if (!cmdLine.isFocused())
|
||||
cmdLine._emit("blur");
|
||||
|
||||
cmdLine.commands.removeCommands(
|
||||
["find", "gotoline", "findall", "replace", "replaceall"]);
|
||||
}
|
||||
|
||||
/***** Lifecycle *****/
|
||||
|
||||
plugin.on("load", function() {
|
||||
load();
|
||||
});
|
||||
plugin.on("enable", function() {
|
||||
|
||||
});
|
||||
plugin.on("disable", function() {
|
||||
|
||||
});
|
||||
plugin.on("unload", function() {
|
||||
loaded = false;
|
||||
});
|
||||
|
||||
/***** Register and define API *****/
|
||||
|
||||
/**
|
||||
*
|
||||
**/
|
||||
plugin.freezePublicAPI({
|
||||
/**
|
||||
* @ignore
|
||||
*/
|
||||
searchStore: searchStore,
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
get aml() { return cmdLine; },
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
get ace() { return cmdLine.ace; },
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
show: show,
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
hide: hide,
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
toggle: toggle
|
||||
});
|
||||
|
||||
register(null, {
|
||||
"vim.cli": plugin
|
||||
});
|
||||
}
|
||||
});
|
|
@ -0,0 +1,33 @@
|
|||
define(function(require, exports, module) {
|
||||
|
||||
exports.showCli = true;
|
||||
exports.aceKeyboardHandler = require("ace/keyboard/emacs").handler;
|
||||
|
||||
|
||||
var keys = [{
|
||||
bindKey: "C-x C-f",
|
||||
name: "navigate"
|
||||
}, {
|
||||
bindKey: "C-x C-s",
|
||||
name: "save"
|
||||
}, {
|
||||
bindKey: "C-x s",
|
||||
name: "saveall"
|
||||
}, {
|
||||
bindKey: "C-x C-w",
|
||||
name: "saveas"
|
||||
}];
|
||||
keys.forEach(function(item) {
|
||||
exports.aceKeyboardHandler.bindKey(item.bindKey, {
|
||||
name: item.name,
|
||||
exec: ideCommand
|
||||
});
|
||||
});
|
||||
|
||||
// todo find a way to integrate ide commands with vim and emacs modes
|
||||
exports.execIdeCommand = null;
|
||||
function ideCommand() {
|
||||
exports.execIdeCommand(this.name);
|
||||
}
|
||||
|
||||
});
|
|
@ -0,0 +1,255 @@
|
|||
define(function(require, exports, module) {
|
||||
main.consumes = [
|
||||
"Plugin", "ui", "ace", "menus", "settings", "vim.cli", "tabManager",
|
||||
"commands", "c9", "tree", "dialog.error"
|
||||
];
|
||||
main.provides = ["keymaps"];
|
||||
return main;
|
||||
|
||||
function main(options, imports, register) {
|
||||
var Plugin = imports.Plugin;
|
||||
var ui = imports.ui;
|
||||
var ace = imports.ace;
|
||||
var menus = imports.menus;
|
||||
var commands = imports.commands;
|
||||
var tabManager = imports.tabManager;
|
||||
var settings = imports.settings;
|
||||
var cli = imports["vim.cli"];
|
||||
var c9 = imports.c9;
|
||||
var tree = imports.tree; // TODO: find a way to make dependency on tree optional
|
||||
var showError = imports["dialog.error"].show;
|
||||
|
||||
/***** Initialization *****/
|
||||
|
||||
var plugin = new Plugin("Ajax.org", main.consumes);
|
||||
// var emit = plugin.getEmitter();
|
||||
|
||||
var currentMode, activeMode;
|
||||
|
||||
var loaded = false;
|
||||
function load() {
|
||||
if (loaded) return false;
|
||||
loaded = true;
|
||||
|
||||
var mnuKbModes = new ui.menu({
|
||||
"onprop.visible": function(e) {
|
||||
if (e.value) {
|
||||
var value = settings.get("user/ace/@keyboardmode");
|
||||
mnuKbModes.select(null, value);
|
||||
}
|
||||
}
|
||||
});
|
||||
menus.addItemByPath("Edit/~", new ui.divider(), 650, plugin);
|
||||
menus.addItemByPath("Edit/Keyboard Mode/", mnuKbModes, 660, plugin);
|
||||
|
||||
var c = 1000;
|
||||
["Default", "Vim", "Emacs", "Sublime"].forEach(function(label) {
|
||||
menus.addItemByPath("Edit/Keyboard Mode/" + label, new ui.item({
|
||||
type: "radio",
|
||||
value: label.toLowerCase(),
|
||||
onclick: function(e) {
|
||||
setMode(mnuKbModes.getValue());
|
||||
}
|
||||
}), c += 100, plugin);
|
||||
});
|
||||
|
||||
settings.on("read", function() {
|
||||
settings.setDefaults("user/ace", [
|
||||
["keyboardmode", "default"]
|
||||
]);
|
||||
|
||||
var mode = settings.get("user/ace/@keyboardmode");
|
||||
if (mode && mode != "default")
|
||||
setMode(mode);
|
||||
}, plugin);
|
||||
|
||||
settings.on("user/ace", function() {
|
||||
var mode = settings.get("user/ace/@keyboardmode");
|
||||
if (currentMode != mode)
|
||||
setMode(mode);
|
||||
}, plugin);
|
||||
|
||||
ace.on("create", function(e) { setMode(null, e); }, plugin);
|
||||
|
||||
setTimeout(checkHostileExtensions, 1000);
|
||||
}
|
||||
|
||||
/***** Methods *****/
|
||||
|
||||
function setMode(mode, tab) {
|
||||
if (!settings.model.loaded)
|
||||
return;
|
||||
|
||||
if (!mode)
|
||||
mode = settings.get("user/ace/@keyboardmode");
|
||||
else if (currentMode != mode) {
|
||||
currentMode = mode;
|
||||
settings.set("user/ace/@keyboardmode", mode);
|
||||
}
|
||||
|
||||
if (mode == "emacs" || mode == "vim" || mode == "sublime") {
|
||||
mode = "plugins/c9.ide.ace.keymaps/" + mode + "/keymap";
|
||||
} else {
|
||||
mode = null;
|
||||
}
|
||||
|
||||
if (mode)
|
||||
require([mode], setKeymap);
|
||||
else
|
||||
setKeymap({});
|
||||
|
||||
function setKeymap(keymap) {
|
||||
if (keymap.showCli)
|
||||
cli.show();
|
||||
else
|
||||
cli.hide();
|
||||
|
||||
(tab ? [tab] : tabManager.getTabs()).forEach(function(tab) {
|
||||
if (tab.editor && tab.editor.type == "ace") {
|
||||
var editor = tab.editor.ace;
|
||||
// Set Mode
|
||||
editor.setKeyboardHandler(keymap.aceKeyboardHandler);
|
||||
|
||||
editor.showCommandLine = showCommandLine;
|
||||
}
|
||||
});
|
||||
|
||||
if (activeMode == mode)
|
||||
return;
|
||||
|
||||
updateIdeKeymap(mode);
|
||||
activeMode = mode;
|
||||
}
|
||||
}
|
||||
|
||||
function updateIdeKeymap(path) {
|
||||
tree.once("ready", function() {
|
||||
var kb = path ? require(path) : {};
|
||||
tree.tree.keyBinding.setKeyboardHandler(kb.treeKeyboardHandler);
|
||||
});
|
||||
c9.once("ready", function() {
|
||||
var allCommands = commands.commands;
|
||||
Object.keys(allCommands).forEach(function(name) {
|
||||
var cmd = allCommands[name];
|
||||
if (cmd && cmd.originalBindKey)
|
||||
cmd.bindKey = cmd.originalBindKey;
|
||||
});
|
||||
|
||||
var kb = path ? require(path) : {};
|
||||
if ("execIdeCommand" in kb)
|
||||
kb.execIdeCommand = commands.exec;
|
||||
|
||||
if (kb.ideCommands) {
|
||||
kb.ideCommands.forEach(function(x) {
|
||||
commands.addCommand(x, plugin);
|
||||
});
|
||||
}
|
||||
|
||||
if (kb.editorCommands) {
|
||||
kb.editorCommands.forEach(function(x) {
|
||||
x.findEditor = findEditor;
|
||||
x.isAvailable = isAvailableAce;
|
||||
commands.addCommand(x, plugin);
|
||||
});
|
||||
}
|
||||
|
||||
if (kb.ideKeymap)
|
||||
kb.ideKeymap.forEach(bindKey);
|
||||
if (kb.editorKeymap)
|
||||
kb.editorKeymap.forEach(bindKey);
|
||||
|
||||
commands.reset();
|
||||
|
||||
function bindKey(x) {
|
||||
var cmd = allCommands[x.name];
|
||||
if (cmd && x.bindKey) {
|
||||
x.bindKey.mac = normalize(x.bindKey.mac);
|
||||
x.bindKey.win = normalize(x.bindKey.win);
|
||||
cmd.bindKey = x.bindKey;
|
||||
}
|
||||
}
|
||||
|
||||
function normalize(str) {
|
||||
return str && str.replace(/(^|-| )(\w)/g, function(_, a, b) {
|
||||
return a + b.toUpperCase();
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function showCommandLine(val, options) {
|
||||
if (!options) options = {};
|
||||
cli.show();
|
||||
this.cmdLine = cli.ace;
|
||||
this.cmdLine.editor = this;
|
||||
if (options.focus !== false) {
|
||||
cli.aml.focus();
|
||||
}
|
||||
if (options.message != null) {
|
||||
if (options.timeout)
|
||||
cli.ace.setTimedMessage(options.message, options.timeout);
|
||||
else
|
||||
cli.ace.setMessage(options.message);
|
||||
}
|
||||
|
||||
if (typeof val == "string")
|
||||
this.cmdLine.setValue(val, 1);
|
||||
}
|
||||
|
||||
|
||||
function isAvailableAce(editor, args, event) {
|
||||
if (!editor || !editor.ace) return false;
|
||||
|
||||
// using this instead of editor.type == "ace" to make
|
||||
// commands avaliable in editors inheriting from ace
|
||||
if (event instanceof KeyboardEvent && (!editor.ace.isFocused()))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function findEditor(editor) {
|
||||
return editor && editor.ace || editor;
|
||||
}
|
||||
|
||||
function checkHostileExtensions() {
|
||||
var messages = [];
|
||||
try {
|
||||
var d = document.body.nextSibling;
|
||||
if (d && d.shadowRoot && d.shadowRoot.querySelector
|
||||
&& d.shadowRoot.querySelector("[class*=vimium]")) {
|
||||
messages.push("Vimium breaks cloud9 keyboard shortcuts, please disable it on this site.");
|
||||
}
|
||||
} catch (e) {
|
||||
}
|
||||
if (messages.length)
|
||||
showError(messages.join("\n"));
|
||||
}
|
||||
|
||||
/***** Lifecycle *****/
|
||||
|
||||
plugin.on("load", function() {
|
||||
load();
|
||||
});
|
||||
plugin.on("enable", function() {
|
||||
|
||||
});
|
||||
plugin.on("disable", function() {
|
||||
|
||||
});
|
||||
plugin.on("unload", function() {
|
||||
currentMode = activeMode = null;
|
||||
loaded = false;
|
||||
});
|
||||
|
||||
/***** Register and define API *****/
|
||||
|
||||
plugin.freezePublicAPI({
|
||||
|
||||
});
|
||||
|
||||
register(null, {
|
||||
keymaps: plugin
|
||||
});
|
||||
}
|
||||
});
|
|
@ -0,0 +1,21 @@
|
|||
{
|
||||
"name": "c9.ide.ace.keymaps",
|
||||
"description": "The repository for c9.ide.ace.keymaps, a Cloud9 core plugin",
|
||||
"version": "3.0.0",
|
||||
"author": "Cloud9",
|
||||
"contributors": [],
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "http://github.com/c9/c9.ide.ace.keymaps.git"
|
||||
},
|
||||
"plugins": {
|
||||
"cli": {},
|
||||
"keymaps": {}
|
||||
},
|
||||
"categories": [
|
||||
"core"
|
||||
],
|
||||
"licenses": [
|
||||
"MIT"
|
||||
]
|
||||
}
|
|
@ -0,0 +1,132 @@
|
|||
var fs = require("fs");
|
||||
var base = __dirname;
|
||||
var commands = {};
|
||||
fs.readdirSync(base).map(function(p) {
|
||||
if (/.sublime-keymap/.test(p)) {
|
||||
var a = eval(fs.readFileSync(base + "/" + p, "utf8"));
|
||||
a.name = /\((\w+)\)/.exec(p)[1].toLowerCase();
|
||||
if (a.name == "osx") a.name = "mac";
|
||||
if (a.name == "windows") a.name = "win";
|
||||
return a;
|
||||
}
|
||||
}).filter(Boolean).map(function(keyMap) {
|
||||
keyMap.forEach(function(sublKey) {
|
||||
var name = sublKey.command;
|
||||
var args = sublKey.args ? JSON.stringify(sublKey.args) : "";
|
||||
var id = name + args;
|
||||
var o = commands[id] || (commands[id] = {
|
||||
bindKey: {},
|
||||
id: id,
|
||||
name: name
|
||||
});
|
||||
var oldKey = o.bindKey[keyMap.name];
|
||||
var newKey = convertKeys(sublKey.keys);
|
||||
if (oldKey) {
|
||||
if (oldKey.split("|").indexOf(newKey) == -1)
|
||||
newKey = oldKey + "|" + newKey;
|
||||
else
|
||||
newKey = oldKey;
|
||||
}
|
||||
o.bindKey[keyMap.name] = newKey;
|
||||
if (sublKey.context)
|
||||
o.context = sublKey.context;
|
||||
if (sublKey.args)
|
||||
o.args = sublKey.args;
|
||||
});
|
||||
});
|
||||
|
||||
commands = Object.keys(commands).map(function(id) {
|
||||
var cmd = commands[id];
|
||||
delete cmd.id;
|
||||
|
||||
if (cmd.bindKey.linux && cmd.bindKey.win == cmd.bindKey.linux)
|
||||
delete cmd.bindKey.linux;
|
||||
return cmd;
|
||||
}).sort(function(a, b) {
|
||||
if (a.context && !b.context) return 1;
|
||||
if (!a.context && b.context) return -1;
|
||||
if (a.context && b.context) {
|
||||
var astr = JSON.stringify(a.context);
|
||||
var bstr = JSON.stringify(b.context);
|
||||
if (astr > bstr) return 1;
|
||||
if (astr < bstr) return -1;
|
||||
}
|
||||
|
||||
if (a.name > b.name) return 1;
|
||||
if (a.name < b.name) return -1;
|
||||
|
||||
if (a.args && !b.args) return 1;
|
||||
if (!a.args && b.args) return -1;
|
||||
}).filter(function(x) {
|
||||
// filter out brace pairing commands as they are handled elsewhere
|
||||
return !/^(["'\[\]{}()\/](\|["'\[\]{}()\/])*|(escape))$/.test(x.bindKey.mac);
|
||||
}).filter(function(x) {
|
||||
// filter out completion commands
|
||||
return !/(^|_)completion($|_)/.test(x.name);
|
||||
});
|
||||
|
||||
void function group() {
|
||||
function nameCore(cmd) {
|
||||
if (/bookmark/.test(cmd.name))
|
||||
return "bookmark";
|
||||
if (/(^|_)mark($|_)/.test(cmd.name))
|
||||
return "mark";
|
||||
var name = cmd.name.replace(/(de|in)crease|prev|next|left|right|upper|lower|un|all/g, "");
|
||||
name = name.replace(/(^_*|_*$)/g, "");
|
||||
return name;
|
||||
}
|
||||
for (var i = 0; i < commands.length; i++) {
|
||||
var name = nameCore(commands[i]);
|
||||
var nameParts = name.split("_");
|
||||
for (var j = i + 1; j < commands.length; j++) {
|
||||
var o = commands[j];
|
||||
if (nameCore(o) == name) {
|
||||
commands.splice(j, 1);
|
||||
i++;
|
||||
commands.splice(i, 0, o);
|
||||
}
|
||||
}
|
||||
}
|
||||
}();
|
||||
|
||||
var cmdString = JSON.stringify(commands, null, 4)
|
||||
.replace(/\n {8}( +|(?=[}\]]))/g, " ")
|
||||
.replace(/"(\w+)":/g, "$1:")
|
||||
.replace(/(\[|\{) |(\S) (\]|\})/g, "$1$2$3")
|
||||
.replace(/"\\""/g, "'\"'")
|
||||
.replace(/},\s*{/g, "}, {")
|
||||
.replace(/\[\s*{/g, "[{")
|
||||
.replace(/}\s*]/g, "}]")
|
||||
.replace(/^ {4}/gm, "");
|
||||
fs.writeFileSync("keymap", cmdString, "utf8");
|
||||
|
||||
function convertKeys(sublKeys) {
|
||||
var sublimeToAceKey = {
|
||||
keypad0: "numpad0",
|
||||
keypad1: "numpad1",
|
||||
keypad2: "numpad2",
|
||||
keypad3: "numpad3",
|
||||
keypad4: "numpad4",
|
||||
keypad5: "numpad5",
|
||||
keypad6: "numpad6",
|
||||
keypad7: "numpad7",
|
||||
keypad8: "numpad8",
|
||||
keypad9: "numpad9",
|
||||
keypad_period: ".",
|
||||
keypad_divide: "/",
|
||||
forward_slash: "/",
|
||||
keypad_multiply: "*",
|
||||
keypad_minus: "-",
|
||||
keypad_plus: "+",
|
||||
keypad_enter: "numpadEnter",
|
||||
equals: "=",
|
||||
plus: "+",
|
||||
super: "cmd"
|
||||
};
|
||||
|
||||
return sublKeys.map(function(combo) {
|
||||
return combo.split("+").map(function(key) {
|
||||
return sublimeToAceKey[key] || key;
|
||||
}).join("-");
|
||||
}).join(" ");
|
||||
}
|
|
@ -0,0 +1,870 @@
|
|||
define(function(require, exports, module) {
|
||||
|
||||
/* ide commands */
|
||||
exports.ideKeymap = [
|
||||
// fallback to c9 defaults
|
||||
// {
|
||||
// bindKey: {mac: "cmd-w", win: "ctrl-w|ctrl-f4"},
|
||||
// name: "close"
|
||||
// }, {
|
||||
// bindKey: {mac: "cmd-shift-w", win: "ctrl-shift-w"},
|
||||
// name: "close_window"
|
||||
// }, {
|
||||
// bindKey: {linux: "ctrl-q", mac: "cmd-q"},
|
||||
// name: "exit"
|
||||
// }, {
|
||||
// bindKey: {mac: "cmd-n", win: "ctrl-n"},
|
||||
// name: "new_file"
|
||||
// },
|
||||
// {
|
||||
// bindKey: {mac: "cmd-o", win: "ctrl-o"},
|
||||
// name: "newfile"
|
||||
// }, {
|
||||
// bindKey: {mac: "cmd-shift-s", win: "ctrl-shift-s"},
|
||||
// name: "saveas"
|
||||
// }, {
|
||||
// bindKey: {mac: "cmd-s", win: "ctrl-s"},
|
||||
// name: "save"
|
||||
// }, {
|
||||
// bindKey: {mac: "cmd-alt-s"},
|
||||
// name: "saveall"
|
||||
// }, {
|
||||
// bindKey: {mac: "cmd-shift-n", win: "ctrl-shift-n"},
|
||||
// name: "newWindow"
|
||||
// },
|
||||
|
||||
{
|
||||
// todo
|
||||
bindKey: { mac: "cmd-ctrl-p", win: "ctrl-alt-p" },
|
||||
name: "prompt_select_workspace"
|
||||
}, {
|
||||
bindKey: { mac: "cmd-shift-t", win: "ctrl-shift-t" },
|
||||
name: "reopenLastTab"
|
||||
},
|
||||
|
||||
{
|
||||
bindKey: { mac: "f7|cmd-b", win: "f7|ctrl-b" },
|
||||
name: "build"
|
||||
}, {
|
||||
bindKey: { mac: "cmd-shift-b", win: "ctrl-shift-b" },
|
||||
name: "run"
|
||||
}, {
|
||||
bindKey: { mac: "ctrl-break", win: "ctrl-break" },
|
||||
name: "stopbuild"
|
||||
},
|
||||
|
||||
{
|
||||
bindKey: { mac: "cmd-t|cmd-p", win: "ctrl-p" },
|
||||
name: "navigate"
|
||||
}, {
|
||||
bindKey: { mac: "cmd-r", win: "ctrl-r" },
|
||||
name: "outline",
|
||||
args: { overlay: "goto", text: "@" }
|
||||
}, {
|
||||
bindKey: { mac: "cmd-shift-r", win: "ctrl-shift-r" },
|
||||
// todo: this should be project outline
|
||||
name: "outline"
|
||||
}, {
|
||||
bindKey: { mac: "ctrl-g", win: "ctrl-g" },
|
||||
name: "gotoline",
|
||||
args: { overlay: "goto", text: ":" }
|
||||
},
|
||||
// todo what is this?
|
||||
// {
|
||||
// bindKey: {win: "ctrl-;"},
|
||||
// name: "show_overlay",
|
||||
// args: {overlay: "goto", text: "#"}
|
||||
// },
|
||||
{
|
||||
bindKey: { mac: "cmd-shift-p", win: "ctrl-shift-p" },
|
||||
name: "commands"
|
||||
}, {
|
||||
bindKey: { mac: "ctrl-`", win: "ctrl-`" },
|
||||
name: "toggleconsole"
|
||||
}, {
|
||||
bindKey: { mac: "cmd-k cmd-b", win: "ctrl-k ctrl-b" },
|
||||
name: "toggletree"
|
||||
},
|
||||
|
||||
{
|
||||
bindKey: { mac: "f12|cmd-alt-down", win: "f12" },
|
||||
name: "jumptodef"
|
||||
},
|
||||
|
||||
/* panels */
|
||||
// {
|
||||
// bindKey: {mac: "cmd-k cmd-down", win: "ctrl-k ctrl-down"},
|
||||
// name: "mergeWithOtherPanels"
|
||||
// },
|
||||
// {
|
||||
// bindKey: {mac: "ctrl-1", win: "ctrl-1"},
|
||||
// name: "focus_group",
|
||||
// args: {group: 0}
|
||||
// }, {
|
||||
// bindKey: {mac: "ctrl-9", win: "ctrl-9"},
|
||||
// name: "focus_group",
|
||||
// args: {group: 8}
|
||||
// }, {
|
||||
// bindKey: {mac: "ctrl-2", win: "ctrl-2"},
|
||||
// name: "focus_group",
|
||||
// args: {group: 1}
|
||||
// }, {
|
||||
// bindKey: {mac: "ctrl-3", win: "ctrl-3"},
|
||||
// name: "focus_group",
|
||||
// args: {group: 2}
|
||||
// }, {
|
||||
// bindKey: {mac: "ctrl-4", win: "ctrl-4"},
|
||||
// name: "focus_group",
|
||||
// args: {group: 3}
|
||||
// }, {
|
||||
// bindKey: {mac: "ctrl-5", win: "ctrl-5"},
|
||||
// name: "focus_group",
|
||||
// args: {group: 4}
|
||||
// }, {
|
||||
// bindKey: {mac: "ctrl-6", win: "ctrl-6"},
|
||||
// name: "focus_group",
|
||||
// args: {group: 5}
|
||||
// }, {
|
||||
// bindKey: {mac: "ctrl-8", win: "ctrl-8"},
|
||||
// name: "focus_group",
|
||||
// args: {group: 7}
|
||||
// }, {
|
||||
// bindKey: {mac: "ctrl-7", win: "ctrl-7"},
|
||||
// name: "focus_group",
|
||||
// args: {group: 6}
|
||||
// }, {
|
||||
// bindKey: {mac: "cmd-k cmd-right", win: "ctrl-k ctrl-right"},
|
||||
// name: "focus_neighboring_group"
|
||||
// }, {
|
||||
// bindKey: {mac: "cmd-k cmd-left", win: "ctrl-k ctrl-left"},
|
||||
// name: "focus_neighboring_group",
|
||||
// args: {forward: false}
|
||||
// }, {
|
||||
// bindKey: {mac: "ctrl-0", win: "ctrl-0"},
|
||||
// name: "focus_side_bar"
|
||||
// }, {
|
||||
// bindKey: {mac: "ctrl-shift-9", win: "ctrl-shift-9"},
|
||||
// name: "move_to_group",
|
||||
// args: {group: 8}
|
||||
// }, {
|
||||
// bindKey: {mac: "ctrl-shift-2", win: "ctrl-shift-2"},
|
||||
// name: "move_to_group",
|
||||
// args: {group: 1}
|
||||
// }, {
|
||||
// bindKey: {mac: "ctrl-shift-8", win: "ctrl-shift-8"},
|
||||
// name: "move_to_group",
|
||||
// args: {group: 7}
|
||||
// }, {
|
||||
// bindKey: {mac: "ctrl-shift-7", win: "ctrl-shift-7"},
|
||||
// name: "move_to_group",
|
||||
// args: {group: 6}
|
||||
// }, {
|
||||
// bindKey: {mac: "ctrl-shift-1", win: "ctrl-shift-1"},
|
||||
// name: "move_to_group",
|
||||
// args: {group: 0}
|
||||
// }, {
|
||||
// bindKey: {mac: "ctrl-shift-6", win: "ctrl-shift-6"},
|
||||
// name: "move_to_group",
|
||||
// args: {group: 5}
|
||||
// }, {
|
||||
// bindKey: {mac: "ctrl-shift-5", win: "ctrl-shift-5"},
|
||||
// name: "move_to_group",
|
||||
// args: {group: 4}
|
||||
// }, {
|
||||
// bindKey: {mac: "ctrl-shift-4", win: "ctrl-shift-4"},
|
||||
// name: "move_to_group",
|
||||
// args: {group: 3}
|
||||
// }, {
|
||||
// bindKey: {mac: "ctrl-shift-3", win: "ctrl-shift-3"},
|
||||
// name: "move_to_group",
|
||||
// args: {group: 2}
|
||||
// }, {
|
||||
// bindKey: {mac: "cmd-9", win: "alt-9"},
|
||||
// name: "select_by_index",
|
||||
// args: {index: 8}
|
||||
// }, {
|
||||
// bindKey: {mac: "cmd-1", win: "alt-1"},
|
||||
// name: "select_by_index",
|
||||
// args: {index: 0}
|
||||
// }, {
|
||||
// bindKey: {mac: "cmd-0", win: "alt-0"},
|
||||
// name: "select_by_index",
|
||||
// args: {index: 9}
|
||||
// }, {
|
||||
// bindKey: {mac: "cmd-8", win: "alt-8"},
|
||||
// name: "select_by_index",
|
||||
// args: {index: 7}
|
||||
// }, {
|
||||
// bindKey: {mac: "cmd-3", win: "alt-3"},
|
||||
// name: "select_by_index",
|
||||
// args: {index: 2}
|
||||
// }, {
|
||||
// bindKey: {mac: "cmd-7", win: "alt-7"},
|
||||
// name: "select_by_index",
|
||||
// args: {index: 6}
|
||||
// }, {
|
||||
// bindKey: {mac: "cmd-2", win: "alt-2"},
|
||||
// name: "select_by_index",
|
||||
// args: {index: 1}
|
||||
// }, {
|
||||
// bindKey: {mac: "cmd-6", win: "alt-6"},
|
||||
// name: "select_by_index",
|
||||
// args: {index: 5}
|
||||
// }, {
|
||||
// bindKey: {mac: "cmd-5", win: "alt-5"},
|
||||
// name: "select_by_index",
|
||||
// args: {index: 4}
|
||||
// }, {
|
||||
// bindKey: {mac: "cmd-4", win: "alt-4"},
|
||||
// name: "select_by_index",
|
||||
// args: {index: 3}
|
||||
// }, {
|
||||
// bindKey: {mac: "cmd-k cmd-shift-right", win: "ctrl-k ctrl-shift-right"},
|
||||
// name: "move_to_neighboring_group"
|
||||
// }, {
|
||||
// bindKey: {mac: "cmd-k cmd-shift-left", win: "ctrl-k ctrl-shift-left"},
|
||||
// name: "move_to_neighboring_group",
|
||||
// args: {forward: false}
|
||||
// }, {
|
||||
// bindKey: {mac: "cmd-k cmd-up", win: "ctrl-k ctrl-up"},
|
||||
// name: "new_pane"
|
||||
// }, {
|
||||
// bindKey: {mac: "cmd-k cmd-shift-up", win: "ctrl-k ctrl-shift-up"},
|
||||
// name: "new_pane",
|
||||
// args: {move: false}
|
||||
// }, {
|
||||
// bindKey: {mac: "cmd-alt-4", win: "alt-shift-4"},
|
||||
// name: "set_layout",
|
||||
// args: {cols: [0, 0.25, 0.5, 0.75, 1], rows: [0, 1], cells: [[0, 0, 1, 1], [1, 0, 2, 1], [2, 0, 3, 1], [3, 0, 4, 1] ]}
|
||||
// }, {
|
||||
// bindKey: {mac: "cmd-alt-shift-2", win: "alt-shift-8"},
|
||||
// name: "set_layout",
|
||||
// args: {cols: [0, 1], rows: [0, 0.5, 1], cells: [[0, 0, 1, 1], [0, 1, 1, 2] ]}
|
||||
// }, {
|
||||
// bindKey: {mac: "cmd-alt-shift-3", win: "alt-shift-9"},
|
||||
// name: "set_layout",
|
||||
// args: {cols: [0, 1], rows: [0, 0.33, 0.66, 1], cells: [[0, 0, 1, 1], [0, 1, 1, 2], [0, 2, 1, 3] ]}
|
||||
// }, {
|
||||
// bindKey: {mac: "cmd-alt-5", win: "alt-shift-5"},
|
||||
// name: "set_layout",
|
||||
// args: {cols: [0, 0.5, 1], rows: [0, 0.5, 1], cells: [[0, 0, 1, 1], [1, 0, 2, 1], [0, 1, 1, 2], [1, 1, 2, 2] ]}
|
||||
// }, {
|
||||
// bindKey: {mac: "cmd-alt-3", win: "alt-shift-3"},
|
||||
// name: "set_layout",
|
||||
// args: {cols: [0, 0.33, 0.66, 1], rows: [0, 1], cells: [[0, 0, 1, 1], [1, 0, 2, 1], [2, 0, 3, 1] ]}
|
||||
// }, {
|
||||
// bindKey: {mac: "cmd-alt-2", win: "alt-shift-2"},
|
||||
// name: "set_layout",
|
||||
// args: {cols: [0, 0.5, 1], rows: [0, 1], cells: [[0, 0, 1, 1], [1, 0, 2, 1] ]}
|
||||
// }, {
|
||||
// bindKey: {mac: "cmd-alt-1", win: "alt-shift-1"},
|
||||
// name: "set_layout",
|
||||
// args: {cols: [0, 1], rows: [0, 1], cells: [[0, 0, 1, 1] ]}
|
||||
// },
|
||||
|
||||
// {
|
||||
// bindKey: {mac: "cmd-ctrl-shift-f", win: "shift-f11"},
|
||||
// name: "toggle_distraction_free"
|
||||
// }, {
|
||||
// bindKey: {mac: "cmd-ctrl-f", win: "f11"},
|
||||
// name: "toggle_full_screen"
|
||||
// },
|
||||
|
||||
{
|
||||
bindKey: { mac: "cmd-shift-]|cmd-alt-right", win: "ctrl-pagedown" },
|
||||
name: "gototabright"
|
||||
}, {
|
||||
bindKey: { mac: "cmd-shift-[|cmd-alt-left", win: "ctrl-pageup" },
|
||||
name: "gototableft"
|
||||
}, {
|
||||
bindKey: { mac: "ctrl-tab", win: "ctrl-tab" },
|
||||
name: "nexttab"
|
||||
}, {
|
||||
bindKey: { mac: "ctrl-shift-tab", win: "ctrl-shift-tab" },
|
||||
name: "previoustab"
|
||||
},
|
||||
|
||||
{
|
||||
bindKey: {},
|
||||
name: "nextpane"
|
||||
},
|
||||
{
|
||||
bindKey: { mac: "ctrl+alt+f", win: "ctrl+alt+f" }, // shortcut from codeformatter plugin
|
||||
name: "formatcode"
|
||||
},
|
||||
|
||||
|
||||
// {
|
||||
// bindKey: {mac: "cmd-alt-up", win: "alt-o"},
|
||||
// name: "switch_file",
|
||||
// args: {extensions: ["cpp", "cxx", "cc", "c", "hpp", "hxx", "h", "ipp", "inl", "m", "mm"] }
|
||||
// },
|
||||
|
||||
{
|
||||
bindKey: { linux: "ctrl--", mac: "cmd--", win: "ctrl--|ctrl-shift-=|ctrl-shift-+" },
|
||||
name: "smallerfont"
|
||||
}, {
|
||||
bindKey: { linux: "ctrl--|ctrl-=", mac: "cmd-=|cmd-+", win: "ctrl--|ctrl-=|ctrl-+" },
|
||||
name: "largerfont"
|
||||
},
|
||||
|
||||
|
||||
/* bookmarks */
|
||||
// {
|
||||
// bindKey: {mac: "cmd-shift-f2", win: "ctrl-shift-f2"},
|
||||
// name: "clear_bookmarks"
|
||||
// }, {
|
||||
// bindKey: {mac: "cmd-k cmd-g", win: "ctrl-k ctrl-g"},
|
||||
// name: "clear_bookmarks",
|
||||
// args: {name: "mark"}
|
||||
// }, {
|
||||
// bindKey: {mac: "f2", win: "f2"},
|
||||
// name: "next_bookmark"
|
||||
// }, {
|
||||
// bindKey: {mac: "shift-f2", win: "shift-f2"},
|
||||
// name: "prev_bookmark"
|
||||
// }, {
|
||||
// bindKey: {mac: "alt-f2", win: "alt-f2"},
|
||||
// name: "select_all_bookmarks"
|
||||
// }, {
|
||||
// bindKey: {mac: "cmd-f2", win: "ctrl-f2"},
|
||||
// name: "toggle_bookmark"
|
||||
// },
|
||||
|
||||
{
|
||||
bindKey: { mac: "cmd-e", win: "ctrl-e" },
|
||||
name: "revealtab" // todo
|
||||
},
|
||||
|
||||
/* find replace */
|
||||
{
|
||||
bindKey: { mac: "cmd-alt-f", win: "ctrl-h" },
|
||||
name: "replace"
|
||||
}, {
|
||||
bindKey: { mac: "cmd-alt-e", win: "ctrl-shift-h" },
|
||||
name: "replacenext"
|
||||
}, {
|
||||
bindKey: { mac: "cmd-e", win: "ctrl-e" },
|
||||
name: "slurp_find_string" // todo
|
||||
}, {
|
||||
bindKey: { mac: "cmd-shift-e", win: "ctrl-shift-e" },
|
||||
name: "slurp_replace_string"
|
||||
}, {
|
||||
bindKey: { mac: "ctrl-alt-enter", win: "ctrl-alt-enter" },
|
||||
name: "replaceall"
|
||||
}, {
|
||||
bindKey: { mac: "cmd-f", win: "ctrl-f" },
|
||||
name: "find"
|
||||
}, {
|
||||
bindKey: { mac: "cmd-shift-f", win: "ctrl-shift-f" },
|
||||
name: "searchinfiles",
|
||||
},
|
||||
// {
|
||||
// bindKey: {mac: "f4", win: "f4"},
|
||||
// name: "next_result"
|
||||
// }, {
|
||||
// bindKey: {mac: "shift-f4", win: "shift-f4"},
|
||||
// name: "prev_result"
|
||||
// },
|
||||
// todo incremental_find
|
||||
// {
|
||||
// bindKey: {mac: "cmd-alt-c", win: "alt-c"},
|
||||
// name: "toggle_case_sensitive",
|
||||
// }, {
|
||||
// bindKey: {mac: "cmd-i", win: "ctrl-i"},
|
||||
// name: "show_panel",
|
||||
// args: {panel: "incremental_find", reverse: false}
|
||||
// }, {
|
||||
// bindKey: {mac: "cmd-shift-i", win: "ctrl-shift-i"},
|
||||
// name: "show_panel",
|
||||
// args: {panel: "incremental_find", reverse: true}
|
||||
// },
|
||||
|
||||
// {
|
||||
// bindKey: {mac: "cmd-alt-a", win: "alt-a"},
|
||||
// name: "toggle_preserve_case",
|
||||
// }, {
|
||||
// bindKey: {mac: "cmd-alt-r", win: "alt-r"},
|
||||
// name: "toggle_regex",
|
||||
// }, {
|
||||
// bindKey: {mac: "cmd-alt-w", win: "alt-w"},
|
||||
// name: "toggle_whole_word",
|
||||
// },
|
||||
];
|
||||
|
||||
|
||||
exports.editorCommands = [{
|
||||
name: "find_all_under",
|
||||
exec: function(editor) {
|
||||
if (editor.selection.isEmpty())
|
||||
editor.selection.selectWord();
|
||||
editor.findAll();
|
||||
},
|
||||
readOnly: true
|
||||
}, {
|
||||
name: "find_under",
|
||||
exec: function(editor) {
|
||||
if (editor.selection.isEmpty())
|
||||
editor.selection.selectWord();
|
||||
editor.findNext();
|
||||
},
|
||||
readOnly: true
|
||||
}, {
|
||||
name: "find_under_prev",
|
||||
exec: function(editor) {
|
||||
if (editor.selection.isEmpty())
|
||||
editor.selection.selectWord();
|
||||
editor.findPrevious();
|
||||
},
|
||||
readOnly: true
|
||||
}, {
|
||||
name: "find_under_expand",
|
||||
exec: function(editor) {
|
||||
editor.selectMore(1, false, true);
|
||||
},
|
||||
scrollIntoView: "animate",
|
||||
readOnly: true
|
||||
}, {
|
||||
name: "find_under_expand_skip",
|
||||
exec: function(editor) {
|
||||
editor.selectMore(1, true, true);
|
||||
},
|
||||
scrollIntoView: "animate",
|
||||
readOnly: true
|
||||
}, {
|
||||
name: "delete_to_hard_bol",
|
||||
exec: function(editor) {
|
||||
var pos = editor.selection.getCursor();
|
||||
editor.session.remove({
|
||||
start: { row: pos.row, column: 0 },
|
||||
end: pos
|
||||
});
|
||||
},
|
||||
multiSelectAction: "forEach",
|
||||
scrollIntoView: "cursor",
|
||||
}, {
|
||||
name: "delete_to_hard_eol",
|
||||
exec: function(editor) {
|
||||
var pos = editor.selection.getCursor();
|
||||
editor.session.remove({
|
||||
start: pos,
|
||||
end: { row: pos.row, column: Infinity }
|
||||
});
|
||||
},
|
||||
multiSelectAction: "forEach",
|
||||
scrollIntoView: "cursor",
|
||||
}, {
|
||||
name: "moveToWordStartLeft",
|
||||
exec: function(editor) {
|
||||
editor.selection.moveCursorLongWordLeft();
|
||||
editor.clearSelection();
|
||||
},
|
||||
multiSelectAction: "forEach",
|
||||
scrollIntoView: "cursor",
|
||||
}, {
|
||||
name: "moveToWordEndRight",
|
||||
exec: function(editor) {
|
||||
editor.selection.moveCursorLongWordRight();
|
||||
editor.clearSelection();
|
||||
},
|
||||
multiSelectAction: "forEach",
|
||||
scrollIntoView: "cursor",
|
||||
}, {
|
||||
name: "selectToWordStartLeft",
|
||||
exec: function(editor) {
|
||||
var sel = editor.selection;
|
||||
sel.$moveSelection(sel.moveCursorLongWordLeft);
|
||||
},
|
||||
multiSelectAction: "forEach",
|
||||
scrollIntoView: "cursor",
|
||||
}, {
|
||||
name: "selectToWordEndRight",
|
||||
exec: function(editor) {
|
||||
var sel = editor.selection;
|
||||
sel.$moveSelection(sel.moveCursorLongWordRight);
|
||||
},
|
||||
multiSelectAction: "forEach",
|
||||
scrollIntoView: "cursor",
|
||||
},
|
||||
];
|
||||
|
||||
/* editor commands */
|
||||
exports.editorKeymap = [{
|
||||
bindKey: { linux: "alt-/|ctrl-space", mac: "ctrl-space", win: "ctrl-space" },
|
||||
name: "complete"
|
||||
},
|
||||
// {
|
||||
// bindKey: {mac: "cmd-c", win: "ctrl-insert|ctrl-c"},
|
||||
// name: "copy"
|
||||
// }, {
|
||||
// bindKey: {mac: "cmd-x", win: "shift-delete|ctrl-x"},
|
||||
// name: "cut"
|
||||
// },
|
||||
|
||||
|
||||
// {
|
||||
// bindKey: {mac: "cmd-k cmd-w", win: "ctrl-k ctrl-w"},
|
||||
// name: "delete_to_mark"
|
||||
// }, {
|
||||
// bindKey: {mac: "cmd-k cmd-a", win: "ctrl-k ctrl-a"},
|
||||
// name: "select_to_mark"
|
||||
// }, {
|
||||
// bindKey: {mac: "cmd-k cmd-space", win: "ctrl-k ctrl-space"},
|
||||
// name: "set_mark"
|
||||
// }, {
|
||||
// bindKey: {mac: "cmd-k cmd-x", win: "ctrl-k ctrl-x"},
|
||||
// name: "swap_with_mark"
|
||||
// }, {
|
||||
// bindKey: {mac: "cmd-k cmd-y|ctrl-y", win: "ctrl-k ctrl-y"},
|
||||
// name: "yank"
|
||||
// },
|
||||
|
||||
// // TODO check if these are same as ace
|
||||
// {
|
||||
// bindKey: {win: "ctrl-delete"},
|
||||
// name: "delete_word",
|
||||
// args: {forward: true}
|
||||
// }, {
|
||||
// bindKey: {mac: "ctrl-backspace"},
|
||||
// name: "delete_word",
|
||||
// args: {forward: false, sub_words: true}
|
||||
// }, {
|
||||
// bindKey: {mac: "ctrl-delete"},
|
||||
// name: "delete_word",
|
||||
// args: {forward: true, sub_words: true}
|
||||
// }, {
|
||||
// bindKey: {win: "ctrl-backspace"},
|
||||
// name: "delete_word",
|
||||
// args: {forward: false}
|
||||
// }, {
|
||||
// bindKey: {win: "backspace|shift-backspace|ctrl-shift-backspace"},
|
||||
// name: "left_delete"
|
||||
// }, {
|
||||
// bindKey: {win: "delete"},
|
||||
// name: "right_delete"
|
||||
// },
|
||||
{
|
||||
bindKey: { mac: "cmd-k cmd-backspace|cmd-backspace", win: "ctrl-shift-backspace|ctrl-k ctrl-backspace" },
|
||||
name: "delete_to_hard_bol"
|
||||
}, {
|
||||
bindKey: { mac: "cmd-k cmd-k|cmd-delete|ctrl-k", win: "ctrl-shift-delete|ctrl-k ctrl-k" },
|
||||
name: "delete_to_hard_eol"
|
||||
},
|
||||
|
||||
{
|
||||
bindKey: { mac: "cmd-shift-d", win: "ctrl-shift-d" },
|
||||
name: "duplicateSelection"
|
||||
}, {
|
||||
bindKey: { mac: "cmd-l", win: "ctrl-l" },
|
||||
name: "expandtoline",
|
||||
},
|
||||
// {
|
||||
// bindKey: {mac: "cmd-shift-a", win: "ctrl-shift-a"},
|
||||
// name: "expand_selection",
|
||||
// args: {to: "tag"}
|
||||
// }, {
|
||||
// bindKey: {mac: "cmd-shift-j", win: "ctrl-shift-j"},
|
||||
// name: "expand_selection",
|
||||
// args: {to: "indentation"}
|
||||
// }, {
|
||||
// bindKey: {mac: "ctrl-shift-m", win: "ctrl-shift-m"},
|
||||
// name: "expand_selection",
|
||||
// args: {to: "brackets"}
|
||||
// }, {
|
||||
// bindKey: {mac: "cmd-shift-space", win: "ctrl-shift-space"},
|
||||
// name: "expand_selection",
|
||||
// args: {to: "scope"}
|
||||
// },
|
||||
{
|
||||
bindKey: { mac: "ctrl-cmd-g", win: "alt-f3" },
|
||||
name: "find_all_under"
|
||||
}, {
|
||||
bindKey: { mac: "alt-cmd-g", win: "ctrl-f3" },
|
||||
name: "find_under"
|
||||
}, {
|
||||
bindKey: { mac: "shift-alt-cmd-g", win: "ctrl-shift-f3" },
|
||||
name: "find_under_prev"
|
||||
}, {
|
||||
bindKey: { mac: "cmd-g", win: "f3" },
|
||||
name: "findnext"
|
||||
}, {
|
||||
bindKey: { mac: "shift-cmd-g", win: "shift-f3" },
|
||||
name: "findprevious"
|
||||
}, {
|
||||
bindKey: { mac: "cmd-d", win: "ctrl-d" },
|
||||
name: "find_under_expand"
|
||||
}, {
|
||||
bindKey: { mac: "cmd-k cmd-d", win: "ctrl-k ctrl-d" },
|
||||
name: "find_under_expand_skip"
|
||||
},
|
||||
|
||||
/* fold */
|
||||
{
|
||||
bindKey: { mac: "cmd-alt-[", win: "ctrl-shift-[" },
|
||||
name: "toggleFoldWidget"
|
||||
}, {
|
||||
bindKey: { mac: "cmd-alt-]", win: "ctrl-shift-]" },
|
||||
name: "unfold"
|
||||
}, {
|
||||
bindKey: { mac: "cmd-k cmd-0|cmd-k cmd-j", win: "ctrl-k ctrl-0|ctrl-k ctrl-j" },
|
||||
name: "unfoldall"
|
||||
}, {
|
||||
bindKey: { mac: "cmd-k cmd-1", win: "ctrl-k ctrl-1" },
|
||||
name: "foldOther",
|
||||
args: { level: 1 }
|
||||
},
|
||||
// {
|
||||
// bindKey: {mac: "cmd-k cmd-2", win: "ctrl-k ctrl-2"},
|
||||
// name: "fold_by_level",
|
||||
// args: {level: 2}
|
||||
// }, {
|
||||
// bindKey: {mac: "cmd-k cmd-3", win: "ctrl-k ctrl-3"},
|
||||
// name: "fold_by_level",
|
||||
// args: {level: 3}
|
||||
// }, {
|
||||
// bindKey: {mac: "cmd-k cmd-4", win: "ctrl-k ctrl-4"},
|
||||
// name: "fold_by_level",
|
||||
// args: {level: 4}
|
||||
// }, {
|
||||
// bindKey: {mac: "cmd-k cmd-5", win: "ctrl-k ctrl-5"},
|
||||
// name: "fold_by_level",
|
||||
// args: {level: 5}
|
||||
// }, {
|
||||
// bindKey: {mac: "cmd-k cmd-6", win: "ctrl-k ctrl-6"},
|
||||
// name: "fold_by_level",
|
||||
// args: {level: 6}
|
||||
// }, {
|
||||
// bindKey: {mac: "cmd-k cmd-7", win: "ctrl-k ctrl-7"},
|
||||
// name: "fold_by_level",
|
||||
// args: {level: 7}
|
||||
// }, {
|
||||
// bindKey: {mac: "cmd-k cmd-8", win: "ctrl-k ctrl-8"},
|
||||
// name: "fold_by_level",
|
||||
// args: {level: 8}
|
||||
// }, {
|
||||
// bindKey: {mac: "cmd-k cmd-9", win: "ctrl-k ctrl-9"},
|
||||
// name: "fold_by_level",
|
||||
// args: {level: 9}
|
||||
// }, {
|
||||
// bindKey: {mac: "cmd-k cmd-t", win: "ctrl-k ctrl-t"},
|
||||
// name: "fold_tag_attributes"
|
||||
// },
|
||||
|
||||
/* move */
|
||||
{
|
||||
bindKey: { win: "ctrl-left", mac: "alt-left" },
|
||||
name: "moveToWordStartLeft"
|
||||
}, {
|
||||
bindKey: { win: "ctrl-right", mac: "alt-right" },
|
||||
name: "moveToWordEndRight"
|
||||
}, {
|
||||
bindKey: { win: "ctrl-shift-left", mac: "alt-shift-left" },
|
||||
name: "selectToWordStartLeft",
|
||||
}, {
|
||||
bindKey: { win: "ctrl-shift-right", mac: "alt-shift-right" },
|
||||
name: "selectToWordEndRight",
|
||||
},
|
||||
|
||||
// todo implement move by subwords
|
||||
// {
|
||||
// bindKey: {mac: "ctrl-alt-shift-right|ctrl-shift-right", win: "alt-shift-right"},
|
||||
// name: "move",
|
||||
// args: {by: "subword_ends", forward: true, extend: true}
|
||||
// }, {
|
||||
// bindKey: {mac: "ctrl-alt-shift-left|ctrl-shift-left", win: "alt-shift-left"},
|
||||
// name: "move",
|
||||
// args: {by: "subwords", forward: false, extend: true}
|
||||
// }, {
|
||||
// bindKey: {mac: "ctrl-alt-right|ctrl-right", win: "alt-right"},
|
||||
// name: "move",
|
||||
// args: {by: "subword_ends", forward: true}
|
||||
// }, {
|
||||
// bindKey: {mac: "ctrl-alt-left|ctrl-left", win: "alt-left"},
|
||||
// name: "move",
|
||||
// args: {by: "subwords", forward: false}
|
||||
// },
|
||||
{
|
||||
bindKey: { mac: "ctrl-m", win: "ctrl-m" },
|
||||
name: "jumptomatching",
|
||||
args: { to: "brackets" }
|
||||
},
|
||||
/* other */
|
||||
{
|
||||
bindKey: { mac: "ctrl-f6", win: "ctrl-f6" },
|
||||
name: "goToNextError"
|
||||
}, {
|
||||
bindKey: { mac: "ctrl-shift-f6", win: "ctrl-shift-f6" },
|
||||
name: "goToPreviousError"
|
||||
},
|
||||
|
||||
{
|
||||
bindKey: { mac: "ctrl-o" },
|
||||
name: "splitline",
|
||||
},
|
||||
// {
|
||||
// bindKey: {mac: "ctrl-shift-w", win: "alt-shift-w"},
|
||||
// name: "surrowndWithTag",
|
||||
// args: {name: "Packages/XML/long-tag.sublime-snippet"}
|
||||
// },{
|
||||
// bindKey: {mac: "cmd-alt-.", win: "alt-."},
|
||||
// name: "close_tag"
|
||||
// },
|
||||
{
|
||||
bindKey: { mac: "cmd-j", win: "ctrl-j" },
|
||||
name: "joinlines"
|
||||
},
|
||||
|
||||
// {
|
||||
// bindKey: {mac: "ctrl--", win: "alt--"},
|
||||
// name: "jump_back"
|
||||
// }, {
|
||||
// bindKey: {mac: "ctrl-shift--", win: "alt-shift--"},
|
||||
// name: "jump_forward"
|
||||
// },
|
||||
|
||||
{
|
||||
bindKey: { mac: "cmd-k cmd-l", win: "ctrl-k ctrl-l" },
|
||||
name: "tolowercase"
|
||||
}, {
|
||||
bindKey: { mac: "cmd-k cmd-u", win: "ctrl-k ctrl-u" },
|
||||
name: "touppercase"
|
||||
},
|
||||
|
||||
// {
|
||||
// bindKey: {mac: "cmd-v", win: "shift-insert|ctrl-v"},
|
||||
// name: "paste"
|
||||
// }, {
|
||||
// bindKey: {mac: "cmd-shift-v", win: "ctrl-shift-v"},
|
||||
// name: "paste_and_indent"
|
||||
// }, {
|
||||
// bindKey: {mac: "cmd-k cmd-v|cmd-alt-v", win: "ctrl-k ctrl-v"},
|
||||
// name: "paste_from_history"
|
||||
// },
|
||||
|
||||
|
||||
// {
|
||||
// bindKey: {mac: "cmd-z", win: "ctrl-z"},
|
||||
// name: "undo"
|
||||
// }, {
|
||||
// bindKey: {mac: "cmd-shift-z", win: "ctrl-shift-z"},
|
||||
// name: "redo"
|
||||
// }, {
|
||||
// bindKey: {mac: "cmd-y", win: "ctrl-y"},
|
||||
// name: "redo_or_repeat"
|
||||
// }, {
|
||||
// bindKey: {mac: "cmd-shift-u", win: "ctrl-shift-u"},
|
||||
// name: "soft_redo"
|
||||
// }, {
|
||||
// bindKey: {mac: "cmd-u", win: "ctrl-u"},
|
||||
// name: "soft_undo"
|
||||
// },
|
||||
|
||||
{
|
||||
bindKey: { mac: "cmd-shift-enter", win: "ctrl-shift-enter" },
|
||||
name: "addLineBefore"
|
||||
}, {
|
||||
bindKey: { mac: "cmd-enter", win: "ctrl-enter" },
|
||||
name: "addLineAfter"
|
||||
}, {
|
||||
bindKey: { mac: "ctrl-shift-k", win: "ctrl-shift-k" },
|
||||
name: "removeline"
|
||||
}, {
|
||||
bindKey: { mac: "ctrl-alt-up", win: "ctrl-up" },
|
||||
name: "scrollup",
|
||||
}, {
|
||||
bindKey: { mac: "ctrl-alt-down", win: "ctrl-down" },
|
||||
name: "scrolldown",
|
||||
}, {
|
||||
bindKey: { mac: "cmd-a", win: "ctrl-a" },
|
||||
name: "selectall"
|
||||
}, {
|
||||
bindKey: { linux: "alt-shift-down", mac: "ctrl-shift-down", win: "ctrl-alt-down" },
|
||||
name: "addCursorBelow",
|
||||
}, {
|
||||
bindKey: { linux: "alt-shift-up", mac: "ctrl-shift-up", win: "ctrl-alt-up" },
|
||||
name: "addCursorAbove",
|
||||
},
|
||||
|
||||
|
||||
{
|
||||
bindKey: { mac: "cmd-k cmd-c|ctrl-l", win: "ctrl-k ctrl-c" },
|
||||
name: "centerselection"
|
||||
},
|
||||
|
||||
{
|
||||
bindKey: { mac: "f5", win: "f9" },
|
||||
name: "sortlines"
|
||||
},
|
||||
// {
|
||||
// bindKey: {mac: "ctrl-f5", win: "ctrl-f9"},
|
||||
// name: "sortlines",
|
||||
// args: {case_sensitive: true}
|
||||
// },
|
||||
{
|
||||
bindKey: { mac: "cmd-shift-l", win: "ctrl-shift-l" },
|
||||
name: "splitIntoLines"
|
||||
}, {
|
||||
bindKey: { mac: "ctrl-cmd-down", win: "ctrl-shift-down" },
|
||||
name: "movelinesdown"
|
||||
}, {
|
||||
bindKey: { mac: "ctrl-cmd-up", win: "ctrl-shift-up" },
|
||||
name: "movelinesup"
|
||||
}, {
|
||||
bindKey: { mac: "alt-down", win: "alt-down" },
|
||||
name: "modifyNumberDown"
|
||||
}, {
|
||||
bindKey: { mac: "alt-up", win: "alt-up" },
|
||||
name: "modifyNumberUp"
|
||||
}, {
|
||||
bindKey: { mac: "cmd-/", win: "ctrl-/" },
|
||||
name: "togglecomment"
|
||||
}, {
|
||||
bindKey: { mac: "cmd-alt-/", win: "ctrl-shift-/" },
|
||||
name: "toggleBlockComment"
|
||||
},
|
||||
|
||||
|
||||
{
|
||||
bindKey: { linux: "ctrl-alt-q", mac: "ctrl-q", win: "ctrl-q" },
|
||||
// name: "toggle_record_macro"
|
||||
name: "togglerecording"
|
||||
}, {
|
||||
bindKey: { linux: "ctrl-alt-shift-q", mac: "ctrl-shift-q", win: "ctrl-shift-q" },
|
||||
// name: "run_macro"
|
||||
name: "replaymacro"
|
||||
},
|
||||
|
||||
|
||||
{
|
||||
bindKey: { mac: "ctrl-t", win: "ctrl-t" },
|
||||
name: "transpose"
|
||||
}
|
||||
|
||||
];
|
||||
|
||||
});
|
||||
|
||||
|
||||
// won't implement
|
||||
// {
|
||||
// bindKey: {mac: "cmd-alt-q", win: "alt-q"},
|
||||
// name: "wrap_lines"
|
||||
// },
|
||||
|
||||
// {
|
||||
// bindKey: {mac: "f6", win: "f6"},
|
||||
// name: "toggle_setting",
|
||||
// args: {setting: "spell_check"}
|
||||
// }, {
|
||||
// bindKey: {mac: "cmd-alt-p|ctrl-shift-p", win: "ctrl-alt-shift-p"},
|
||||
// name: "show_scope_name"
|
||||
// },
|
||||
|
||||
// {
|
||||
// bindKey: {mac: "cmd-alt-o", win: "insert"},
|
||||
// name: "toggle_overwrite"
|
||||
// },
|
||||
// {
|
||||
// bindKey: {mac: "alt-f2", win: "context_menu"},
|
||||
// name: "context_menu"
|
||||
// },
|
|
@ -0,0 +1,72 @@
|
|||
define(function(require, exports, module) {
|
||||
|
||||
exports.showCli = true;
|
||||
var Vim = require("ace/keyboard/vim").Vim;
|
||||
var HashHandler = require("ace/keyboard/hash_handler").HashHandler;
|
||||
exports.aceKeyboardHandler = require("ace/keyboard/vim").handler;
|
||||
|
||||
exports.aceKeyboardHandler.defaultKeymap.unshift(
|
||||
{ keys: ':', type: 'action', action: 'aceCommand', actionArgs: { exec: function(ace) {
|
||||
ace.showCommandLine(":");
|
||||
} }}
|
||||
);
|
||||
|
||||
exports.aceKeyboardHandler.defaultKeymap.push(
|
||||
{ keys: 'gt', type: 'action', action: 'aceCommand', actionArgs: { exec: ideCommand, name: 'gototabright', args: { editorType: "ace" }}},
|
||||
{ keys: 'gT', type: 'action', action: 'aceCommand', actionArgs: { exec: ideCommand, name: 'gototableft', args: { editorType: "ace" }}}
|
||||
);
|
||||
|
||||
exports.execIdeCommand = null;
|
||||
function ideCommand() {
|
||||
exports.execIdeCommand(this.name, null, this.args);
|
||||
}
|
||||
/**
|
||||
* require(["plugins/c9.ide.ace.keymaps/vim/keymap"], function(vim) {
|
||||
* vim.map("J", "8j", "normal")
|
||||
* vim.map("K", "8k", "normal")
|
||||
* vim.map(",b", "c9:build", "normal")
|
||||
* vim.map(",g", "c9:run", "normal")
|
||||
* });
|
||||
*/
|
||||
exports.map = function(keys, action, context) {
|
||||
if (!action)
|
||||
return Vim.unmap(keys, context);
|
||||
var mapping;
|
||||
if (typeof action == "function") {
|
||||
mapping = {
|
||||
keys: keys,
|
||||
type: 'action',
|
||||
action: 'aceCommand',
|
||||
actionArgs: { exec: ideCommand, name: 'gototableft' }
|
||||
};
|
||||
}
|
||||
if (/^c9:/.test(action)) {
|
||||
var commandName = action.substr(3);
|
||||
|
||||
mapping = {
|
||||
keys: keys,
|
||||
type: 'action',
|
||||
action: 'aceCommand',
|
||||
actionArgs: { exec: ideCommand, name: commandName }
|
||||
};
|
||||
}
|
||||
if (mapping) {
|
||||
if (context)
|
||||
mapping.context = context;
|
||||
mapping.user = true;
|
||||
exports.aceKeyboardHandler.defaultKeymap.unshift(mapping);
|
||||
} else {
|
||||
Vim.map(keys, action, context);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
exports.treeKeyboardHandler = new HashHandler();
|
||||
exports.treeKeyboardHandler.bindKeys({
|
||||
"k": "goUp",
|
||||
"j": "goDown",
|
||||
"h": "levelUp",
|
||||
"l": "levelDown"
|
||||
});
|
||||
|
||||
});
|
|
@ -0,0 +1,21 @@
|
|||
|
||||
Copyright (c) 2015, Ajax.org B.V.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
|
@ -0,0 +1 @@
|
|||
# c9.ide.ace.repl
|
|
@ -0,0 +1,92 @@
|
|||
define(function(require, exports, module) {
|
||||
main.consumes = ["editors"];
|
||||
main.provides = ["ace.repl"];
|
||||
return main;
|
||||
|
||||
function main(options, imports, register) {
|
||||
var editors = imports.editors;
|
||||
|
||||
var Repl = require("./repl").Repl;
|
||||
|
||||
/***** Initialization *****/
|
||||
|
||||
var extensions = [];
|
||||
|
||||
function ReplEditor() {
|
||||
var Baseclass = editors.findEditor("ace");
|
||||
var plugin = new Baseclass(true, []);
|
||||
// var emit = plugin.getEmitter();
|
||||
|
||||
var currentSession;
|
||||
var ace;
|
||||
|
||||
plugin.on("draw", function(e) {
|
||||
ace = plugin.ace;
|
||||
|
||||
if (currentSession)
|
||||
currentSession.repl.attach(ace);
|
||||
});
|
||||
|
||||
/***** Method *****/
|
||||
|
||||
/***** Lifecycle *****/
|
||||
|
||||
plugin.on("load", function() {
|
||||
});
|
||||
|
||||
plugin.on("documentLoad", function(e) {
|
||||
var session = e.doc.getSession();
|
||||
|
||||
if (session.repl) return;
|
||||
|
||||
session.repl = new Repl(session.session, {
|
||||
mode: e.state.mode,
|
||||
evaluator: e.state.evaluator,
|
||||
message: e.state.message
|
||||
});
|
||||
});
|
||||
plugin.on("documentActivate", function(e) {
|
||||
currentSession = e.doc.getSession();
|
||||
if (ace)
|
||||
currentSession.repl.attach(ace);
|
||||
});
|
||||
plugin.on("documentUnload", function(e) {
|
||||
var session = e.doc.getSession();
|
||||
session.repl.detach();
|
||||
delete session.repl;
|
||||
});
|
||||
plugin.on("getState", function(e) {
|
||||
});
|
||||
plugin.on("setState", function(e) {
|
||||
});
|
||||
plugin.on("clear", function() {
|
||||
});
|
||||
plugin.on("focus", function() {
|
||||
});
|
||||
plugin.on("enable", function() {
|
||||
});
|
||||
plugin.on("disable", function() {
|
||||
});
|
||||
plugin.on("unload", function() {
|
||||
});
|
||||
|
||||
/***** Register and define API *****/
|
||||
|
||||
/**
|
||||
* Read Only Image Editor
|
||||
**/
|
||||
plugin.freezePublicAPI({
|
||||
|
||||
});
|
||||
|
||||
plugin.load(null, "ace.repl");
|
||||
|
||||
return plugin;
|
||||
}
|
||||
|
||||
register(null, {
|
||||
"ace.repl": editors.register("ace.repl", "Ace REPL",
|
||||
ReplEditor, extensions)
|
||||
});
|
||||
}
|
||||
});
|
|
@ -0,0 +1,96 @@
|
|||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Distributed under the BSD license:
|
||||
*
|
||||
* Copyright (c) 2010, Ajax.org B.V.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Ajax.org B.V. nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
|
||||
define(function(require, exports, module) {
|
||||
"use strict";
|
||||
|
||||
var Renderer = require("ace/virtual_renderer").VirtualRenderer;
|
||||
var Editor = require("ace/editor").Editor;
|
||||
|
||||
var Evaluator = function() {
|
||||
};
|
||||
|
||||
(function() {
|
||||
this.canEvaluate = function(str) {
|
||||
return !!str.trim();
|
||||
};
|
||||
|
||||
this.evaluate = function(str, cell, cb) {
|
||||
if (/table|ace/.test(str)) {
|
||||
cb("");
|
||||
var editor = new Editor(new Renderer);
|
||||
editor.setValue("command " + str);
|
||||
editor.container.addEventListener("mousedown", function(e) {
|
||||
e.stopPropagation();
|
||||
});
|
||||
cell.addWidget({ rowCount: 8, el: editor.container, editor: editor });
|
||||
setTimeout(function() {
|
||||
editor.resize(true);
|
||||
}, 80);
|
||||
} else if (/repl/.test(str)) {
|
||||
cb("");
|
||||
var editor = new Editor(new Renderer);
|
||||
cell.session.repl.constructor.fromEditor(editor, {
|
||||
mode: "repl_demo/logiql_command_mode",
|
||||
evaluator: this,
|
||||
message: "welcome to inner Repl!"
|
||||
});
|
||||
|
||||
editor.container.addEventListener("mousedown", function(e) {
|
||||
e.stopPropagation();
|
||||
});
|
||||
cell.addWidget({ rowCount: 12, el: editor.container, editor: editor });
|
||||
setTimeout(function() {
|
||||
editor.resize(true);
|
||||
}, 80);
|
||||
} else if (/logo/.test(str)) {
|
||||
setTimeout(function() {
|
||||
cb("");
|
||||
cell.addWidget({ rowCount: 6, html: "<img src='http://martin.bravenboer.name/logo-trans-85.png'>" });
|
||||
}, 300);
|
||||
} else if (/lorem/.test(str)) {
|
||||
var a = [];
|
||||
for (var i = 0; i < 100; i++) {
|
||||
a.push(i);
|
||||
}
|
||||
cb(a.join("\n"));
|
||||
} else if (/\n|slow/.test(str)) {
|
||||
setTimeout(function() {
|
||||
cb("evaluated slow command:" + str);
|
||||
}, 1000);
|
||||
} else {
|
||||
cb("evaluated command: " + str);
|
||||
}
|
||||
};
|
||||
}).call(Evaluator.prototype);
|
||||
exports.Evaluator = Evaluator;
|
||||
|
||||
});
|
|
@ -0,0 +1,283 @@
|
|||
define(function(require, exports, module) {
|
||||
|
||||
var oop = require("ace/lib/oop");
|
||||
var TextMode = require("ace/mode/text").Mode;
|
||||
var Range = require("ace/range").Range;
|
||||
var dom = require("ace/lib/dom");
|
||||
var FoldMode = require("ace/mode/folding/cstyle").FoldMode;
|
||||
|
||||
var foldMode = new FoldMode();
|
||||
foldMode.foldingStartMarker = /(^#>>)|(\{|\[)[^\}\]]*$|^\s*(\/\*)/;
|
||||
foldMode.getFoldWidgetRange = function(session, foldStyle, row) {
|
||||
var line = session.getLine(row);
|
||||
var match = line.match(this.foldingStartMarker);
|
||||
if (match) {
|
||||
var i = match.index;
|
||||
|
||||
if (match[1]) {
|
||||
var cell = session.$mode.getCellBounds(row);
|
||||
var start = { row: row, column: session.$mode.dl };
|
||||
var end = { row: cell.bodyEnd, column: session.getLine(cell.bodyEnd).length };
|
||||
var placeholder = session.getLine(cell.headerStart).slice(0, 10) + "=====================";
|
||||
range = Range.fromPoints(start, end);
|
||||
range.placeholder = placeholder;
|
||||
return range;
|
||||
}
|
||||
|
||||
if (match[3]) {
|
||||
var range = session.getCommentFoldRange(row, i + match[0].length);
|
||||
range.end.column -= 2;
|
||||
return range;
|
||||
}
|
||||
|
||||
var start = { row: row, column: i + 1 };
|
||||
var end = session.$findClosingBracket(match[2], start);
|
||||
if (!end)
|
||||
return;
|
||||
|
||||
var fw = session.foldWidgets[end.row];
|
||||
if (fw == null)
|
||||
fw = this.getFoldWidget(session, end.row);
|
||||
|
||||
if (fw == "start") {
|
||||
end.row --;
|
||||
end.column = session.getLine(end.row).length;
|
||||
}
|
||||
return Range.fromPoints(start, end);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
dom.importCssString("\
|
||||
\
|
||||
.ace_cellHead{\
|
||||
background:rgba(120,50,100,0.1);\
|
||||
color: darkred;\
|
||||
}\
|
||||
.ace_filler{\
|
||||
position: absolute;\
|
||||
left: 0;\
|
||||
display: inline-block;\
|
||||
border-top: 1px solid gray;\
|
||||
width: 100%;\
|
||||
}");
|
||||
|
||||
var commands = [{
|
||||
name: "newCell",
|
||||
bindKey: { win: "Shift-Return", mac: "Shift-Return" },
|
||||
exec: function(editor) {
|
||||
var session = editor.session;
|
||||
var c = editor.getCursorPosition();
|
||||
var end = session.insert(c, c.column == 0 ? "#>>" : "\n#>>");
|
||||
|
||||
var addNewLine = (c.column != 0 || c.row == 0)
|
||||
&& c.column != session.getLine(c.row).length;
|
||||
|
||||
if (addNewLine) {
|
||||
session.insert(end, "\n");
|
||||
editor.selection.setSelectionRange({ start: end, end: end });
|
||||
}
|
||||
}
|
||||
}];
|
||||
|
||||
var JSMode = require("ace/mode/javascript").Mode;
|
||||
|
||||
var modes = {
|
||||
js: new JSMode()
|
||||
};
|
||||
var jsMode = modes.js;
|
||||
|
||||
|
||||
|
||||
var tk = {};
|
||||
var delimiter = '#>>';
|
||||
var dl = delimiter.length;
|
||||
function testLang(lang, fullName) {
|
||||
lang = lang.toLowerCase();
|
||||
fullName = fullName.toLowerCase();
|
||||
var lastI = 0;
|
||||
for (var j = 0, ftLen = lang.length; j < ftLen; j++) {
|
||||
lastI = fullName.indexOf(lang[j], lastI);
|
||||
if (lastI === -1)
|
||||
return false; // doesn't match
|
||||
lastI++;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
var states = {};
|
||||
var $getState = function(state, lang, isHeader) {
|
||||
var g = lang + state + isHeader;
|
||||
return states[g] || (states[g] = { state: state, lang: lang, isHeader: isHeader });
|
||||
};
|
||||
tk.getLineTokens = function(line, startState) {
|
||||
var match, lang, isHeader = 0;
|
||||
if (typeof startState == 'object') {
|
||||
lang = startState.lang;
|
||||
isHeader = startState.isHeader || 0;
|
||||
startState = startState.state || "start";
|
||||
} else {
|
||||
lang = 'js';
|
||||
}
|
||||
|
||||
if (line.substr(0, dl) == delimiter) {
|
||||
var index = dl;
|
||||
var type = !isHeader ? 'firstcell.' : '';
|
||||
var tok = [{ type: type + 'cellHead', value: delimiter }];
|
||||
if ((match = line.match(/lang\s*=\s*(\w+)\b/))) {
|
||||
lang = testLang(match[1], 'coffeeScript') ? 'coffee' : 'js';
|
||||
|
||||
if (dl < match.index) {
|
||||
tok.push({ type: type, value: line.substring(dl, match.index) });
|
||||
}
|
||||
tok.push({ type: type + 'comment.doc', value: match[0] });
|
||||
index = match.index + match[0].length;
|
||||
}
|
||||
tok.push({ type: type, value: line.substr(index) });
|
||||
|
||||
if (!isHeader) {
|
||||
tok.push({ type: 'filler', value: ' ' });
|
||||
}
|
||||
ans = {
|
||||
tokens: tok,
|
||||
state: $getState("start", lang, isHeader + 1)
|
||||
};
|
||||
}
|
||||
else {
|
||||
var ans = (modes[lang] || jsMode).getTokenizer().getLineTokens(line, startState);
|
||||
ans.state = $getState(ans.state, lang);
|
||||
}
|
||||
return ans;
|
||||
};
|
||||
|
||||
var Mode = function() {
|
||||
this.$tokenizer = tk;
|
||||
this.foldingRules = foldMode;
|
||||
};
|
||||
oop.inherits(Mode, TextMode);
|
||||
|
||||
(function() {
|
||||
this.delimiter = delimiter;
|
||||
this.dl = dl;
|
||||
this.getCellBounds = function(row, editor) {
|
||||
var lines = editor.session.doc.$lines;
|
||||
var cur = row;
|
||||
|
||||
// go to header end if row is inside header
|
||||
var line = lines[row];
|
||||
while (line && line.substr(0, dl) == delimiter) {
|
||||
line = lines[++row];
|
||||
}
|
||||
if (!line)
|
||||
line = lines[--row];
|
||||
|
||||
// read up to header
|
||||
var i = row;
|
||||
while (line != null) {
|
||||
if (line.substr(0, dl) == delimiter)
|
||||
break;
|
||||
line = lines[--i];
|
||||
}
|
||||
var minI = i + 1;
|
||||
|
||||
// read header
|
||||
while (line != null) {
|
||||
if (line.substr(0, dl) != delimiter)
|
||||
break;
|
||||
line = lines[--i];
|
||||
}
|
||||
var headerI = i + 1;
|
||||
// read rest of the body
|
||||
i = row + 1;
|
||||
line = lines[i];
|
||||
while (line != null) {
|
||||
if (line.substr(0, dl) == delimiter)
|
||||
break;
|
||||
line = lines[++i];
|
||||
}
|
||||
var maxI = i - 1;
|
||||
|
||||
return {
|
||||
headerStart: headerI,
|
||||
headerEnd: minI - 1,
|
||||
bodyStart: minI,
|
||||
bodyEnd: maxI,
|
||||
cursor: cur
|
||||
};
|
||||
};
|
||||
|
||||
this.getHeaderText = function(cell) {
|
||||
if (cell) {
|
||||
cell.headerText = cell.header.join('\n')
|
||||
.replace(/lang\s*=\s*(\w+)\b/g, '')
|
||||
.replace(delimiter, '', 'g')
|
||||
.trim();
|
||||
return cell;
|
||||
}
|
||||
};
|
||||
|
||||
this.getCurrentCell = function(editor) {
|
||||
var cursor = editor.getCursorPosition();
|
||||
var session = editor.session;
|
||||
|
||||
var cell = this.getCellBounds(cursor.row, editor);
|
||||
cell.header = session.getLines(cell.headerStart, cell.headerEnd);
|
||||
cell.body = session.getLines(cell.bodyStart, cell.bodyEnd);
|
||||
cell.lang = session.getState(cell.headerStart).lang;
|
||||
|
||||
this.getHeaderText(cell);
|
||||
|
||||
return cell;
|
||||
};
|
||||
|
||||
this.setCellText = function(text, editor) {
|
||||
var cursor = editor.getCursorPosition();
|
||||
var session = editor.session;
|
||||
|
||||
var cell = this.getCellBounds(cursor.row, editor);
|
||||
var end = session.getLine(cell.bodyEnd).length;
|
||||
|
||||
if (cell.bodyStart > cell.bodyEnd) { // empty cell
|
||||
var range = new Range(cell.bodyEnd, end, cell.bodyEnd, end);
|
||||
text = '\n' + text;
|
||||
} else
|
||||
var range = new Range(cell.bodyStart, 0, cell.bodyEnd, end);
|
||||
|
||||
session.replace(range, text);
|
||||
return text;
|
||||
};
|
||||
|
||||
|
||||
this.toggleCommentLines = function(state, doc, startRow, endRow) {
|
||||
(modes[state.lang] || jsMode).toggleCommentLines(state.state, doc, startRow, endRow);
|
||||
};
|
||||
|
||||
this.getNextLineIndent = function(state, line, tab, lineEnd) {
|
||||
return (modes[state.lang] || jsMode).getNextLineIndent(state.state, line, tab, lineEnd);
|
||||
};
|
||||
|
||||
this.checkOutdent = function(state, line, input) {
|
||||
return (modes[state.lang] || jsMode).checkOutdent(state.state, line, input);
|
||||
};
|
||||
|
||||
this.autoOutdent = function(state, doc, row) {
|
||||
return (modes[state.lang] || jsMode).autoOutdent(state.state, doc, row);
|
||||
};
|
||||
|
||||
this.createWorker = function(session) {
|
||||
return null;
|
||||
};
|
||||
|
||||
this.transformAction = function(state, action, editor, session, param) {
|
||||
return (modes[state.lang] || jsMode).transformAction(state.state, action, editor, session, param);
|
||||
};
|
||||
|
||||
}).call(Mode.prototype);
|
||||
|
||||
|
||||
|
||||
|
||||
exports.Mode = Mode;
|
||||
exports.commands = commands;
|
||||
|
||||
|
||||
});
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"name": "c9.ide.ace.repl",
|
||||
"description": "The repository for c9.ide.ace.repl, a Cloud9 core plugin",
|
||||
"version": "3.0.0",
|
||||
"author": "Cloud9",
|
||||
"contributors": [],
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "http://github.com/c9/c9.ide.ace.repl.git"
|
||||
},
|
||||
"plugins": {
|
||||
"editor": {}
|
||||
},
|
||||
"categories": [
|
||||
"core"
|
||||
],
|
||||
"licenses": [
|
||||
"MIT"
|
||||
]
|
||||
}
|
|
@ -0,0 +1,920 @@
|
|||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Distributed under the BSD license:
|
||||
*
|
||||
* Copyright (c) 2010, Ajax.org B.V.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Ajax.org B.V. nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
|
||||
define(function(require, exports, module) {
|
||||
"use strict";
|
||||
|
||||
var dom = require("ace/lib/dom");
|
||||
var Anchor = require("ace/anchor").Anchor;
|
||||
|
||||
var HashHandler = require("ace/keyboard/hash_handler").HashHandler;
|
||||
var Range = require("ace/range").Range;
|
||||
var comparePoints = Range.comparePoints;
|
||||
|
||||
var ReplCell = require("./repl_cell").ReplCell;
|
||||
|
||||
var css = require("ace/requirejs/text!./repl.css");
|
||||
dom.importCssString(css, "ace_repl");
|
||||
|
||||
|
||||
var replCommands = new HashHandler([{
|
||||
name: "newLine",
|
||||
bindKey: { win: "Shift-Return|Alt-Enter", mac: "Shift-Return|Alt-Enter" },
|
||||
exec: function(editor) {editor.insert("\n");},
|
||||
scrollIntoView: "cursor"
|
||||
}, {
|
||||
name: "eval",
|
||||
bindKey: "Ctrl-Return|Cmd-Return",
|
||||
exec: function(editor) {return editor.repl.eval(true);},
|
||||
scrollIntoView: "cursor"
|
||||
}, {
|
||||
name: "evalOrNewLine",
|
||||
bindKey: "Return",
|
||||
exec: function(editor) {return editor.repl.eval();},
|
||||
scrollIntoView: "cursor"
|
||||
}, {
|
||||
name: "down",
|
||||
bindKey: "down",
|
||||
exec: function(editor) {return editor.repl.navigateHistory(1);},
|
||||
scrollIntoView: "center-animate"
|
||||
}, {
|
||||
name: "up",
|
||||
bindKey: "up",
|
||||
exec: function(editor) {return editor.repl.navigateHistory(-1);},
|
||||
scrollIntoView: "center-animate"
|
||||
}, {
|
||||
name: "prevCell",
|
||||
bindKey: { mac: "cmd-up", win: "ctrl-up" },
|
||||
exec: function(editor) {return editor.repl.moveByCells(-1, null, "input");},
|
||||
scrollIntoView: "center-animate"
|
||||
}, {
|
||||
name: "nextCell",
|
||||
bindKey: { mac: "cmd-down", win: "ctrl-down" },
|
||||
exec: function(editor) {return editor.repl.moveByCells(1, null, "input");},
|
||||
scrollIntoView: "center-animate"
|
||||
}, {
|
||||
name: "firstCell",
|
||||
bindKey: { mac: "alt-up|ctrl-home", win: "ctrl-home" },
|
||||
exec: function(editor) {return editor.repl.moveByCells("first", null, "input");},
|
||||
scrollIntoView: "center-animate"
|
||||
}, {
|
||||
name: "lastCell",
|
||||
bindKey: { mac: "alt-down|ctrl-end", win: "ctrl-end" },
|
||||
exec: function(editor) {return editor.repl.moveByCells("last", null, "input");},
|
||||
scrollIntoView: "center-animate"
|
||||
}, {
|
||||
name: "clear",
|
||||
bindKey: { mac: "cmd-k", win: "Alt-k" },
|
||||
exec: function(editor) {return editor.repl.clear();},
|
||||
scrollIntoView: "center-animate"
|
||||
}, {
|
||||
name: "removeOutputCell",
|
||||
bindKey: { mac: "Shift-delete", win: "Shift-delete" },
|
||||
exec: function(editor) {return editor.repl.removeOutputCell();},
|
||||
scrollIntoView: "cursor"
|
||||
}, {
|
||||
name: "newInputCell",
|
||||
bindKey: { mac: "ctrl-insert", win: "ctrl-insert" },
|
||||
exec: function(editor) { editor.repl.insertCell();},
|
||||
scrollIntoView: "center-animate"
|
||||
}]);
|
||||
|
||||
|
||||
for (var key in replCommands.commands) {
|
||||
replCommands.commands[key].isRepl = true;
|
||||
}
|
||||
|
||||
|
||||
/******************************************************/
|
||||
var Repl = function(session, options) {
|
||||
options = options || {};
|
||||
this.history = new History();
|
||||
this.evaluator = options.evaluator || new Evaluator();
|
||||
this.session = session;
|
||||
|
||||
this._replResize = this._replResize.bind(this);
|
||||
this.updateCellsOnChange = this.updateCellsOnChange.bind(this);
|
||||
this.updateWidgets = this.updateWidgets.bind(this);
|
||||
this.measureWidgets = this.measureWidgets.bind(this);
|
||||
this.session._changedWidgets = [];
|
||||
this.detach = this.detach.bind(this);
|
||||
|
||||
this.session.on("change", this.updateCellsOnChange);
|
||||
|
||||
session.repl = this;
|
||||
|
||||
session.replCells = [];
|
||||
var pos = { row: session.getLength(), column: 0 };
|
||||
if (!session.getValue() && options.message)
|
||||
pos = session.insert(pos, options.message);
|
||||
if (session.getValue())
|
||||
this.insertCell({ row: 0, column: 0 }, { type: "start" }, true);
|
||||
var last = this.insertCell(pos, { type: "input" });
|
||||
this.select(last.range.end);
|
||||
|
||||
|
||||
this.session.on("changeMode", function() {
|
||||
session.getFoldWidget = function(row) {
|
||||
if (!session.replCells[row])
|
||||
return;
|
||||
if (session.replCells[row + 1])
|
||||
return;
|
||||
if (row == session.replCells.length - 1)
|
||||
return;
|
||||
if (session.replCells[row].lineWidget)
|
||||
return;
|
||||
return "start";
|
||||
};
|
||||
session.getFoldWidgetRange = function(row) {
|
||||
return session.repl.getCellAt(row).range;
|
||||
};
|
||||
session.bgTokenizer.$tokenizeRow = session.repl.$tokenizeRow;
|
||||
session.bgTokenizer.session = session;
|
||||
});
|
||||
|
||||
session.getRowLength = function(row) {
|
||||
if (this.lineWidgets)
|
||||
var h = this.lineWidgets[row] && this.lineWidgets[row].rowCount || 0;
|
||||
else
|
||||
h = 0;
|
||||
if (!this.$useWrapMode || !this.$wrapData[row]) {
|
||||
return 1 + h;
|
||||
} else {
|
||||
return this.$wrapData[row].length + 1 + h;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
session.$getWidgetScreenLength = function() {
|
||||
var screenRows = 0;
|
||||
this.lineWidgets.forEach(function(w) {
|
||||
if (w && w.rowCount)
|
||||
screenRows += w.rowCount;
|
||||
});
|
||||
return screenRows;
|
||||
};
|
||||
|
||||
session.gutterRenderer = this.gutterRenderer;
|
||||
|
||||
this.session.setMode(options.mode);
|
||||
|
||||
this.$updateSession();
|
||||
this._addCursorMonitor();
|
||||
};
|
||||
|
||||
(function() {
|
||||
this.message = "\nWelcome to ace repl!\n";
|
||||
|
||||
this.setEvaluator = function(evaluator) {
|
||||
this.evaluator = evaluator;
|
||||
};
|
||||
this.getEvaluator = function() {
|
||||
return this.evaluator;
|
||||
};
|
||||
|
||||
this._replResize = function(oldSize, renderer) {
|
||||
var session = renderer.session;
|
||||
var size = renderer.$size;
|
||||
var dh = size.scrollerHeight - oldSize.scrollerHeight;
|
||||
if (dh > 0)
|
||||
return;
|
||||
|
||||
var oldMaxScrollTop = renderer.layerConfig.maxHeight
|
||||
- oldSize.scrollerHeight + renderer.scrollMargin.v;
|
||||
var scrollTop = session.getScrollTop();
|
||||
if (scrollTop > oldMaxScrollTop - renderer.layerConfig.lineHeight) {
|
||||
session.setScrollTop(scrollTop - dh);
|
||||
}
|
||||
};
|
||||
|
||||
this.gutterRenderer = {
|
||||
getText: function(session, row) {
|
||||
var cell = session.replCells[row];
|
||||
if (cell)
|
||||
return cell.prompt || "";
|
||||
return row + "";
|
||||
},
|
||||
getWidth: function(session, lastLineNumber, config) {
|
||||
var chars = Math.max(lastLineNumber.toString().length, session.maxPromptLength || 0);
|
||||
return chars * config.characterWidth;
|
||||
}
|
||||
};
|
||||
|
||||
// hide cursor in non editable cells
|
||||
this._addCursorMonitor = function() {
|
||||
this._updateCursorVisibility = this._updateCursorVisibility.bind(this);
|
||||
this.$cursorChanged = false;
|
||||
var markDirty = function() {
|
||||
this.$cursorChanged = true;
|
||||
}.bind(this);
|
||||
this.session.selection.on("changeSelection", markDirty);
|
||||
this.session.selection.on("changeCursor", markDirty);
|
||||
};
|
||||
|
||||
this._updateCursorVisibility = function(e, renderer) {
|
||||
if (!this.$cursorChanged) return;
|
||||
this.$cursorChanged = false;
|
||||
var cell = this.getCurrentCell();
|
||||
var visible = cell && cell.type === "input";
|
||||
if (visible != renderer.$cursorLayer.inEditableCell) {
|
||||
renderer.$cursorLayer.inEditableCell = visible;
|
||||
renderer.$cursorLayer.element.style.opacity = visible ? "" : "0";
|
||||
}
|
||||
};
|
||||
|
||||
this.onMouseUp = function(e) {
|
||||
var editor = e.editor;
|
||||
if (editor.repl.$mouseTimer)
|
||||
clearTimeout(editor.repl.$mouseTimer);
|
||||
editor.repl.$mouseTimer = setTimeout(function() {
|
||||
var sel = editor.selection;
|
||||
if (sel.isEmpty() && !sel.rangeCount) {
|
||||
var cell = editor.repl.getCurrentCell();
|
||||
if (cell.type != "input") {
|
||||
var r = cell.getRange();
|
||||
if (r && (sel.lead.row - r.start.row) / (r.end.row - r.start.row) < 0.1)
|
||||
cell = editor.repl.getSiblingCell(-1, cell, "input");
|
||||
if (!cell)
|
||||
cell = editor.repl.getSiblingCell(1, cell, "input");
|
||||
if (cell)
|
||||
editor.repl.select(cell.range.end);
|
||||
}
|
||||
}
|
||||
}, 250);
|
||||
};
|
||||
|
||||
// commands
|
||||
this.beforeCommand = function(e) {
|
||||
var editor = e.editor;
|
||||
var command = e.command;
|
||||
var cell = editor.repl.getCurrentCell();
|
||||
if (!editor.curReplOp)
|
||||
editor.curReplOp = { command: command, cell: cell };
|
||||
|
||||
if (command.isRepl)
|
||||
return;
|
||||
if (cell && cell.lineWidget) {
|
||||
editor.repl.moveByCells(-1, cell);
|
||||
e.preventDefault();
|
||||
}
|
||||
|
||||
if (cell && cell.type != "input") {
|
||||
if (!command.readOnly)
|
||||
e.preventDefault();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!cell)
|
||||
return;
|
||||
|
||||
var op = editor.curReplOp;
|
||||
|
||||
if (command.readOnly) {
|
||||
op.clipSelection = "before";
|
||||
if (editor.lastReplOp && editor.lastReplOp.command === command) {
|
||||
// todo allow some commands to go outside of cell
|
||||
op.clipSelection = "after";
|
||||
}
|
||||
} else if (command) {
|
||||
op.clipSelection = "before";
|
||||
}
|
||||
|
||||
if (op.clipSelection == "before") {
|
||||
var range = cell.getRange();
|
||||
editor.repl.$trackedRange = range;
|
||||
setClipToRange(editor.selection.lead, range);
|
||||
setClipToRange(editor.selection.anchor, range);
|
||||
}
|
||||
};
|
||||
|
||||
this.afterCommand = function(e) {
|
||||
var editor = e.editor;
|
||||
var op = editor.curReplOp;
|
||||
editor.curReplOp = null;
|
||||
editor.lastReplOp = op;
|
||||
if (!op)
|
||||
return;
|
||||
var command = op.command;
|
||||
if (op.clipSelection == "before") {
|
||||
setClipToRange(editor.selection.lead, false);
|
||||
setClipToRange(editor.selection.anchor, false);
|
||||
} else if (op.clipSelection == "after") {
|
||||
var range = editor.selection.toOrientedRange();
|
||||
if (op.cell) {
|
||||
range.clip(op.cell.getRange());
|
||||
editor.selection.fromOrientedRange(range);
|
||||
}
|
||||
}
|
||||
editor.repl.$trackedRange = null;
|
||||
if (!command.readOnly && !command.isRepl)
|
||||
editor.repl.ensureLastInputCell();
|
||||
};
|
||||
|
||||
this.attach = function(editor) {
|
||||
if (editor.repl && editor.repl != this)
|
||||
editor.repl.detach();
|
||||
|
||||
if (this.editor == editor)
|
||||
return;
|
||||
|
||||
this.detach();
|
||||
this.editor = editor;
|
||||
|
||||
this.editor.on("changeSession", this.detach);
|
||||
|
||||
editor.keyBinding.addKeyboardHandler(replCommands);
|
||||
editor.commands.on("exec", this.beforeCommand);
|
||||
editor.commands.on("afterExec", this.afterCommand);
|
||||
|
||||
editor.on("mouseup", this.onMouseUp);
|
||||
|
||||
editor.repl = this;
|
||||
|
||||
// editor.setOption("enableLineWidgets", true);
|
||||
editor.renderer.on("beforeRender", this.measureWidgets);
|
||||
editor.renderer.on("afterRender", this.updateWidgets);
|
||||
editor.renderer.on("afterRender", this._updateCursorVisibility);
|
||||
editor.renderer.on("resize", this._replResize);
|
||||
};
|
||||
this.detach = function(e) {
|
||||
console.log("detach", this.session.getLength(), e);
|
||||
if (e && e.session == this.session)
|
||||
return; // sometimes attach can be called before setSession
|
||||
var editor = this.editor;
|
||||
if (!editor)
|
||||
return;
|
||||
|
||||
editor.keyBinding.removeKeyboardHandler(replCommands);
|
||||
editor.commands.off("exec", this.beforeCommand);
|
||||
editor.commands.off("afterExec", this.afterCommand);
|
||||
|
||||
editor.off("mouseup", this.onMouseUp);
|
||||
editor.off("changeSession", this.detach);
|
||||
|
||||
delete editor.renderer.$gutterLayer.update;
|
||||
this.editor = null;
|
||||
editor.repl = null;
|
||||
|
||||
editor.renderer.off("beforeRender", this.measureWidgets);
|
||||
editor.renderer.off("afterRender", this.updateWidgets);
|
||||
this.session.lineWidgets.forEach(function(w) {
|
||||
if (w && w.el && w.el.parentNode) {
|
||||
w._inDocument = false;
|
||||
w.el.parentNode.removeChild(w.el);
|
||||
}
|
||||
});
|
||||
|
||||
editor.renderer.off("afterRender", this._updateCursorVisibility);
|
||||
editor.renderer.off("resize", this._replResize);
|
||||
};
|
||||
this.navigateHistory = function(dir) {
|
||||
var cell = this.getCurrentCell();
|
||||
if (!cell || cell.type != "input")
|
||||
return false;
|
||||
var row = this.editor.getCursorPosition().row;
|
||||
if (dir == 1 && cell.range.end.row != row)
|
||||
return false;
|
||||
else if (dir == -1 && cell.range.start.row != row)
|
||||
return false;
|
||||
var val = cell.getValue();
|
||||
val = this.history.navigateList(dir == -1 ? "prev" : "next", val);
|
||||
if (typeof val == "string") {
|
||||
dir = -dir;
|
||||
if (dir == -1 && val.indexOf("\n") == -1)
|
||||
dir = 1;
|
||||
cell.setValue(val, dir);
|
||||
}
|
||||
};
|
||||
this.getCurrentCell = function(returnAdjacent) {
|
||||
var range = this.editor.getSelectionRange();
|
||||
var cell = this.getCellAt(range.start);
|
||||
|
||||
if (returnAdjacent || cell && cell.range.contains(range.end.row, range.end.column)) {
|
||||
return cell;
|
||||
}
|
||||
};
|
||||
this.getCellAt = function(pos) {
|
||||
if (pos == undefined)
|
||||
pos = { row: this.session.getLength(), column: 0 };
|
||||
else if (typeof pos == "number")
|
||||
pos = { row: pos, column: 0 };
|
||||
|
||||
var cells = this.session.replCells;
|
||||
|
||||
for (var i = pos.row; i > 0; i--) {
|
||||
if (cells[i])
|
||||
break;
|
||||
}
|
||||
var cell = cells[i];
|
||||
if (!cell)
|
||||
return;
|
||||
cell.row = i;
|
||||
for (var i = pos.row + 1, l = this.session.getLength(); i < l; i++) {
|
||||
if (cells[i])
|
||||
break;
|
||||
}
|
||||
cell.endRow = Math.min(i - 1, l - 1);
|
||||
cell.range = new Range(cell.row, 0, cell.endRow, Number.MAX_VALUE);
|
||||
return cell;
|
||||
};
|
||||
|
||||
this.getSiblingCell = function(dir, cell, type) {
|
||||
if (dir == -1) {
|
||||
var pos = cell.range.clone().start;
|
||||
pos.row--;
|
||||
} else {
|
||||
var pos = cell.range.clone().end;
|
||||
pos.row++;
|
||||
}
|
||||
cell = this.getCellAt(pos);
|
||||
if (!cell)
|
||||
return;
|
||||
if (!type || cell.type == type)
|
||||
return cell;
|
||||
return this.getSiblingCell(dir, cell, type);
|
||||
};
|
||||
|
||||
this.moveByCells = function(dir, cell, type) {
|
||||
if (dir == "first")
|
||||
cell = this.getFirstCell(type);
|
||||
else if (dir == "last")
|
||||
cell = this.getLastCell(type);
|
||||
else
|
||||
cell = this.getSiblingCell(dir, cell || this.getCurrentCell(), type);
|
||||
|
||||
if (cell)
|
||||
return this.select(cell.range.end);
|
||||
};
|
||||
|
||||
this.getFirstCell = function(type) {
|
||||
var cell = this.getCellAt(0);
|
||||
if (type && cell.type != type)
|
||||
return this.getSiblingCell(1, cell, type);
|
||||
return cell;
|
||||
};
|
||||
this.getLastCell = function(type) {
|
||||
var cell = this.getCellAt(null);
|
||||
if (type && cell.type != type)
|
||||
return this.getSiblingCell(-1, cell, type);
|
||||
return cell;
|
||||
};
|
||||
|
||||
this.removeOutputCell = function(cell) {
|
||||
cell = cell || this.getCurrentCell();
|
||||
if (cell && cell.type == "input")
|
||||
cell = cell.output;
|
||||
if (cell)
|
||||
this.removeCell(cell);
|
||||
};
|
||||
|
||||
this.removeCell = function(cell) {
|
||||
var range = cell.getRange().clone();
|
||||
range.start.row--;
|
||||
range.start.column = this.session.getLine(range.start.row).length;
|
||||
this.session.replace(range, "");
|
||||
};
|
||||
|
||||
this.clear = function() {
|
||||
this.session.setValue("");
|
||||
this.ensureLastInputCell();
|
||||
};
|
||||
|
||||
this.ensureLastInputCell = function() {
|
||||
var end = { row: this.session.getLength(), column: 0 };
|
||||
var cell = this.getCellAt(end);
|
||||
if (!cell || cell.type != "input") {
|
||||
this.insertCell(end, { type: "input" }, true);
|
||||
}
|
||||
};
|
||||
|
||||
this.select = function(pos) {
|
||||
var sel = this.session.selection;
|
||||
if (typeof pos.row == "number" && typeof pos.column == "number") {
|
||||
if (sel.rangeCount)
|
||||
sel.toSingleRange();
|
||||
sel.setRange(Range.fromPoints(pos, pos));
|
||||
}
|
||||
};
|
||||
|
||||
this.eval = function(force, cell) {
|
||||
cell = cell || this.getCurrentCell();
|
||||
if (!cell)
|
||||
return;
|
||||
if (!force && cell.type != "input")
|
||||
cell = cell.input || this.getSiblingCell(1, cell) || this.getSiblingCell(-1, cell);
|
||||
var str = cell.getValue();
|
||||
if (force || this.evaluator.canEvaluate(str)) {
|
||||
this.session.getUndoManager().reset();
|
||||
|
||||
if (!cell.output || !cell.output.session) {
|
||||
cell.output = this.insertCell(cell.range.end, { type: "output" });
|
||||
var newCell = this.getSiblingCell(1, cell.output);
|
||||
}
|
||||
this.history.add(str);
|
||||
cell.output.waiting = true;
|
||||
cell.output.input = cell;
|
||||
|
||||
var self = this;
|
||||
var success = this.evaluator.evaluate(str, cell.output, function(result) {
|
||||
cell.output.waiting = false;
|
||||
if (result !== undefined)
|
||||
cell.output.setValue(result);
|
||||
|
||||
var renderer = self.editor.renderer;
|
||||
renderer.scrollSelectionIntoView(cell.range.end, cell.range.start);
|
||||
renderer.scrollCursorIntoView();
|
||||
});
|
||||
if (success !== false && newCell && newCell.type != "input") {
|
||||
newCell = this.insertCell(cell.output.range.end, { type: "input" });
|
||||
this.session.selection.setRange(newCell.range);
|
||||
}
|
||||
if (cell.output.waiting) {
|
||||
cell.output.setPlaceholder("...");
|
||||
}
|
||||
} else
|
||||
this.editor.insert("\n");
|
||||
|
||||
if (this.evaluator.afterEvaluate)
|
||||
this.evaluator.afterEvaluate(cell, newCell);
|
||||
};
|
||||
this.insertCell = function(pos, options, allowSplit) {
|
||||
pos = pos || this.session.selection.getCursor();
|
||||
if (!options)
|
||||
options = { type: "input" };
|
||||
|
||||
var cell = !allowSplit && this.getCellAt(pos);
|
||||
|
||||
if (cell) {
|
||||
pos = cell.range.end;
|
||||
pos = this.session.insert(pos, "\n");
|
||||
}
|
||||
var newCell = new ReplCell(options, this.session);
|
||||
|
||||
pos.row = Math.max(0, Math.min(pos.row, this.session.getLength() - 1));
|
||||
|
||||
var range = Range.fromPoints(pos, pos);
|
||||
range.end.column = Number.MAX_VALUE;
|
||||
range.start.column = 0;
|
||||
newCell.range = range;
|
||||
newCell.row = range.end.row;
|
||||
|
||||
var oldCell = this.session.replCells[pos.row];
|
||||
if (oldCell)
|
||||
oldCell.destroy();
|
||||
|
||||
this.session.replCells[pos.row] = newCell;
|
||||
this.$updateSession();
|
||||
return newCell;
|
||||
};
|
||||
|
||||
this.$updateSession = function() {
|
||||
var session = this.session;
|
||||
session.$decorations = [];
|
||||
var lastType = "";
|
||||
session.replCells.forEach(function(c, row) {
|
||||
if (!c) {
|
||||
if (lastType)
|
||||
session.$decorations[row] = lastType;
|
||||
return;
|
||||
}
|
||||
var dec = "";
|
||||
if (c.type == "input") {
|
||||
dec = "repl_prompt ";
|
||||
lastType = "repl_dots ";
|
||||
} else if (c.type == "output") {
|
||||
dec = "repl_output ";
|
||||
lastType = "repl_nonum ";
|
||||
}
|
||||
|
||||
if (c && c.waiting)
|
||||
dec += "waiting ";
|
||||
session.$decorations[row] = dec;
|
||||
});
|
||||
session.addGutterDecoration(0, session.$decorations[0]);
|
||||
|
||||
session.lineWidgets = session.replCells.map(function(c) {
|
||||
return c && c.lineWidget;
|
||||
});
|
||||
};
|
||||
|
||||
this.updateCellsOnChange = function(delta) {
|
||||
var startRow = delta.start.row;
|
||||
var len = delta.end.row - startRow;
|
||||
|
||||
var range = this.$trackedRange;
|
||||
var cells = this.session.replCells;
|
||||
if (len === 0) {
|
||||
|
||||
} else if (delta.action == "remove") {
|
||||
var removed = cells.splice(startRow + 1, len);
|
||||
removed.forEach(function(cell) {
|
||||
if (cell) {
|
||||
cell.destroy();
|
||||
}
|
||||
});
|
||||
if (range && range.start.row <= startRow && range.end.row >= startRow) {
|
||||
this.$trackedRange.end.row = Math.max(this.$trackedRange.end.row - len, this.$trackedRange.start.row);
|
||||
}
|
||||
if (cells.length <= 1)
|
||||
this.ensureLastInputCell();
|
||||
} else {
|
||||
var args = Array(len);
|
||||
args.unshift(startRow + 1, 0);
|
||||
cells.splice.apply(cells, args);
|
||||
if (range && range.start.row <= startRow && range.end.row >= startRow) {
|
||||
this.$trackedRange.end.row += len;
|
||||
}
|
||||
}
|
||||
this.$updateSession();
|
||||
this.session._signal("updateCells");
|
||||
};
|
||||
|
||||
this.$tokenizeRow = function(row) {
|
||||
var line = this.doc.getLine(row);
|
||||
var state = this.states[row - 1];
|
||||
|
||||
var cell = this.session.replCells[row];
|
||||
if (!cell && !state) {
|
||||
cell = this.session.repl.getCellAt(row);
|
||||
}
|
||||
if (cell) {
|
||||
state = cell.tokenizerState || cell.type;
|
||||
if (!this.tokenizer.regExps[state])
|
||||
state = "start";
|
||||
}
|
||||
|
||||
var data = this.tokenizer.getLineTokens(line, state, row);
|
||||
|
||||
if (this.states[row] + "" !== data.state + "") {
|
||||
if (!this.states[row] || !this.states[row].cellData)
|
||||
this.states[row] = data.state;
|
||||
this.lines[row + 1] = null;
|
||||
if (this.currentLine > row + 1)
|
||||
this.currentLine = row + 1;
|
||||
} else if (this.currentLine == row) {
|
||||
this.currentLine = row + 1;
|
||||
}
|
||||
if (this.session.repl.onTokenizeRow)
|
||||
data.tokens = this.session.repl.onTokenizeRow(row, data.tokens) || data.tokens;
|
||||
return this.lines[row] = data.tokens;
|
||||
};
|
||||
|
||||
this.addLineWidget = function(w) {
|
||||
var renderer = this.editor.renderer;
|
||||
if (w.html && !w.el) {
|
||||
w.el = dom.createElement("div");
|
||||
w.el.innerHTML = w.html;
|
||||
}
|
||||
if (w.el) {
|
||||
dom.addCssClass(w.el, "ace_lineWidgetContainer");
|
||||
renderer.container.appendChild(w.el);
|
||||
w._inDocument = true;
|
||||
}
|
||||
|
||||
if (!w.coverGutter) {
|
||||
w.el.style.zIndex = 3;
|
||||
}
|
||||
if (!w.pixelHeight) {
|
||||
w.pixelHeight = w.el.offsetHeight;
|
||||
}
|
||||
if (w.rowCount == null)
|
||||
w.rowCount = w.pixelHeight / renderer.layerConfig.lineHeight;
|
||||
|
||||
this.session._emit("changeFold", { data: { start: { row: w.row }}});
|
||||
|
||||
this.$updateSession();
|
||||
this.updateWidgets(null, renderer);
|
||||
return w;
|
||||
};
|
||||
|
||||
this.removeLineWidget = function(w) {
|
||||
w._inDocument = false;
|
||||
if (w.el && w.el.parentNode)
|
||||
w.el.parentNode.removeChild(w.el);
|
||||
if (w.editor && w.editor.destroy) try {
|
||||
w.editor.destroy();
|
||||
} catch (e) {}
|
||||
this.session._emit("changeFold", { data: { start: { row: w.row }}});
|
||||
this.$updateSession();
|
||||
};
|
||||
|
||||
this.onWidgetChanged = function(w) {
|
||||
this.session._changedWidgets.push(w);
|
||||
this.editor && this.editor.renderer.updateFull();
|
||||
};
|
||||
|
||||
this.measureWidgets = function(e, renderer) {
|
||||
var ws = this.session._changedWidgets;
|
||||
var config = renderer.layerConfig;
|
||||
|
||||
if (!ws || !ws.length) return;
|
||||
var min = Infinity;
|
||||
for (var i = 0; i < ws.length; i++) {
|
||||
var w = ws[i].lineWidget;
|
||||
if (!w._inDocument) {
|
||||
w._inDocument = true;
|
||||
renderer.container.appendChild(w.el);
|
||||
}
|
||||
|
||||
w.h = w.el.offsetHeight;
|
||||
|
||||
if (!w.fixedWidth) {
|
||||
w.w = w.el.offsetWidth;
|
||||
w.screenWidth = Math.ceil(w.w / config.characterWidth);
|
||||
}
|
||||
|
||||
var rowCount = w.h / config.lineHeight;
|
||||
if (w.coverLine)
|
||||
rowCount -= this.session.getRowLineCount(w.row);
|
||||
|
||||
if (w.rowCount != rowCount) {
|
||||
w.rowCount = rowCount;
|
||||
if (w.row < min)
|
||||
min = w.row;
|
||||
}
|
||||
}
|
||||
if (min != Infinity) {
|
||||
this.session._emit("changeFold", { data: { start: { row: min }}});
|
||||
this.session.lineWidgetWidth = null;
|
||||
}
|
||||
this.session._changedWidgets = [];
|
||||
};
|
||||
|
||||
this.updateWidgets = function(e, renderer) {
|
||||
var config = renderer.layerConfig;
|
||||
var cells = this.session.replCells;
|
||||
if (!cells)
|
||||
return;
|
||||
var first = Math.min(this.firstRow, config.firstRow);
|
||||
var last = Math.max(this.lastRow, config.lastRow, cells.length);
|
||||
|
||||
while (first > 0 && !cells[first])
|
||||
first--;
|
||||
|
||||
this.firstRow = config.firstRow;
|
||||
this.lastRow = config.lastRow;
|
||||
|
||||
renderer.$cursorLayer.config = config;
|
||||
for (var i = first; i <= last; i++) {
|
||||
var c = cells[i];
|
||||
var w = c && c.lineWidget;
|
||||
if (!w || !w.el) continue;
|
||||
|
||||
if (!w._inDocument) {
|
||||
w._inDocument = true;
|
||||
renderer.container.appendChild(w.el);
|
||||
}
|
||||
var top = renderer.$cursorLayer.getPixelPosition({ row: i, column: 0 }, true).top;
|
||||
if (!w.coverLine)
|
||||
top += config.lineHeight * this.session.getRowLineCount(w.row);
|
||||
w.el.style.top = top - config.offset + "px";
|
||||
|
||||
var left = w.coverGutter ? 0 : renderer.gutterWidth;
|
||||
if (!w.fixedWidth)
|
||||
left -= renderer.scrollLeft;
|
||||
w.el.style.left = left + "px";
|
||||
|
||||
if (w.fixedWidth) {
|
||||
w.el.style.right = renderer.scrollBar.getWidth() + "px";
|
||||
} else {
|
||||
w.el.style.right = "";
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
}).call(Repl.prototype);
|
||||
|
||||
var History = function() {
|
||||
this._data = [];
|
||||
this._tempData = Object.create(null);
|
||||
this.position = 0;
|
||||
};
|
||||
History.prototype = {
|
||||
add: function(text) {
|
||||
if (text && this._data[0] !== text) {
|
||||
this._data.unshift(text);
|
||||
}
|
||||
delete this._tempData[this.position];
|
||||
this.position = -1;
|
||||
this._tempData[-1] = "";
|
||||
return this._data;
|
||||
},
|
||||
navigateList: function(type, value) {
|
||||
var lines = this._data;
|
||||
if (value && (lines[this.position] != value)) {
|
||||
this._tempData[this.position] = value;
|
||||
}
|
||||
|
||||
if (type == "next") {
|
||||
if (this.position <= 0) {
|
||||
this.position = -1;
|
||||
return this._tempData[this.position] || "";
|
||||
}
|
||||
var next = Math.max(0, this.position - 1);
|
||||
}
|
||||
else if (type == "prev")
|
||||
next = Math.min(lines.length - 1, this.position + 1);
|
||||
else if (type == "last")
|
||||
next = Math.max(lines.length - 1, 0);
|
||||
else if (type == "first")
|
||||
next = 0;
|
||||
|
||||
if (lines[next] && next != this.position) {
|
||||
this.position = next;
|
||||
return this._tempData[next] || lines[next];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
function clonePos(pos) {
|
||||
return { row: pos.row, column: pos.column };
|
||||
}
|
||||
function setPos(pos, newPos) {
|
||||
pos.row = newPos.row;
|
||||
pos.column = newPos.column;
|
||||
}
|
||||
function clipPos(pos, range) {
|
||||
if (comparePoints(pos, range.start) < 0) {
|
||||
setPos(pos, range.start);
|
||||
} else if (comparePoints(pos, range.end) > 0) {
|
||||
setPos(pos, range.end);
|
||||
}
|
||||
}
|
||||
Range.prototype.clip = function(range) {
|
||||
clipPos(this.start, range);
|
||||
clipPos(this.end, range);
|
||||
};
|
||||
|
||||
|
||||
function setClipToRange(anchor, range) {
|
||||
anchor.$clip_default = Anchor.prototype.$clipPositionToDocument;
|
||||
if (!range) {
|
||||
anchor.$clipPositionToDocument = anchor.$clip_default;
|
||||
return;
|
||||
}
|
||||
anchor.$clipPositionToDocument = function(row, column) {
|
||||
var pos = this.$clip_default(row, column);
|
||||
clipPos(pos, range);
|
||||
return pos;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
// dummy example
|
||||
var Evaluator = function() {
|
||||
};
|
||||
|
||||
(function() {
|
||||
this.canEvaluate = function(str) {
|
||||
return !!str.trim();
|
||||
};
|
||||
|
||||
this.evaluate = function(str, cell, cb) {
|
||||
cb("evaluator is missing!");
|
||||
};
|
||||
}).call(Evaluator.prototype);
|
||||
|
||||
Repl.fromEditor = function(editor, options) {
|
||||
// todo different modes for input and output
|
||||
var repl = new Repl(editor.session, options);
|
||||
// this should happen on session.changeEditor event
|
||||
repl.attach(editor);
|
||||
|
||||
editor.setOption("showPrintMargin", false);
|
||||
return repl;
|
||||
};
|
||||
exports.Repl = Repl;
|
||||
|
||||
});
|
|
@ -0,0 +1,154 @@
|
|||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Distributed under the BSD license:
|
||||
*
|
||||
* Copyright (c) 2010, Ajax.org B.V.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Ajax.org B.V. nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
|
||||
define(function(require, exports, module) {
|
||||
"use strict";
|
||||
|
||||
var Anchor = require("ace/anchor").Anchor;
|
||||
var Range = require("ace/range").Range;
|
||||
var comparePoints = Range.comparePoints;
|
||||
var lang = require("ace/lib/lang");
|
||||
|
||||
|
||||
var ReplCell = function(options, session) {
|
||||
this.session = session;
|
||||
this.type = options.type;
|
||||
};
|
||||
|
||||
(function() {
|
||||
this.insert = function(pos, text) {
|
||||
if (typeof pos == "string") {
|
||||
text = pos;
|
||||
pos = this.getRange().end;
|
||||
}
|
||||
this.session.insert(pos, text);
|
||||
};
|
||||
this.setPlaceholder = function(str) {
|
||||
this.placeholder = str;
|
||||
|
||||
};
|
||||
this.setMode = function() {
|
||||
|
||||
};
|
||||
this.setWaiting = function(val) {
|
||||
this.waiting = val;
|
||||
this.session.repl.$updateSession();
|
||||
};
|
||||
this.prompt = "";
|
||||
this.promptType = null;
|
||||
this.setPrompt = function(str, type) {
|
||||
if (this.prompt == str)
|
||||
return;
|
||||
this.promptType = type;
|
||||
this.prompt = (str || "") + " ";
|
||||
this.session.maxPromptLength = Math.max(this.session.maxPromptLength || 0, this.prompt.length);
|
||||
};
|
||||
|
||||
this.setValue = function(val, selection) {
|
||||
if (!this.session)
|
||||
return;
|
||||
if (val == null)
|
||||
return this.remove();
|
||||
if (this.lineWidget && val.trim())
|
||||
this.removeWidget();
|
||||
this.$updateRange();
|
||||
var pos = this.session.doc.replace(this.range, val);
|
||||
this.range.setEnd(pos);
|
||||
if (selection == 1)
|
||||
this.session.selection.setRange({ start: this.range.end, end: this.range.end });
|
||||
else if (selection == -1)
|
||||
this.session.selection.setRange({ start: this.range.start, end: this.range.start });
|
||||
};
|
||||
this.getValue = function() {
|
||||
if (!this.session)
|
||||
return "";
|
||||
return this.session.doc.getTextRange(this.range);
|
||||
};
|
||||
this.getRange = function() {
|
||||
this.$updateRange();
|
||||
return this.range;
|
||||
};
|
||||
this.$updateRange = function(row) {
|
||||
var cells = this.session.replCells;
|
||||
if (row == null)
|
||||
row = cells.indexOf(this);
|
||||
|
||||
for (i = row; i > 0; i--) {
|
||||
if (cells[i])
|
||||
break;
|
||||
}
|
||||
var cell = cells[i];
|
||||
if (!cell)
|
||||
return;
|
||||
cell.row = i;
|
||||
for (var i = row + 1; i < this.session.getLength(); i++) {
|
||||
if (cells[i])
|
||||
break;
|
||||
}
|
||||
cell.endRow = i - 1;
|
||||
cell.range = new Range(cell.row, 0, cell.endRow, Number.MAX_VALUE);
|
||||
|
||||
return this.range;
|
||||
};
|
||||
|
||||
this.removeWidget = function() {
|
||||
if (this.lineWidget) {
|
||||
var w = this.lineWidget;
|
||||
this.lineWidget = null;
|
||||
this.session.repl.removeLineWidget(w);
|
||||
}
|
||||
};
|
||||
|
||||
this.addWidget = function(options) {
|
||||
if (this.lineWidget)
|
||||
this.removeWidget();
|
||||
|
||||
this.setValue("");
|
||||
options.row = this.range.end.row;
|
||||
this.lineWidget = options;
|
||||
this.session.repl.addLineWidget(this.lineWidget);
|
||||
};
|
||||
|
||||
this.destroy = function() {
|
||||
this.removeWidget();
|
||||
this.session = null;
|
||||
};
|
||||
|
||||
this.remove = function() {
|
||||
if (this.session)
|
||||
this.session.repl.removeCell(this);
|
||||
};
|
||||
|
||||
}).call(ReplCell.prototype);
|
||||
|
||||
exports.ReplCell = ReplCell;
|
||||
|
||||
});
|
|
@ -0,0 +1,33 @@
|
|||
<!DOCTYPE html>
|
||||
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>ReplAce</title>
|
||||
<style>
|
||||
#editor {
|
||||
position: absolute;
|
||||
top:0;
|
||||
bottom:0;
|
||||
left:0;
|
||||
right:0;
|
||||
}
|
||||
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="editor"></div>
|
||||
|
||||
<script src="/static/mini_require.js"></script>
|
||||
<script src="/configs/require_config.js"></script>
|
||||
<script src="/static/test.js"></script>
|
||||
|
||||
<script>
|
||||
require(["ace/ace", "plugins/c9.ide.ace.repl/repl", "plugins/c9.ide.ace.repl/notebook"], function(ace, repl, nb) {
|
||||
editor = ace.edit("editor")
|
||||
editor.session.setMode(new nb.Mode)
|
||||
editor.commands.addCommands(nb.commands)
|
||||
});
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,95 @@
|
|||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Distributed under the BSD license:
|
||||
*
|
||||
* Copyright (c) 2010, Ajax.org B.V.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Ajax.org B.V. nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
|
||||
define(function(require, exports, module) {
|
||||
"use strict";
|
||||
|
||||
require("ace/lib/fixoldbrowsers");
|
||||
require("ace/config").set("packaged", false);
|
||||
require("ace/line_widgets");
|
||||
|
||||
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 MultiSelect = require("ace/multi_select").MultiSelect;
|
||||
|
||||
|
||||
|
||||
/*********** create editor ***************************/
|
||||
var container = document.getElementById("editor");
|
||||
|
||||
|
||||
var editor = new Editor(new Renderer(container));
|
||||
require("ace/multi_select").MultiSelect(editor);
|
||||
|
||||
editor.session.setUndoManager(new UndoManager);
|
||||
|
||||
editor.on("click", function(e) {
|
||||
if (e.domEvent.target.className == "ace_repl_button") {
|
||||
var pos = e.getDocumentPosition();
|
||||
var input = document.forms[0].upfile;
|
||||
input.onchange = function() {
|
||||
editor.session.insert(pos, "\x01" + input.value + "\x02");
|
||||
};
|
||||
input.click();
|
||||
}
|
||||
});
|
||||
window.editor = editor;
|
||||
|
||||
|
||||
|
||||
require("./repl").Repl.fromEditor(editor, {
|
||||
mode: "logiql_command_mode",
|
||||
evaluator: new (require("./evaluator").Evaluator)
|
||||
});
|
||||
|
||||
|
||||
/*********** manage layout ***************************/
|
||||
var consoleHeight = 20;
|
||||
function onResize() {
|
||||
editor.resize();
|
||||
}
|
||||
|
||||
window.onresize = onResize;
|
||||
onResize();
|
||||
|
||||
});
|
|
@ -0,0 +1,21 @@
|
|||
|
||||
Copyright (c) 2015, Ajax.org B.V.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
|
@ -0,0 +1 @@
|
|||
# c9.ide.ace.split
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"name": "c9.ide.ace.split",
|
||||
"description": "The repository for c9.ide.ace.split, a Cloud9 core plugin",
|
||||
"version": "3.0.0",
|
||||
"author": "Cloud9",
|
||||
"contributors": [],
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "http://github.com/c9/c9.ide.ace.split.git"
|
||||
},
|
||||
"plugins": {
|
||||
"split": {}
|
||||
},
|
||||
"categories": [
|
||||
"core"
|
||||
],
|
||||
"licenses": [
|
||||
"MIT"
|
||||
]
|
||||
}
|
|
@ -0,0 +1,372 @@
|
|||
define(function(require, exports, module) {
|
||||
main.consumes = [
|
||||
"Plugin", "ui", "ace", "tabbehavior", "menus", "tabManager"
|
||||
];
|
||||
main.provides = ["ace.split"];
|
||||
return main;
|
||||
|
||||
function main(options, imports, register) {
|
||||
var Plugin = imports.Plugin;
|
||||
var ui = imports.ui;
|
||||
var ace = imports.ace;
|
||||
|
||||
var event = require("ace/lib/event");
|
||||
var Editor = require("ace/editor").Editor;
|
||||
var Renderer = require("ace/virtual_renderer").VirtualRenderer;
|
||||
|
||||
/***** Initialization *****/
|
||||
|
||||
var plugin = new Plugin("Ajax.org", main.consumes);
|
||||
var emit = plugin.getEmitter();
|
||||
|
||||
var editors = [], splits = {};
|
||||
|
||||
var loaded = false;
|
||||
function load() {
|
||||
if (loaded) return false;
|
||||
loaded = true;
|
||||
|
||||
ace.on("create", function(e) {
|
||||
if (e.editor.type != "ace")
|
||||
return;
|
||||
|
||||
draw();
|
||||
|
||||
var editor = e.editor;
|
||||
var grabber;
|
||||
|
||||
editor.once("draw", function() {
|
||||
grabber = createGrabber(editor);
|
||||
}, plugin);
|
||||
|
||||
editor.on("documentActivate", function(e) {
|
||||
var doc = e.doc;
|
||||
var session = doc.getSession();
|
||||
var split = session.split;
|
||||
var splitInfo = splits[doc.editor.name];
|
||||
|
||||
// If we are not in split mode and the editor is not split
|
||||
// lets do nothing.
|
||||
if (!split && !splitInfo)
|
||||
return;
|
||||
|
||||
if (split) {
|
||||
// Make sure we have a split inited for this editor
|
||||
if (!splitInfo)
|
||||
splitInfo = initSplit(editor, split.height);
|
||||
|
||||
var editor2 = splitInfo.editor2;
|
||||
|
||||
// Set Session
|
||||
editor2.setSession(split.session2);
|
||||
|
||||
// Set Height
|
||||
splitInfo.topPane.setHeight(split.height);
|
||||
|
||||
// Show bottom pane
|
||||
splitInfo.topPane.show();
|
||||
|
||||
// Hide Grabber
|
||||
grabber.style.display = "none";
|
||||
}
|
||||
else {
|
||||
// Hide bottom pane
|
||||
splitInfo.topPane.hide();
|
||||
|
||||
// Show Grabber
|
||||
grabber.style.display = "block";
|
||||
}
|
||||
});
|
||||
|
||||
editor.on("getState", function(e) {
|
||||
var session = e.doc.getSession();
|
||||
var state = e.state;
|
||||
|
||||
if (e.filter || !session.split)
|
||||
return;
|
||||
|
||||
var session2 = session.split.session2;
|
||||
|
||||
state.split = {
|
||||
height: session.split.height,
|
||||
|
||||
// Scroll state
|
||||
scrolltop: session2.getScrollTop(),
|
||||
scrollleft: session2.getScrollLeft(),
|
||||
|
||||
// Selection
|
||||
selection: session2.selection.toJSON()
|
||||
};
|
||||
});
|
||||
|
||||
editor.on("setState", function(e) {
|
||||
var state = e.state.split;
|
||||
var session = e.doc.getSession();
|
||||
|
||||
if (!state)
|
||||
return;
|
||||
|
||||
var splitInfo = initSplit(editor, state.height);
|
||||
var session2 = ace.cloneSession(session.session);
|
||||
|
||||
session.split = {
|
||||
height: state.height,
|
||||
session2: session2
|
||||
};
|
||||
|
||||
// Set 2nd Session
|
||||
splitInfo.editor2.setSession(session2);
|
||||
|
||||
// Set selection
|
||||
if (state.selection)
|
||||
session2.selection.fromJSON(state.selection);
|
||||
|
||||
// Set scroll state
|
||||
if (state.scrolltop)
|
||||
session2.setScrollTop(state.scrolltop);
|
||||
if (state.scrollleft)
|
||||
session2.setScrollLeft(state.scrollleft);
|
||||
|
||||
if (state.options)
|
||||
session2.setOptions(state.options);
|
||||
|
||||
var grabber = editor.aml.$int.querySelector(".splitgrabber");
|
||||
grabber.style.display = "none";
|
||||
});
|
||||
|
||||
editor.on("resize", function(e) {
|
||||
var splitInfo = splits[editor.name];
|
||||
if (!splitInfo || !splitInfo.topPane.visible) return;
|
||||
splitInfo.editor2.resize(true); // @Harutyun
|
||||
});
|
||||
|
||||
editor.on("unload", function(e) {
|
||||
delete splits[editor.name];
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
var drawn = false;
|
||||
function draw() {
|
||||
if (drawn) return;
|
||||
drawn = true;
|
||||
|
||||
// Insert CSS
|
||||
ui.insertCss(require("text!./style.css"), plugin);
|
||||
|
||||
emit("draw");
|
||||
}
|
||||
|
||||
function createGrabber(editor) {
|
||||
var htmlNode = editor.ace.container.parentNode;
|
||||
var grabber = document.createElement("div");
|
||||
htmlNode.appendChild(grabber);
|
||||
grabber.className = "splitgrabber";
|
||||
grabber.innerHTML = "=";
|
||||
|
||||
grabber.addEventListener("mousedown", function(e) {
|
||||
startSplit(e, grabber, editor);
|
||||
});
|
||||
|
||||
plugin.addOther(function() {
|
||||
grabber.parentNode.removeChild(grabber);
|
||||
});
|
||||
|
||||
return grabber;
|
||||
}
|
||||
|
||||
/***** Methods *****/
|
||||
|
||||
function startSplit(e, grabber, editor) {
|
||||
var container = grabber;
|
||||
var drag = grabber;
|
||||
|
||||
// Set Top
|
||||
drag.style.zIndex = 1000000;
|
||||
|
||||
var offsetY = e.clientY - (parseInt(container.style.top, 10) || 0);
|
||||
var moved = false;
|
||||
var startY = e.clientY - offsetY;
|
||||
var offset = e.offsetY;
|
||||
|
||||
var session = editor.activeDocument.getSession();
|
||||
|
||||
event.capture(container, function(e) {
|
||||
var y = e.clientY - offsetY;
|
||||
|
||||
if (!moved) {
|
||||
if (Math.abs(y - startY) > 3) {
|
||||
moved = true;
|
||||
var percentage = ((y - startY) / grabber.parentNode.offsetHeight) * 100;
|
||||
session.split = {
|
||||
height: percentage + "%",
|
||||
session2: ace.cloneSession(session.session)
|
||||
};
|
||||
var splitInfo = initSplit(editor, percentage);
|
||||
|
||||
// Set 2nd Session
|
||||
splitInfo.editor2.setSession(session.split.session2);
|
||||
|
||||
// Start splitter
|
||||
splitInfo.splitbox.$handle.$ext.onmousedown({
|
||||
clientY: e.clientY,
|
||||
offsetY: -7 + offset
|
||||
});
|
||||
|
||||
// Hide Grabber
|
||||
grabber.style.display = "none";
|
||||
}
|
||||
else return;
|
||||
}
|
||||
}, function() {
|
||||
if (moved)
|
||||
setFinalState(editor, session);
|
||||
});
|
||||
|
||||
event.stopEvent(e);
|
||||
}
|
||||
|
||||
function initSplit(editor, percentage) {
|
||||
if (splits[editor.name]) {
|
||||
var splitInfo = splits[editor.name];
|
||||
splitInfo.topPane.show();
|
||||
return splitInfo;
|
||||
}
|
||||
|
||||
var container = editor.aml.$int;
|
||||
var amlNode = container.host;
|
||||
// @todo detect if this already happened
|
||||
|
||||
var splitbox = amlNode.appendChild(new ui.vsplitbox({
|
||||
"class": "ace_split",
|
||||
padding: 7,
|
||||
edge: "7 0 0 0",
|
||||
splitter: true
|
||||
}));
|
||||
|
||||
var topPane = splitbox.appendChild(new ui.bar({
|
||||
height: percentage + "%"
|
||||
}));
|
||||
var bottomPane = splitbox.appendChild(new ui.bar());
|
||||
|
||||
// Original Editor
|
||||
bottomPane.$int.appendChild(editor.ace.container);
|
||||
editor.ace.container.style.top = "0px";
|
||||
|
||||
// New Editor
|
||||
var editor2 = new Editor(new Renderer(topPane.$int, ace.theme));
|
||||
editors.push(editor2);
|
||||
|
||||
splitbox.$handle.on("dragmove", function() {
|
||||
editor.resize();
|
||||
editor2.resize();
|
||||
});
|
||||
splitbox.$handle.on("dragdrop", function() {
|
||||
editor.resize();
|
||||
editor2.resize();
|
||||
|
||||
var session = editor.activeDocument.getSession();
|
||||
setFinalState(editor, session);
|
||||
});
|
||||
ace.on("settingsUpdate", function(e) {
|
||||
editor2.setOptions(e.options);
|
||||
}, editor);
|
||||
|
||||
function setTheme() {
|
||||
var theme = ace.theme;
|
||||
editor2.setTheme(theme.path);
|
||||
|
||||
var node = splitbox.$ext.parentNode;
|
||||
if (theme.isDark)
|
||||
ui.setStyleClass(node, "dark");
|
||||
else
|
||||
ui.setStyleClass(node, "", ["dark"]);
|
||||
}
|
||||
ace.on("themeChange", setTheme, editor);
|
||||
setTheme();
|
||||
|
||||
var lastFocused = editor2;
|
||||
editor2.on("focus", function() {
|
||||
lastFocused = editor2;
|
||||
});
|
||||
|
||||
editor.ace.on("focus", function() {
|
||||
lastFocused = null;
|
||||
});
|
||||
|
||||
editor.on("getAce", function() {
|
||||
if (lastFocused)
|
||||
return editor2;
|
||||
});
|
||||
|
||||
editor.addEditor(editor2);
|
||||
|
||||
splits[editor.name] = {
|
||||
splitbox: splitbox,
|
||||
topPane: topPane,
|
||||
bottomPane: bottomPane,
|
||||
editor: editor,
|
||||
editor2: editor2
|
||||
};
|
||||
|
||||
return splits[editor.name];
|
||||
}
|
||||
|
||||
function setFinalState(editor, session) {
|
||||
var splitInfo = splits[editor.name];
|
||||
var pixelHeight = splitInfo.topPane.getHeight();
|
||||
|
||||
var grabber = editor.aml.$int.querySelector(".splitgrabber");
|
||||
|
||||
if (pixelHeight < 3) {
|
||||
// Remove the split
|
||||
splitInfo.topPane.hide();
|
||||
delete session.split;
|
||||
|
||||
// Show Grabber
|
||||
grabber.style.display = "block";
|
||||
}
|
||||
else {
|
||||
// Record the height
|
||||
session.split.height = splitInfo.topPane.height;
|
||||
|
||||
// Hide Grabber
|
||||
grabber.style.display = "none";
|
||||
}
|
||||
}
|
||||
|
||||
/***** Lifecycle *****/
|
||||
|
||||
plugin.on("load", function() {
|
||||
load();
|
||||
});
|
||||
plugin.on("enable", function() {
|
||||
|
||||
});
|
||||
plugin.on("disable", function() {
|
||||
|
||||
});
|
||||
plugin.on("unload", function() {
|
||||
loaded = false;
|
||||
drawn = false;
|
||||
});
|
||||
|
||||
/***** Register and define API *****/
|
||||
|
||||
/**
|
||||
*
|
||||
**/
|
||||
plugin.freezePublicAPI({
|
||||
_events: [
|
||||
/**
|
||||
* @event draw
|
||||
*/
|
||||
"draw"
|
||||
]
|
||||
});
|
||||
|
||||
register(null, {
|
||||
"ace.split": plugin
|
||||
});
|
||||
}
|
||||
});
|
|
@ -0,0 +1,61 @@
|
|||
.splitgrabber {
|
||||
display: block;
|
||||
width: 16px;
|
||||
height: 7px;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
box-sizing: border-box;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
vertical-align: top;
|
||||
line-height: 9px;
|
||||
font-family: Courier New;
|
||||
font-size: 15px;
|
||||
cursor: ns-resize;
|
||||
pointer-events: all;
|
||||
text-align: right;
|
||||
padding-right: 3px;
|
||||
z-index: 100;
|
||||
color: rgba(0, 0, 0, 0.4);
|
||||
text-shadow: 0 1px white;
|
||||
}
|
||||
.dark .splitgrabber {
|
||||
color: rgba(255, 255, 255, 0.2);
|
||||
text-shadow: 0 1px rgba(0, 0, 0, 0.34);
|
||||
}
|
||||
|
||||
.splitgrabber:hover{
|
||||
|
||||
}
|
||||
|
||||
.ace_split{
|
||||
height : 100%;
|
||||
}
|
||||
|
||||
.ace_split .splitter.horizontal:after {
|
||||
content: "=";
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
/*background: rgba(255, 255, 255, 0.105);*/
|
||||
position: absolute;
|
||||
height: 7px;
|
||||
line-height: 8px;
|
||||
font-size: 15px;
|
||||
font-family: Courier New;
|
||||
box-sizing: border-box;
|
||||
color: rgba(0, 0, 0, 0.3);
|
||||
border-top: 1px solid rgba(255, 255, 255, 0.6);
|
||||
box-shadow: 0 -1px rgb(211, 211, 211);
|
||||
text-shadow: 0 1px white;
|
||||
}
|
||||
|
||||
.dark .ace_split .splitter.horizontal:after {
|
||||
color: rgba(255, 255, 255, 0.23);
|
||||
border-top: 1px solid rgba(255, 255, 255, 0.06);
|
||||
box-shadow: 0 -1px rgba(0, 0, 0, 0.29);
|
||||
text-shadow: none;
|
||||
}
|
||||
|
||||
.ace_split .splitter.horizontal{
|
||||
background: transparent !important;
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
|
||||
Copyright (c) 2015, Ajax.org B.V.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
|
@ -0,0 +1 @@
|
|||
# c9.ide.ace.statusbar
|
|
@ -0,0 +1,37 @@
|
|||
<a:application xmlns:a="http://ajax.org/2005/aml">
|
||||
<a:menu id="menu"
|
||||
class = "mnuSbPrefs"
|
||||
sticky = "false"
|
||||
render = "runtime">
|
||||
<a:item type="check" caption="Show Invisibles" checked="user/ace/@showInvisibles" />
|
||||
<a:item type="check" caption="Show Gutter" checked="user/ace/@showGutter" />
|
||||
<a:item type="check" caption="Auto-pair Brackets, Quotes, etc." checked="user/ace/@behavioursEnabled" />
|
||||
<a:item type="check" caption="Wrap Lines" id="itmSbWrap" />
|
||||
<a:item type="check" style="margin-left:18px;position:relative" caption="↳ to Print Margin" id="itmSbWrapPM" />
|
||||
<a:divider />
|
||||
<a:hbox edge="0 5 2 23" align="center">
|
||||
<a:label flex="1" style="padding: 2px 2px 2px 0">Font Size</a:label>
|
||||
<a:spinner value="user/ace/@fontSize" realtime="true" min="1" max="72" width="50" />
|
||||
</a:hbox>
|
||||
</a:menu>
|
||||
|
||||
<a:menu id="menuTabs" render="runtime" class="mnuSbPrefs">
|
||||
<a:item type="check" caption="Soft Tabs (spaces)" />
|
||||
<a:divider />
|
||||
<a:item disabled="true">Tab Size</a:item>
|
||||
<a:item type="radio" caption="2" style="padding-left:35px" />
|
||||
<a:item type="radio" caption="3" style="padding-left:35px" />
|
||||
<a:item type="radio" caption="4" style="padding-left:35px" />
|
||||
<a:item type="radio" caption="8" style="padding-left:35px" />
|
||||
<a:divider />
|
||||
<a:hbox edge="0 10 2 23" align="center">
|
||||
<a:label style="padding: 2px 10px 2px 3px;flex:1 1 50px;">Other</a:label>
|
||||
<a:spinner id="itmTabSize" realtime="true" min="1" max="64" width="50" />
|
||||
</a:hbox>
|
||||
<a:divider />
|
||||
<a:item caption="Guess Tab Size" />
|
||||
<a:divider />
|
||||
<a:item caption="Convert to Spaces" />
|
||||
<a:item caption="Convert to Tabs" />
|
||||
</a:menu>
|
||||
</a:application>
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"name": "c9.ide.ace.statusbar",
|
||||
"description": "The repository for c9.ide.ace.statusbar, a Cloud9 core plugin",
|
||||
"version": "3.0.0",
|
||||
"author": "Cloud9",
|
||||
"contributors": [],
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "http://github.com/c9/c9.ide.ace.statusbar.git"
|
||||
},
|
||||
"plugins": {
|
||||
"statusbar": {}
|
||||
},
|
||||
"categories": [
|
||||
"core"
|
||||
],
|
||||
"licenses": [
|
||||
"MIT"
|
||||
]
|
||||
}
|
|
@ -0,0 +1,113 @@
|
|||
<?xml version='1.0'?>
|
||||
<a:skin xmlns:a="http://ajax.org/2005/aml" xmlns="http://www.w3.org/1999/xhtml">
|
||||
<a:bar name="bar-status">
|
||||
<a:style><![CDATA[
|
||||
.bar-status {
|
||||
position : absolute;
|
||||
color: rgba(0, 0, 0, 0.4);
|
||||
border-radius: 4px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.bar-status > *{
|
||||
display : inline-block;
|
||||
vertical-align : middle;
|
||||
}
|
||||
|
||||
.bar-status .lbl_row_col {
|
||||
text-align: center;
|
||||
color: rgba(0, 0, 0, 0.4);
|
||||
}
|
||||
|
||||
.bar-status .label {
|
||||
color: @ace-status-color;
|
||||
padding: @ace-status-padding;
|
||||
overflow: visible;
|
||||
text-shadow: 0px 1px 0px rgba(255,255,255,0.5);
|
||||
.font-smoothing(true);
|
||||
font-weight: @ace-status-font-weight;
|
||||
font-size: @ace-status-font-size;
|
||||
}
|
||||
|
||||
.bar-status.ace_dark .label {
|
||||
color: rgba(255,255,255,0.5);
|
||||
text-shadow: 0px 1px 0px rgba(0, 0, 0, 0.57);
|
||||
}
|
||||
|
||||
.mnuSbPrefs{
|
||||
margin-top : -8px;
|
||||
margin-left : 1px;
|
||||
}
|
||||
|
||||
.mnuSbPrefs .label {
|
||||
color : @menu-color;
|
||||
}
|
||||
|
||||
.bar-status .label:hover{
|
||||
text-decoration : @ace-status-label-hover-decoration;
|
||||
color : @ace-status-label-hover-color;
|
||||
}
|
||||
.bar-status .label.labelDown{
|
||||
color : @ace-status-label-active-color;
|
||||
}
|
||||
|
||||
.bar-status .nounderline:hover{
|
||||
text-decoration : none;
|
||||
}
|
||||
]]></a:style>
|
||||
|
||||
<a:presentation>
|
||||
<a:main container=".">
|
||||
<div class="bar-status">
|
||||
</div>
|
||||
</a:main>
|
||||
</a:presentation>
|
||||
</a:bar>
|
||||
<a:button name="btn-statusbar-icon">
|
||||
<a:style><![CDATA[
|
||||
.btn-statusbar-icon {
|
||||
height : 23px;
|
||||
width : 22px;
|
||||
overflow : hidden;
|
||||
cursor : pointer;
|
||||
position : relative;
|
||||
cursor : default;
|
||||
-moz-user-select : none;
|
||||
-khtml-user-select : none;
|
||||
user-select : none;
|
||||
background-position : 0 0;
|
||||
background-repeat : no-repeat;
|
||||
margin: @ace-status-icon-margin !important;
|
||||
.image-2x(@ace-status-icon-image, @ace-status-icon-image-width, @ace-status-icon-image-height, no-repeat, true);
|
||||
}
|
||||
.btn-statusbar-iconOver {
|
||||
background-position : 0 -23px;
|
||||
}
|
||||
|
||||
.btn-statusbar-iconDown {
|
||||
background-position : 0 -46px;
|
||||
}
|
||||
|
||||
.ace_dark .btn-statusbar-icon {
|
||||
background-position : -22px 0;
|
||||
}
|
||||
|
||||
.ace_dark .btn-statusbar-iconOver {
|
||||
background-position : -22px -23px;
|
||||
}
|
||||
|
||||
.ace_dark .btn-statusbar-iconDown {
|
||||
background-position : -22px -46px;
|
||||
}
|
||||
]]></a:style>
|
||||
|
||||
<a:presentation>
|
||||
<a:main
|
||||
caption = "text()"
|
||||
background = "."
|
||||
icon = ".">
|
||||
<div class="btn-statusbar-icon"> </div>
|
||||
</a:main>
|
||||
</a:presentation>
|
||||
</a:button>
|
||||
</a:skin>
|
|
@ -0,0 +1,485 @@
|
|||
define(function(require, exports, module) {
|
||||
main.consumes = [
|
||||
"Plugin", "c9", "settings", "ui", "menus", "ace",
|
||||
"ace.gotoline", "tabManager"
|
||||
];
|
||||
main.provides = ["ace.status"];
|
||||
return main;
|
||||
|
||||
function main(options, imports, register) {
|
||||
var c9 = imports.c9;
|
||||
var Plugin = imports.Plugin;
|
||||
var settings = imports.settings;
|
||||
var ui = imports.ui;
|
||||
var tabs = imports.tabManager;
|
||||
var menus = imports.menus;
|
||||
var gotoline = imports["ace.gotoline"];
|
||||
var aceHandle = imports.ace;
|
||||
|
||||
var skin = require("text!./skin.xml");
|
||||
var markup = require("text!./statusbar.xml");
|
||||
var menuAml = require("text!./menu.xml");
|
||||
|
||||
var aceWhitespace = require("ace/ext/whitespace");
|
||||
var lang = require("ace/lib/lang");
|
||||
|
||||
/***** Generic Load *****/
|
||||
|
||||
// Set up the generic handle
|
||||
var handle = new Plugin("Ajax.org", main.consumes);
|
||||
var statusbars = {};
|
||||
var menuItem, menu, menuTabs;
|
||||
|
||||
handle.on("load", function() {
|
||||
settings.on("read", function(e) {
|
||||
settings.setDefaults("user/ace/statusbar", [["show", "true"]]);
|
||||
}, handle);
|
||||
|
||||
menuItem = new ui.item({
|
||||
test: "1",
|
||||
type: "check",
|
||||
checked: "user/ace/statusbar/@show"
|
||||
// -> if you're looking for disabled, check the init function :-)
|
||||
// the moment that someone clicks this thing well call preinit
|
||||
// (its already called if the user has it checked on IDE load)
|
||||
});
|
||||
|
||||
menus.addItemByPath("View/Status Bar", menuItem, 600, handle);
|
||||
|
||||
aceHandle.on("create", function(e) {
|
||||
if (e.editor.type != "ace")
|
||||
return;
|
||||
|
||||
var editor = e.editor;
|
||||
var statusbar;
|
||||
|
||||
editor.once("draw", function() {
|
||||
statusbar = new Statusbar(editor);
|
||||
}, editor);
|
||||
editor.once("unload", function h2() {
|
||||
if (statusbar) statusbar.unload();
|
||||
}, editor);
|
||||
});
|
||||
|
||||
ui.insertMarkup(null, menuAml, handle);
|
||||
|
||||
menu = handle.getElement("menu");
|
||||
menuTabs = handle.getElement("menuTabs");
|
||||
|
||||
var currentSession;
|
||||
function setCurrentSession(menu) {
|
||||
var node = menu.opener;
|
||||
while (node && node.localName != "tab")
|
||||
node = node.parentNode;
|
||||
if (!node) return;
|
||||
|
||||
var tab = node.cloud9pane.getTab();
|
||||
currentSession = tab.document.getSession();
|
||||
}
|
||||
|
||||
function setOption(name, value) {
|
||||
if (currentSession) {
|
||||
if (currentSession.setOption)
|
||||
currentSession.setOption(name, value);
|
||||
currentSession.statusBar.update();
|
||||
}
|
||||
}
|
||||
|
||||
function getOption(name) {
|
||||
return currentSession && currentSession.session.getOption(name);
|
||||
}
|
||||
|
||||
// Checkboxes
|
||||
menu.on("afterrender", function(e) {
|
||||
var itmSbWrap = window.itmSbWrap;
|
||||
var itmSbWrapPM = window.itmSbWrapPM;
|
||||
|
||||
itmSbWrap.on("click", function() {
|
||||
setOption("wrap", itmSbWrap.checked
|
||||
? itmSbWrapPM.checked ? "printMargin" : true
|
||||
: false);
|
||||
});
|
||||
itmSbWrapPM.on("click", function() {
|
||||
setOption("wrap", itmSbWrapPM.checked
|
||||
? "printMargin"
|
||||
: itmSbWrap.checked);
|
||||
});
|
||||
|
||||
function update(e) {
|
||||
if (!e || e.value) {
|
||||
setCurrentSession(menu);
|
||||
|
||||
var wrap = getOption("wrap");
|
||||
itmSbWrap.setAttribute("checked", !ui.isFalse(wrap));
|
||||
itmSbWrapPM.setAttribute("checked", wrap == "printMargin");
|
||||
}
|
||||
}
|
||||
|
||||
menu.on("prop.visible", update);
|
||||
update();
|
||||
});
|
||||
|
||||
// Menu Tab functionality
|
||||
var handlers = [
|
||||
function() { setOption("useSoftTabs", this.checked); },
|
||||
function() {},
|
||||
function() { setOption("tabSize", 2); },
|
||||
function() { setOption("tabSize", 3); },
|
||||
function() { setOption("tabSize", 4); },
|
||||
function() { setOption("tabSize", 8); },
|
||||
function() {
|
||||
if (!currentSession) return;
|
||||
aceWhitespace.detectIndentation(currentSession.session);
|
||||
currentSession.setOption("guessTabSize", true);
|
||||
currentSession.statusBar.update();
|
||||
},
|
||||
// Tabs to Spaces
|
||||
function() {
|
||||
if (!currentSession) return;
|
||||
aceWhitespace.convertIndentation(currentSession.session, " ");
|
||||
currentSession.statusBar.update();
|
||||
},
|
||||
// Spaces to Tabs
|
||||
function() {
|
||||
if (!currentSession) return;
|
||||
aceWhitespace.convertIndentation(currentSession.session, "\t");
|
||||
currentSession.statusBar.update();
|
||||
}
|
||||
];
|
||||
|
||||
menuTabs.on("afterrender", function(e) {
|
||||
var items = menuTabs.selectNodes("a:item");
|
||||
items.forEach(function(node, idx) {
|
||||
node.on("click", handlers[idx]);
|
||||
});
|
||||
|
||||
var itmTabSize = window.itmTabSize;
|
||||
itmTabSize.on("afterchange", function() {
|
||||
setOption("tabSize", this.value);
|
||||
update();
|
||||
});
|
||||
|
||||
var lut = [0, 0, 2, 3, 4, 0, 0, 0, 5];
|
||||
|
||||
function update(e) {
|
||||
if (e && !e.value)
|
||||
return;
|
||||
|
||||
setCurrentSession(menuTabs);
|
||||
|
||||
items[0].setAttribute("checked", getOption("useSoftTabs"));
|
||||
|
||||
var tabSize = getOption("tabSize") || 1;
|
||||
items.forEach(function(node, idx) {
|
||||
node.setAttribute("selected", "false");
|
||||
});
|
||||
if (lut[tabSize])
|
||||
items[lut[tabSize]].setAttribute("selected", "true");
|
||||
itmTabSize.setAttribute("value", getOption("tabSize"));
|
||||
}
|
||||
|
||||
menuTabs.on("prop.visible", update);
|
||||
update();
|
||||
});
|
||||
});
|
||||
|
||||
handle.on("unload", function() {
|
||||
drawn = false;
|
||||
|
||||
Object.keys(statusbars).forEach(function(name) {
|
||||
statusbars[name].unload();
|
||||
});
|
||||
|
||||
statusbars = {};
|
||||
menuItem = null;
|
||||
menu = null;
|
||||
menuTabs = null;
|
||||
});
|
||||
|
||||
/***** Methods *****/
|
||||
|
||||
var drawn = false;
|
||||
function draw() {
|
||||
if (drawn) return;
|
||||
drawn = true;
|
||||
|
||||
// Import Skin
|
||||
ui.insertSkin({
|
||||
name: "c9statusbar",
|
||||
data: skin,
|
||||
"media-path": options.staticPrefix + "/images/",
|
||||
"icon-path": options.staticPrefix + "/icons/"
|
||||
}, handle);
|
||||
}
|
||||
|
||||
function getStatusbar(editor) {
|
||||
return statusbars[editor.name];
|
||||
}
|
||||
|
||||
function show() {
|
||||
settings.set("user/ace/statusbar/@show", "true");
|
||||
}
|
||||
|
||||
function hide() {
|
||||
settings.set("user/ace/statusbar/@show", "false");
|
||||
}
|
||||
|
||||
/***** Register and define API *****/
|
||||
|
||||
/**
|
||||
* Manages the status bar instances for ace.
|
||||
* @singleton
|
||||
**/
|
||||
handle.freezePublicAPI({
|
||||
/**
|
||||
* Show all the status bars.
|
||||
*/
|
||||
show: show,
|
||||
|
||||
/**
|
||||
* Hide all the status bars.
|
||||
*/
|
||||
hide: hide,
|
||||
|
||||
/**
|
||||
* Retrieve the status bar that belongs to an ace editor.
|
||||
* @param {Editor} ace The ace editor the status bar belongs to.
|
||||
* @return {ace.status.Statusbar}
|
||||
*/
|
||||
getStatusbar: getStatusbar,
|
||||
|
||||
/**
|
||||
* Inserts CSS for the statusbar.
|
||||
* @private
|
||||
*/
|
||||
draw: draw
|
||||
});
|
||||
|
||||
/***** Initialization *****/
|
||||
|
||||
function Statusbar(editor) {
|
||||
var plugin = new Plugin("Ajax.org", main.consumes);
|
||||
var emit = plugin.getEmitter();
|
||||
|
||||
var showRange;
|
||||
|
||||
var bar, lblSelection, lblStatus, lblRowCol, lblTabs, lblSyntax; // ui elements
|
||||
|
||||
statusbars[editor.name] = plugin;
|
||||
|
||||
var loaded = false;
|
||||
function load() {
|
||||
if (loaded) return false;
|
||||
loaded = true;
|
||||
|
||||
function updateBarVisible() {
|
||||
if (!settings.getBool("user/ace/statusbar/@show")) {
|
||||
bar && bar.hide();
|
||||
menuItem.enable();
|
||||
}
|
||||
else {
|
||||
draw();
|
||||
bar.show();
|
||||
menuItem.enable();
|
||||
}
|
||||
}
|
||||
|
||||
settings.on("user/ace/statusbar", updateBarVisible, plugin);
|
||||
|
||||
if (settings.getBool("user/ace/statusbar/@show"))
|
||||
draw();
|
||||
|
||||
editor.on("documentLoad", function(e) {
|
||||
var doc = e.doc;
|
||||
var session = doc.getSession();
|
||||
session.statusBar = plugin;
|
||||
session.session.on("changeMode", function(e) { statusUpdate.schedule(); });
|
||||
|
||||
if (!doc.hasValue())
|
||||
doc.once("setValue", function() {
|
||||
statusUpdate.schedule();
|
||||
}, doc);
|
||||
}, plugin);
|
||||
editor.on("documentActivate", function(e) { statusUpdate.schedule(); }, plugin);
|
||||
editor.on("documentUnload", function(e) {
|
||||
delete e.doc.getSession().statusBar;
|
||||
}, plugin);
|
||||
}
|
||||
|
||||
var drawn = false;
|
||||
function draw() {
|
||||
if (drawn) return;
|
||||
drawn = true;
|
||||
|
||||
handle.draw();
|
||||
|
||||
// Create UI elements
|
||||
var htmlNode = editor.aml; //ace.container.parentNode.host;
|
||||
ui.insertMarkup(htmlNode, markup, plugin);
|
||||
|
||||
function setTheme(e) {
|
||||
var theme = e.theme;
|
||||
if (!theme) return;
|
||||
|
||||
var cssClass = theme.cssClass;
|
||||
var isDark = theme.isDark;
|
||||
|
||||
var bg = ui.getStyleRule("." + cssClass, "backgroundColor");
|
||||
|
||||
bar.setAttribute("class", isDark ? "ace_dark" : "");
|
||||
if (bg) {
|
||||
// bg = bg.replace(/rgb\((.*)\)/, "rgba($1, 0.9)");
|
||||
bar.$ext.style.backgroundColor = bg;
|
||||
}
|
||||
}
|
||||
editor.on("themeChange", setTheme);
|
||||
|
||||
bar = plugin.getElement("bar");
|
||||
lblSelection = plugin.getElement("lblSelectionLength");
|
||||
lblStatus = plugin.getElement("lblEditorStatus");
|
||||
lblRowCol = plugin.getElement("lblRowCol");
|
||||
lblTabs = plugin.getElement("lblTabs");
|
||||
lblSyntax = plugin.getElement("lblSyntax");
|
||||
|
||||
// For editor search of submenus
|
||||
bar.editor = editor;
|
||||
|
||||
// Set sub menus
|
||||
var button = plugin.getElement("btnSbPrefs");
|
||||
button.setAttribute("submenu", menu);
|
||||
|
||||
lblTabs.setAttribute("submenu", menuTabs);
|
||||
|
||||
var mnuSyntax = menus.get("View/Syntax").menu;
|
||||
lblSyntax.setAttribute("submenu", mnuSyntax);
|
||||
lblSyntax.on("mousedown", function() {
|
||||
if (editor.activeDocument)
|
||||
tabs.focusTab(editor.activeDocument.tab);
|
||||
});
|
||||
|
||||
// Click behavior for the labels
|
||||
lblSelection.on("click", function() {
|
||||
showRange = !showRange;
|
||||
updateStatus();
|
||||
});
|
||||
|
||||
lblRowCol.on("click", function() {
|
||||
gotoline.gotoline(null, null, true);
|
||||
});
|
||||
|
||||
// Hook into ace
|
||||
var ace = editor.ace;
|
||||
if (!ace.$hasStatusBar) {
|
||||
// Throttle UI updates
|
||||
ace.on("changeSelection", function() { selStatusUpdate.schedule(); });
|
||||
ace.on("changeStatus", function() { statusUpdate.schedule(); });
|
||||
ace.on("keyboardActivity", function() { statusUpdate.schedule(); });
|
||||
ace.renderer.on("scrollbarVisibilityChanged", function(e, renderer) {
|
||||
bar.$ext.style.right = renderer.scrollBarV.getWidth() + 5 + "px";
|
||||
bar.$ext.style.bottom = renderer.scrollBarH.getHeight() + 3 + "px";
|
||||
})(null, ace.renderer);
|
||||
ace.$hasStatusBar = true;
|
||||
|
||||
var theme = editor.theme;
|
||||
setTheme({ theme: theme });
|
||||
}
|
||||
|
||||
// Update status information
|
||||
updateStatus();
|
||||
|
||||
emit("draw");
|
||||
}
|
||||
|
||||
var selStatusUpdate = lang.delayedCall(updateSelStatus, 10);
|
||||
var statusUpdate = lang.delayedCall(updateStatus, 10);
|
||||
|
||||
/***** Helper Functions *****/
|
||||
|
||||
function updateSelStatus() {
|
||||
var ace = editor.ace;
|
||||
if (!ace || !drawn || !ace.selection) return;
|
||||
|
||||
if (!ace.selection.isEmpty()) {
|
||||
var range = ace.getSelectionRange();
|
||||
var selLen;
|
||||
|
||||
if (showRange) {
|
||||
selLen = "(" +
|
||||
(range.end.row - range.start.row) + ":" +
|
||||
(range.end.column - range.start.column) + ")";
|
||||
}
|
||||
else {
|
||||
selLen = "(" + ace.session.getTextRange(range).length + " Bytes)";
|
||||
}
|
||||
|
||||
lblSelection.setAttribute("caption", selLen);
|
||||
}
|
||||
else {
|
||||
lblSelection.setAttribute("caption", "");
|
||||
}
|
||||
|
||||
var cursor = ace.selection.lead;
|
||||
var columnText = (cursor.row + 1) + ":" + (cursor.column + 1);
|
||||
if (ace.selection.rangeCount)
|
||||
columnText += " [" + ace.selection.rangeCount + "\u202f]";
|
||||
lblRowCol.setAttribute("caption", columnText);
|
||||
}
|
||||
|
||||
function updateStatus() {
|
||||
var ace = editor.ace;
|
||||
if (!ace || !drawn) return;
|
||||
|
||||
updateSelStatus();
|
||||
|
||||
lblTabs.setAttribute("caption",
|
||||
(ace.getOption("useSoftTabs") ? "Spaces" : "Tabs") + ": "
|
||||
+ ace.getOption("tabSize")); // "\\[" + + "\\]");
|
||||
|
||||
var status = ace.keyBinding.getStatusText() || "";
|
||||
if (ace.commands.recording)
|
||||
status = "REC";
|
||||
|
||||
lblStatus.setAttribute("caption", status);
|
||||
|
||||
var caption = aceHandle.getSyntaxCaption(ace.session.syntax);
|
||||
lblSyntax && lblSyntax.setAttribute("caption", caption);
|
||||
}
|
||||
|
||||
/***** Lifecycle *****/
|
||||
|
||||
plugin.on("load", function() {
|
||||
load();
|
||||
});
|
||||
plugin.on("enable", function() {
|
||||
|
||||
});
|
||||
plugin.on("disable", function() {
|
||||
|
||||
});
|
||||
plugin.on("unload", function() {
|
||||
loaded = false;
|
||||
});
|
||||
|
||||
/***** Register and define API *****/
|
||||
|
||||
/**
|
||||
* The status bar for ace editors.
|
||||
* @class ace.status.Statusbar
|
||||
**/
|
||||
plugin.freezePublicAPI({
|
||||
/**
|
||||
* Redraw the display of the statusbar
|
||||
*/
|
||||
update: updateStatus
|
||||
});
|
||||
|
||||
plugin.load(null, "acestatus");
|
||||
|
||||
return plugin;
|
||||
}
|
||||
|
||||
register(null, {
|
||||
"ace.status": handle
|
||||
});
|
||||
}
|
||||
});
|
|
@ -0,0 +1,44 @@
|
|||
<a:application xmlns:a="http://ajax.org/2005/aml">
|
||||
<a:bar id="bar"
|
||||
skin = "bar-status"
|
||||
skinset = "c9statusbar"
|
||||
class = ""
|
||||
zindex = "10000"
|
||||
style = "position:absolute;right:5px;bottom:3px;"
|
||||
height = "23">
|
||||
<a:label id="lblSelectionLength"
|
||||
caption = ""
|
||||
style = "cursor:pointer"
|
||||
margin = "1 10 4 3"
|
||||
visible = "{this.caption != ''}" />
|
||||
<a:label id="lblRowCol"
|
||||
class = "lbl_row_col"
|
||||
style = "cursor:pointer"
|
||||
margin = "1 10 4 3" />
|
||||
<a:label id="lblEditorStatus"
|
||||
caption = ""
|
||||
class = "nounderline"
|
||||
margin = "1 10 4 3"
|
||||
visible = "{this.caption != ''}" />
|
||||
<a:button id="lblSyntax"
|
||||
skin = "label"
|
||||
style = "cursor:pointer"
|
||||
caption = ""
|
||||
margin = "1 10 4 3"
|
||||
visible = "{this.caption != ''}"
|
||||
submenudir = "up" />
|
||||
<a:button id="lblTabs"
|
||||
skin = "label"
|
||||
style = "cursor:pointer"
|
||||
caption = ""
|
||||
margin = "1 10 4 3"
|
||||
visible = "{this.caption != ''}"
|
||||
submenudir = "up" />
|
||||
<a:button id="btnSbPrefs"
|
||||
skin = "btn-statusbar-icon"
|
||||
skinset = "c9statusbar"
|
||||
height = "23"
|
||||
icon = "pref-ico.png"
|
||||
submenudir = "up" />
|
||||
</a:bar>
|
||||
</a:application>
|
|
@ -0,0 +1,199 @@
|
|||
/*global describe it before after apf bar = */
|
||||
|
||||
"use client";
|
||||
|
||||
require(["lib/architect/architect", "lib/chai/chai"], function (architect, chai) {
|
||||
var expect = chai.expect;
|
||||
|
||||
expect.setupArchitectTest([
|
||||
{
|
||||
packagePath: "plugins/c9.core/c9",
|
||||
workspaceId: "ubuntu/ip-10-35-77-180",
|
||||
startdate: new Date(),
|
||||
debug: true,
|
||||
hosted: true,
|
||||
local: false,
|
||||
davPrefix: "/"
|
||||
},
|
||||
|
||||
"plugins/c9.core/ext",
|
||||
"plugins/c9.core/http-xhr",
|
||||
"plugins/c9.core/util",
|
||||
"plugins/c9.ide.ui/lib_apf",
|
||||
"plugins/c9.ide.ui/menus",
|
||||
{
|
||||
packagePath: "plugins/c9.core/settings",
|
||||
testing: true
|
||||
},
|
||||
"plugins/c9.core/api.js",
|
||||
{
|
||||
packagePath: "plugins/c9.ide.ui/ui",
|
||||
staticPrefix: "plugins/c9.ide.ui"
|
||||
},
|
||||
"plugins/c9.ide.editors/document",
|
||||
"plugins/c9.ide.editors/undomanager",
|
||||
{
|
||||
packagePath: "plugins/c9.ide.editors/editors",
|
||||
defaultEditor: "ace"
|
||||
},
|
||||
"plugins/c9.ide.editors/editor",
|
||||
"plugins/c9.ide.editors/tabmanager",
|
||||
"plugins/c9.ide.ui/focus",
|
||||
"plugins/c9.ide.editors/pane",
|
||||
"plugins/c9.ide.editors/tab",
|
||||
"plugins/c9.ide.ace/ace",
|
||||
{
|
||||
packagePath: "plugins/c9.ide.ace.statusbar/statusbar",
|
||||
staticPrefix: "plugins/c9.ide.layout.classic"
|
||||
},
|
||||
"plugins/c9.ide.keys/commands",
|
||||
"plugins/c9.fs/proc",
|
||||
"plugins/c9.vfs.client/vfs_client",
|
||||
"plugins/c9.vfs.client/endpoint",
|
||||
"plugins/c9.ide.auth/auth",
|
||||
"plugins/c9.fs/fs",
|
||||
|
||||
// Mock plugins
|
||||
{
|
||||
consumes: ["apf", "ui", "Plugin"],
|
||||
provides: [
|
||||
"commands", "menus", "layout", "watcher",
|
||||
"save", "preferences", "anims", "gotoline", "clipboard",
|
||||
"dialog.alert", "auth.bootstrap", "info", "ace.gotoline",
|
||||
"dialog.error"
|
||||
],
|
||||
setup: expect.html.mocked
|
||||
},
|
||||
{
|
||||
consumes: ["tabManager", "ace"],
|
||||
provides: [],
|
||||
setup: main
|
||||
}
|
||||
], architect);
|
||||
|
||||
function main(options, imports, register) {
|
||||
var tabs = imports.tabManager;
|
||||
var ace = imports.ace;
|
||||
|
||||
function getTabHtml(tab) {
|
||||
return tab.pane.aml.getPage("editor::" + tab.editorType).$ext;
|
||||
}
|
||||
|
||||
expect.html.setConstructor(function(tab) {
|
||||
if (typeof tab == "object")
|
||||
return tab.$ext;
|
||||
});
|
||||
|
||||
describe('statusbar', function() {
|
||||
before(function(done) {
|
||||
apf.config.setProperty("allow-select", false);
|
||||
apf.config.setProperty("allow-blur", false);
|
||||
|
||||
bar.$ext.style.background = "rgba(220, 220, 220, 0.93)";
|
||||
bar.$ext.style.position = "fixed";
|
||||
bar.$ext.style.left = "20px";
|
||||
bar.$ext.style.right = "20px";
|
||||
bar.$ext.style.bottom = "20px";
|
||||
bar.$ext.style.height = "33%";
|
||||
|
||||
document.body.style.marginBottom = "33%";
|
||||
|
||||
tabs.once("ready", function() {
|
||||
tabs.getPanes()[0].focus();
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
describe("open", function() {
|
||||
this.timeout(10000);
|
||||
|
||||
it('should open a pane with just an editor', function(done) {
|
||||
tabs.openFile("/file.txt", function(err, tab) {
|
||||
expect(!err);
|
||||
expect(tabs.getTabs()).length(1);
|
||||
// statusbar is added only after editor draw event
|
||||
// TODO make this api easier to use
|
||||
setTimeout(function() {
|
||||
var sb = tab.document.getSession().statusBar;
|
||||
var bar = sb.getElement("bar");
|
||||
expect.html(bar, "rowcol").text("1:1");
|
||||
|
||||
tab.document.editor.ace.selectAll();
|
||||
setTimeout(function() {
|
||||
expect.html(bar, "rowcol sel").text("2:1");
|
||||
expect.html(bar, "sel").text("23 Bytes");
|
||||
|
||||
done();
|
||||
}, 10);
|
||||
});
|
||||
});
|
||||
});
|
||||
it('should handle multiple documents in the same pane', function(done) {
|
||||
tabs.openFile("/listing.json", function(err, tab) {
|
||||
expect(!err);
|
||||
expect(tabs.getTabs()).length(2);
|
||||
|
||||
tab.activate();
|
||||
|
||||
setTimeout(function() {
|
||||
var sb = tab.document.getSession().statusBar;
|
||||
expect.html(sb.getElement("bar"), "caption").text("1:1");
|
||||
|
||||
done();
|
||||
}, 10);
|
||||
});
|
||||
});
|
||||
});
|
||||
describe("split(), pane.unload()", function() {
|
||||
it('should split a pane horizontally, making the existing pane the left one', function(done) {
|
||||
var pane = tabs.focussedTab.pane;
|
||||
var righttab = pane.hsplit(true);
|
||||
tabs.focussedTab.attachTo(righttab);
|
||||
|
||||
setTimeout(function() {
|
||||
expect.html(pane.aml, "pane").text("2:1");
|
||||
expect.html(righttab.aml, "righttab").text("1:1");
|
||||
|
||||
done();
|
||||
}, 100);
|
||||
});
|
||||
// it('should remove the left pane from a horizontal split', function(done) {
|
||||
// var pane = tabs.getPanes()[0];
|
||||
// var tab = tabs.getPanes()[1].getTab();
|
||||
// pane.unload();
|
||||
// expect(tabs.getPanes()).length(1);
|
||||
// expect(tabs.getTabs()).length(2);
|
||||
// tabs.focusTab(tab);
|
||||
// done();
|
||||
// });
|
||||
});
|
||||
// describe("Change Theme", function(){
|
||||
// this.timeout(10000);
|
||||
//
|
||||
// it('should change a theme', function(done) {
|
||||
// var editor = tabs.focussedTab.editor;
|
||||
// ace.on("themeInit", function setTheme(){
|
||||
// ace.off("theme.init", setTheme);
|
||||
// expect.html(getTabHtml(tabs.focussedTab).childNodes[1]).className("ace-monokai");
|
||||
// editor.setOption("theme", "ace/theme/textmate");
|
||||
// done();
|
||||
// });
|
||||
// editor.setOption("theme", "ace/theme/monokai");
|
||||
// });
|
||||
// });
|
||||
|
||||
// @todo test split api and menu
|
||||
|
||||
if (!onload.remain) {
|
||||
after(function(done) {
|
||||
tabs.unload();
|
||||
|
||||
document.body.style.marginBottom = "";
|
||||
done();
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
onload && onload();
|
||||
}
|
||||
});
|
|
@ -0,0 +1,21 @@
|
|||
|
||||
Copyright (c) 2015, Ajax.org B.V.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
|
@ -0,0 +1 @@
|
|||
# c9.ide.ace.stripws
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"name": "c9.ide.ace.stripws",
|
||||
"description": "The repository for c9.ide.ace.stripws, a Cloud9 core plugin",
|
||||
"version": "3.0.0",
|
||||
"author": "Cloud9",
|
||||
"contributors": [],
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "http://github.com/c9/c9.ide.ace.stripws.git"
|
||||
},
|
||||
"plugins": {
|
||||
"stripws": {}
|
||||
},
|
||||
"categories": [
|
||||
"core"
|
||||
],
|
||||
"licenses": [
|
||||
"MIT"
|
||||
]
|
||||
}
|
|
@ -0,0 +1,130 @@
|
|||
define(function(require, exports, module) {
|
||||
main.consumes = [
|
||||
"Plugin", "menus", "commands", "tabManager", "settings",
|
||||
"preferences", "save", "ui"
|
||||
];
|
||||
main.provides = ["ace.stripws"];
|
||||
return main;
|
||||
|
||||
function main(options, imports, register) {
|
||||
var Plugin = imports.Plugin;
|
||||
var menus = imports.menus;
|
||||
var commands = imports.commands;
|
||||
var tabs = imports.tabManager;
|
||||
var settings = imports.settings;
|
||||
var prefs = imports.preferences;
|
||||
var save = imports.save;
|
||||
var ui = imports.ui;
|
||||
|
||||
var whitespaceUtil = require("ace/ext/whitespace");
|
||||
|
||||
/***** Initialization *****/
|
||||
|
||||
var plugin = new Plugin("Ajax.org", main.consumes);
|
||||
// var emit = plugin.getEmitter();
|
||||
|
||||
var disabled = false;
|
||||
|
||||
var loaded = false;
|
||||
function load() {
|
||||
if (loaded) return false;
|
||||
loaded = true;
|
||||
|
||||
commands.addCommand({
|
||||
name: "stripws",
|
||||
hint: "strip whitespace at the end of each line",
|
||||
exec: function() {
|
||||
stripws();
|
||||
},
|
||||
isAvailable: function (editor) {
|
||||
return editor && tabs.focussedTab &&
|
||||
typeof tabs.focussedTab.path == "string";
|
||||
}
|
||||
}, plugin);
|
||||
|
||||
menus.addItemByPath("Tools/Strip Trailing Space", new ui.item({
|
||||
command: "stripws"
|
||||
}), 100, plugin);
|
||||
|
||||
menus.addItemByPath("Tools/~", new ui.divider(), 200, plugin);
|
||||
|
||||
save.on("beforeSave", function (e) {
|
||||
var shouldStrip = settings.getBool("project/general/@stripws");
|
||||
if (!shouldStrip || e.options.silentsave)
|
||||
return;
|
||||
var keepCursorPosition = settings.getBool("user/general/@stripwsKeepCursorPosition");
|
||||
stripws(e.document.tab, {
|
||||
keepCursorPosition: keepCursorPosition
|
||||
});
|
||||
}, plugin);
|
||||
|
||||
settings.on("read", function(e) {
|
||||
settings.setDefaults("project/general", [
|
||||
["stripws", !!settings.getBool("user/general/@stripws")]
|
||||
]);
|
||||
}, plugin);
|
||||
|
||||
prefs.add({
|
||||
"Project": {
|
||||
position: 150,
|
||||
"Code Editor (Ace)": {
|
||||
"On Save, Strip Whitespace": {
|
||||
type: "checkbox",
|
||||
position: 900,
|
||||
path: "project/general/@stripws"
|
||||
}
|
||||
}
|
||||
}
|
||||
}, plugin);
|
||||
}
|
||||
|
||||
/***** Methods *****/
|
||||
|
||||
function stripws(tab, options) {
|
||||
tab = tab || tabs.focussedTab;
|
||||
if (!tab || !tab.path || disabled)
|
||||
return;
|
||||
|
||||
options = options || {};
|
||||
options.trimEmpty = true;
|
||||
var session = tab.document.getSession().session;
|
||||
whitespaceUtil.trimTrailingSpace(session, options);
|
||||
session.$syncInformUndoManager();
|
||||
}
|
||||
|
||||
/***** Lifecycle *****/
|
||||
|
||||
plugin.on("load", function() {
|
||||
load();
|
||||
});
|
||||
plugin.on("enable", function() {
|
||||
disabled = false;
|
||||
});
|
||||
plugin.on("disable", function() {
|
||||
disabled = true;
|
||||
});
|
||||
plugin.on("unload", function() {
|
||||
loaded = false;
|
||||
});
|
||||
|
||||
/***** Register and define API *****/
|
||||
|
||||
/**
|
||||
* Strips trailing whitespace from lines just before a file is saved.
|
||||
* @singleton
|
||||
**/
|
||||
plugin.freezePublicAPI({
|
||||
/*
|
||||
* Strips whitespace at the end of each line in the given page
|
||||
* @param {Tab} tab The tab to strip the whitespace from
|
||||
* If not provided, the currently focussed tab will be used instead
|
||||
*/
|
||||
strpws: stripws
|
||||
});
|
||||
|
||||
register(null, {
|
||||
"ace.stripws": plugin
|
||||
});
|
||||
}
|
||||
});
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
|
||||
Copyright (c) 2015, Ajax.org B.V.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
|
@ -0,0 +1 @@
|
|||
# c9.ide.behaviors
|
|
@ -0,0 +1,291 @@
|
|||
define(function(require, exports, module) {
|
||||
main.consumes = ["Plugin", "ui", "dashboard"];
|
||||
main.provides = ["dashboardbehavior"];
|
||||
return main;
|
||||
|
||||
function main(options, imports, register) {
|
||||
var Plugin = imports.Plugin;
|
||||
var ui = imports.ui;
|
||||
var dashboard = imports.dashboard;
|
||||
|
||||
/***** Initialization *****/
|
||||
|
||||
var handle = new Plugin("Ajax.org", main.consumes);
|
||||
// var emit = handle.getEmitter();
|
||||
|
||||
var divSplit;
|
||||
|
||||
var loaded = false;
|
||||
function load() {
|
||||
if (loaded) return false;
|
||||
loaded = true;
|
||||
|
||||
// @todo how to make sure this happens only once?
|
||||
var css = require("text!./style.css");
|
||||
ui.insertCss(css, options.staticPrefix, handle);
|
||||
|
||||
dashboard.on("widgetCreate", function(e) {
|
||||
if (e.widget.dashboard.configurable)
|
||||
addInteraction(e.widget);
|
||||
}, handle);
|
||||
|
||||
dashboard.on("widgetAfterClose", function(e) {
|
||||
//@todo keep total tree small
|
||||
}, handle);
|
||||
}
|
||||
|
||||
function addInteraction(plugin) {
|
||||
var widget = plugin.aml;
|
||||
var titlebar = widget.oCaption && widget.oCaption.parentNode;
|
||||
if (!titlebar) return;
|
||||
var container = widget.$ext;
|
||||
|
||||
var offsetX, offsetY, startX, startY, dragWidth, dragHeight;
|
||||
var originalBox, box, started, start;
|
||||
var originalPosition, splitDirection, splitBox, originalSize;
|
||||
|
||||
function finish() {
|
||||
divSplit.style.display = "none";
|
||||
|
||||
apf.removeListener(document, "mousemove", mouseMoveSplit);
|
||||
apf.removeListener(document, "mouseup", mouseUpSplit);
|
||||
|
||||
container.style.zIndex =
|
||||
container.style.opacity =
|
||||
container.style.pointerEvents = "";
|
||||
|
||||
plugin.dashboard.widgets.forEach(function(widget) {
|
||||
widget.aml.$int.style.pointerEvents = "";
|
||||
});
|
||||
|
||||
widget.$dragging = false;
|
||||
}
|
||||
|
||||
titlebar.addEventListener("mousedown", function(e) {
|
||||
// APF stuff
|
||||
widget.$dragging = true;
|
||||
|
||||
// Calculate where on the titlebar was clicked
|
||||
var rect = container.getBoundingClientRect();
|
||||
startX = e.clientX;
|
||||
startY = e.clientY;
|
||||
offsetX = startX - rect.left;
|
||||
offsetY = startY - rect.top;
|
||||
|
||||
// Use mine
|
||||
started = false;
|
||||
|
||||
// Set current box
|
||||
box = widget.parentNode;
|
||||
|
||||
// Store original info
|
||||
originalBox = box;
|
||||
originalPosition = container.nextSibling;
|
||||
originalSize = [container.style.left, container.style.top,
|
||||
container.style.width, container.style.height,
|
||||
ui.getStyle(container, "margin")];
|
||||
dragWidth = container.offsetWidth;
|
||||
dragHeight = container.offsetHeight;
|
||||
|
||||
// Div that shows where to insert split
|
||||
if (!divSplit) {
|
||||
divSplit = document.createElement("div");
|
||||
divSplit.className = "split-area dark";
|
||||
document.body.appendChild(divSplit);
|
||||
}
|
||||
|
||||
// Fixate current position and width
|
||||
start = function() {
|
||||
rect = container.getBoundingClientRect();
|
||||
container.style.width = (dragWidth - ui.getWidthDiff(container)) + "px";
|
||||
container.style.height = (dragHeight - ui.getHeightDiff(container)) + "px";
|
||||
|
||||
// Prepare titlebar for dragging
|
||||
container.style.zIndex = 100000;
|
||||
container.style.opacity = 0.7;
|
||||
container.style.margin = 0;
|
||||
container.style.pointerEvents = "none";
|
||||
|
||||
plugin.dashboard.widgets.forEach(function(widget) {
|
||||
widget.aml.$int.style.pointerEvents = "none";
|
||||
});
|
||||
|
||||
document.body.appendChild(container);
|
||||
};
|
||||
|
||||
apf.addListener(document, "mousemove", mouseMoveSplit);
|
||||
apf.addListener(document, "mouseup", mouseUpSplit);
|
||||
});
|
||||
|
||||
function showSplitPosition(e) {
|
||||
var el = document.elementFromPoint(e.clientX, e.clientY);
|
||||
var aml = apf.findHost(el);
|
||||
|
||||
while (aml && aml.localName != "frame")
|
||||
aml = aml.parentNode;
|
||||
|
||||
// If aml is not the box we seek, lets abort
|
||||
if (!aml) {
|
||||
divSplit.style.display = "none";
|
||||
splitBox = null;
|
||||
splitDirection = null;
|
||||
return;
|
||||
}
|
||||
|
||||
// Find the rotated quarter that we're in
|
||||
var rect = aml.$ext.getBoundingClientRect();
|
||||
var left = (e.clientX - rect.left) / rect.width;
|
||||
var right = 1 - left;
|
||||
var top = (e.clientY - rect.top) / rect.height;
|
||||
var bottom = 1 - top;
|
||||
|
||||
// Cannot split box that would be removed later
|
||||
//@todo this needs to be a check against self
|
||||
// if (aml.getWidgets().length === 0) { // && aml == originalBox
|
||||
// divSplit.style.display = "none";
|
||||
// splitBox = null;
|
||||
// splitDirection = null;
|
||||
// return;
|
||||
// }
|
||||
splitBox = aml;
|
||||
|
||||
// Anchor to closes side
|
||||
var min = Math.min(left, top, right, bottom);
|
||||
|
||||
// Get titlebars height
|
||||
var bHeight = aml.oCaption.parentNode.offsetHeight;
|
||||
|
||||
// Left
|
||||
if (min == left) {
|
||||
divSplit.style.left = rect.left + "px";
|
||||
divSplit.style.top = (bHeight + rect.top) + "px";
|
||||
divSplit.style.width = (rect.width / 2) + "px";
|
||||
divSplit.style.height = (rect.height - bHeight) + "px";
|
||||
splitDirection = "w";
|
||||
}
|
||||
// Right
|
||||
else if (min == right) {
|
||||
divSplit.style.left = rect.left + (rect.width / 2) + "px";
|
||||
divSplit.style.top = (bHeight + rect.top) + "px";
|
||||
divSplit.style.width = (rect.width / 2) + "px";
|
||||
divSplit.style.height = (rect.height - bHeight) + "px";
|
||||
splitDirection = "e";
|
||||
}
|
||||
// Top
|
||||
else if (min == top) {
|
||||
divSplit.style.left = rect.left + "px";
|
||||
divSplit.style.top = (bHeight + rect.top) + "px";
|
||||
divSplit.style.width = rect.width + "px";
|
||||
divSplit.style.height = ((rect.height / 2) - bHeight) + "px";
|
||||
splitDirection = "n";
|
||||
}
|
||||
// Bottom
|
||||
else if (min == bottom) {
|
||||
divSplit.style.left = rect.left + "px";
|
||||
divSplit.style.top = (rect.top + (rect.height / 2)) + "px";
|
||||
divSplit.style.width = rect.width + "px";
|
||||
divSplit.style.height = (rect.height / 2) + "px";
|
||||
splitDirection = "s";
|
||||
}
|
||||
|
||||
divSplit.style.cursor = splitDirection + "-resize";
|
||||
divSplit.style.display = "block";
|
||||
}
|
||||
|
||||
function mouseMoveSplit(e) {
|
||||
if (!started) {
|
||||
if (Math.abs(startX - e.clientX) < 4
|
||||
&& Math.abs(startY - e.clientY) < 4)
|
||||
return;
|
||||
started = true;
|
||||
start();
|
||||
}
|
||||
|
||||
container.style.left = (e.clientX - offsetX) + "px";
|
||||
container.style.top = (e.clientY - offsetY) + "px";
|
||||
|
||||
return showSplitPosition(e);
|
||||
}
|
||||
|
||||
function mouseUpSplit(e) {
|
||||
apf.removeListener(document, "mousemove", mouseMoveSplit);
|
||||
apf.removeListener(document, "mouseup", mouseUpSplit);
|
||||
|
||||
if (!started) return finish();
|
||||
|
||||
container.style.left = (e.clientX - offsetX) + "px";
|
||||
container.style.top = (e.clientY - offsetY) + "px";
|
||||
|
||||
showSplitPosition(e);
|
||||
|
||||
if (splitBox) {
|
||||
if (splitDirection == "n")
|
||||
plugin.vsplit(splitBox, widget, false);
|
||||
else if (splitDirection == "s")
|
||||
plugin.vsplit(splitBox, widget, true);
|
||||
else if (splitDirection == "w")
|
||||
plugin.hsplit(splitBox, widget, false);
|
||||
else if (splitDirection == "e")
|
||||
plugin.hsplit(splitBox, widget, true);
|
||||
|
||||
var child = box.childNodes[0];
|
||||
if (child.localName == "splitter")
|
||||
child = box.childNodes[1];
|
||||
var pNode = box.parentNode;
|
||||
var next = box.nextSibling;
|
||||
pNode.removeChild(box);
|
||||
pNode.insertBefore(child, next);
|
||||
if (box.edge) {
|
||||
child.setAttribute("edge", box.edge);
|
||||
child.$ext.style.margin = "";
|
||||
}
|
||||
child.setAttribute("width", box.width || "");
|
||||
child.setAttribute("height", box.height || "");
|
||||
box.destroy(true, true);
|
||||
}
|
||||
else {
|
||||
originalBox.insertBefore(widget, originalPosition);
|
||||
|
||||
container.style.left = originalSize[0];
|
||||
container.style.top = originalSize[1];
|
||||
container.style.width = originalSize[2];
|
||||
container.style.height = originalSize[3];
|
||||
container.style.margin = originalSize[4];
|
||||
}
|
||||
|
||||
// Remove box if empty
|
||||
// if (originalBox && originalBox.getWidgets().length === 0)
|
||||
// originalBox.cloud9box.unload();
|
||||
|
||||
finish();
|
||||
}
|
||||
}
|
||||
|
||||
/***** Methods *****/
|
||||
|
||||
/***** Lifecycle *****/
|
||||
|
||||
handle.on("load", function() {
|
||||
load();
|
||||
});
|
||||
handle.on("enable", function() {
|
||||
|
||||
});
|
||||
handle.on("disable", function() {
|
||||
|
||||
});
|
||||
handle.on("unload", function() {
|
||||
loaded = false;
|
||||
});
|
||||
|
||||
/***** Register and define API *****/
|
||||
|
||||
/**
|
||||
**/
|
||||
handle.freezePublicAPI({});
|
||||
|
||||
register(null, {
|
||||
dashboardbehavior: handle
|
||||
});
|
||||
}
|
||||
});
|
Po Szerokość: | Wysokość: | Rozmiar: 3.1 KiB |
Po Szerokość: | Wysokość: | Rozmiar: 3.1 KiB |
Po Szerokość: | Wysokość: | Rozmiar: 658 B |
Po Szerokość: | Wysokość: | Rozmiar: 1.3 KiB |
Po Szerokość: | Wysokość: | Rozmiar: 647 B |
Po Szerokość: | Wysokość: | Rozmiar: 1.3 KiB |
|
@ -0,0 +1,22 @@
|
|||
{
|
||||
"name": "c9.ide.behaviors",
|
||||
"description": "The repository for c9.ide.behaviors, a Cloud9 core plugin",
|
||||
"version": "3.0.0",
|
||||
"author": "Cloud9",
|
||||
"contributors": [],
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "http://github.com/c9/c9.ide.behaviors.git"
|
||||
},
|
||||
"plugins": {
|
||||
"dashboard": {},
|
||||
"page": {},
|
||||
"tabs": {}
|
||||
},
|
||||
"categories": [
|
||||
"core"
|
||||
],
|
||||
"licenses": [
|
||||
"MIT"
|
||||
]
|
||||
}
|
|
@ -0,0 +1,704 @@
|
|||
define(function(require, exports, module) {
|
||||
main.consumes = [
|
||||
"Plugin", "ui", "tabManager", "ace", "anims", "settings", "preferences"
|
||||
];
|
||||
main.provides = ["tabinteraction"];
|
||||
return main;
|
||||
|
||||
function main(options, imports, register) {
|
||||
var Plugin = imports.Plugin;
|
||||
var ui = imports.ui;
|
||||
var anims = imports.anims;
|
||||
var settings = imports.settings;
|
||||
var prefs = imports.preferences;
|
||||
var tabs = imports.tabManager;
|
||||
var aceHandle = imports.ace;
|
||||
|
||||
var css = require("text!./style.css");
|
||||
|
||||
/***** Initialization *****/
|
||||
|
||||
var handle = new Plugin("Ajax.org", main.consumes);
|
||||
// var emit = handle.getEmitter();
|
||||
|
||||
var divSplit, divButton, plusMargin = 11;
|
||||
|
||||
var loaded = false;
|
||||
function load() {
|
||||
if (loaded) return false;
|
||||
loaded = true;
|
||||
|
||||
// Insert CSS
|
||||
ui.insertCss(css, options.staticPrefix, handle);
|
||||
|
||||
settings.on("read", function() {
|
||||
settings.setDefaults("user/tabs", [["autoclosepanes", true]]);
|
||||
}, handle);
|
||||
|
||||
tabs.on("tabCreate", function(e) {
|
||||
var tab = e.tab;
|
||||
|
||||
addInteraction(tab);
|
||||
|
||||
// Make sure that events are put on the button when the skin changes
|
||||
tab.aml.on("$skinchange", function() {
|
||||
addInteraction(tab);
|
||||
});
|
||||
}, handle);
|
||||
|
||||
tabs.on("tabDestroy", function(e) {
|
||||
if (e.tab.meta.$skipAnimation)
|
||||
setTimeout(function() { e.tab.pane.unload(); }, 0);
|
||||
}, handle);
|
||||
|
||||
tabs.on("tabAfterClose", function(e) {
|
||||
if (e.last && canTabBeRemoved(e.tab.pane, 1)
|
||||
&& settings.getBool("user/tabs/@autoclosepanes")) {
|
||||
e.tab.meta.$skipAnimation = true;
|
||||
}
|
||||
}, handle);
|
||||
|
||||
prefs.add({
|
||||
"General": {
|
||||
"User Interface": {
|
||||
position: 20,
|
||||
"Automatically Close Empty Panes": {
|
||||
type: "checkbox",
|
||||
path: "user/tabs/@autoclosepanes",
|
||||
position: 1150
|
||||
}
|
||||
}
|
||||
}
|
||||
}, handle);
|
||||
|
||||
ui.insertCss("* { }", false, handle);
|
||||
}
|
||||
|
||||
function canTabBeRemoved(pane, min) {
|
||||
if (!pane || pane.getTabs().length > (min || 0))
|
||||
return false;
|
||||
|
||||
var containers = tabs.containers;
|
||||
for (var i = 0; i < containers.length; i++) {
|
||||
if (ui.isChildOf(containers[i], pane.aml)) {
|
||||
return containers[i]
|
||||
.getElementsByTagNameNS(apf.ns.aml, "tab").length > 1;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function addInteraction(plugin) {
|
||||
var tab = plugin.aml;
|
||||
var button = tab.$button;
|
||||
if (!button) return;
|
||||
|
||||
var offsetX, offsetY, startX, startY, dragWidth;
|
||||
var mode, rightPadding, originalTab, btnPlus, pane;
|
||||
var started, tabWidth, leftPadding, leftPos, start, initMouse;
|
||||
var pages, clean, originalPosition, splitDirection, splitTab;
|
||||
|
||||
function setOrderMode(toTab, e) {
|
||||
if (toTab.isOrderCleaned === false) {
|
||||
return setTimeout(function() {
|
||||
setOrderMode(toTab, e);
|
||||
}, 10);
|
||||
}
|
||||
|
||||
mode = "order";
|
||||
clean && clean();
|
||||
|
||||
var lastPane = pane;
|
||||
|
||||
// Set new pane
|
||||
pane = toTab;
|
||||
|
||||
// Plus Button
|
||||
btnPlus = pane.$ext.querySelector(".plus_tab_button");
|
||||
|
||||
// Attach tab to pane
|
||||
if (e) {
|
||||
var curpage = pane.getPage();
|
||||
if (curpage) {
|
||||
var curbtn = curpage.$button;
|
||||
ui.setStyleClass(curbtn, "", ["curbtn"]);
|
||||
}
|
||||
|
||||
ui.setStyleClass(tab.$button, "curbtn");
|
||||
}
|
||||
|
||||
var container = pane.$buttons;
|
||||
var nodes = container.childNodes;
|
||||
var rect = container.getBoundingClientRect();
|
||||
var btn = (pane.getPage() || { $button: button }).$button;
|
||||
var diff = ui.getWidthDiff(btn);
|
||||
|
||||
var leftMargin = parseInt(ui.getStyle(btn, "marginLeft"), 10) || 0;
|
||||
var rightMargin = parseInt(ui.getStyle(btn, "marginRight"), 10) || 0;
|
||||
var maxWidth = parseInt(ui.getStyle(btn, "maxWidth"), 10) || 150;
|
||||
if (maxWidth > 500) maxWidth = 150;
|
||||
|
||||
leftPos = rect.left;
|
||||
pages = pane.getPages();
|
||||
leftPadding = parseInt(ui.getStyle(container, "paddingLeft"), 10) || 0;
|
||||
rightPadding = (parseInt(ui.getStyle(container, "paddingRight"), 10) || 0) + 24;
|
||||
|
||||
var addOne = pages.indexOf(tab) == -1;
|
||||
var maxTabWidth = Math.min(maxWidth + diff,
|
||||
((rect.width - leftPadding - rightPadding + rightMargin)
|
||||
/ (pages.length + (addOne ? 1 : 0))) - rightMargin);
|
||||
var newTabWidth = maxTabWidth - diff;
|
||||
|
||||
tabWidth = maxTabWidth + leftMargin + rightMargin;
|
||||
|
||||
// Get the positions info of the tab buttons
|
||||
var info = [];
|
||||
for (var i = nodes.length - 1; i >= 0; i--) {
|
||||
if ((btn = nodes[i]).nodeType != 1) continue;
|
||||
info.push([btn, btn.offsetLeft, btn.offsetTop, btn.offsetWidth]);
|
||||
}
|
||||
|
||||
// Append the button to the button container
|
||||
if (e || addOne) {
|
||||
pane.$buttons.appendChild(button);
|
||||
info.push([button, 0, button.offsetTop, dragWidth]);
|
||||
}
|
||||
|
||||
// Set the info
|
||||
var iter;
|
||||
while ((iter = info.pop())) {
|
||||
btn = iter[0];
|
||||
btn.style.left = (iter[1]) + "px";
|
||||
btn.style.top = (iter[2]) + "px";
|
||||
btn.style.width = (iter[3] - ui.getWidthDiff(btn)) + "px";
|
||||
btn.style.margin = 0;
|
||||
btn.style.position = "absolute";
|
||||
}
|
||||
|
||||
start = function() {
|
||||
// Remove from childNodes of old pane
|
||||
var lastIndex = pane.childNodes.indexOf(tab);
|
||||
pane.childNodes.remove(tab);
|
||||
};
|
||||
|
||||
if (started)
|
||||
start();
|
||||
|
||||
// Set initial position
|
||||
if (e || addOne)
|
||||
mouseMoveOrder(e, newTabWidth); //, lastPane == pane);
|
||||
|
||||
apf.addListener(document, "mousemove", mouseMoveOrder);
|
||||
apf.addListener(document, "mouseup", mouseUpOrder);
|
||||
|
||||
clean = function(change, toTab) {
|
||||
if (!toTab)
|
||||
toTab = pane;
|
||||
toTab.isOrderCleaned = false;
|
||||
|
||||
if (change !== false) {
|
||||
apf.removeListener(document, "mousemove", mouseMoveOrder);
|
||||
apf.removeListener(document, "mouseup", mouseUpOrder);
|
||||
}
|
||||
|
||||
if (change === true) {
|
||||
var maxTabWidth = Math.min(maxWidth + diff,
|
||||
((rect.width - leftPadding - rightPadding + rightMargin + 3)
|
||||
/ pane.getPages().length) - rightMargin);
|
||||
tabWidth = maxTabWidth + leftMargin + rightMargin;
|
||||
|
||||
var cb = clean.bind(this, false, toTab);
|
||||
return animateTabs(cb, null, maxTabWidth - diff);
|
||||
}
|
||||
|
||||
if (curbtn && curpage.parentNode && curpage == curpage.parentNode.getPage()) {
|
||||
ui.setStyleClass(curbtn, "curbtn");
|
||||
curbtn = null;
|
||||
}
|
||||
|
||||
for (var i = nodes.length - 1; i >= 0; i--) {
|
||||
if ((btn = nodes[i]).nodeType != 1) continue;
|
||||
btn.style.left =
|
||||
btn.style.top =
|
||||
btn.style.width =
|
||||
btn.style.margin =
|
||||
btn.style.position = "";
|
||||
}
|
||||
|
||||
toTab.isOrderCleaned = true;
|
||||
};
|
||||
}
|
||||
|
||||
function setSplitMode(e) {
|
||||
mode = "split";
|
||||
|
||||
// Div that shows where to insert split
|
||||
if (!divSplit) {
|
||||
divSplit = document.createElement("div");
|
||||
divSplit.className = "split-area";
|
||||
document.body.appendChild(divSplit);
|
||||
}
|
||||
|
||||
// Remove all pointer events from iframes
|
||||
var frames = document.getElementsByTagName("iframe");
|
||||
for (var i = 0; i < frames.length; i++)
|
||||
frames[i].style.pointerEvents = "none";
|
||||
|
||||
start = function() {
|
||||
// Fixate current position and width
|
||||
var rect = button.getBoundingClientRect();
|
||||
button.style.left = (rect.left) + "px";
|
||||
button.style.top = (rect.top) + "px";
|
||||
button.style.width = (dragWidth - ui.getWidthDiff(button)) + "px";
|
||||
button.style.position = "absolute";
|
||||
|
||||
// Attach tab to body
|
||||
if (!divButton) {
|
||||
divButton = document.createElement("div");
|
||||
document.body.appendChild(divButton);
|
||||
}
|
||||
|
||||
var theme = aceHandle.theme || {};
|
||||
divButton.className =
|
||||
(theme.isDark ? "dark " : "") + (theme.cssClass || "");
|
||||
divButton.appendChild(button);
|
||||
|
||||
// Remove from parent childNodes
|
||||
pane.childNodes.remove(tab);
|
||||
|
||||
ui.setStyleRule("*", "cursor", "default!important");
|
||||
};
|
||||
|
||||
apf.addListener(document, "mousemove", mouseMoveSplit);
|
||||
apf.addListener(document, "mouseup", mouseUpSplit);
|
||||
|
||||
if (started)
|
||||
start();
|
||||
|
||||
clean && clean(true);
|
||||
|
||||
clean = function() {
|
||||
button.style.left =
|
||||
button.style.top =
|
||||
button.style.width =
|
||||
button.style.margin =
|
||||
button.style.position = "";
|
||||
|
||||
divSplit.style.display = "none";
|
||||
|
||||
ui.setStyleRule("*", "cursor", "");
|
||||
|
||||
apf.removeListener(document, "mousemove", mouseMoveSplit);
|
||||
apf.removeListener(document, "mouseup", mouseUpSplit);
|
||||
};
|
||||
|
||||
if (started) {
|
||||
// Set initial position and detect immediate snap
|
||||
if (mouseMoveSplit(e) === false)
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
function finish() {
|
||||
if (!initMouse) {
|
||||
clean(null, null, true);
|
||||
|
||||
button.style.zIndex =
|
||||
button.style.pointerEvents = "";
|
||||
|
||||
// Return all pointer events to iframes
|
||||
var frames = document.getElementsByTagName("iframe");
|
||||
for (var i = 0; i < frames.length; i++)
|
||||
frames[i].style.pointerEvents = "";
|
||||
}
|
||||
|
||||
tab.$dragging = false;
|
||||
}
|
||||
|
||||
button.addEventListener("mousedown", function(e) {
|
||||
// Tab needs to support ordering
|
||||
if (!tab.parentNode.$order || tab.$dragging || e.button)
|
||||
return;
|
||||
|
||||
// APF stuff
|
||||
tab.$dragging = true;
|
||||
|
||||
startX = e.clientX;
|
||||
startY = e.clientY;
|
||||
|
||||
initMouse = function() {
|
||||
// Calculate where on the button was clicked
|
||||
var rect = button.getBoundingClientRect();
|
||||
offsetX = startX - rect.left;
|
||||
offsetY = startY - rect.top;
|
||||
|
||||
// Prepare button for dragging
|
||||
button.style.zIndex = 100000;
|
||||
button.style.pointerEvents = "none";
|
||||
|
||||
// Initialize with order mode
|
||||
setOrderMode(tab.parentNode, e);
|
||||
|
||||
initMouse = null;
|
||||
};
|
||||
|
||||
// Use mine
|
||||
started = false;
|
||||
|
||||
// Set current pane
|
||||
pane = plugin.pane.aml;
|
||||
|
||||
// Store original info
|
||||
originalTab = pane;
|
||||
originalPosition = button.nextSibling;
|
||||
dragWidth = button.offsetWidth;
|
||||
|
||||
apf.addListener(document, "mousemove", mouseMoveOrder);
|
||||
apf.addListener(document, "mouseup", mouseUpOrder);
|
||||
}, true);
|
||||
|
||||
function isNotSnapped(e, container) {
|
||||
if (!container)
|
||||
container = pane.$buttons;
|
||||
var rect = container.getBoundingClientRect();
|
||||
|
||||
var x = e.clientX;
|
||||
var y = e.clientY;
|
||||
var diff = 10;
|
||||
|
||||
return (
|
||||
x < rect.left - diff ||
|
||||
x > rect.left + rect.width + diff ||
|
||||
y < rect.top - 5 ||
|
||||
y > rect.top + rect.height + diff
|
||||
);
|
||||
}
|
||||
|
||||
function showOrderPosition(idx, toWidth, finalize, finish) {
|
||||
if (idx < 0) idx = 0;
|
||||
|
||||
var orderTab = (pages[idx - 1] == tab
|
||||
? pages[idx + 1]
|
||||
: pages[idx]) || null;
|
||||
|
||||
// Remove tab from childNodes
|
||||
pane.childNodes.remove(tab);
|
||||
|
||||
if (finalize) {
|
||||
// Get new pages with new order
|
||||
pages = pane.getPages();
|
||||
|
||||
// Reparent for real
|
||||
var insert = pages[idx] && pages[idx].cloud9tab;
|
||||
plugin.attachTo(pane.cloud9pane, insert, true);
|
||||
}
|
||||
else {
|
||||
// If we're already at this position do nothing
|
||||
if (orderTab == tab)
|
||||
return;
|
||||
|
||||
// Move tab to new position
|
||||
idx = pane.childNodes.indexOf(orderTab);
|
||||
if (idx > -1) pane.childNodes.splice(idx, 0, tab);
|
||||
else pane.childNodes.push(tab);
|
||||
|
||||
pane.$buttons.insertBefore(tab.$button,
|
||||
orderTab && orderTab.$button || btnPlus);
|
||||
}
|
||||
|
||||
// Patch + button which is changed to "" again
|
||||
// btnPlus.style.position = "absolute";
|
||||
// btnPlus.style.top = "6px";
|
||||
|
||||
animateTabs(finish, finalize, toWidth);
|
||||
}
|
||||
|
||||
function animateTabs(finish, includeTab, toWidth) {
|
||||
// Get new pages array (with new order)
|
||||
pages = pane.getPages();
|
||||
pages.push({ $button: btnPlus });
|
||||
|
||||
// Animate all pages to their right position
|
||||
var p, tweens = [], offset = 0;
|
||||
for (var i = 0, l = pages.length; i < l; i++) {
|
||||
p = pages[i];
|
||||
|
||||
// Ignore the tab we are dragging
|
||||
if (!includeTab && tab === p) {
|
||||
if (p.$button.parentNode == document.body)
|
||||
offset = 1;
|
||||
if (toWidth)
|
||||
p.$button.style.width = toWidth + "px";
|
||||
continue;
|
||||
}
|
||||
|
||||
var curLeft = p.$button.offsetLeft;
|
||||
var toLeft = leftPadding + ((i - offset) * tabWidth)
|
||||
+ (!p.localName ? plusMargin : 0);
|
||||
|
||||
if (toWidth || toLeft != curLeft) {
|
||||
var tween = {
|
||||
node: p.$button,
|
||||
duration: tab === p ? 0.20 : 0.15,
|
||||
timingFunction: tab === p
|
||||
? "cubic-bezier(.30, .08, 0, 1)"
|
||||
: "linear"
|
||||
};
|
||||
if (includeTab || tab !== p)
|
||||
tween.left = toLeft + "px";
|
||||
if (toWidth && p.localName)
|
||||
tween.width = toWidth + "px";
|
||||
|
||||
tweens.push(tween);
|
||||
}
|
||||
}
|
||||
|
||||
anims.animateMultiple(tweens, function() {
|
||||
finish && finish();
|
||||
});
|
||||
}
|
||||
|
||||
function mouseMoveOrder(e, toWidth, finalize) {
|
||||
if (!e) e = event;
|
||||
|
||||
if (!started) {
|
||||
if (Math.abs(startX - e.clientX) < 4
|
||||
&& Math.abs(startY - e.clientY) < 4)
|
||||
return;
|
||||
started = true;
|
||||
initMouse();
|
||||
start();
|
||||
}
|
||||
|
||||
if (isNotSnapped(e))
|
||||
return setSplitMode(e);
|
||||
|
||||
button.style.left = (e.clientX - leftPos - offsetX) + "px";
|
||||
|
||||
var x = button.offsetLeft - leftPadding + (tabWidth / 2);
|
||||
var idx = Math.floor(x / tabWidth);
|
||||
|
||||
showOrderPosition(idx, toWidth, finalize);
|
||||
}
|
||||
|
||||
function mouseUpOrder(e) {
|
||||
apf.removeListener(document, "mousemove", mouseMoveOrder);
|
||||
apf.removeListener(document, "mouseup", mouseUpOrder);
|
||||
|
||||
if (!started)
|
||||
return finish();
|
||||
|
||||
button.style.left = (e.clientX - leftPos - offsetX) + "px";
|
||||
|
||||
var x = button.offsetLeft - leftPadding + (tabWidth / 2);
|
||||
var idx = Math.floor(x / tabWidth);
|
||||
|
||||
// Show final order
|
||||
var orderTab = showOrderPosition(idx, null, true, finish);
|
||||
|
||||
// Activate tab
|
||||
plugin.activate();
|
||||
|
||||
// Remove pane if empty
|
||||
if (originalTab && canTabBeRemoved(originalTab.cloud9pane)
|
||||
&& settings.getBool("user/tabs/@autoclosepanes"))
|
||||
originalTab.cloud9pane.unload();
|
||||
}
|
||||
|
||||
function showSplitPosition(e) {
|
||||
var el = document.elementFromPoint(e.clientX, e.clientY);
|
||||
var aml = apf.findHost(el);
|
||||
|
||||
while (aml && aml.localName != "tab")
|
||||
aml = aml.parentNode;
|
||||
|
||||
// If aml is not the pane we seek, lets abort
|
||||
if (!aml) {
|
||||
divSplit.style.display = "none";
|
||||
splitTab = null;
|
||||
splitDirection = null;
|
||||
return;
|
||||
}
|
||||
|
||||
var tab = (aml.getPage() || {}).cloud9tab;
|
||||
var dark = !tab || tab.classList.names.indexOf("dark") > -1;
|
||||
divSplit.className = "split-area" + (dark ? " dark" : "");
|
||||
|
||||
// Find the rotated quarter that we're in
|
||||
var rect = aml.$ext.getBoundingClientRect();
|
||||
var left = (e.clientX - rect.left) / rect.width;
|
||||
var right = 1 - left;
|
||||
var top = (e.clientY - rect.top) / rect.height;
|
||||
var bottom = 1 - top;
|
||||
|
||||
// Check whether we're going to dock
|
||||
if (!isNotSnapped(e, aml.$buttons)) {
|
||||
setOrderMode(aml, e);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Cannot split pane that would be removed later
|
||||
if (aml.getPages().length === 0) { // && aml == originalTab
|
||||
divSplit.style.display = "none";
|
||||
splitTab = null;
|
||||
splitDirection = null;
|
||||
return;
|
||||
}
|
||||
splitTab = aml;
|
||||
|
||||
// Anchor to closes side
|
||||
var min = Math.min(left, top, right, bottom);
|
||||
|
||||
// Get buttons height
|
||||
var bHeight = pane.$buttons.offsetHeight - 1;
|
||||
|
||||
// Left
|
||||
if (min == left) {
|
||||
divSplit.style.left = rect.left + "px";
|
||||
divSplit.style.top = (bHeight + rect.top) + "px";
|
||||
divSplit.style.width = (rect.width / 2) + "px";
|
||||
divSplit.style.height = (rect.height - bHeight) + "px";
|
||||
splitDirection = "w";
|
||||
}
|
||||
// Right
|
||||
else if (min == right) {
|
||||
divSplit.style.left = rect.left + (rect.width / 2) + "px";
|
||||
divSplit.style.top = (bHeight + rect.top) + "px";
|
||||
divSplit.style.width = (rect.width / 2) + "px";
|
||||
divSplit.style.height = (rect.height - bHeight) + "px";
|
||||
splitDirection = "e";
|
||||
}
|
||||
// Top
|
||||
else if (min == top) {
|
||||
divSplit.style.left = rect.left + "px";
|
||||
divSplit.style.top = (bHeight + rect.top) + "px";
|
||||
divSplit.style.width = rect.width + "px";
|
||||
divSplit.style.height = ((rect.height / 2) - bHeight) + "px";
|
||||
splitDirection = "n";
|
||||
}
|
||||
// Bottom
|
||||
else if (min == bottom) {
|
||||
divSplit.style.left = rect.left + "px";
|
||||
divSplit.style.top = (rect.top + (rect.height / 2)) + "px";
|
||||
divSplit.style.width = rect.width + "px";
|
||||
divSplit.style.height = (rect.height / 2) + "px";
|
||||
splitDirection = "s";
|
||||
}
|
||||
|
||||
divSplit.style.cursor = splitDirection + "-resize";
|
||||
divSplit.style.display = "block";
|
||||
}
|
||||
|
||||
function mouseMoveSplit(e) {
|
||||
if (!started) {
|
||||
if (Math.abs(startX - e.clientX) < 4
|
||||
&& Math.abs(startY - e.clientY) < 4)
|
||||
return;
|
||||
started = true;
|
||||
initMouse();
|
||||
start();
|
||||
}
|
||||
|
||||
button.style.left = (e.clientX - offsetX) + "px";
|
||||
button.style.top = (e.clientY - offsetY) + "px";
|
||||
|
||||
return showSplitPosition(e);
|
||||
}
|
||||
|
||||
// var waiting;
|
||||
// function schedule(e){
|
||||
// if (waiting) {
|
||||
// waiting = e;
|
||||
// return;
|
||||
// }
|
||||
|
||||
// waiting = e;
|
||||
// setTimeout(function(){
|
||||
// showSplitPosition(waiting);
|
||||
// waiting = false;
|
||||
// }, 1000);
|
||||
// }
|
||||
|
||||
function mouseUpSplit(e) {
|
||||
button.style.left = (e.clientX - offsetX) + "px";
|
||||
button.style.top = (e.clientY - offsetY) + "px";
|
||||
|
||||
apf.removeListener(document, "mousemove", mouseMoveSplit);
|
||||
apf.removeListener(document, "mouseup", mouseUpSplit);
|
||||
|
||||
showSplitPosition(e);
|
||||
|
||||
if (splitTab) {
|
||||
splitTab = splitTab.cloud9pane;
|
||||
var newTab;
|
||||
if (splitDirection == "n")
|
||||
newTab = splitTab.vsplit();
|
||||
else if (splitDirection == "s")
|
||||
newTab = splitTab.vsplit(true);
|
||||
else if (splitDirection == "w")
|
||||
newTab = splitTab.hsplit();
|
||||
else if (splitDirection == "e")
|
||||
newTab = splitTab.hsplit(true);
|
||||
|
||||
var oldTab = pane;
|
||||
plugin.attachTo(newTab, null, true);
|
||||
pane = newTab.aml;
|
||||
|
||||
if (oldTab && canTabBeRemoved(oldTab.cloud9pane)) {
|
||||
oldTab.cloud9pane.unload();
|
||||
originalTab = null;
|
||||
}
|
||||
}
|
||||
else {
|
||||
tab.parentNode.$buttons.insertBefore(button,
|
||||
originalPosition);
|
||||
|
||||
if (originalTab == tab.parentNode) {
|
||||
var idx = tab.parentNode.childNodes.indexOf(tab.nextSibling);
|
||||
if (idx == -1)
|
||||
tab.parentNode.childNodes.push(tab);
|
||||
else
|
||||
tab.parentNode.childNodes.splice(idx, 0, tab);
|
||||
}
|
||||
}
|
||||
|
||||
// Remove pane if empty
|
||||
if (originalTab && originalTab != tab.parentNode
|
||||
&& canTabBeRemoved(originalTab.cloud9pane))
|
||||
originalTab.cloud9pane.unload();
|
||||
|
||||
finish();
|
||||
}
|
||||
}
|
||||
|
||||
/***** Methods *****/
|
||||
|
||||
/***** Lifecycle *****/
|
||||
|
||||
handle.on("load", function() {
|
||||
load();
|
||||
});
|
||||
handle.on("enable", function() {
|
||||
|
||||
});
|
||||
handle.on("disable", function() {
|
||||
|
||||
});
|
||||
handle.on("unload", function() {
|
||||
loaded = false;
|
||||
});
|
||||
|
||||
/***** Register and define API *****/
|
||||
|
||||
/**
|
||||
**/
|
||||
handle.freezePublicAPI({
|
||||
get plusMargin() { return plusMargin; },
|
||||
set plusMargin(v) { plusMargin = v; }
|
||||
});
|
||||
|
||||
register(null, {
|
||||
tabinteraction: handle
|
||||
});
|
||||
}
|
||||
});
|
|
@ -0,0 +1,50 @@
|
|||
.splits{
|
||||
padding : 6px 20px 3px 22px !important;
|
||||
background: transparent;
|
||||
}
|
||||
.splits span{
|
||||
.image-2x(@pane-menu-splits-image, @pane-menu-splits-image-width, @pane-menu-splits-image-height);
|
||||
background-position: 0 0;
|
||||
width : 23px;
|
||||
height : 18px;
|
||||
display : inline-block;
|
||||
margin : 0 10px 0 0;
|
||||
cursor : pointer;
|
||||
}
|
||||
.splits span.nosplit { background-position: 0 0; }
|
||||
.splits span.nosplit:hover { background-position: 0 -18px; }
|
||||
.splits span.nosplit:active { background-position: 0 -36px; }
|
||||
.splits span.twovsplit { background-position: -23px 0; }
|
||||
.splits span.twovsplit:hover { background-position: -23px -18px; }
|
||||
.splits span.twovsplit:active { background-position: -23px -36px; }
|
||||
.splits span.twohsplit { background-position: -46px 0; }
|
||||
.splits span.twohsplit:hover { background-position: -46px -18px; }
|
||||
.splits span.twohsplit:active { background-position: -46px -36px; }
|
||||
.splits span.foursplit { background-position: -69px 0; }
|
||||
.splits span.foursplit:hover { background-position: -69px -18px; }
|
||||
.splits span.foursplit:active { background-position: -69px -36px; }
|
||||
.splits span.threeleft { background-position: -92px 0; }
|
||||
.splits span.threeleft:hover { background-position: -92px -18px; }
|
||||
.splits span.threeleft:active { background-position: -92px -36px; }
|
||||
.splits span.threeright { background-position: -115px 0; }
|
||||
.splits span.threeright:hover { background-position: -115px -18px; }
|
||||
.splits span.threeright:active { background-position: -115px -36px; }
|
||||
|
||||
.split-area {
|
||||
position: absolute;
|
||||
background: rgba(0,0,0,0.05);
|
||||
border: 1px solid rgba(0, 0, 0, 0.2);
|
||||
/*box-shadow: 0 0 200px rgba(0, 0, 0,0.2) inset;*/
|
||||
display: none;
|
||||
z-index: 100000;
|
||||
pointer-events: none;
|
||||
.box-sizing(border-box);
|
||||
cursor : default;
|
||||
}
|
||||
|
||||
.dark.split-area {
|
||||
background: rgba(255,255,255,0.05);
|
||||
border: 1px solid rgba(255,255,255, 0.3);
|
||||
box-shadow: 0 0 200px rgba(255,255,255,0.15) inset;
|
||||
outline: 1px solid black;
|
||||
}
|
|
@ -0,0 +1,80 @@
|
|||
define(function(require, exports, module) {
|
||||
main.consumes = ["Plugin", "tabManager", "preferences", "settings"];
|
||||
main.provides = ["zen"];
|
||||
return main;
|
||||
|
||||
function main(options, imports, register) {
|
||||
var Plugin = imports.Plugin;
|
||||
var tabManager = imports.tabManager;
|
||||
var prefs = imports.preferences;
|
||||
var settings = imports.settings;
|
||||
|
||||
/***** Initialization *****/
|
||||
var plugin = new Plugin("Ajax.org", main.consumes);
|
||||
|
||||
function load() {
|
||||
prefs.add({
|
||||
"Project": {
|
||||
position: 10,
|
||||
"Tabs and IDE Layout": {
|
||||
position: 1500,
|
||||
"Limit number of open tabs per pane (ZenTabs)": {
|
||||
type: "checked-spinner",
|
||||
checkboxPath: "user/zentabs/@useZenTabs",
|
||||
path: "user/zentabs/@tabLimit",
|
||||
min: 0,
|
||||
max: 1000,
|
||||
position: 100
|
||||
}
|
||||
}
|
||||
}
|
||||
}, plugin);
|
||||
|
||||
tabManager.on("open", runZenTabs, plugin);
|
||||
}
|
||||
|
||||
/***** Methods *****/
|
||||
function runZenTabs(event) {
|
||||
var tab = event.tab;
|
||||
|
||||
var zentabsEnabled = settings.getBool("user/zentabs/@useZenTabs");
|
||||
if (!zentabsEnabled) return;
|
||||
|
||||
var tabLimit = settings.get("user/zentabs/@tabLimit");
|
||||
var openTabs = tab.pane.meta.accessList;
|
||||
var tabsToRemove = openTabs.length - tabLimit;
|
||||
|
||||
// Try to close excess tabs, unless it's the one just opened
|
||||
for (var i = openTabs.length - 1; i > 0; i--) {
|
||||
if (tabsToRemove < 1) {
|
||||
tab.pane.aml.$waitForMouseOut = false;
|
||||
tab.pane.aml.$scaleinit(null, "sync");
|
||||
return;
|
||||
}
|
||||
else if (!openTabs[i].document.changed) {
|
||||
openTabs[i].close();
|
||||
tabsToRemove--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/***** Lifecycle *****/
|
||||
|
||||
plugin.on("load", function() {
|
||||
load();
|
||||
});
|
||||
plugin.on("unload", function() {
|
||||
|
||||
});
|
||||
|
||||
/***** Register and define API *****/
|
||||
|
||||
plugin.freezePublicAPI({
|
||||
|
||||
});
|
||||
|
||||
register(null, {
|
||||
"zen": plugin
|
||||
});
|
||||
}
|
||||
});
|
|
@ -0,0 +1,21 @@
|
|||
|
||||
Copyright (c) 2015, Ajax.org B.V.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
|
@ -0,0 +1 @@
|
|||
# c9.ide.closeconfirmation
|
|
@ -0,0 +1,104 @@
|
|||
define(function(require, exports, module) {
|
||||
"use strict";
|
||||
|
||||
main.consumes = [
|
||||
"Plugin", "tabManager", "settings", "preferences", "auth"
|
||||
];
|
||||
main.provides = ["closeconfirmation"];
|
||||
return main;
|
||||
|
||||
function main(options, imports, register) {
|
||||
var Plugin = imports.Plugin;
|
||||
var tabs = imports.tabManager;
|
||||
var settings = imports.settings;
|
||||
var prefs = imports.preferences;
|
||||
var auth = imports.auth;
|
||||
var ideProviderName = options.ideProviderName || "Cloud9";
|
||||
|
||||
/***** Initialization *****/
|
||||
|
||||
var plugin = new Plugin("Ajax.org", main.consumes);
|
||||
var emit = plugin.getEmitter();
|
||||
|
||||
var defaultValue = options.defaultValue;
|
||||
|
||||
var loaded = false;
|
||||
function load(callback) {
|
||||
if (loaded) return false;
|
||||
loaded = true;
|
||||
|
||||
// when unloading the window
|
||||
window.onbeforeunload = onBeforeUnloadHandler;
|
||||
|
||||
settings.on("read", function() {
|
||||
settings.setDefaults("user/general", [
|
||||
["confirmexit", defaultValue]
|
||||
]);
|
||||
}, plugin);
|
||||
|
||||
prefs.add({
|
||||
"General": {
|
||||
"General": {
|
||||
"Warn Before Exiting": {
|
||||
type: "checkbox",
|
||||
position: 8000,
|
||||
path: "user/general/@confirmexit"
|
||||
}
|
||||
}
|
||||
}
|
||||
}, plugin);
|
||||
}
|
||||
|
||||
function unload() {
|
||||
if (window.onbeforeunload === onBeforeUnloadHandler)
|
||||
window.onbeforeunload = null;
|
||||
}
|
||||
|
||||
/***** Methods *****/
|
||||
|
||||
function onBeforeUnloadHandler() {
|
||||
var changed = tabs.getTabs().some(function(tab) {
|
||||
return tab.document.value && tab.document.changed;
|
||||
});
|
||||
|
||||
emit("exit", { changed: changed });
|
||||
|
||||
// see what's in the settings
|
||||
var confirmExit = settings.getBool("user/general/@confirmexit") && auth.loggedIn;
|
||||
if (confirmExit) {
|
||||
if (changed)
|
||||
return "You have unsaved changes. Your changes will be lost if you don't save them";
|
||||
else
|
||||
return "You're about to leave " + ideProviderName + ".";
|
||||
}
|
||||
}
|
||||
|
||||
/***** Lifecycle *****/
|
||||
|
||||
plugin.on("load", function() {
|
||||
load();
|
||||
});
|
||||
plugin.on("enable", function() {
|
||||
|
||||
});
|
||||
plugin.on("disable", function() {
|
||||
|
||||
});
|
||||
plugin.on("unload", function() {
|
||||
unload();
|
||||
loaded = false;
|
||||
});
|
||||
|
||||
/***** Register and define API *****/
|
||||
/**
|
||||
* Shows a 'close confirmation' popup when closing the IDE
|
||||
* @singleton
|
||||
*/
|
||||
plugin.freezePublicAPI({
|
||||
});
|
||||
|
||||
register(null, {
|
||||
closeconfirmation: plugin
|
||||
});
|
||||
}
|
||||
});
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"name": "c9.ide.closeconfirmation",
|
||||
"description": "The repository for c9.ide.closeconfirmation, a Cloud9 core plugin",
|
||||
"version": "3.0.0",
|
||||
"author": "Cloud9",
|
||||
"contributors": [],
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "http://github.com/c9/c9.ide.closeconfirmation.git"
|
||||
},
|
||||
"plugins": {
|
||||
"closeconfirmation": {}
|
||||
},
|
||||
"categories": [
|
||||
"core"
|
||||
],
|
||||
"licenses": [
|
||||
"MIT"
|
||||
]
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
Copyright (C) 2015, Ajax.org B.V.
|
||||
|
||||
This software is available under the Cloud9 SDK License, available from https://github.com/c9/core/blob/master/LICENSE
|
|
@ -0,0 +1 @@
|
|||
# c9.ide.collab
|
|
@ -0,0 +1,458 @@
|
|||
define(function(require, module, exports) {
|
||||
main.consumes = ["Plugin", "ace", "settings", "collab.workspace", "collab.util", "ui", "menus"];
|
||||
main.provides = ["AuthorLayer"];
|
||||
return main;
|
||||
|
||||
function main(options, imports, register) {
|
||||
var Plugin = imports.Plugin;
|
||||
var settings = imports.settings;
|
||||
var ui = imports.ui;
|
||||
var ace = imports.ace;
|
||||
var util = imports["collab.util"];
|
||||
var menus = imports.menus;
|
||||
var workspace = imports["collab.workspace"];
|
||||
|
||||
var dom = require("ace/lib/dom");
|
||||
var event = require("ace/lib/event");
|
||||
var Range = require("ace/range").Range;
|
||||
|
||||
var AuthorAttributes = require("./ot/author_attributes")();
|
||||
|
||||
var showAuthorInfo = true;
|
||||
var showAuthorInfoKey = "user/collab/@show-author-info";
|
||||
|
||||
settings.on("user/collab", function () {
|
||||
showAuthorInfo = settings.getBool(showAuthorInfoKey);
|
||||
}, workspace);
|
||||
|
||||
ace.on("create", function(e) {
|
||||
showAuthorInfo = settings.getBool(showAuthorInfoKey);
|
||||
initGutterLayer(e.editor.ace);
|
||||
}, workspace);
|
||||
|
||||
menus.addItemByPath("context/ace-gutter/Gutter Options/Show Authorship Info", new ui.item({
|
||||
type: "check",
|
||||
checked: showAuthorInfoKey
|
||||
}), 1000, workspace);
|
||||
|
||||
function AuthorLayer(session) {
|
||||
var plugin = new Plugin("Ajax.org", main.consumes);
|
||||
// var emit = plugin.getEmitter();
|
||||
var marker = session.addDynamicMarker({ update: drawAuthInfos }, false);
|
||||
|
||||
function refresh() {
|
||||
var doc = session.collabDoc.original;
|
||||
var ace = doc.editor && doc.editor.ace;
|
||||
var aceSession = ace && ace.session;
|
||||
if (aceSession !== session)
|
||||
return;
|
||||
|
||||
session._emit("changeBackMarker");
|
||||
var gutter = ace.renderer.$gutterLayer;
|
||||
gutter.update = updateGutter;
|
||||
gutter.update(ace.renderer.layerConfig);
|
||||
}
|
||||
|
||||
function drawAuthInfos(html, markerLayer, session, config) {
|
||||
if (!showAuthorInfo || !util.isRealCollab(workspace))
|
||||
return;
|
||||
|
||||
var doc = session.collabDoc;
|
||||
var editorDoc = session.doc;
|
||||
var colorPool = workspace.colorPool;
|
||||
var reversedAuthorPool = workspace.reversedAuthorPool;
|
||||
|
||||
var firstRow = config.firstRow;
|
||||
var lastRow = config.lastRow;
|
||||
|
||||
var range = new Range(firstRow, 0, lastRow, editorDoc.getLine(lastRow).length);
|
||||
|
||||
var cache = createAuthorKeyCache(editorDoc, doc.authAttribs, range);
|
||||
var authKeyCache = cache.authorKeys;
|
||||
var rowScores = cache.rowScores;
|
||||
|
||||
var fold = session.getNextFoldLine(firstRow);
|
||||
var foldStart = fold ? fold.start.row : Infinity;
|
||||
|
||||
for (var i = firstRow; i < lastRow; i++) {
|
||||
if (i > foldStart) {
|
||||
i = fold.end.row + 1;
|
||||
fold = session.getNextFoldLine(i, fold);
|
||||
foldStart = fold ? fold.start.row : Infinity;
|
||||
}
|
||||
if (i > lastRow)
|
||||
break;
|
||||
|
||||
if (!authKeyCache[i] || !rowScores[i])
|
||||
continue;
|
||||
|
||||
var rowScore = rowScores[i];
|
||||
for (var authVal in rowScore) {
|
||||
if (authVal == authKeyCache[i])
|
||||
continue;
|
||||
var edits = rowScore[authVal].edits;
|
||||
for (var j = 0; j < edits.length; j++) {
|
||||
var edit = edits[j];
|
||||
var uid = reversedAuthorPool[authVal];
|
||||
var bgColor = colorPool[uid];
|
||||
var extraStyle = "position:absolute;border-bottom:solid 2px " + util.formatColor(bgColor) + ";z-index: 2000";
|
||||
var startPos = session.documentToScreenPosition(edit.pos);
|
||||
markerLayer.drawSingleLineMarker(html,
|
||||
new Range(startPos.row, startPos.column, startPos.row, startPos.column + edit.length),
|
||||
"", config, 0, extraStyle);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function updateGutter(config) {
|
||||
var session = this.session;
|
||||
var firstRow = config.firstRow;
|
||||
var lastRow = Math.min(config.lastRow + config.gutterOffset, // needed to compensate for hor scollbar
|
||||
session.getLength() - 1);
|
||||
var fold = session.getNextFoldLine(firstRow);
|
||||
var foldStart = fold ? fold.start.row : Infinity;
|
||||
var foldWidgets = this.$showFoldWidgets && session.foldWidgets;
|
||||
var breakpoints = session.$breakpoints;
|
||||
var decorations = session.$decorations;
|
||||
var firstLineNumber = session.$firstLineNumber;
|
||||
var lastLineNumber = 0;
|
||||
|
||||
var gutterRenderer = session.gutterRenderer || this.$renderer;
|
||||
|
||||
var editorDoc = session.doc;
|
||||
var doc = session.collabDoc;
|
||||
var range = new Range(firstRow, 0, lastRow, editorDoc.getLine(lastRow).length);
|
||||
var isCollabGutter = doc && showAuthorInfo && util.isRealCollab(workspace);
|
||||
var authorKeysCache = isCollabGutter && createAuthorKeyCache(editorDoc, doc.authAttribs, range).authorKeys;
|
||||
|
||||
var colorPool = workspace.colorPool;
|
||||
var reversedAuthorPool = workspace.reversedAuthorPool;
|
||||
|
||||
var cell = null;
|
||||
var index = -1;
|
||||
var row = firstRow;
|
||||
while (true) {
|
||||
if (row > foldStart) {
|
||||
row = fold.end.row + 1;
|
||||
fold = session.getNextFoldLine(row, fold);
|
||||
foldStart = fold ? fold.start.row : Infinity;
|
||||
}
|
||||
if (row > lastRow) {
|
||||
while (this.$cells.length > index + 1) {
|
||||
cell = this.$cells.pop();
|
||||
this.element.removeChild(cell.element);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
cell = this.$cells[++index];
|
||||
if (!cell) {
|
||||
cell = { element: null, textNode: null, foldWidget: null };
|
||||
cell.element = dom.createElement("div");
|
||||
cell.textNode = document.createTextNode('');
|
||||
cell.element.appendChild(cell.textNode);
|
||||
this.element.appendChild(cell.element);
|
||||
this.$cells[index] = cell;
|
||||
}
|
||||
|
||||
var className = "ace_gutter-cell ";
|
||||
if (breakpoints[row])
|
||||
className += breakpoints[row];
|
||||
if (decorations[row])
|
||||
className += decorations[row];
|
||||
if (this.$annotations[row])
|
||||
className += this.$annotations[row].className;
|
||||
if (cell.element.className != className)
|
||||
cell.element.className = className;
|
||||
|
||||
var height = session.getRowLength(row) * config.lineHeight + "px";
|
||||
if (height != cell.element.style.height)
|
||||
cell.element.style.height = height;
|
||||
|
||||
if (isCollabGutter) {
|
||||
var authorKey = authorKeysCache[row];
|
||||
var authorColor = "transparent";
|
||||
var fullname = null;
|
||||
if (authorKey) {
|
||||
var uid = reversedAuthorPool[authorKey];
|
||||
authorColor = util.formatColor(colorPool[uid]);
|
||||
var user = workspace.users[uid];
|
||||
fullname = user && user.fullname;
|
||||
}
|
||||
cell.element.style.borderLeft = "solid 5px " + authorColor;
|
||||
cell.element.setAttribute("uid", fullname ? uid : "");
|
||||
} else {
|
||||
cell.element.style.borderLeft = "";
|
||||
cell.element.setAttribute("uid", "");
|
||||
}
|
||||
|
||||
if (foldWidgets) {
|
||||
var c = foldWidgets[row];
|
||||
// check if cached value is invalidated and we need to recompute
|
||||
if (c == null)
|
||||
c = foldWidgets[row] = session.getFoldWidget(row);
|
||||
}
|
||||
|
||||
if (c) {
|
||||
if (!cell.foldWidget) {
|
||||
cell.foldWidget = dom.createElement("span");
|
||||
cell.element.appendChild(cell.foldWidget);
|
||||
}
|
||||
var className = "ace_fold-widget ace_" + c;
|
||||
if (c == "start" && row == foldStart && row < fold.end.row)
|
||||
className += " ace_closed";
|
||||
else
|
||||
className += " ace_open";
|
||||
if (cell.foldWidget.className != className)
|
||||
cell.foldWidget.className = className;
|
||||
|
||||
var height = config.lineHeight + "px";
|
||||
if (cell.foldWidget.style.height != height)
|
||||
cell.foldWidget.style.height = height;
|
||||
} else {
|
||||
if (cell.foldWidget) {
|
||||
cell.element.removeChild(cell.foldWidget);
|
||||
cell.foldWidget = null;
|
||||
}
|
||||
}
|
||||
|
||||
var text = lastLineNumber = gutterRenderer
|
||||
? gutterRenderer.getText(session, row)
|
||||
: row + firstLineNumber;
|
||||
if (text != cell.textNode.data)
|
||||
cell.textNode.data = text;
|
||||
|
||||
row++;
|
||||
}
|
||||
|
||||
this.element.style.height = config.minHeight + "px";
|
||||
|
||||
if (this.$fixedWidth || session.$useWrapMode)
|
||||
lastLineNumber = session.getLength() + firstLineNumber;
|
||||
|
||||
var gutterWidth = gutterRenderer
|
||||
? gutterRenderer.getWidth(session, lastLineNumber, config)
|
||||
: lastLineNumber.toString().length * config.characterWidth;
|
||||
|
||||
var padding = this.$padding || this.$computePadding();
|
||||
gutterWidth += padding.left + padding.right + (isCollabGutter ? 5 : 0);
|
||||
if (gutterWidth !== this.gutterWidth && !isNaN(gutterWidth)) {
|
||||
this.gutterWidth = gutterWidth;
|
||||
this.element.style.width = Math.ceil(this.gutterWidth) + "px";
|
||||
this._emit("changeGutterWidth", gutterWidth);
|
||||
}
|
||||
}
|
||||
|
||||
function createAuthorKeyCache (editorDoc, authAttribs, range) {
|
||||
var startI = editorDoc.positionToIndex(range.start);
|
||||
var endI = editorDoc.positionToIndex(range.end);
|
||||
|
||||
var authKeyCache = {};
|
||||
var rowScores = {};
|
||||
var lastPos = range.start;
|
||||
|
||||
function processScore(index, length, value) {
|
||||
var line = editorDoc.getLine(lastPos.row);
|
||||
var rowScore = rowScores[lastPos.row] = rowScores[lastPos.row] || {};
|
||||
var score = Math.min(line.length - lastPos.column, length);
|
||||
var scoreObj = rowScore[value] = rowScore[value] || { edits: [], score: 0 };
|
||||
scoreObj.edits.push({ pos: lastPos, length: score });
|
||||
scoreObj.score += score;
|
||||
var pos = editorDoc.indexToPosition(index + length);
|
||||
if (lastPos.row !== pos.row) {
|
||||
if (value) {
|
||||
for (var i = lastPos.row + 1; i < pos.row; i++)
|
||||
authKeyCache[i] = value;
|
||||
}
|
||||
line = editorDoc.getLine(pos.row);
|
||||
rowScore = rowScores[pos.row] = rowScores[pos.row] || {};
|
||||
score = pos.column;
|
||||
scoreObj = rowScore[value] = rowScore[value] || { edits: [], score: 0 };
|
||||
scoreObj.edits.push({ pos: pos, length: score });
|
||||
scoreObj.score += score;
|
||||
}
|
||||
lastPos = pos;
|
||||
}
|
||||
AuthorAttributes.traverse(authAttribs, startI, endI, processScore);
|
||||
|
||||
for (var rowNum in rowScores) {
|
||||
var rowScore = rowScores[rowNum];
|
||||
delete rowScore[null];
|
||||
delete rowScore[undefined];
|
||||
delete rowScore[0];
|
||||
var authorKeys = Object.keys(rowScore);
|
||||
|
||||
if (authorKeys.length === 0) {
|
||||
delete rowScores[rowNum];
|
||||
// authKeyCache[rowNum] = null;
|
||||
}
|
||||
else if (authorKeys.length === 1) {
|
||||
authKeyCache[rowNum] = parseInt(authorKeys[0], 10);
|
||||
}
|
||||
else {
|
||||
var biggestScore = 0;
|
||||
var authKey;
|
||||
for (var key in rowScore) {
|
||||
if (rowScore[key].score > biggestScore) {
|
||||
biggestScore = rowScore[key].score;
|
||||
authKey = key;
|
||||
}
|
||||
}
|
||||
authKeyCache[rowNum] = parseInt(authKey, 10);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
authorKeys: authKeyCache,
|
||||
rowScores: rowScores
|
||||
};
|
||||
}
|
||||
|
||||
function dispose () {
|
||||
session.removeMarker(marker.id);
|
||||
}
|
||||
|
||||
plugin.freezePublicAPI({
|
||||
get colorPool() { return workspace.colorPool; },
|
||||
refresh: refresh,
|
||||
dispose: dispose
|
||||
});
|
||||
|
||||
return plugin;
|
||||
}
|
||||
|
||||
function getLineAuthorKey(session, authAttribs, row) {
|
||||
var editorDoc = session.doc;
|
||||
|
||||
var line = editorDoc.getLine(row);
|
||||
var lineStart = editorDoc.positionToIndex({ row: row, column: 0 }) - 1;
|
||||
var lineEnd = lineStart + line.length + 1;
|
||||
var scores = {};
|
||||
AuthorAttributes.traverse(authAttribs, lineStart, lineEnd, function (index, length, value) {
|
||||
if (value)
|
||||
scores[value] = (scores[value] || 0) + length;
|
||||
});
|
||||
|
||||
var authorKeys = Object.keys(scores);
|
||||
|
||||
if (authorKeys.length === 0)
|
||||
return null;
|
||||
|
||||
if (authorKeys.length === 1)
|
||||
return parseInt(authorKeys[0], 10);
|
||||
|
||||
var biggestScore = 0;
|
||||
var authorKey;
|
||||
for (var key in scores) {
|
||||
if (scores[key] > biggestScore) {
|
||||
biggestScore = scores[key];
|
||||
authorKey = key;
|
||||
}
|
||||
}
|
||||
|
||||
return parseInt(authorKey, 10);
|
||||
}
|
||||
|
||||
function initGutterLayer(editor) {
|
||||
if (!editor || editor.$authorGutterInited) return;
|
||||
editor.$authorGutterInited = true;
|
||||
|
||||
var highlightedCell;
|
||||
|
||||
var tooltip = editor.tooltip = dom.createElement("div");
|
||||
tooltip.className = "ace_gutter-tooltip";
|
||||
tooltip.style.display = "none";
|
||||
editor.container.appendChild(tooltip);
|
||||
|
||||
function onGutterMouseout(e) {
|
||||
tooltip.style.display = "none";
|
||||
highlightedCell = null;
|
||||
}
|
||||
|
||||
var gutterEl = editor.renderer.$gutter;
|
||||
// var gutterEl = editor.renderer.$gutterLayer.element;
|
||||
// event.addListener(gutterEl, "mousemove", onMousemove);
|
||||
event.addListener(gutterEl, "mouseout", onGutterMouseout);
|
||||
|
||||
gutterEl.addEventListener("mousemove", function(e) {
|
||||
if (!showAuthorInfo || !util.isRealCollab(workspace))
|
||||
return;
|
||||
var target = e.target;
|
||||
|
||||
if (highlightedCell != target) {
|
||||
var uid = target.getAttribute("uid");
|
||||
if (uid) {
|
||||
tooltip.style.display = "block";
|
||||
highlightedCell = target;
|
||||
var user = workspace.users[uid];
|
||||
tooltip.textContent = user ? user.fullname : "";
|
||||
}
|
||||
}
|
||||
if (highlightedCell) {
|
||||
tooltip.style.top = e.clientY - 15 + "px";
|
||||
tooltip.style.left = e.clientX + "px";
|
||||
} else {
|
||||
onGutterMouseout();
|
||||
}
|
||||
});
|
||||
|
||||
var mousePos;
|
||||
editor.addEventListener("mousemove", function(e) {
|
||||
if (!showAuthorInfo || !util.isRealCollab(workspace))
|
||||
return;
|
||||
mousePos = { x: e.x, y: e.y };
|
||||
if (!editor.authorTooltipTimeout)
|
||||
editor.authorTooltipTimeout = setTimeout(updateTooltip, tooltip.style.display === "block" ? 100 : 300);
|
||||
});
|
||||
editor.renderer.container.addEventListener("mouseout", function(e) {
|
||||
tooltip.style.display = "none";
|
||||
});
|
||||
|
||||
function updateTooltip() {
|
||||
editor.authorTooltipTimeout = null;
|
||||
var session = editor.session;
|
||||
var otDoc = session.collabDoc;
|
||||
if (!otDoc)
|
||||
return;
|
||||
|
||||
var editorDoc = session.doc;
|
||||
var authAttribs = otDoc.authAttribs;
|
||||
|
||||
var screenPos = editor.renderer.pixelToScreenCoordinates(mousePos.x, mousePos.y);
|
||||
var docPos = session.screenToDocumentPosition(screenPos.row, screenPos.column);
|
||||
var line = editorDoc.getLine(docPos.row);
|
||||
|
||||
var hoverIndex = editorDoc.positionToIndex({ row: docPos.row, column: docPos.column });
|
||||
var authorKey = AuthorAttributes.valueAtIndex(authAttribs, hoverIndex);
|
||||
|
||||
// ignore newline tooltip and out of text hovering
|
||||
if (!authorKey || line.length <= screenPos.column || editorDoc.$lines.length < screenPos.row)
|
||||
return tooltip.style.display = "none";
|
||||
var lineOwnerKey = getLineAuthorKey(session, authAttribs, docPos.row);
|
||||
if (!lineOwnerKey || lineOwnerKey === authorKey)
|
||||
return tooltip.style.display = "none";
|
||||
|
||||
var reversedAuthorPool = workspace.reversedAuthorPool;
|
||||
var uid = reversedAuthorPool[authorKey];
|
||||
var fullname = workspace.users[uid] && workspace.users[uid].fullname;
|
||||
|
||||
tooltip.style.display = "block";
|
||||
tooltip.textContent = fullname;
|
||||
tooltip.style.top = mousePos.y + 10 + "px";
|
||||
tooltip.style.left = mousePos.x + 10 + "px";
|
||||
}
|
||||
|
||||
editor.addEventListener("mousewheel", function documentScroll() {
|
||||
clearTimeout(editor.authorTooltipTimeout);
|
||||
delete editor.authorTooltipTimeout;
|
||||
tooltip.style.display = "none";
|
||||
});
|
||||
}
|
||||
|
||||
/***** Register and define API *****/
|
||||
register(null, {
|
||||
AuthorLayer: AuthorLayer
|
||||
});
|
||||
}
|
||||
});
|
|
@ -0,0 +1,113 @@
|
|||
#chatThrob {
|
||||
display: none;
|
||||
position: absolute;
|
||||
bottom: 40px;
|
||||
font-size: 12px;
|
||||
width: 150px;
|
||||
height: 40px;
|
||||
right: 20px;
|
||||
z-index: 1000;
|
||||
color: rgb(39, 39, 39);
|
||||
background-color: rgb(236, 236, 236);
|
||||
background-color: rgba(236, 236, 236, 0.7);
|
||||
padding: 10px;
|
||||
border-radius: 6px;
|
||||
opacity: .8;
|
||||
}
|
||||
|
||||
.chatContainer{
|
||||
.display-flex();
|
||||
.flex-direction(column);
|
||||
}
|
||||
|
||||
.chatText a {
|
||||
color: #909090;
|
||||
}
|
||||
|
||||
.chatText {
|
||||
overflow: auto;
|
||||
font-size: @collab-chat-font-size;
|
||||
padding: 0 0 0 0;
|
||||
.flex(1);
|
||||
background: @collab-chat-background;
|
||||
}
|
||||
.chatText p {
|
||||
padding: @collab-chat-item-padding;
|
||||
word-wrap: break-word;
|
||||
border-bottom : @collab-chat-item-border-bottom;
|
||||
border-top : @collab-chat-item-border-top;
|
||||
position : relative;
|
||||
margin : 0;
|
||||
}
|
||||
.chatText p:first-child{
|
||||
border-top: 0;
|
||||
}
|
||||
.chatText .chatBorder{
|
||||
position : absolute;
|
||||
left : 0;
|
||||
top : 0;
|
||||
bottom : 0;
|
||||
width : 0;
|
||||
border-left : @collab-chat-color-size solid white;
|
||||
}
|
||||
.chatText .emoji {
|
||||
vertical-align: middle;
|
||||
}
|
||||
.chatText .authorName {
|
||||
text-decoration: none;
|
||||
color: @collab-chat-author-color;
|
||||
display : block;
|
||||
}
|
||||
.chatText .chatmessage {
|
||||
color : @collab-chat-message-color;
|
||||
display : block;
|
||||
padding: 3px 0 0 0;
|
||||
}
|
||||
.chatText .chattime {
|
||||
position : absolute;
|
||||
right: @collab-chat-time-right;
|
||||
top: @collab-chat-time-top;
|
||||
color: @collab-chat-time-color;
|
||||
font-size: 10px;
|
||||
font-weight: @collab-chat-time-font-weight;
|
||||
text-shadow: @collab-chat-time-text-shadow;
|
||||
}
|
||||
.chatContainer .searchTxt.tb_console {
|
||||
background: @collab-chat-input-background;
|
||||
height:30px;
|
||||
padding: 0;
|
||||
cursor: text;
|
||||
-webkit-flex-shrink: 0;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.chatContainer .searchTxt.tb_console .sbtb_middle{
|
||||
min-height : 37px;
|
||||
|
||||
border-color: @collab-chat-input-border-color;
|
||||
border-radius: 0;
|
||||
border-width: 1px 0 0 0;
|
||||
box-shadow: @collab-chat-input-box-shadow;
|
||||
background: @collab-chat-input-background;
|
||||
padding: 4px 4px 3px 4px;
|
||||
}
|
||||
.chatContainer .tb_textboxInitialMsg {
|
||||
color: #B9B9B9 !important;
|
||||
text-indent: 3px;
|
||||
text-shadow: none;
|
||||
}
|
||||
.chatContainer .searchbox .sbtb_middle .input{
|
||||
color: @collab-chat-input-color;
|
||||
.font-smoothing(@collab-chat-input-font-smoothing);
|
||||
}
|
||||
.chatContainer .ace-tm .ace_marker-layer .ace_selection {
|
||||
background: @collab-chat-input-selection-color;
|
||||
}
|
||||
.chatContainer .ace-tm .ace_cursor {
|
||||
color : @collab-chat-input-cursor-color;
|
||||
}
|
||||
|
||||
#chatCounter {
|
||||
color: #777;
|
||||
font-size: 10px;
|
||||
vertical-align: middle;
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
<audio style="display: none;" id="chatNotif" preload="true" src="/static/plugins/c9.ide.collab/chat/sounds/chat_notif.mp3"></audio>
|
||||
<div id="chatThrob"></div>
|
||||
<div id="chatCounter"></div>
|
|
@ -0,0 +1,404 @@
|
|||
define(function(require, exports, module) {
|
||||
"use strict";
|
||||
|
||||
main.consumes = [
|
||||
"CollabPanel", "ui", "panels", "collab.util", "collab.workspace",
|
||||
"collab", "Menu", "MenuItem"
|
||||
];
|
||||
main.provides = ["chat"];
|
||||
return main;
|
||||
|
||||
function main(options, imports, register) {
|
||||
var CollabPanel = imports.CollabPanel;
|
||||
var ui = imports.ui;
|
||||
var panels = imports.panels;
|
||||
var util = imports["collab.util"];
|
||||
var workspace = imports["collab.workspace"];
|
||||
var collab = imports.collab;
|
||||
var Menu = imports.Menu;
|
||||
var MenuItem = imports.MenuItem;
|
||||
|
||||
var html = require("text!./chat.html");
|
||||
var css = require("text!./chat.css");
|
||||
var timeago = require("timeago");
|
||||
var staticPrefix = options.staticPrefix;
|
||||
|
||||
var toDeleteMessage;
|
||||
|
||||
var ROLE_NONE = "n";
|
||||
var ROLE_VISITOR = "v";
|
||||
var ROLE_COLLABORATOR = "c";
|
||||
var ROLE_ADMIN = "a";
|
||||
|
||||
var plugin = new CollabPanel("Ajax.org", main.consumes, {
|
||||
name: "chat",
|
||||
index: 200,
|
||||
caption: "Group Chat",
|
||||
textselect: true,
|
||||
style: "flex:1;-webkit-flex:1"
|
||||
});
|
||||
|
||||
// var emit = plugin.getEmitter();
|
||||
var emoji = require("./my_emoji");
|
||||
|
||||
// panel-relared UI elements
|
||||
var chatInput, chatText, mnuCtxTreeEl, parent;
|
||||
// non-panel related UI elements
|
||||
var chatThrob, chatCounter, chatNotif;
|
||||
|
||||
function $(id) {
|
||||
return document.getElementById(id);
|
||||
}
|
||||
|
||||
var loaded = false;
|
||||
function load() {
|
||||
if (loaded) return;
|
||||
loaded = true;
|
||||
|
||||
collab.on("chatMessage", onChatMessage);
|
||||
collab.on("chatClear", onChatClear);
|
||||
}
|
||||
|
||||
var drawn = false;
|
||||
function draw(options) {
|
||||
if (drawn) return;
|
||||
drawn = true;
|
||||
|
||||
drawNonPanelElements();
|
||||
|
||||
parent = options.aml;
|
||||
var parentExt = parent.$int;
|
||||
parentExt.className += " chatContainer";
|
||||
|
||||
chatText = parentExt.appendChild(document.createElement("div"));
|
||||
chatText.setAttribute("class", "chatText");
|
||||
|
||||
chatInput = new apf.codebox({
|
||||
htmlNode: parentExt,
|
||||
skin: "codebox",
|
||||
"initial-message": "Enter your message here",
|
||||
// clearbutton : "true",
|
||||
focusselect: "true"
|
||||
});
|
||||
|
||||
chatInput.$ext.classList.remove("ace_searchbox");
|
||||
chatInput.ace.setOptions({
|
||||
wrap: "free",
|
||||
indentedSoftWrap: false,
|
||||
maxLines: 5,
|
||||
minLines: 2,
|
||||
fontFamily: "inherit"
|
||||
});
|
||||
chatInput.ace.session.$setFontMetrics(chatInput.ace.renderer.$fontMetrics);
|
||||
|
||||
plugin.addElement(chatInput);
|
||||
|
||||
function onWorkspaceSync() {
|
||||
var accessInfo = workspace.accessInfo;
|
||||
if (accessInfo.private && (!accessInfo.member || accessInfo.pending))
|
||||
return console.warn("Don't have read access - You can't use chat");
|
||||
var chatHistory = workspace.chatHistory || [];
|
||||
chatHistory.forEach(function(msg) {
|
||||
addMessage(msg, msg.increment);
|
||||
});
|
||||
scrollDown();
|
||||
chatCounter.innerHTML = chatHistory.length;
|
||||
}
|
||||
|
||||
chatInput.ace.commands.addCommands([
|
||||
{
|
||||
bindKey: "ESC",
|
||||
exec: function() {
|
||||
if (chatInput.getValue())
|
||||
chatInput.setValue("");
|
||||
else
|
||||
collab.hide();
|
||||
}
|
||||
}, {
|
||||
bindKey: "Enter",
|
||||
exec: send
|
||||
}
|
||||
]);
|
||||
|
||||
function listenWorkspaceSync() {
|
||||
workspace.off("sync", onWorkspaceSync);
|
||||
workspace.on("sync", onWorkspaceSync);
|
||||
}
|
||||
|
||||
if (panels.isActive("collab"))
|
||||
listenWorkspaceSync();
|
||||
|
||||
collab.on("show", function() {
|
||||
chatInput.focus();
|
||||
listenWorkspaceSync();
|
||||
});
|
||||
|
||||
collab.on("hide", function() {
|
||||
workspace.off("sync", onWorkspaceSync);
|
||||
});
|
||||
|
||||
var deleteMsgItem = new MenuItem({
|
||||
caption: "Delete Message",
|
||||
match: "rw",
|
||||
onclick: clearChatMessage,
|
||||
disabled: true
|
||||
});
|
||||
|
||||
var clearHistoryItem = new MenuItem({
|
||||
caption: "Clear history",
|
||||
match: "rw",
|
||||
onclick: clearChatHistory,
|
||||
disabled: true
|
||||
});
|
||||
|
||||
var mnuCtxTree = new Menu({
|
||||
id: "mnuChat",
|
||||
items: [
|
||||
deleteMsgItem,
|
||||
clearHistoryItem
|
||||
]
|
||||
}, plugin);
|
||||
|
||||
mnuCtxTreeEl = mnuCtxTree.aml;
|
||||
|
||||
parent.setAttribute("contextmenu", mnuCtxTreeEl);
|
||||
|
||||
mnuCtxTree.on("show", function() {
|
||||
setTimeout(function() {
|
||||
var hasReadWrite = workspace.fs === "rw";
|
||||
var collabConnected = collab.connected;
|
||||
clearHistoryItem.disabled = !hasReadWrite || !collabConnected || !chatText.firstChild;
|
||||
toDeleteMessage = findMessageToDelete(hasReadWrite);
|
||||
deleteMsgItem.disabled = !toDeleteMessage || !collabConnected;
|
||||
}, 10);
|
||||
});
|
||||
|
||||
parent.on("resize", function() {
|
||||
if (isOpen()) {
|
||||
pending.forEach(addMessage);
|
||||
pending = [];
|
||||
updateCaption();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function findMessageToDelete(hasReadWrite) {
|
||||
var menuRect = mnuCtxTreeEl.$int.getBoundingClientRect();
|
||||
var msg;
|
||||
[].slice.apply(chatText.getElementsByTagName("p")).forEach(function(msgP) {
|
||||
var rect = msgP.getBoundingClientRect();
|
||||
if (rect.left < menuRect.left && (rect.left + rect.width) > menuRect.left
|
||||
&& rect.top < menuRect.top && (rect.top + rect.height) > menuRect.top
|
||||
&& (hasReadWrite || msgP.userId == workspace.myUserId))
|
||||
msg = msgP;
|
||||
});
|
||||
return msg;
|
||||
}
|
||||
|
||||
function clearChatMessage() {
|
||||
if (!toDeleteMessage || !toDeleteMessage.id)
|
||||
return console.error("[OT] Chat: no message found to delete!");
|
||||
var msgId = toDeleteMessage.id.match(/ot_chat_(\d+)/)[1];
|
||||
collab.send("CLEAR_CHAT", { id: msgId });
|
||||
}
|
||||
|
||||
function clearChatHistory() {
|
||||
collab.send("CLEAR_CHAT", { clear: true });
|
||||
}
|
||||
|
||||
var seenMsgs = {};
|
||||
var pending = [];
|
||||
var throbTimeout;
|
||||
|
||||
function scrollDown() {
|
||||
chatText.scrollTop = chatText.scrollHeight;
|
||||
}
|
||||
|
||||
function isOpen() {
|
||||
return panels.isActive("collab") && parent.state[0] != "m";
|
||||
}
|
||||
|
||||
function send() {
|
||||
var text = chatInput.getValue().trim();
|
||||
if (!text)
|
||||
return;
|
||||
text = emoji.toEmojiUnicode(text);
|
||||
collab.send("CHAT_MESSAGE", { text: text });
|
||||
chatInput.setValue("");
|
||||
}
|
||||
|
||||
function getAuthorName(userId) {
|
||||
if (userId == workspace.myUserId)
|
||||
return "You";
|
||||
|
||||
var user = workspace.users[userId];
|
||||
return util.escapeHTML(user.fullname);
|
||||
}
|
||||
|
||||
function getAuthorColor(userId) {
|
||||
var color = workspace.colorPool[userId];
|
||||
return util.formatColor(color);
|
||||
}
|
||||
|
||||
function formatMessageText(text) {
|
||||
text = util.escapeHtmlWithClickableLinks(text.trim(), "_blank");
|
||||
text = text.replace(/\n/g, "<br/>");
|
||||
text = emoji.emoji(text, staticPrefix);
|
||||
return text;
|
||||
}
|
||||
|
||||
function onChatMessage(msg) {
|
||||
drawNonPanelElements();
|
||||
workspace.addChatMessage(msg);
|
||||
if (isOpen()) {
|
||||
addMessage(msg);
|
||||
}
|
||||
else {
|
||||
pending.push(msg);
|
||||
updateCaption();
|
||||
|
||||
var throbText = "<b>" + getAuthorName(msg.userId) + "</b> ";
|
||||
var text = formatMessageText(msg.text);
|
||||
var notif = msg.notification;
|
||||
if (notif) {
|
||||
throbText += text + " " + notif.linkText;
|
||||
} else {
|
||||
throbText += ": " + text;
|
||||
}
|
||||
|
||||
chatThrob.innerHTML = throbText;
|
||||
chatThrob.style.display = "block";
|
||||
clearTimeout(throbTimeout);
|
||||
throbTimeout = setTimeout(function () {
|
||||
chatThrob.style.display = "none";
|
||||
}, 5000);
|
||||
}
|
||||
|
||||
if (msg.increment) {
|
||||
var count = Number(chatCounter.innerHTML);
|
||||
chatCounter.innerHTML = count + 1;
|
||||
}
|
||||
|
||||
var inputFocussed = chatInput && chatInput.ace.isFocused();
|
||||
if (!inputFocussed)
|
||||
chatNotif.play();
|
||||
|
||||
}
|
||||
|
||||
function onChatClear(data) {
|
||||
if (data.clear) {
|
||||
// fast way to delete all children - not innerHtml = ''
|
||||
while (chatText.firstChild)
|
||||
chatText.removeChild(chatText.firstChild);
|
||||
seenMsgs = {}; // sql truncate table would lead to same msg ids
|
||||
workspace.chatHistory = [];
|
||||
}
|
||||
else if (data.id) {
|
||||
var msgHtml = $("ot_chat_" + data.id);
|
||||
msgHtml && msgHtml.parentNode.removeChild(msgHtml);
|
||||
}
|
||||
}
|
||||
|
||||
function addMessage(msg) {
|
||||
if (seenMsgs[msg.id])
|
||||
return;
|
||||
seenMsgs[msg.id] = true;
|
||||
// correct the time
|
||||
// msg.timestamp += clientTimeOffset;
|
||||
var msgDate = new Date(msg.timestamp);
|
||||
|
||||
// create the time string
|
||||
var text = formatMessageText(msg.text);
|
||||
var authorName = getAuthorName(msg.userId);
|
||||
var authorColor = getAuthorColor(msg.userId);
|
||||
|
||||
var authorNameEl = document.createElement("a");
|
||||
authorNameEl.href = "javascript:void(0)";
|
||||
authorNameEl.className = "authorName";
|
||||
authorNameEl.innerHTML = "<b>" + authorName + "</b>";
|
||||
// authorNameEl.addEventListener("click", function () {
|
||||
// });
|
||||
|
||||
var html = document.createElement("p");
|
||||
html.id = "ot_chat_" + msg.id;
|
||||
html.userId = msg.userId;
|
||||
|
||||
var borderEl = document.createElement("span");
|
||||
html.appendChild(borderEl);
|
||||
borderEl.className = "chatBorder";
|
||||
borderEl.style.borderLeftColor = authorColor;
|
||||
|
||||
html.appendChild(authorNameEl);
|
||||
var textEl = document.createElement("span");
|
||||
textEl.className = "chatmessage";
|
||||
textEl.innerHTML = text + "<br/>";
|
||||
html.appendChild(textEl);
|
||||
var timeEl = document.createElement("span");
|
||||
timeEl.className = "chattime";
|
||||
timeEl.title = msgDate.toISOString();
|
||||
timeEl.innerHTML = msgDate;
|
||||
timeago(timeEl);
|
||||
html.appendChild(timeEl);
|
||||
|
||||
chatText.appendChild(html);
|
||||
scrollDown();
|
||||
}
|
||||
|
||||
function updateCaption() {
|
||||
var caption = "Group Chat";
|
||||
if (pending.length)
|
||||
caption += "(" + pending.length + ")";
|
||||
if (parent)
|
||||
parent.setAttribute("caption", caption);
|
||||
}
|
||||
|
||||
var nonPanelDrawn = false;
|
||||
function drawNonPanelElements () {
|
||||
if (nonPanelDrawn) return;
|
||||
nonPanelDrawn = true;
|
||||
|
||||
ui.insertHtml(null, html, plugin);
|
||||
ui.insertCss(css, staticPrefix, plugin);
|
||||
|
||||
chatThrob = $("chatThrob");
|
||||
chatCounter = $("chatCounter");
|
||||
chatNotif = $("chatNotif");
|
||||
|
||||
chatThrob.addEventListener("click", function () {
|
||||
chatThrob.style.display = "none";
|
||||
plugin.show();
|
||||
});
|
||||
}
|
||||
|
||||
/***** Lifecycle *****/
|
||||
plugin.on("load", function() {
|
||||
load();
|
||||
plugin.once("draw", draw);
|
||||
});
|
||||
plugin.on("enable", function() {
|
||||
|
||||
});
|
||||
plugin.on("disable", function() {
|
||||
});
|
||||
|
||||
plugin.on("unload", function() {
|
||||
loaded = false;
|
||||
drawn = false;
|
||||
});
|
||||
|
||||
/***** Register and define API *****/
|
||||
|
||||
/**
|
||||
* Adds File->New File and File->New Folder menu items as well as the
|
||||
* commands for opening a new file as well as an API.
|
||||
* @singleton
|
||||
**/
|
||||
plugin.freezePublicAPI({
|
||||
});
|
||||
|
||||
register(null, {
|
||||
chat: plugin
|
||||
});
|
||||
}
|
||||
|
||||
});
|
Po Szerokość: | Wysokość: | Rozmiar: 1.0 KiB |
Po Szerokość: | Wysokość: | Rozmiar: 1.1 KiB |
Po Szerokość: | Wysokość: | Rozmiar: 572 B |
Po Szerokość: | Wysokość: | Rozmiar: 1016 B |
Po Szerokość: | Wysokość: | Rozmiar: 1.1 KiB |
Po Szerokość: | Wysokość: | Rozmiar: 934 B |
Po Szerokość: | Wysokość: | Rozmiar: 863 B |
Po Szerokość: | Wysokość: | Rozmiar: 875 B |
Po Szerokość: | Wysokość: | Rozmiar: 1009 B |
Po Szerokość: | Wysokość: | Rozmiar: 790 B |
Po Szerokość: | Wysokość: | Rozmiar: 878 B |
Po Szerokość: | Wysokość: | Rozmiar: 604 B |
Po Szerokość: | Wysokość: | Rozmiar: 961 B |
Po Szerokość: | Wysokość: | Rozmiar: 939 B |
Po Szerokość: | Wysokość: | Rozmiar: 686 B |
Po Szerokość: | Wysokość: | Rozmiar: 1.0 KiB |
Po Szerokość: | Wysokość: | Rozmiar: 884 B |
Po Szerokość: | Wysokość: | Rozmiar: 822 B |
Po Szerokość: | Wysokość: | Rozmiar: 849 B |
Po Szerokość: | Wysokość: | Rozmiar: 1017 B |
Po Szerokość: | Wysokość: | Rozmiar: 1.1 KiB |
Po Szerokość: | Wysokość: | Rozmiar: 1.1 KiB |
Po Szerokość: | Wysokość: | Rozmiar: 1.1 KiB |