| // Copyright 2016 The Chromium Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| /** | |
| * @unrestricted | |
| */ | |
| export class Text { | |
| /** | |
| * @param {string} value | |
| */ | |
| constructor(value) { | |
| this._value = value; | |
| } | |
| /** | |
| * @return {!Array<number>} | |
| */ | |
| lineEndings() { | |
| if (!this._lineEndings) { | |
| this._lineEndings = this._value.computeLineEndings(); | |
| } | |
| return this._lineEndings; | |
| } | |
| /** | |
| * @return {string} | |
| */ | |
| value() { | |
| return this._value; | |
| } | |
| /** | |
| * @return {number} | |
| */ | |
| lineCount() { | |
| const lineEndings = this.lineEndings(); | |
| return lineEndings.length; | |
| } | |
| /** | |
| * @param {number} lineNumber | |
| * @param {number} columnNumber | |
| * @return {number} | |
| */ | |
| offsetFromPosition(lineNumber, columnNumber) { | |
| return (lineNumber ? this.lineEndings()[lineNumber - 1] + 1 : 0) + columnNumber; | |
| } | |
| /** | |
| * @param {number} offset | |
| * @return {!TextUtils.Text.Position} | |
| */ | |
| positionFromOffset(offset) { | |
| const lineEndings = this.lineEndings(); | |
| const lineNumber = lineEndings.lowerBound(offset); | |
| return {lineNumber: lineNumber, columnNumber: offset - (lineNumber && (lineEndings[lineNumber - 1] + 1))}; | |
| } | |
| /** | |
| * @return {string} | |
| */ | |
| lineAt(lineNumber) { | |
| const lineEndings = this.lineEndings(); | |
| const lineStart = lineNumber > 0 ? lineEndings[lineNumber - 1] + 1 : 0; | |
| const lineEnd = lineEndings[lineNumber]; | |
| let lineContent = this._value.substring(lineStart, lineEnd); | |
| if (lineContent.length > 0 && lineContent.charAt(lineContent.length - 1) === '\r') { | |
| lineContent = lineContent.substring(0, lineContent.length - 1); | |
| } | |
| return lineContent; | |
| } | |
| /** | |
| * @param {!TextUtils.TextRange} range | |
| * @return {!TextUtils.SourceRange} | |
| */ | |
| toSourceRange(range) { | |
| const start = this.offsetFromPosition(range.startLine, range.startColumn); | |
| const end = this.offsetFromPosition(range.endLine, range.endColumn); | |
| return new TextUtils.SourceRange(start, end - start); | |
| } | |
| /** | |
| * @param {!TextUtils.SourceRange} sourceRange | |
| * @return {!TextUtils.TextRange} | |
| */ | |
| toTextRange(sourceRange) { | |
| const cursor = new TextCursor(this.lineEndings()); | |
| const result = TextUtils.TextRange.createFromLocation(0, 0); | |
| cursor.resetTo(sourceRange.offset); | |
| result.startLine = cursor.lineNumber(); | |
| result.startColumn = cursor.columnNumber(); | |
| cursor.advance(sourceRange.offset + sourceRange.length); | |
| result.endLine = cursor.lineNumber(); | |
| result.endColumn = cursor.columnNumber(); | |
| return result; | |
| } | |
| /** | |
| * @param {!TextUtils.TextRange} range | |
| * @param {string} replacement | |
| * @return {string} | |
| */ | |
| replaceRange(range, replacement) { | |
| const sourceRange = this.toSourceRange(range); | |
| return this._value.substring(0, sourceRange.offset) + replacement + | |
| this._value.substring(sourceRange.offset + sourceRange.length); | |
| } | |
| /** | |
| * @param {!TextUtils.TextRange} range | |
| * @return {string} | |
| */ | |
| extract(range) { | |
| const sourceRange = this.toSourceRange(range); | |
| return this._value.substr(sourceRange.offset, sourceRange.length); | |
| } | |
| } | |
| /** | |
| * @unrestricted | |
| */ | |
| export class TextCursor { | |
| /** | |
| * @param {!Array<number>} lineEndings | |
| */ | |
| constructor(lineEndings) { | |
| this._lineEndings = lineEndings; | |
| this._offset = 0; | |
| this._lineNumber = 0; | |
| this._columnNumber = 0; | |
| } | |
| /** | |
| * @param {number} offset | |
| */ | |
| advance(offset) { | |
| this._offset = offset; | |
| while (this._lineNumber < this._lineEndings.length && this._lineEndings[this._lineNumber] < this._offset) { | |
| ++this._lineNumber; | |
| } | |
| this._columnNumber = this._lineNumber ? this._offset - this._lineEndings[this._lineNumber - 1] - 1 : this._offset; | |
| } | |
| /** | |
| * @return {number} | |
| */ | |
| offset() { | |
| return this._offset; | |
| } | |
| /** | |
| * @param {number} offset | |
| */ | |
| resetTo(offset) { | |
| this._offset = offset; | |
| this._lineNumber = this._lineEndings.lowerBound(offset); | |
| this._columnNumber = this._lineNumber ? this._offset - this._lineEndings[this._lineNumber - 1] - 1 : this._offset; | |
| } | |
| /** | |
| * @return {number} | |
| */ | |
| lineNumber() { | |
| return this._lineNumber; | |
| } | |
| /** | |
| * @return {number} | |
| */ | |
| columnNumber() { | |
| return this._columnNumber; | |
| } | |
| } | |
| /* Legacy exported object */ | |
| self.TextUtils = self.TextUtils || {}; | |
| /* Legacy exported object */ | |
| TextUtils = TextUtils || {}; | |
| /** @constructor */ | |
| TextUtils.Text = Text; | |
| /** @constructor */ | |
| TextUtils.TextCursor = TextCursor; | |
| /** @typedef {{lineNumber: number, columnNumber: number}} */ | |
| TextUtils.Text.Position; |