diff --git a/index.html b/index.html
index 2feeded4..8eb64ac6 100755
--- a/index.html
+++ b/index.html
@@ -29,10 +29,12 @@
+
-
+
+
@@ -168,6 +170,14 @@
+
Help
diff --git a/keybindings.js b/keybindings.js
index 479450ed..2b652e5d 100644
--- a/keybindings.js
+++ b/keybindings.js
@@ -21,5 +21,6 @@
"toggleComment": ["Ctrl-Shift-C", "Command-Shift-C"],
"synchronize": ["Alt-Shift-S", "Command-Option-S"],
"preferences": ["Alt-Shift-P", "Ctrl-Command-P"],
- "openApp": ["Ctrl-Alt-R", "Command-Option-R"]
+ "openApp": ["Ctrl-Alt-R", "Command-Option-R"],
+ "formatCode": ["Ctrl-Shift-P", "Command-Shift-P"]
}
diff --git a/src/ace-modes.js b/src/ace-modes.js
index bcc8456f..9be1eb73 100644
--- a/src/ace-modes.js
+++ b/src/ace-modes.js
@@ -222,37 +222,41 @@ define("eXide/mode/xquery", function(require, exports, module) {
var oop = require("ace/lib/oop");
var TextMode = require("ace/mode/text").Mode;
+var XQueryLexer = require("xqlint/XQueryLexer").XQueryLexer;
+
var Tokenizer = require("ace/tokenizer").Tokenizer;
-var XQueryHighlightRules = require("eXide/mode/xquery_highlight_rules").XQueryHighlightRules;
var XQueryBehaviour = require("eXide/mode/behaviour/xquery").XQueryBehaviour;
+var CStyleFoldMode = require("ace/mode/folding/cstyle").FoldMode;
var Range = require("ace/range").Range;
var Mode = function(parent) {
- this.$tokenizer = new Tokenizer(new XQueryHighlightRules().getRules());
+ //this.$tokenizer = new Tokenizer(new XQueryHighlightRules().getRules());
+ this.$tokenizer = new XQueryLexer();
this.$behaviour = new XQueryBehaviour(parent);
+ this.foldingRules = new CStyleFoldMode();
};
oop.inherits(Mode, TextMode);
(function() {
-
+
this.getNextLineIndent = function(state, line, tab) {
- var indent = this.$getIndent(line);
- var match = line.match(/\s*(?:then|else|return|[{\(]|<\w+>)\s*$/);
- if (match)
- indent += tab;
+ var indent = this.$getIndent(line);
+ var match = line.match(/\s*(?:then|else|return|[{\(]|<\w+>)\s*$/);
+ if (match)
+ indent += tab;
return indent;
};
this.checkOutdent = function(state, line, input) {
- if (! /^\s+$/.test(line))
+ if (! /^\s+$/.test(line))
return false;
return /^\s*[\}\)]/.test(input);
};
this.autoOutdent = function(state, doc, row) {
- var line = doc.getLine(row);
+ var line = doc.getLine(row);
var match = line.match(/^(\s*[\}\)])/);
if (!match) return 0;
@@ -297,6 +301,61 @@ oop.inherits(Mode, TextMode);
doc.replace(range, outdent ? line.match(re)[1] : "(:" + line + ":)");
}
};
+
+ /*this.createWorker = function(session) {
+ this.$deltas = [];
+ var worker = new WorkerClient(["ace"], "ace/mode/xquery_worker", "XQueryWorker");
+ var that = this;
+
+ session.getDocument().on('change', function(evt){
+ that.$deltas.push(evt.data);
+ });
+
+ worker.attachToDocument(session.getDocument());
+
+ worker.on("start", function(e) {
+ that.$deltas = [];
+ });
+
+ worker.on("error", function(e) {
+ // errors are ignored because they are reported by eXist's compiler
+ });
+
+ worker.on("xqlint", function(e) {
+ var annotations = [];
+ for (var i = 0; i < e.data.length; i++) {
+ if (e.data[i].type !== "error") {
+ annotations.push({
+ row: e.data[i].pos.sl,
+ text: e.data[i].message,
+ type: e.data[i].type
+ });
+ }
+ }
+ session.setAnnotations(annotations);
+ });
+
+ worker.on("ok", function(e) {
+ session.clearAnnotations();
+ });
+
+ worker.on("highlight", function(tokens) {
+ if(that.$deltas.length > 0) return;
+
+ var firstRow = 0;
+ var lastRow = session.getLength() - 1;
+
+ var lines = tokens.data.lines;
+ var states = tokens.data.states;
+
+ session.bgTokenizer.lines = lines;
+ session.bgTokenizer.states = states;
+ session.bgTokenizer.fireUpdateEvent(firstRow, lastRow);
+ });
+
+ return worker;
+ };*/
+
}).call(Mode.prototype);
exports.Mode = Mode;
diff --git a/src/commands.js b/src/commands.js
index 0a4c58c8..0a035246 100644
--- a/src/commands.js
+++ b/src/commands.js
@@ -52,7 +52,7 @@ eXide.edit.commands = (function () {
return {
init: function (editor) {
- commands = editor.editor.commands;
+ var commands = editor.editor.commands;
$.ajax({
url: "keybindings.js",
dataType: 'json',
@@ -137,6 +137,14 @@ eXide.edit.commands = (function () {
editor.previousTab();
}
});
+ commands.addCommand({
+ name: "formatCode",
+ bindkey: bindKey(bindings.formatCode),
+ exec: function(env, args, request) {
+ $.log("Formatting");
+ editor.exec("format");
+ }
+ });
commands.addCommand({
name: "functionDoc",
bindKey: bindKey(bindings.functionDoc),
diff --git a/src/eXide.js b/src/eXide.js
index 0d3f8f9f..9e6afc2f 100755
--- a/src/eXide.js
+++ b/src/eXide.js
@@ -63,7 +63,7 @@ eXide.app = (function() {
var projects;
var preferences;
var templates = {};
-
+ var menu;
var hitCount = 0;
var startOffset = 0;
var currentOffset = 0;
@@ -75,9 +75,9 @@ eXide.app = (function() {
var hasFocus = true;
return {
-
+
init: function(afterInitCallback) {
- var menu = new eXide.util.Menubar($(".menu"));
+ menu = new eXide.util.Menubar($(".menu"));
projects = new eXide.edit.Projects();
editor = new eXide.edit.Editor(document.getElementById("editor"), menu);
deploymentEditor = new eXide.edit.PackageEditor(projects);
@@ -947,7 +947,6 @@ eXide.app = (function() {
menu.click("#menu-edit-preferences", function() {
preferences.show();
}, "preferences");
-
menu.click("#menu-navigate-definition", function () {
editor.exec("gotoDefinition");
}, "gotoDefinition");
diff --git a/src/editor.js b/src/editor.js
index 552832f5..a80a93d8 100755
--- a/src/editor.js
+++ b/src/editor.js
@@ -54,6 +54,10 @@ eXide.edit.Document = (function() {
return this.$session.getValue();
};
+ Constr.prototype.setText = function(text) {
+ this.$session.setValue(text);
+ };
+
Constr.prototype.getName = function() {
return this.name;
};
@@ -185,9 +189,11 @@ eXide.edit.Editor = (function () {
this.editor.setBehavioursEnabled(true);
this.editor.setShowFoldWidgets(true);
this.editor.setFadeFoldWidgets(false);
-
+ // enable multiple cursors
+ require("ace/multi_select").MultiSelect(this.editor);
+ // register keybindings
eXide.edit.commands.init($this);
-
+
this.outline = new eXide.edit.Outline();
this.addEventListener("activate", this.outline, this.outline.updateOutline);
this.addEventListener("validate", this.outline, this.outline.updateOutline);
@@ -233,7 +239,7 @@ eXide.edit.Editor = (function () {
// register mode helpers
$this.modes = {
- "xquery": new eXide.edit.XQueryModeHelper($this),
+ "xquery": new eXide.edit.XQueryModeHelper($this, menubar),
"xml": new eXide.edit.XMLModeHelper($this),
"html": new eXide.edit.XMLModeHelper($this),
"less": new eXide.edit.LessModeHelper($this),
@@ -380,7 +386,6 @@ eXide.edit.Editor = (function () {
eXide.util.message("Opening " + resource.path + " readonly!");
else
eXide.util.message("Opening " + resource.path);
- $.log("external: %s", externalPath);
var doc = new eXide.edit.Document(resource.name, resource.path, new EditSession(data));
doc.editable = resource.writable;
doc.mime = mime;
@@ -415,6 +420,11 @@ eXide.edit.Editor = (function () {
$this.editor.setSession(doc.$session);
$this.editor.resize();
$this.editor.focus();
+
+ $this.triggerCheck();
+ if (doc.getModeHelper()) {
+ doc.getModeHelper().activate();
+ }
};
Constr.prototype.setMode = function(mode) {
@@ -643,6 +653,10 @@ eXide.edit.Editor = (function () {
};
Constr.prototype.switchTo = function(doc) {
+ var helper = this.activeDoc.getModeHelper();
+ if (helper) {
+ helper.deactivate();
+ }
this.editor.setSession(doc.$session);
this.editor.resize();
this.activeDoc = doc;
@@ -658,6 +672,11 @@ eXide.edit.Editor = (function () {
});
this.updateStatus("");
this.$triggerEvent("activate", [doc]);
+
+ helper = doc.getModeHelper();
+ if (helper) {
+ helper.activate();
+ }
};
Constr.prototype.updateTabStatus = function(oldPath, doc) {
diff --git a/src/mode-helper.js b/src/mode-helper.js
index a37b5e0f..31c2dab7 100644
--- a/src/mode-helper.js
+++ b/src/mode-helper.js
@@ -34,6 +34,12 @@ eXide.edit.ModeHelper = (function () {
Constr.prototype = {
+ activate: function() {
+ },
+
+ deactivate: function() {
+ },
+
/**
* Add a command which can be invoked dynamically by the editor
*/
diff --git a/src/preferences.js b/src/preferences.js
index 91d55fb9..e45b18ca 100644
--- a/src/preferences.js
+++ b/src/preferences.js
@@ -97,7 +97,6 @@ eXide.util.Preferences = (function () {
};
Constr.prototype.applyPreferences = function () {
- $.log("Applying preferences: %o", this.preferences);
var $this = this;
this.editor.setTheme(this.preferences.theme);
this.editor.editor.setShowInvisibles(this.preferences.showInvisibles);
diff --git a/src/xquery-helper.js b/src/xquery-helper.js
index 5ea0d779..7fd131f7 100644
--- a/src/xquery-helper.js
+++ b/src/xquery-helper.js
@@ -25,7 +25,13 @@ eXide.edit.XQueryModeHelper = (function () {
var RE_FUNC_NAME = /^[\$\w:\-_\.]+/;
- Constr = function(editor) {
+ var SemanticHighlighter = require("xqlint/visitors/SemanticHighlighter").SemanticHighlighter;
+ var XQueryParser = require("xqlint/XQueryParser").XQueryParser;
+ var JSONParseTreeHandler = require("xqlint/JSONParseTreeHandler").JSONParseTreeHandler;
+ var Translator = require("xqlint/Translator").Translator;
+ var CodeFormatter = require("xqlint/visitors/CodeFormatter").CodeFormatter;
+
+ Constr = function(editor, menubar) {
this.parent = editor;
this.editor = this.parent.editor;
this.xqDebugger = null;
@@ -39,6 +45,7 @@ eXide.edit.XQueryModeHelper = (function () {
// added to clean function name :
this.trimRe = /^[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u2028\u2029\u202f\u205f\u3000]+|[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u2028\u2029\u202f\u205f\u3000]+$/g;
+ this.addCommand("format", this.format);
this.addCommand("showFunctionDoc", this.showFunctionDoc);
this.addCommand("gotoDefinition", this.gotoDefinition);
this.addCommand("locate", this.locate);
@@ -47,11 +54,25 @@ eXide.edit.XQueryModeHelper = (function () {
this.addCommand("debug", this.initDebugger);
this.addCommand("stepOver", this.stepOver);
this.addCommand("stepInto", this.stepInto);
- }
+
+ var self = this;
+ this.menu = $("#menu-xquery").hide();
+ menubar.click("#menu-xquery-format", function() {
+ self.format(editor.getActiveDocument());
+ }, "formatCode");
+ };
// extends ModeHelper
eXide.util.oop.inherit(Constr, eXide.edit.ModeHelper);
+ Constr.prototype.activate = function() {
+ this.menu.show();
+ };
+
+ Constr.prototype.deactivate = function() {
+ this.menu.hide();
+ };
+
Constr.prototype.closeTag = function (doc, text, row) {
var basePath = "xmldb:exist://" + doc.getBasePath();
var $this = this;
@@ -88,10 +109,15 @@ eXide.edit.XQueryModeHelper = (function () {
dataType: "json",
success: function (data) {
$this.compileError(data, doc);
- onComplete.call(this, true);
+ $this.xqlint(doc);
+ if (onComplete) {
+ onComplete.call(this, true);
+ }
},
error: function (xhr, status) {
- onComplete.call(this, true);
+ if (onComplete) {
+ onComplete.call(this, true);
+ }
$.log("Compile error: %s - %s", status, xhr.responseText);
}
});
@@ -103,19 +129,81 @@ eXide.edit.XQueryModeHelper = (function () {
Constr.prototype.compileError = function(data, doc) {
if (data.result == "fail") {
var err = parseErrMsg(data.error);
- var annotation = [{
+ var annotation = {
row: err.line,
text: err.msg,
type: "error"
- }];
+ };
this.parent.updateStatus(err.msg, doc.getPath() + "#" + err.line);
- doc.getSession().setAnnotations(annotation);
+ var annotations = this.clearAnnotations(doc, "error");
+ annotations.push(annotation);
+ doc.getSession().setAnnotations(annotations);
} else {
- this.parent.clearErrors();
+ doc.getSession().setAnnotations(this.clearAnnotations(doc, "error"));
this.parent.updateStatus("");
}
- }
+ };
+ Constr.prototype.xqlint = function(doc) {
+ var session = doc.getSession();
+ var value = doc.getText();
+ var h = new JSONParseTreeHandler(value);
+ var parser = new XQueryParser(value, h);
+ try {
+ parser.parse_XQuery();
+ var ast = h.getParseTree();
+
+ var highlighter = new SemanticHighlighter(ast, value);
+
+ var mode = doc.getSession().getMode();
+
+ mode.$tokenizer.tokens = highlighter.getTokens();
+ mode.$tokenizer.lines = session.getDocument().getAllLines();
+ session.bgTokenizer.lines = [];
+ session.bgTokenizer.states = [];
+
+ var rows = Object.keys(mode.$tokenizer.tokens);
+ for(var i=0; i < rows.length; i++) {
+ var row = parseInt(rows[i]);
+ session.bgTokenizer.fireUpdateEvent(row, row);
+ }
+
+ var translator = new Translator(ast);
+ ast = translator.translate();
+
+ var markers = ast.markers;
+ var annotations = this.clearAnnotations(doc, "warning");
+ for (var i = 0; i < markers.length; i++) {
+ if (markers[i].type !== "error") {
+ annotations.push({
+ row: markers[i].pos.sl,
+ text: markers[i].message,
+ type: markers[i].type
+ });
+ }
+ }
+ session.setAnnotations(annotations);
+ } catch(e) {
+ $.log("error: %o", e);
+ if(e instanceof parser.ParseException) {
+ // ignore
+ } else {
+ throw e;
+ }
+ }
+ };
+
+ Constr.prototype.clearAnnotations = function(doc, type) {
+ var na = [];
+ var a = doc.getSession().getAnnotations();
+ for (var i = 0; i < a.length; i++) {
+ if (a[i].type !== type) {
+ na.push(a[i]);
+ }
+ }
+ return na;
+ };
+
Constr.prototype.autocomplete = function(doc) {
var lang = require("pilot/lang");
var Range = require("ace/range").Range;
@@ -337,6 +425,17 @@ eXide.edit.XQueryModeHelper = (function () {
}
}
+ Constr.prototype.format = function(doc) {
+ var code = doc.getText();
+ var h = new JSONParseTreeHandler(code);
+ var parser = new XQueryParser(code, h);
+ parser.parse_XQuery();
+ var ast = h.getParseTree();
+ var codeFormatter = new CodeFormatter(ast);
+ var formatted = codeFormatter.format();
+ doc.setText(formatted);
+ };
+
Constr.prototype.gotoFunction = function (doc, name) {
$.log("Goto function %s", name);
var prefix = this.getModuleNamespacePrefix();