| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386 |
- /* ***** 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 Tokenizer = require("../tokenizer").Tokenizer;
- var TextHighlightRules = require("./text_highlight_rules").TextHighlightRules;
- var Behaviour = require("./behaviour").Behaviour;
- var unicode = require("../unicode");
- var lang = require("../lib/lang");
- var TokenIterator = require("../token_iterator").TokenIterator;
- var Range = require("../range").Range;
- var Mode = function() {
- this.HighlightRules = TextHighlightRules;
- this.$behaviour = new Behaviour();
- };
- (function() {
- this.tokenRe = new RegExp("^["
- + unicode.packages.L
- + unicode.packages.Mn + unicode.packages.Mc
- + unicode.packages.Nd
- + unicode.packages.Pc + "\\$_]+", "g"
- );
- this.nonTokenRe = new RegExp("^(?:[^"
- + unicode.packages.L
- + unicode.packages.Mn + unicode.packages.Mc
- + unicode.packages.Nd
- + unicode.packages.Pc + "\\$_]|\\s])+", "g"
- );
- this.getTokenizer = function() {
- if (!this.$tokenizer) {
- this.$highlightRules = this.$highlightRules || new this.HighlightRules();
- this.$tokenizer = new Tokenizer(this.$highlightRules.getRules());
- }
- return this.$tokenizer;
- };
- this.lineCommentStart = "";
- this.blockComment = "";
- this.toggleCommentLines = function(state, session, startRow, endRow) {
- var doc = session.doc;
- var ignoreBlankLines = true;
- var shouldRemove = true;
- var minIndent = Infinity;
- var tabSize = session.getTabSize();
- var insertAtTabStop = false;
- if (!this.lineCommentStart) {
- if (!this.blockComment)
- return false;
- var lineCommentStart = this.blockComment.start;
- var lineCommentEnd = this.blockComment.end;
- var regexpStart = new RegExp("^(\\s*)(?:" + lang.escapeRegExp(lineCommentStart) + ")");
- var regexpEnd = new RegExp("(?:" + lang.escapeRegExp(lineCommentEnd) + ")\\s*$");
- var comment = function(line, i) {
- if (testRemove(line, i))
- return;
- if (!ignoreBlankLines || /\S/.test(line)) {
- doc.insertInLine({row: i, column: line.length}, lineCommentEnd);
- doc.insertInLine({row: i, column: minIndent}, lineCommentStart);
- }
- };
- var uncomment = function(line, i) {
- var m;
- if (m = line.match(regexpEnd))
- doc.removeInLine(i, line.length - m[0].length, line.length);
- if (m = line.match(regexpStart))
- doc.removeInLine(i, m[1].length, m[0].length);
- };
- var testRemove = function(line, row) {
- if (regexpStart.test(line))
- return true;
- var tokens = session.getTokens(row);
- for (var i = 0; i < tokens.length; i++) {
- if (tokens[i].type === 'comment')
- return true;
- }
- };
- } else {
- if (Array.isArray(this.lineCommentStart)) {
- var regexpStart = this.lineCommentStart.map(lang.escapeRegExp).join("|");
- var lineCommentStart = this.lineCommentStart[0];
- } else {
- var regexpStart = lang.escapeRegExp(this.lineCommentStart);
- var lineCommentStart = this.lineCommentStart;
- }
- regexpStart = new RegExp("^(\\s*)(?:" + regexpStart + ") ?");
-
- insertAtTabStop = session.getUseSoftTabs();
- var uncomment = function(line, i) {
- var m = line.match(regexpStart);
- if (!m) return;
- var start = m[1].length, end = m[0].length;
- if (!shouldInsertSpace(line, start, end) && m[0][end - 1] == " ")
- end--;
- doc.removeInLine(i, start, end);
- };
- var commentWithSpace = lineCommentStart + " ";
- var comment = function(line, i) {
- if (!ignoreBlankLines || /\S/.test(line)) {
- if (shouldInsertSpace(line, minIndent, minIndent))
- doc.insertInLine({row: i, column: minIndent}, commentWithSpace);
- else
- doc.insertInLine({row: i, column: minIndent}, lineCommentStart);
- }
- };
- var testRemove = function(line, i) {
- return regexpStart.test(line);
- };
-
- var shouldInsertSpace = function(line, before, after) {
- var spaces = 0;
- while (before-- && line.charAt(before) == " ")
- spaces++;
- if (spaces % tabSize != 0)
- return false;
- var spaces = 0;
- while (line.charAt(after++) == " ")
- spaces++;
- if (tabSize > 2)
- return spaces % tabSize != tabSize - 1;
- else
- return spaces % tabSize == 0;
- return true;
- };
- }
- function iter(fun) {
- for (var i = startRow; i <= endRow; i++)
- fun(doc.getLine(i), i);
- }
- var minEmptyLength = Infinity;
- iter(function(line, i) {
- var indent = line.search(/\S/);
- if (indent !== -1) {
- if (indent < minIndent)
- minIndent = indent;
- if (shouldRemove && !testRemove(line, i))
- shouldRemove = false;
- } else if (minEmptyLength > line.length) {
- minEmptyLength = line.length;
- }
- });
- if (minIndent == Infinity) {
- minIndent = minEmptyLength;
- ignoreBlankLines = false;
- shouldRemove = false;
- }
- if (insertAtTabStop && minIndent % tabSize != 0)
- minIndent = Math.floor(minIndent / tabSize) * tabSize;
- iter(shouldRemove ? uncomment : comment);
- };
- this.toggleBlockComment = function(state, session, range, cursor) {
- var comment = this.blockComment;
- if (!comment)
- return;
- if (!comment.start && comment[0])
- comment = comment[0];
- var iterator = new TokenIterator(session, cursor.row, cursor.column);
- var token = iterator.getCurrentToken();
- var sel = session.selection;
- var initialRange = session.selection.toOrientedRange();
- var startRow, colDiff;
- if (token && /comment/.test(token.type)) {
- var startRange, endRange;
- while (token && /comment/.test(token.type)) {
- var i = token.value.indexOf(comment.start);
- if (i != -1) {
- var row = iterator.getCurrentTokenRow();
- var column = iterator.getCurrentTokenColumn() + i;
- startRange = new Range(row, column, row, column + comment.start.length);
- break;
- }
- token = iterator.stepBackward();
- }
- var iterator = new TokenIterator(session, cursor.row, cursor.column);
- var token = iterator.getCurrentToken();
- while (token && /comment/.test(token.type)) {
- var i = token.value.indexOf(comment.end);
- if (i != -1) {
- var row = iterator.getCurrentTokenRow();
- var column = iterator.getCurrentTokenColumn() + i;
- endRange = new Range(row, column, row, column + comment.end.length);
- break;
- }
- token = iterator.stepForward();
- }
- if (endRange)
- session.remove(endRange);
- if (startRange) {
- session.remove(startRange);
- startRow = startRange.start.row;
- colDiff = -comment.start.length;
- }
- } else {
- colDiff = comment.start.length;
- startRow = range.start.row;
- session.insert(range.end, comment.end);
- session.insert(range.start, comment.start);
- }
- // todo: selection should have ended up in the right place automatically!
- if (initialRange.start.row == startRow)
- initialRange.start.column += colDiff;
- if (initialRange.end.row == startRow)
- initialRange.end.column += colDiff;
- session.selection.fromOrientedRange(initialRange);
- };
- this.getNextLineIndent = function(state, line, tab) {
- return this.$getIndent(line);
- };
- this.checkOutdent = function(state, line, input) {
- return false;
- };
- this.autoOutdent = function(state, doc, row) {
- };
- this.$getIndent = function(line) {
- return line.match(/^\s*/)[0];
- };
- this.createWorker = function(session) {
- return null;
- };
- this.createModeDelegates = function (mapping) {
- this.$embeds = [];
- this.$modes = {};
- for (var i in mapping) {
- if (mapping[i]) {
- this.$embeds.push(i);
- this.$modes[i] = new mapping[i]();
- }
- }
- var delegations = ['toggleBlockComment', 'toggleCommentLines', 'getNextLineIndent',
- 'checkOutdent', 'autoOutdent', 'transformAction', 'getCompletions'];
- for (var i = 0; i < delegations.length; i++) {
- (function(scope) {
- var functionName = delegations[i];
- var defaultHandler = scope[functionName];
- scope[delegations[i]] = function() {
- return this.$delegator(functionName, arguments, defaultHandler);
- };
- } (this));
- }
- };
- this.$delegator = function(method, args, defaultHandler) {
- var state = args[0];
- if (typeof state != "string")
- state = state[0];
- for (var i = 0; i < this.$embeds.length; i++) {
- if (!this.$modes[this.$embeds[i]]) continue;
- var split = state.split(this.$embeds[i]);
- if (!split[0] && split[1]) {
- args[0] = split[1];
- var mode = this.$modes[this.$embeds[i]];
- return mode[method].apply(mode, args);
- }
- }
- var ret = defaultHandler.apply(this, args);
- return defaultHandler ? ret : undefined;
- };
- this.transformAction = function(state, action, editor, session, param) {
- if (this.$behaviour) {
- var behaviours = this.$behaviour.getBehaviours();
- for (var key in behaviours) {
- if (behaviours[key][action]) {
- var ret = behaviours[key][action].apply(this, arguments);
- if (ret) {
- return ret;
- }
- }
- }
- }
- };
-
- this.getKeywords = function(append) {
- // this is for autocompletion to pick up regexp'ed keywords
- if (!this.completionKeywords) {
- var rules = this.$tokenizer.rules;
- var completionKeywords = [];
- for (var rule in rules) {
- var ruleItr = rules[rule];
- for (var r = 0, l = ruleItr.length; r < l; r++) {
- if (typeof ruleItr[r].token === "string") {
- if (/keyword|support|storage/.test(ruleItr[r].token))
- completionKeywords.push(ruleItr[r].regex);
- }
- else if (typeof ruleItr[r].token === "object") {
- for (var a = 0, aLength = ruleItr[r].token.length; a < aLength; a++) {
- if (/keyword|support|storage/.test(ruleItr[r].token[a])) {
- // drop surrounding parens
- var rule = ruleItr[r].regex.match(/\(.+?\)/g)[a];
- completionKeywords.push(rule.substr(1, rule.length - 2));
- }
- }
- }
- }
- }
- this.completionKeywords = completionKeywords;
- }
- // this is for highlighting embed rules, like HAML/Ruby or Obj-C/C
- if (!append)
- return this.$keywordList;
- return completionKeywords.concat(this.$keywordList || []);
- };
-
- this.$createKeywordList = function() {
- if (!this.$highlightRules)
- this.getTokenizer();
- return this.$keywordList = this.$highlightRules.$keywordList || [];
- };
- this.getCompletions = function(state, session, pos, prefix) {
- var keywords = this.$keywordList || this.$createKeywordList();
- return keywords.map(function(word) {
- return {
- name: word,
- value: word,
- score: 0,
- meta: "keyword"
- };
- });
- };
- this.$id = "ace/mode/text";
- }).call(Mode.prototype);
- exports.Mode = Mode;
- });
|