/lenasys/trunk

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/lenasys/trunk
15.1.1 by galaxyAbstractor
Started implementation of a new codeviewer using Ace
1
/* ***** BEGIN LICENSE BLOCK *****
2
 * Distributed under the BSD license:
3
 *
4
 * Copyright (c) 2010, Ajax.org B.V.
5
 * All rights reserved.
6
 * 
7
 * Redistribution and use in source and binary forms, with or without
8
 * modification, are permitted provided that the following conditions are met:
9
 *     * Redistributions of source code must retain the above copyright
10
 *       notice, this list of conditions and the following disclaimer.
11
 *     * Redistributions in binary form must reproduce the above copyright
12
 *       notice, this list of conditions and the following disclaimer in the
13
 *       documentation and/or other materials provided with the distribution.
14
 *     * Neither the name of Ajax.org B.V. nor the
15
 *       names of its contributors may be used to endorse or promote products
16
 *       derived from this software without specific prior written permission.
17
 * 
18
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
19
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21
 * DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
22
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
 *
29
 * ***** END LICENSE BLOCK ***** */
30
31
define('ace/mode/json', ['require', 'exports', 'module' , 'ace/lib/oop', 'ace/mode/text', 'ace/tokenizer', 'ace/mode/json_highlight_rules', 'ace/mode/matching_brace_outdent', 'ace/mode/behaviour/cstyle', 'ace/mode/folding/cstyle', 'ace/worker/worker_client'], function(require, exports, module) {
32
33
34
var oop = require("../lib/oop");
35
var TextMode = require("./text").Mode;
36
var Tokenizer = require("../tokenizer").Tokenizer;
37
var HighlightRules = require("./json_highlight_rules").JsonHighlightRules;
38
var MatchingBraceOutdent = require("./matching_brace_outdent").MatchingBraceOutdent;
39
var CstyleBehaviour = require("./behaviour/cstyle").CstyleBehaviour;
40
var CStyleFoldMode = require("./folding/cstyle").FoldMode;
41
var WorkerClient = require("../worker/worker_client").WorkerClient;
42
43
var Mode = function() {
44
    this.$tokenizer = new Tokenizer(new HighlightRules().getRules());
45
    this.$outdent = new MatchingBraceOutdent();
46
    this.$behaviour = new CstyleBehaviour();
47
    this.foldingRules = new CStyleFoldMode();
48
};
49
oop.inherits(Mode, TextMode);
50
51
(function() {
52
53
    this.getNextLineIndent = function(state, line, tab) {
54
        var indent = this.$getIndent(line);
55
56
        if (state == "start") {
57
            var match = line.match(/^.*[\{\(\[]\s*$/);
58
            if (match) {
59
                indent += tab;
60
            }
61
        }
62
63
        return indent;
64
    };
65
66
    this.checkOutdent = function(state, line, input) {
67
        return this.$outdent.checkOutdent(line, input);
68
    };
69
70
    this.autoOutdent = function(state, doc, row) {
71
        this.$outdent.autoOutdent(doc, row);
72
    };
73
74
    this.createWorker = function(session) {
75
        var worker = new WorkerClient(["ace"], "ace/mode/json_worker", "JsonWorker");
76
        worker.attachToDocument(session.getDocument());
77
78
        worker.on("error", function(e) {
79
            session.setAnnotations([e.data]);
80
        });
81
82
        worker.on("ok", function() {
83
            session.clearAnnotations();
84
        });
85
86
        return worker;
87
    };
88
89
90
}).call(Mode.prototype);
91
92
exports.Mode = Mode;
93
});
94
95
define('ace/mode/json_highlight_rules', ['require', 'exports', 'module' , 'ace/lib/oop', 'ace/mode/text_highlight_rules'], function(require, exports, module) {
96
97
98
var oop = require("../lib/oop");
99
var TextHighlightRules = require("./text_highlight_rules").TextHighlightRules;
100
101
var JsonHighlightRules = function() {
102
    this.$rules = {
103
        "start" : [
104
            {
105
                token : "variable", // single line
106
                regex : '["](?:(?:\\\\.)|(?:[^"\\\\]))*?["]\\s*(?=:)'
107
            }, {
108
                token : "string", // single line
109
                regex : '"',
110
                next  : "string"
111
            }, {
112
                token : "constant.numeric", // hex
113
                regex : "0[xX][0-9a-fA-F]+\\b"
114
            }, {
115
                token : "constant.numeric", // float
116
                regex : "[+-]?\\d+(?:(?:\\.\\d*)?(?:[eE][+-]?\\d+)?)?\\b"
117
            }, {
118
                token : "constant.language.boolean",
119
                regex : "(?:true|false)\\b"
120
            }, {
121
                token : "invalid.illegal", // single quoted strings are not allowed
122
                regex : "['](?:(?:\\\\.)|(?:[^'\\\\]))*?[']"
123
            }, {
124
                token : "invalid.illegal", // comments are not allowed
125
                regex : "\\/\\/.*$"
126
            }, {
127
                token : "paren.lparen",
128
                regex : "[[({]"
129
            }, {
130
                token : "paren.rparen",
131
                regex : "[\\])}]"
132
            }, {
133
                token : "text",
134
                regex : "\\s+"
135
            }
136
        ],
137
        "string" : [
138
            {
139
                token : "constant.language.escape",
140
                regex : /\\(?:x[0-9a-fA-F]{2}|u[0-9a-fA-F]{4}|["\\\/bfnrt])/
141
            }, {
142
                token : "string",
143
                regex : '[^"\\\\]+',
144
            }, {
145
                token : "string",
146
                regex : '"',
147
                next  : "start",
148
            }, {
149
                token : "string",
150
                regex : "",
151
                next  : "start",
152
            }
153
        ]
154
    };
155
    
156
};
157
158
oop.inherits(JsonHighlightRules, TextHighlightRules);
159
160
exports.JsonHighlightRules = JsonHighlightRules;
161
});
162
163
define('ace/mode/matching_brace_outdent', ['require', 'exports', 'module' , 'ace/range'], function(require, exports, module) {
164
165
166
var Range = require("../range").Range;
167
168
var MatchingBraceOutdent = function() {};
169
170
(function() {
171
172
    this.checkOutdent = function(line, input) {
173
        if (! /^\s+$/.test(line))
174
            return false;
175
176
        return /^\s*\}/.test(input);
177
    };
178
179
    this.autoOutdent = function(doc, row) {
180
        var line = doc.getLine(row);
181
        var match = line.match(/^(\s*\})/);
182
183
        if (!match) return 0;
184
185
        var column = match[1].length;
186
        var openBracePos = doc.findMatchingBracket({row: row, column: column});
187
188
        if (!openBracePos || openBracePos.row == row) return 0;
189
190
        var indent = this.$getIndent(doc.getLine(openBracePos.row));
191
        doc.replace(new Range(row, 0, row, column-1), indent);
192
    };
193
194
    this.$getIndent = function(line) {
195
        return line.match(/^\s*/)[0];
196
    };
197
198
}).call(MatchingBraceOutdent.prototype);
199
200
exports.MatchingBraceOutdent = MatchingBraceOutdent;
201
});
202
203
define('ace/mode/behaviour/cstyle', ['require', 'exports', 'module' , 'ace/lib/oop', 'ace/mode/behaviour', 'ace/token_iterator', 'ace/lib/lang'], function(require, exports, module) {
204
205
206
var oop = require("../../lib/oop");
207
var Behaviour = require("../behaviour").Behaviour;
208
var TokenIterator = require("../../token_iterator").TokenIterator;
209
var lang = require("../../lib/lang");
210
211
var SAFE_INSERT_IN_TOKENS =
212
    ["text", "paren.rparen", "punctuation.operator"];
213
var SAFE_INSERT_BEFORE_TOKENS =
214
    ["text", "paren.rparen", "punctuation.operator", "comment"];
215
216
217
var autoInsertedBrackets = 0;
218
var autoInsertedRow = -1;
219
var autoInsertedLineEnd = "";
220
var maybeInsertedBrackets = 0;
221
var maybeInsertedRow = -1;
222
var maybeInsertedLineStart = "";
223
var maybeInsertedLineEnd = "";
224
225
var CstyleBehaviour = function () {
226
    
227
    CstyleBehaviour.isSaneInsertion = function(editor, session) {
228
        var cursor = editor.getCursorPosition();
229
        var iterator = new TokenIterator(session, cursor.row, cursor.column);
230
        if (!this.$matchTokenType(iterator.getCurrentToken() || "text", SAFE_INSERT_IN_TOKENS)) {
231
            var iterator2 = new TokenIterator(session, cursor.row, cursor.column + 1);
232
            if (!this.$matchTokenType(iterator2.getCurrentToken() || "text", SAFE_INSERT_IN_TOKENS))
233
                return false;
234
        }
235
        iterator.stepForward();
236
        return iterator.getCurrentTokenRow() !== cursor.row ||
237
            this.$matchTokenType(iterator.getCurrentToken() || "text", SAFE_INSERT_BEFORE_TOKENS);
238
    };
239
    
240
    CstyleBehaviour.$matchTokenType = function(token, types) {
241
        return types.indexOf(token.type || token) > -1;
242
    };
243
    
244
    CstyleBehaviour.recordAutoInsert = function(editor, session, bracket) {
245
        var cursor = editor.getCursorPosition();
246
        var line = session.doc.getLine(cursor.row);
247
        if (!this.isAutoInsertedClosing(cursor, line, autoInsertedLineEnd[0]))
248
            autoInsertedBrackets = 0;
249
        autoInsertedRow = cursor.row;
250
        autoInsertedLineEnd = bracket + line.substr(cursor.column);
251
        autoInsertedBrackets++;
252
    };
253
    
254
    CstyleBehaviour.recordMaybeInsert = function(editor, session, bracket) {
255
        var cursor = editor.getCursorPosition();
256
        var line = session.doc.getLine(cursor.row);
257
        if (!this.isMaybeInsertedClosing(cursor, line))
258
            maybeInsertedBrackets = 0;
259
        maybeInsertedRow = cursor.row;
260
        maybeInsertedLineStart = line.substr(0, cursor.column) + bracket;
261
        maybeInsertedLineEnd = line.substr(cursor.column);
262
        maybeInsertedBrackets++;
263
    };
264
    
265
    CstyleBehaviour.isAutoInsertedClosing = function(cursor, line, bracket) {
266
        return autoInsertedBrackets > 0 &&
267
            cursor.row === autoInsertedRow &&
268
            bracket === autoInsertedLineEnd[0] &&
269
            line.substr(cursor.column) === autoInsertedLineEnd;
270
    };
271
    
272
    CstyleBehaviour.isMaybeInsertedClosing = function(cursor, line) {
273
        return maybeInsertedBrackets > 0 &&
274
            cursor.row === maybeInsertedRow &&
275
            line.substr(cursor.column) === maybeInsertedLineEnd &&
276
            line.substr(0, cursor.column) == maybeInsertedLineStart;
277
    };
278
    
279
    CstyleBehaviour.popAutoInsertedClosing = function() {
280
        autoInsertedLineEnd = autoInsertedLineEnd.substr(1);
281
        autoInsertedBrackets--;
282
    };
283
    
284
    CstyleBehaviour.clearMaybeInsertedClosing = function() {
285
        maybeInsertedBrackets = 0;
286
        maybeInsertedRow = -1;
287
    };
288
289
    this.add("braces", "insertion", function (state, action, editor, session, text) {
290
        var cursor = editor.getCursorPosition();
291
        var line = session.doc.getLine(cursor.row);
292
        if (text == '{') {
293
            var selection = editor.getSelectionRange();
294
            var selected = session.doc.getTextRange(selection);
295
            if (selected !== "" && selected !== "{" && editor.getWrapBehavioursEnabled()) {
296
                return {
297
                    text: '{' + selected + '}',
298
                    selection: false
299
                };
300
            } else if (CstyleBehaviour.isSaneInsertion(editor, session)) {
301
                if (/[\]\}\)]/.test(line[cursor.column])) {
302
                    CstyleBehaviour.recordAutoInsert(editor, session, "}");
303
                    return {
304
                        text: '{}',
305
                        selection: [1, 1]
306
                    };
307
                } else {
308
                    CstyleBehaviour.recordMaybeInsert(editor, session, "{");
309
                    return {
310
                        text: '{',
311
                        selection: [1, 1]
312
                    };
313
                }
314
            }
315
        } else if (text == '}') {
316
            var rightChar = line.substring(cursor.column, cursor.column + 1);
317
            if (rightChar == '}') {
318
                var matching = session.$findOpeningBracket('}', {column: cursor.column + 1, row: cursor.row});
319
                if (matching !== null && CstyleBehaviour.isAutoInsertedClosing(cursor, line, text)) {
320
                    CstyleBehaviour.popAutoInsertedClosing();
321
                    return {
322
                        text: '',
323
                        selection: [1, 1]
324
                    };
325
                }
326
            }
327
        } else if (text == "\n" || text == "\r\n") {
328
            var closing = "";
329
            if (CstyleBehaviour.isMaybeInsertedClosing(cursor, line)) {
330
                closing = lang.stringRepeat("}", maybeInsertedBrackets);
331
                CstyleBehaviour.clearMaybeInsertedClosing();
332
            }
333
            var rightChar = line.substring(cursor.column, cursor.column + 1);
334
            if (rightChar == '}' || closing !== "") {
335
                var openBracePos = session.findMatchingBracket({row: cursor.row, column: cursor.column}, '}');
336
                if (!openBracePos)
337
                     return null;
338
339
                var indent = this.getNextLineIndent(state, line.substring(0, cursor.column), session.getTabString());
340
                var next_indent = this.$getIndent(line);
341
342
                return {
343
                    text: '\n' + indent + '\n' + next_indent + closing,
344
                    selection: [1, indent.length, 1, indent.length]
345
                };
346
            }
347
        }
348
    });
349
350
    this.add("braces", "deletion", function (state, action, editor, session, range) {
351
        var selected = session.doc.getTextRange(range);
352
        if (!range.isMultiLine() && selected == '{') {
353
            var line = session.doc.getLine(range.start.row);
354
            var rightChar = line.substring(range.end.column, range.end.column + 1);
355
            if (rightChar == '}') {
356
                range.end.column++;
357
                return range;
358
            } else {
359
                maybeInsertedBrackets--;
360
            }
361
        }
362
    });
363
364
    this.add("parens", "insertion", function (state, action, editor, session, text) {
365
        if (text == '(') {
366
            var selection = editor.getSelectionRange();
367
            var selected = session.doc.getTextRange(selection);
368
            if (selected !== "" && editor.getWrapBehavioursEnabled()) {
369
                return {
370
                    text: '(' + selected + ')',
371
                    selection: false
372
                };
373
            } else if (CstyleBehaviour.isSaneInsertion(editor, session)) {
374
                CstyleBehaviour.recordAutoInsert(editor, session, ")");
375
                return {
376
                    text: '()',
377
                    selection: [1, 1]
378
                };
379
            }
380
        } else if (text == ')') {
381
            var cursor = editor.getCursorPosition();
382
            var line = session.doc.getLine(cursor.row);
383
            var rightChar = line.substring(cursor.column, cursor.column + 1);
384
            if (rightChar == ')') {
385
                var matching = session.$findOpeningBracket(')', {column: cursor.column + 1, row: cursor.row});
386
                if (matching !== null && CstyleBehaviour.isAutoInsertedClosing(cursor, line, text)) {
387
                    CstyleBehaviour.popAutoInsertedClosing();
388
                    return {
389
                        text: '',
390
                        selection: [1, 1]
391
                    };
392
                }
393
            }
394
        }
395
    });
396
397
    this.add("parens", "deletion", function (state, action, editor, session, range) {
398
        var selected = session.doc.getTextRange(range);
399
        if (!range.isMultiLine() && selected == '(') {
400
            var line = session.doc.getLine(range.start.row);
401
            var rightChar = line.substring(range.start.column + 1, range.start.column + 2);
402
            if (rightChar == ')') {
403
                range.end.column++;
404
                return range;
405
            }
406
        }
407
    });
408
409
    this.add("brackets", "insertion", function (state, action, editor, session, text) {
410
        if (text == '[') {
411
            var selection = editor.getSelectionRange();
412
            var selected = session.doc.getTextRange(selection);
413
            if (selected !== "" && editor.getWrapBehavioursEnabled()) {
414
                return {
415
                    text: '[' + selected + ']',
416
                    selection: false
417
                };
418
            } else if (CstyleBehaviour.isSaneInsertion(editor, session)) {
419
                CstyleBehaviour.recordAutoInsert(editor, session, "]");
420
                return {
421
                    text: '[]',
422
                    selection: [1, 1]
423
                };
424
            }
425
        } else if (text == ']') {
426
            var cursor = editor.getCursorPosition();
427
            var line = session.doc.getLine(cursor.row);
428
            var rightChar = line.substring(cursor.column, cursor.column + 1);
429
            if (rightChar == ']') {
430
                var matching = session.$findOpeningBracket(']', {column: cursor.column + 1, row: cursor.row});
431
                if (matching !== null && CstyleBehaviour.isAutoInsertedClosing(cursor, line, text)) {
432
                    CstyleBehaviour.popAutoInsertedClosing();
433
                    return {
434
                        text: '',
435
                        selection: [1, 1]
436
                    };
437
                }
438
            }
439
        }
440
    });
441
442
    this.add("brackets", "deletion", function (state, action, editor, session, range) {
443
        var selected = session.doc.getTextRange(range);
444
        if (!range.isMultiLine() && selected == '[') {
445
            var line = session.doc.getLine(range.start.row);
446
            var rightChar = line.substring(range.start.column + 1, range.start.column + 2);
447
            if (rightChar == ']') {
448
                range.end.column++;
449
                return range;
450
            }
451
        }
452
    });
453
454
    this.add("string_dquotes", "insertion", function (state, action, editor, session, text) {
455
        if (text == '"' || text == "'") {
456
            var quote = text;
457
            var selection = editor.getSelectionRange();
458
            var selected = session.doc.getTextRange(selection);
459
            if (selected !== "" && selected !== "'" && selected != '"' && editor.getWrapBehavioursEnabled()) {
460
                return {
461
                    text: quote + selected + quote,
462
                    selection: false
463
                };
464
            } else {
465
                var cursor = editor.getCursorPosition();
466
                var line = session.doc.getLine(cursor.row);
467
                var leftChar = line.substring(cursor.column-1, cursor.column);
468
                if (leftChar == '\\') {
469
                    return null;
470
                }
471
                var tokens = session.getTokens(selection.start.row);
472
                var col = 0, token;
473
                var quotepos = -1; // Track whether we're inside an open quote.
474
475
                for (var x = 0; x < tokens.length; x++) {
476
                    token = tokens[x];
477
                    if (token.type == "string") {
478
                      quotepos = -1;
479
                    } else if (quotepos < 0) {
480
                      quotepos = token.value.indexOf(quote);
481
                    }
482
                    if ((token.value.length + col) > selection.start.column) {
483
                        break;
484
                    }
485
                    col += tokens[x].value.length;
486
                }
487
                if (!token || (quotepos < 0 && token.type !== "comment" && (token.type !== "string" || ((selection.start.column !== token.value.length+col-1) && token.value.lastIndexOf(quote) === token.value.length-1)))) {
488
                    if (!CstyleBehaviour.isSaneInsertion(editor, session))
489
                        return;
490
                    return {
491
                        text: quote + quote,
492
                        selection: [1,1]
493
                    };
494
                } else if (token && token.type === "string") {
495
                    var rightChar = line.substring(cursor.column, cursor.column + 1);
496
                    if (rightChar == quote) {
497
                        return {
498
                            text: '',
499
                            selection: [1, 1]
500
                        };
501
                    }
502
                }
503
            }
504
        }
505
    });
506
507
    this.add("string_dquotes", "deletion", function (state, action, editor, session, range) {
508
        var selected = session.doc.getTextRange(range);
509
        if (!range.isMultiLine() && (selected == '"' || selected == "'")) {
510
            var line = session.doc.getLine(range.start.row);
511
            var rightChar = line.substring(range.start.column + 1, range.start.column + 2);
512
            if (rightChar == selected) {
513
                range.end.column++;
514
                return range;
515
            }
516
        }
517
    });
518
519
};
520
521
oop.inherits(CstyleBehaviour, Behaviour);
522
523
exports.CstyleBehaviour = CstyleBehaviour;
524
});
525
526
define('ace/mode/folding/cstyle', ['require', 'exports', 'module' , 'ace/lib/oop', 'ace/range', 'ace/mode/folding/fold_mode'], function(require, exports, module) {
527
528
529
var oop = require("../../lib/oop");
530
var Range = require("../../range").Range;
531
var BaseFoldMode = require("./fold_mode").FoldMode;
532
533
var FoldMode = exports.FoldMode = function(commentRegex) {
534
    if (commentRegex) {
535
        this.foldingStartMarker = new RegExp(
536
            this.foldingStartMarker.source.replace(/\|[^|]*?$/, "|" + commentRegex.start)
537
        );
538
        this.foldingStopMarker = new RegExp(
539
            this.foldingStopMarker.source.replace(/\|[^|]*?$/, "|" + commentRegex.end)
540
        );
541
    }
542
};
543
oop.inherits(FoldMode, BaseFoldMode);
544
545
(function() {
546
547
    this.foldingStartMarker = /(\{|\[)[^\}\]]*$|^\s*(\/\*)/;
548
    this.foldingStopMarker = /^[^\[\{]*(\}|\])|^[\s\*]*(\*\/)/;
549
550
    this.getFoldWidgetRange = function(session, foldStyle, row) {
551
        var line = session.getLine(row);
552
        var match = line.match(this.foldingStartMarker);
553
        if (match) {
554
            var i = match.index;
555
556
            if (match[1])
557
                return this.openingBracketBlock(session, match[1], row, i);
558
559
            return session.getCommentFoldRange(row, i + match[0].length, 1);
560
        }
561
562
        if (foldStyle !== "markbeginend")
563
            return;
564
565
        var match = line.match(this.foldingStopMarker);
566
        if (match) {
567
            var i = match.index + match[0].length;
568
569
            if (match[1])
570
                return this.closingBracketBlock(session, match[1], row, i);
571
572
            return session.getCommentFoldRange(row, i, -1);
573
        }
574
    };
575
576
}).call(FoldMode.prototype);
577
578
});