/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45/* Based on @sergeche's work in his emmet plugin */67import { TextDocument } from 'vscode';89/**10* A stream reader for VSCode's `TextDocument`11* Based on @emmetio/stream-reader and @emmetio/atom-plugin12*/13export class DocumentStreamReader {14private document: TextDocument;15private start: number;16private _eof: number;17private _sof: number;18public pos: number;1920constructor(document: TextDocument, pos?: number, limit?: [number, number]) {21this.document = document;22this.start = this.pos = pos ? pos : 0;23this._sof = limit ? limit[0] : 0;24this._eof = limit ? limit[1] : document.getText().length;25}2627/**28* Returns true only if the stream is at the start of the file.29*/30sof(): boolean {31return this.pos <= this._sof;32}3334/**35* Returns true only if the stream is at the end of the file.36*/37eof(): boolean {38return this.pos >= this._eof;39}4041/**42* Creates a new stream instance which is limited to given range for given document43*/44limit(start: number, end: number): DocumentStreamReader {45return new DocumentStreamReader(this.document, start, [start, end]);46}4748/**49* Returns the next character code in the stream without advancing it.50* Will return NaN at the end of the file.51*/52peek(): number {53if (this.eof()) {54return NaN;55}56return this.document.getText().charCodeAt(this.pos);57}5859/**60* Returns the next character in the stream and advances it.61* Also returns NaN when no more characters are available.62*/63next(): number {64if (this.eof()) {65return NaN;66}6768const code = this.document.getText().charCodeAt(this.pos);69this.pos++;7071if (this.eof()) {72// restrict pos to eof, if in case it got moved beyond eof73this.pos = this._eof;74}7576return code;77}7879/**80* Backs up the stream n characters. Backing it up further than the81* start of the current token will cause things to break, so be careful.82*/83backUp(n: number): number {84this.pos -= n;85if (this.pos < 0) {86this.pos = 0;87}88return this.peek();89}9091/**92* Get the string between the start of the current token and the93* current stream position.94*/95current(): string {96return this.substring(this.start, this.pos);97}9899/**100* Returns contents for given range101*/102substring(from: number, to: number): string {103return this.document.getText().substring(from, to);104}105106/**107* Creates error object with current stream state108*/109error(message: string): Error {110const err = new Error(`${message} at offset ${this.pos}`);111return err;112}113114/**115* `match` can be a character code or a function that takes a character code116* and returns a boolean. If the next character in the stream 'matches'117* the given argument, it is consumed and returned.118* Otherwise, `false` is returned.119*/120eat(match: number | Function): boolean {121const ch = this.peek();122const ok = typeof match === 'function' ? match(ch) : ch === match;123124if (ok) {125this.next();126}127128return ok;129}130131/**132* Repeatedly calls <code>eat</code> with the given argument, until it133* fails. Returns <code>true</code> if any characters were eaten.134*/135eatWhile(match: number | Function): boolean {136const start = this.pos;137while (!this.eof() && this.eat(match)) { }138return this.pos !== start;139}140}141142143