Merge pull request #1160 from ajaxorg/mode-loading

Module loading
This commit is contained in:
Harutyun Amirjanyan 2013-01-07 11:28:13 -08:00
commit 8669a7d2d9
11 changed files with 130 additions and 125 deletions

View file

@ -163,6 +163,6 @@ Continuous Integration status
-----------------------------
This project is tested with [Travis CI](http://travis-ci.org)
[![Build Status](https://secure.travis-ci.org/ajaxorg/ace.png)](http://travis-ci.org/ajaxorg/ace)
[![Build Status](https://secure.travis-ci.org/ajaxorg/ace.png?branch=master)](http://travis-ci.org/ajaxorg/ace)

View file

@ -46,8 +46,6 @@ var theme = require("ace/theme/textmate");
var EditSession = require("ace/edit_session").EditSession;
var UndoManager = require("ace/undomanager").UndoManager;
var vim = require("ace/keyboard/vim").handler;
var emacs = require("ace/keyboard/emacs").handler;
var HashHandler = require("ace/keyboard/hash_handler").HashHandler;
var Renderer = require("ace/virtual_renderer").VirtualRenderer;
@ -162,11 +160,10 @@ commands.addCommand({
exec: function() {alert("Fake Save File");}
});
var keybindings = {
// Null = use "default" keymapping
ace: null,
vim: vim,
emacs: emacs,
var keybindings = {
ace: null, // Null = use "default" keymapping
vim: require("ace/keyboard/vim").handler,
emacs: "ace/keyboard/emacs",
// This is a way to define simple keyboard remappings
custom: new HashHandler({
"gotoright": "Tab",

View file

@ -13,13 +13,13 @@
<!--DEVEL-->
<link rel="stylesheet" href="demo/kitchen-sink/styles.css" type="text/css" media="screen" charset="utf-8">
<script src="http://use.edgefonts.net/source-code-pro.js"></script>
<script async="true" src="http://use.edgefonts.net/source-code-pro.js"></script>
<!--DEVEL-->
<link href="./doc/resources/ace/skeleton/images/favicon.ico" rel="icon" type="image/x-icon">
<!--PACKAGE
<link rel="stylesheet" href="kitchen-sink/styles.css" type="text/css" media="screen" charset="utf-8">
<script src="http://use.edgefonts.net/source-code-pro.js"></script>
<script async="true" src="http://use.edgefonts.net/source-code-pro.js"></script>
PACKAGE-->
</head>
<body>
@ -280,4 +280,4 @@
PACKAGE-->
</body>
</html>
</html>

View file

@ -32,6 +32,8 @@ define(function(require, exports, module) {
"no use strict";
var lang = require("./lib/lang");
var oop = require("./lib/oop");
var EventEmitter = require("./lib/event_emitter").EventEmitter;
var global = (function() {
return this;
@ -65,6 +67,9 @@ exports.all = function() {
return lang.copyObject(options);
};
// module loading
oop.implement(exports, EventEmitter);
exports.moduleUrl = function(name, component) {
if (options.$moduleUrls[name])
return options.$moduleUrls[name];
@ -87,6 +92,32 @@ exports.setModuleUrl = function(name, subst) {
return options.$moduleUrls[name] = subst;
};
exports.loadModule = function(moduleName, onLoad) {
var module, moduleType;
if (Array.isArray(moduleName)) {
moduleType = moduleName[0];
moduleName = moduleName[1];
}
try {
module = require(moduleName);
} catch (e) {};
if (module)
return onLoad(module);
var afterLoad = function() {
require([moduleName], function(module) {
exports._emit("load.module", {name: moduleName, module: module});
onLoad(module);
});
};
if (!exports.get("packaged"))
return afterLoad();
net.loadScript(exports.moduleUrl(moduleName, moduleType), afterLoad);
};
// initialization
exports.init = function() {
options.packaged = require.packaged || module.packaged || (global.define && define.packaged);

View file

@ -898,69 +898,6 @@ var EditSession = function(text, mode) {
};
this.$modes = {};
this._loadMode = function(mode, callback) {
if (!this.$modes["null"])
this.$modes["null"] = this.$modes["ace/mode/text"] = new TextMode();
if (this.$modes[mode])
return callback(this.$modes[mode]);
var _self = this;
var module;
try {
module = require(mode);
} catch (e) {};
// sometimes require returns empty object (this bug is present in requirejs 2 as well)
if (module && module.Mode)
return done(module);
// set mode to text until loading is finished
if (!this.$mode)
this.$setModePlaceholder();
fetch(mode, function() {
require([mode], done);
});
function done(module) {
if (_self.$modes[mode])
return callback(_self.$modes[mode]);
_self.$modes[mode] = new module.Mode();
_self.$modes[mode].$id = mode;
_self._emit("loadmode", {
name: mode,
mode: _self.$modes[mode]
});
callback(_self.$modes[mode]);
}
function fetch(name, callback) {
if (!config.get("packaged"))
return callback();
net.loadScript(config.moduleUrl(name, "mode"), callback);
}
};
this.$setModePlaceholder = function() {
this.$mode = this.$modes["null"];
var tokenizer = this.$mode.getTokenizer();
if (!this.bgTokenizer) {
this.bgTokenizer = new BackgroundTokenizer(tokenizer);
var _self = this;
this.bgTokenizer.addEventListener("update", function(e) {
_self._emit("tokenizerUpdate", e);
});
} else {
this.bgTokenizer.setTokenizer(tokenizer);
}
this.bgTokenizer.setDocument(this.getDocument());
this.tokenRe = this.$mode.tokenRe;
this.nonTokenRe = this.$mode.nonTokenRe;
};
/**
* Sets a new text mode for the `EditSession`. This method also emits the `'changeMode'` event. If a [[BackgroundTokenizer `BackgroundTokenizer`]] is set, the `'tokenizerUpdate'` event is also emitted.
@ -970,23 +907,45 @@ var EditSession = function(text, mode) {
this.$mode = null;
this.$modeId = null;
this.setMode = function(mode) {
mode = mode || "null";
// load on demand
if (typeof mode === "string") {
if (this.$modeId == mode)
return;
this.$modeId = mode;
var _self = this;
this._loadMode(mode, function(module) {
if (_self.$modeId !== mode)
return;
_self.setMode(module);
});
return;
if (mode && typeof mode === "object") {
if (mode.getTokenizer)
return this.$onChangeMode(mode);
var options = mode;
var path = options.path;
} else {
path = mode || "ace/mode/text";
}
// this is needed if ace isn't on require path (e.g tests in node)
if (!this.$modes["ace/mode/text"])
this.$modes["ace/mode/text"] = new TextMode();
if (this.$modes[path] && !options)
return this.$onChangeMode(this.$modes[path]);
// load on demand
this.$modeId = path;
config.loadModule(["mode", path], function(m) {
if (this.$modeId !== path)
return;
if (this.$modes[path] && !options)
return this.$onChangeMode(this.$modes[path]);
if (m && m.Mode) {
m = new m.Mode(options);
if (!options) {
this.$modes[path] = m;
m.$id = path;
}
this.$onChangeMode(m)
}
}.bind(this));
// set mode to text until loading is finished
if (!this.$mode)
this.$onChangeMode(this.$modes["ace/mode/text"], true);
};
this.$onChangeMode = function(mode, $isPlaceholder) {
if (this.$mode === mode) return;
this.$mode = mode;
this.$modeId = mode.$id;
@ -1014,14 +973,16 @@ var EditSession = function(text, mode) {
}
this.bgTokenizer.setDocument(this.getDocument());
this.bgTokenizer.start(0);
this.tokenRe = mode.tokenRe;
this.nonTokenRe = mode.nonTokenRe;
this.$setFolding(mode.foldingRules);
this._emit("changeMode");
if (!$isPlaceholder) {
this.$setFolding(mode.foldingRules);
this._emit("changeMode");
this.bgTokenizer.start(0);
}
};

View file

@ -46,6 +46,7 @@ var Range = require("./range").Range;
var EventEmitter = require("./lib/event_emitter").EventEmitter;
var CommandManager = require("./commands/command_manager").CommandManager;
var defaultCommands = require("./commands/default_commands").commands;
var config = require("./config");
/**
*
@ -100,7 +101,17 @@ var Editor = function(renderer, session) {
*
**/
this.setKeyboardHandler = function(keyboardHandler) {
this.keyBinding.setKeyboardHandler(keyboardHandler);
if (typeof keyboardHandler == "string" && keyboardHandler) {
this.$keybindingId = keyboardHandler;
var _self = this;
config.loadModule(["keybinding", keyboardHandler], function(module) {
if (_self.$keybindingId == keyboardHandler)
_self.keyBinding.setKeyboardHandler(module && module.handler);
});
} else {
delete this.$keybindingId;
this.keyBinding.setKeyboardHandler(keyboardHandler);
}
};
/**
@ -2149,4 +2160,4 @@ var Editor = function(renderer, session) {
exports.Editor = Editor;
});
});

View file

@ -324,6 +324,19 @@ function setupApi(editor, editorDiv, settingDiv, ace, options, loader) {
editorDiv.style.fontSize = value;
break;
case "keybindings":
switch (value) {
case "vim":
editor.setKeyboardHandler("ace/keyboard/vim");
break;
case "emacs":
editor.setKeyboardHandler("ace/keyboard/emacs");
break;
default:
editor.setKeyboardHandler(null);
}
break;
case "softWrap":
switch (value) {
case "off":
@ -391,6 +404,7 @@ function setupSettingPanel(settingDiv, settingOpener, editor, options) {
theme: "Theme:",
fontSize: "Font Size:",
softWrap: "Soft Wrap:",
keybindings: "Keyboard",
showPrintMargin: "Show Print Margin:",
useSoftTabs: "Use Soft Tabs:",
showInvisibles: "Show Invisibles"
@ -456,6 +470,11 @@ function setupSettingPanel(settingDiv, settingOpener, editor, options) {
80: "80",
free: "Free"
},
keybindings: {
ace: "ace",
vim: "vim",
emacs: "emacs"
},
showPrintMargin: BOOL,
useSoftTabs: BOOL,
showInvisibles: BOOL
@ -518,6 +537,7 @@ exports.options = {
gutter: "false",
fontSize: "12px",
softWrap: "off",
keybindings: "ace",
showPrintMargin: "false",
useSoftTabs: "true",
showInvisibles: "true"

View file

@ -33,14 +33,14 @@ define(function(require, exports, module) {
var dom = require("../lib/dom");
var screenToTextBlockCoordinates = function(pageX, pageY) {
var screenToTextBlockCoordinates = function(x, y) {
var canvasPos = this.scroller.getBoundingClientRect();
var col = Math.floor(
(pageX + this.scrollLeft - canvasPos.left - this.$padding - dom.getPageScrollLeft()) / this.characterWidth
(x + this.scrollLeft - canvasPos.left - this.$padding) / this.characterWidth
);
var row = Math.floor(
(pageY + this.scrollTop - canvasPos.top - dom.getPageScrollTop()) / this.lineHeight
(y + this.scrollTop - canvasPos.top) / this.lineHeight
);
return this.session.screenToDocumentPosition(row, col);

View file

@ -35,14 +35,17 @@ var oop = require("../lib/oop");
var TextMode = require("./text").Mode;
var Tokenizer = require("../tokenizer").Tokenizer;
var PhpHighlightRules = require("./php_highlight_rules").PhpHighlightRules;
var PhpLangHighlightRules = require("./php_highlight_rules").PhpLangHighlightRules;
var MatchingBraceOutdent = require("./matching_brace_outdent").MatchingBraceOutdent;
var Range = require("../range").Range;
var CstyleBehaviour = require("./behaviour/cstyle").CstyleBehaviour;
var CStyleFoldMode = require("./folding/cstyle").FoldMode;
var unicode = require("../unicode");
var Mode = function() {
this.$tokenizer = new Tokenizer(new PhpHighlightRules().getRules());
var Mode = function(opts) {
var inline = opts && opts.inline;
var HighlightRules = inline ? PhpLangHighlightRules : PhpHighlightRules;
this.$tokenizer = new Tokenizer(new HighlightRules().getRules());
this.$outdent = new MatchingBraceOutdent();
this.$behaviour = new CstyleBehaviour();
this.foldingRules = new CStyleFoldMode();

View file

@ -1062,4 +1062,5 @@ var PhpHighlightRules = function() {
oop.inherits(PhpHighlightRules, HtmlHighlightRules);
exports.PhpHighlightRules = PhpHighlightRules;
exports.PhpLangHighlightRules = PhpLangHighlightRules;
});

View file

@ -69,16 +69,16 @@ dom.importCssString(editorCss, "ace_editor");
var VirtualRenderer = function(container, theme) {
var _self = this;
this.container = container;
this.container = container || dom.createElement("div");
// TODO: this breaks rendering in Cloud9 with multiple ace instances
// // Imports CSS once per DOM document ('ace_editor' serves as an identifier).
// dom.importCssString(editorCss, "ace_editor", container.ownerDocument);
// // Imports CSS once per DOM document ('ace_editor' serves as an identifier).
// dom.importCssString(editorCss, "ace_editor", container.ownerDocument);
// in IE <= 9 the native cursor always shines through
this.$keepTextAreaAtCursor = !useragent.isIE;
dom.addCssClass(container, "ace_editor");
dom.addCssClass(this.container, "ace_editor");
this.setTheme(theme);
@ -113,7 +113,7 @@ var VirtualRenderer = function(container, theme) {
this.$animatedScroll = false;
this.scrollBar = new ScrollBar(container);
this.scrollBar = new ScrollBar(this.container);
this.scrollBar.addEventListener("scroll", function(e) {
if (!_self.$inScrollAnimation)
_self.session.setScrollTop(e.data);
@ -1278,10 +1278,7 @@ var VirtualRenderer = function(container, theme) {
};
this._loadTheme = function(name, callback) {
if (!config.get("packaged"))
return callback();
net.loadScript(config.moduleUrl(name, "theme"), callback);
};
/**
@ -1292,28 +1289,12 @@ var VirtualRenderer = function(container, theme) {
**/
this.setTheme = function(theme) {
var _self = this;
this.$themeValue = theme;
_self._dispatchEvent('themeChange',{theme:theme});
if (!theme || typeof theme == "string") {
var moduleName = theme || "ace/theme/textmate";
var module;
try {
module = require(moduleName);
} catch (e) {};
if (module)
return afterLoad(module);
_self._loadTheme(moduleName, function() {
require([moduleName], function(module) {
if (_self.$themeValue !== theme)
return;
afterLoad(module);
});
});
config.loadModule(["theme", moduleName], afterLoad);
} else {
afterLoad(theme);
}