\n" +
"?- " + query + "\n" +
"*/\n", {
line: lineno,
ch: line.length
});
}
return this;
},
/**
* @param {RegExp} re is the regular expression to search for
* @param {Object} [options]
* @param {number} [options.max] is the max number of hits to return
* @param {number} [options.dir=1] is -1 to search backwards
* @param {number} [options.start] to start at a given line
* @param {number} [options.end] to end at a given line
* @returns {Array.object} list of objects holding the matching line
* content and line number.
*/
search: function(re, options) {
var cm = this.data(pluginName).cm;
var dir = options.dir || 1;
var start = options.start == undefined ? cm.firstLine() : options.start;
var end = options.end == undefined ? cm.lastLine() : options.end;
var matches = [];
if ((dir == -1 && end > start) ||
(dir == 1 && start > end)) {
var tmp = start;
start = end;
end = tmp;
}
if ((dir > 0 && start > end) ||
(dir < 0 && start < end))
return matches;
end += dir;
for (var i = start; i != end; i += dir) {
var line = cm.getLine(i);
if (line.search(re) >= 0) {
matches.push({
line: i + 1,
text: line
});
if (options.max && options.max === matches.length)
return matches;
}
}
return matches;
},
/**
* Go to a given 1-based line number and optionally highlight the
* match(es).
*
* @param {number} line
* @param {Object} [options]
* @param {RegExp} [options.regex] If provided, highlight the
* matches.
* @param {Boolean} [options.showAllMatches] if `true`, show all
* matches in the viewport.
*/
gotoLine: function(line, options) {
var data = this.data(pluginName);
var cm = data.cm;
var ch = 0;
var re;
function clearSearchMarkers(cm) {
if (cm._searchMarkers !== undefined) {
for (var i = 0; i < cm._searchMarkers.length; i++)
cm._searchMarkers[i].clear();
cm.off("cursorActivity", clearSearchMarkers);
}
cm._searchMarkers = [];
}
clearSearchMarkers(cm);
options = options || {};
re = options.regex;
line = line - 1;
if (re) {
ch = cm.getLine(line).search(re);
if (ch < 0)
ch = 0;
}
cm.setCursor({
line: line,
ch: ch
});
var myHeight = cm.getScrollInfo().clientHeight;
var coords = cm.charCoords({
line: line,
ch: 0
}, "local");
cm.scrollTo(null, (coords.top + coords.bottom - myHeight) / 2);
if (re) {
function markMatches(line, className) {
var match;
while ((match = re.exec(cm.getLine(line)))) {
cm._searchMarkers.push(
cm.markText({
line: line,
ch: match.index
}, {
line: line,
ch: match.index + match[0].length
}, {
className: className,
clearOnEnter: true,
clearWhenEmpty: true,
title: "Search match"
}));
}
}
markMatches(line, "CodeMirror-search-match");
if (options.showAllMatches) {
var vp = cm.getViewport();
for (var i = vp.from; i < vp.to; i++) {
if (i != line) {
markMatches(i, "CodeMirror-search-alt-match");
}
}
}
if (cm._searchMarkers.length > 0)
cm.on("cursorActivity", clearSearchMarkers);
} else {
/* mark entire line */
cm._searchMarkers.push(
cm.markText({
line: line,
ch: 0
}, {
line: line,
ch: cm.getLine(line).length
}, {
className: "CodeMirror-search-match",
clearOnEnter: true,
clearWhenEmpty: true,
title: "Target line"
}));
}
return this;
},
/**
* @return {Integer} change generation for this editor
*/
changeGen: function() {
return this.data(pluginName).cm.changeGeneration();
},
isClean: function(gen) {
return this.data(pluginName).cm.isClean(gen);
},
/**
* Associate the editor with the server side (gitty) source
*/
setupStorage: function(storage) {
var data = this.data(pluginName);
var elem = this;
storage.setValue = function(source) {
elem.prologEditor('setSource', source, true);
};
storage.getValue = function() {
return data.cm.getValue();
};
storage.changeGen = function() {
return data.cm.changeGeneration();
};
storage.isClean = function(generation) {
return data.cm.isClean(generation);
};
storage.markClean = function(clean) {
elem.prologEditor('markClean', clean);
};
storage.cleanGeneration = data.cm.changeGeneration();
storage.cleanData = data.cm.getValue();
storage.cleanCheckpoint = "load";
this.storage(storage);
return this;
},
/**
* Act on the current token. Normally invoked after a long click.
*/
contextAction: function() {
var elem = this;
var data = this.data(pluginName);
var here = data.cm.getCursor();
var token = data.cm.getTokenAt(here, true);
var et = data.cm.getEnrichedToken(token);
var locations = data.cm.getTokenReferences(et);
if (locations && locations.length > 0) {
var ul = $.el.ul();
var select = $.el.div({
class: "goto-source"
}, $.el.div("Go to"), ul);
var modalel = $.el.div({
class: "edit-modal"
},
$.el.div({
class: "mask"
}),
select)
for (var i = 0; i < locations.length; i++) {
var loc = locations[i];
$(ul).append($.el.li($.el.a({
'data-locindex': i
}, loc.title)));
}
var coord = data.cm.cursorCoords(true);
$(select).css({
top: coord.bottom,
left: coord.left
});
$("body").append(modalel);
$(modalel).on("click", function(ev) {
var i = $(ev.target).data('locindex');
$(modalel).remove();
if (i !== undefined) {
var loc = locations[i];
if (loc.file) {
elem.closest(".swish").swish('playFile', loc);
} else {
var editor;
// If we are the query editor, we must find the related
// program editor.
if (data.role == "query") {
editor = elem.closest(".prolog-query-editor")
.queryEditor('getProgramEditor');
if (!editor[0])
modal.alert("No related program editor");
} else {
editor = elem;
}
if (editor && editor[0])
editor.prologEditor('gotoLine', loc.line, loc).focus();
}
}
});
$(modalel).show();
}
return this;
},
/*******************************
* QUERY MANIPULATION *
*******************************/
/**
* @param {String} [query] query to get the variables from
* @param {Object} [options]
* @param {Boolean} [options.anon] if `true`, also include _X
* variables.
* @param {Boolean} [options.projection] if `true` and there is
* a projection, only include the variables from the projection.
* @return {List.string} is a list of Prolog variables
* without duplicates
*/
variables: function(query, options) {
var qspan = $.el.span({
class: "query cm-s-prolog"
});
var vars = [];
options = options || {};
CodeMirror.runMode(query, "prolog", qspan);
function addVars(selector) {
var incl = true;
var use_proj = false;
$(qspan).find(selector).each(function() {
var elem = $(this);
var name = elem.text();
if (elem.hasClass("cm-functor")) {
if (name == "projection") {
use_proj = true;
} else if (use_proj) {
incl = false;
}
} else {
if (incl && vars.indexOf(name) < 0)
vars.push(name);
}
});
}
if (options.projection) {
addVars("span.cm-var,span.cm-var-2,span.cm-functor");
} else {
addVars("span.cm-var");
if (options.anon)
addVars("span.cm-var-2");
}
return vars;
},
/**
* Wrap current query in a solution modifier.
* TBD: If there is a selection, only wrap the selection
*
* @param {String} wrapper defines the type of wrapper to use.
*/
wrapSolution: function(wrapper) {
var query = prolog.trimFullStop(this.prologEditor('getSource', "query"));
var that = this;
var vars = this.prologEditor('variables', query);
function wrapQuery(pre, post) {
that.prologEditor('setSource', pre + "(" + query + ")" + post + ".")
.focus();
return that;
}
function prefixQuery(pre) {
that.prologEditor('setSource', pre + query + ".")
.focus();
return that;
}
function order(l) {
var order = [];
for (var i = 0; i < vars.length; i++)
order.push("asc(" + vars[i] + ")");
return order.join(",");
}
switch (wrapper) {
case "Aggregate (count all)":
return wrapQuery("aggregate_all(count, ", ", Count)");
case "Projection":
return prefixQuery("projection([" + vars.join(",") + "]),\n");
case "Order by":
return wrapQuery("order_by([" + order(vars) + "], ", ")");
case "Distinct":
return wrapQuery("distinct([" + vars.join(",") + "], ", ")");
case "Limit":
return wrapQuery("limit(10, ", ")");
case "Time":
return wrapQuery("time(", ")");
case "Debug (trace)":
return wrapQuery("trace, ", "");
default:
alert("Unknown wrapper: \"" + wrapper + "\"");
}
}
}; // methods
tabbed.tabTypes.program = {
dataType: "pl",
typeName: "program",
label: "Program",
contentType: "text/x-prolog",
order: 100,
create: function(dom, options) {
$(dom).addClass("prolog-editor")
.prologEditor($.extend({
save: true
}, options))
.prologEditor('makeCurrent', options);
}
};
if (config.swish.tab_types) {
var editDefaults = {
save: true,
lineNumbers: true
};
for (var i = 0; i < config.swish.tab_types.length; i++) {
var tabType = config.swish.tab_types[i];
if (tabType.editor) {
var options = $.extend({
typeName: tabType.typeName
},
editDefaults,
tabType.editor);
tabType.create = function(dom) {
$(dom).addClass("prolog-editor")
.prologEditor(options);
};
tabbed.tabTypes[tabType.typeName] = tabType;
}
}
}
/**
* The prologEditor jQuery plugin converts a `` into an code
* editor based on [CodeMirror](http://codemirror.net)
*
* @class prologEditor
* @tutorial jquery-doc
* @memberOf $.fn
* @example // Create a default Prolog editor
* $("#editor").prologEditor();
* @example // Extract embedded examples
* $("#editor").prologEditor('getExamples');
* @param {String|Object} [method] Either a method name or the jQuery
* plugin initialization object.
* @param [...] Zero or more arguments passed to the jQuery `method`
*/
$.fn.prologEditor = function(method) {
if (methods[method]) {
return methods[method]
.apply(this, Array.prototype.slice.call(arguments, 1));
} else if (typeof method === 'object' || !method) {
return methods._init.apply(this, arguments);
} else {
$.error('Method ' + method + ' does not exist on jQuery.' + pluginName);
}
};
}(jQuery));
/*******************************
* FUNCTIONS *
*******************************/
CodeMirror.prototype.charOffsetToPos = function(offset) {
var line = this.firstLine();
var last = this.lastLine();
var charno = 0;
for (; line < last; line++) {
var text = this.getLine(line);
if (charno <= offset && charno + text.length >= offset)
return {
line: line,
ch: offset - charno
};
charno += text.length + 1; /* one extra for the newline */
}
};
/*******************************
* EMACS *
*******************************/
CodeMirror.keyMap.emacs.Enter = "newlineAndIndent";
/*******************************
* STYLE CONFIGURATION *
*******************************/
/**
* Include styles provided through the configuration object.
*
* @param {Object} style is an object mapping style names into style
* properties. The properties are also in an object, linking style
* names to values. For example:
*
* ```
* { column: {color: "#8b008b},
* table: {color: "#8b008b, "font-weight":"bold"}
* }
* ```
*/
function loadStyleExtensions(style, prefix) {
var parts = [];
prefix = prefix || "";
parts.push("\n");
$("body").append(parts.join(""));
}
if (config.swish.cm_style)
loadStyleExtensions(config.swish.cm_style,
".cm-s-prolog span.cm-");
if (config.swish.cm_hover_style)
loadStyleExtensions(config.swish.cm_hover_style,
".CodeMirror-hover-tooltip ");
}); // define