8c1c04223a3ba0b3cc3bbc7168abc1209e0c89ca
[redakcja.git] / redakcja / static / js / lib / codemirror / stringstream.js
1 /* String streams are the things fed to parsers (which can feed them
2  * to a tokenizer if they want). They provide peek and next methods
3  * for looking at the current character (next 'consumes' this
4  * character, peek does not), and a get method for retrieving all the
5  * text that was consumed since the last time get was called.
6  *
7  * An easy mistake to make is to let a StopIteration exception finish
8  * the token stream while there are still characters pending in the
9  * string stream (hitting the end of the buffer while parsing a
10  * token). To make it easier to detect such errors, the stringstreams
11  * throw an exception when this happens.
12  */
13
14 // Make a stringstream stream out of an iterator that returns strings.
15 // This is applied to the result of traverseDOM (see codemirror.js),
16 // and the resulting stream is fed to the parser.
17 window.stringStream = function(source){
18   // String that's currently being iterated over.
19   var current = "";
20   // Position in that string.
21   var pos = 0;
22   // Accumulator for strings that have been iterated over but not
23   // get()-ed yet.
24   var accum = "";
25   // Make sure there are more characters ready, or throw
26   // StopIteration.
27   function ensureChars() {
28     while (pos == current.length) {
29       accum += current;
30       current = ""; // In case source.next() throws
31       pos = 0;
32       try {current = source.next();}
33       catch (e) {
34         if (e != StopIteration) throw e;
35         else return false;
36       }
37     }
38     return true;
39   }
40
41   return {
42     // Return the next character in the stream.
43     peek: function() {
44       if (!ensureChars()) return null;
45       return current.charAt(pos);
46     },
47     // Get the next character, throw StopIteration if at end, check
48     // for unused content.
49     next: function() {
50       if (!ensureChars()) {
51         if (accum.length > 0)
52           throw "End of stringstream reached without emptying buffer ('" + accum + "').";
53         else
54           throw StopIteration;
55       }
56       return current.charAt(pos++);
57     },
58     // Return the characters iterated over since the last call to
59     // .get().
60     get: function() {
61       var temp = accum;
62       accum = "";
63       if (pos > 0){
64         temp += current.slice(0, pos);
65         current = current.slice(pos);
66         pos = 0;
67       }
68       return temp;
69     },
70     // Push a string back into the stream.
71     push: function(str) {
72       current = current.slice(0, pos) + str + current.slice(pos);
73     },
74     lookAhead: function(str, consume, skipSpaces, caseInsensitive) {
75       function cased(str) {return caseInsensitive ? str.toLowerCase() : str;}
76       str = cased(str);
77       var found = false;
78
79       var _accum = accum, _pos = pos;
80       if (skipSpaces) this.nextWhileMatches(/[\s\u00a0]/);
81
82       while (true) {
83         var end = pos + str.length, left = current.length - pos;
84         if (end <= current.length) {
85           found = str == cased(current.slice(pos, end));
86           pos = end;
87           break;
88         }
89         else if (str.slice(0, left) == cased(current.slice(pos))) {
90           accum += current; current = "";
91           try {current = source.next();}
92           catch (e) {break;}
93           pos = 0;
94           str = str.slice(left);
95         }
96         else {
97           break;
98         }
99       }
100
101       if (!(found && consume)) {
102         current = accum.slice(_accum.length) + current;
103         pos = _pos;
104         accum = _accum;
105       }
106
107       return found;
108     },
109
110     // Utils built on top of the above
111     more: function() {
112       return this.peek() !== null;
113     },
114     applies: function(test) {
115       var next = this.peek();
116       return (next !== null && test(next));
117     },
118     nextWhile: function(test) {
119       var next;
120       while ((next = this.peek()) !== null && test(next))
121         this.next();
122     },
123     matches: function(re) {
124       var next = this.peek();
125       return (next !== null && re.test(next));
126     },
127     nextWhileMatches: function(re) {
128       var next;
129       while ((next = this.peek()) !== null && re.test(next))
130         this.next();
131     },
132     equals: function(ch) {
133       return ch === this.peek();
134     },
135     endOfLine: function() {
136       var next = this.peek();
137       return next == null || next == "\n";
138     }
139   };
140 };