X-Git-Url: https://git.mdrn.pl/redakcja.git/blobdiff_plain/e8fe7696e2fc6334ef96495ea6d9d71354c53b6d..65048f64d018c105be3921ca4bcb9f5183e2382e:/project/static/js/codemirror/stringstream.js?ds=inline diff --git a/project/static/js/codemirror/stringstream.js b/project/static/js/codemirror/stringstream.js new file mode 100644 index 00000000..6d9355f4 --- /dev/null +++ b/project/static/js/codemirror/stringstream.js @@ -0,0 +1,140 @@ +/* String streams are the things fed to parsers (which can feed them + * to a tokenizer if they want). They provide peek and next methods + * for looking at the current character (next 'consumes' this + * character, peek does not), and a get method for retrieving all the + * text that was consumed since the last time get was called. + * + * An easy mistake to make is to let a StopIteration exception finish + * the token stream while there are still characters pending in the + * string stream (hitting the end of the buffer while parsing a + * token). To make it easier to detect such errors, the strings throw + * an exception when this happens. + */ + +// Make a string stream out of an iterator that returns strings. This +// is applied to the result of traverseDOM (see codemirror.js), and +// the resulting stream is fed to the parser. +window.stringStream = function(source){ + // String that's currently being iterated over. + var current = ""; + // Position in that string. + var pos = 0; + // Accumulator for strings that have been iterated over but not + // get()-ed yet. + var accum = ""; + // Make sure there are more characters ready, or throw + // StopIteration. + function ensureChars() { + while (pos == current.length) { + accum += current; + current = ""; // In case source.next() throws + pos = 0; + try {current = source.next();} + catch (e) { + if (e != StopIteration) throw e; + else return false; + } + } + return true; + } + + return { + // Return the next character in the stream. + peek: function() { + if (!ensureChars()) return null; + return current.charAt(pos); + }, + // Get the next character, throw StopIteration if at end, check + // for unused content. + next: function() { + if (!ensureChars()) { + if (accum.length > 0) + throw "End of stringstream reached without emptying buffer ('" + accum + "')."; + else + throw StopIteration; + } + return current.charAt(pos++); + }, + // Return the characters iterated over since the last call to + // .get(). + get: function() { + var temp = accum; + accum = ""; + if (pos > 0){ + temp += current.slice(0, pos); + current = current.slice(pos); + pos = 0; + } + return temp; + }, + // Push a string back into the stream. + push: function(str) { + current = current.slice(0, pos) + str + current.slice(pos); + }, + lookAhead: function(str, consume, skipSpaces, caseInsensitive) { + function cased(str) {return caseInsensitive ? str.toLowerCase() : str;} + str = cased(str); + var found = false; + + var _accum = accum, _pos = pos; + if (skipSpaces) this.nextWhileMatches(/[\s\u00a0]/); + + while (true) { + var end = pos + str.length, left = current.length - pos; + if (end <= current.length) { + found = str == cased(current.slice(pos, end)); + pos = end; + break; + } + else if (str.slice(0, left) == cased(current.slice(pos))) { + accum += current; current = ""; + try {current = source.next();} + catch (e) {break;} + pos = 0; + str = str.slice(left); + } + else { + break; + } + } + + if (!(found && consume)) { + current = accum.slice(_accum.length) + current; + pos = _pos; + accum = _accum; + } + + return found; + }, + + // Utils built on top of the above + more: function() { + return this.peek() !== null; + }, + applies: function(test) { + var next = this.peek(); + return (next !== null && test(next)); + }, + nextWhile: function(test) { + var next; + while ((next = this.peek()) !== null && test(next)) + this.next(); + }, + matches: function(re) { + var next = this.peek(); + return (next !== null && re.test(next)); + }, + nextWhileMatches: function(re) { + var next; + while ((next = this.peek()) !== null && re.test(next)) + this.next(); + }, + equals: function(ch) { + return ch === this.peek(); + }, + endOfLine: function() { + var next = this.peek(); + return next == null || next == "\n"; + } + }; +};