| // Copyright 2016 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| import * as Platform from '../../core/platform/platform.js'; |
| |
| import {TextCursor} from './TextCursor.js'; |
| import {SourceRange, TextRange} from './TextRange.js'; |
| |
| export class Text { |
| readonly #value: string; |
| #lineEndings?: number[]; |
| |
| constructor(value: string) { |
| this.#value = value; |
| } |
| |
| lineEndings(): number[] { |
| if (!this.#lineEndings) { |
| this.#lineEndings = Platform.StringUtilities.findLineEndingIndexes(this.#value); |
| } |
| return this.#lineEndings; |
| } |
| |
| value(): string { |
| return this.#value; |
| } |
| |
| lineCount(): number { |
| const lineEndings = this.lineEndings(); |
| return lineEndings.length; |
| } |
| |
| offsetFromPosition(lineNumber: number, columnNumber: number): number { |
| return (lineNumber ? this.lineEndings()[lineNumber - 1] + 1 : 0) + columnNumber; |
| } |
| |
| positionFromOffset(offset: number): Position { |
| const lineEndings = this.lineEndings(); |
| const lineNumber = |
| Platform.ArrayUtilities.lowerBound(lineEndings, offset, Platform.ArrayUtilities.DEFAULT_COMPARATOR); |
| return {lineNumber, columnNumber: offset - (lineNumber && (lineEndings[lineNumber - 1] + 1))}; |
| } |
| |
| lineAt(lineNumber: number): string { |
| 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; |
| } |
| |
| toSourceRange(range: TextRange): SourceRange { |
| const start = this.offsetFromPosition(range.startLine, range.startColumn); |
| const end = this.offsetFromPosition(range.endLine, range.endColumn); |
| return new SourceRange(start, end - start); |
| } |
| |
| toTextRange(sourceRange: SourceRange): TextRange { |
| const cursor = new TextCursor(this.lineEndings()); |
| const result = 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; |
| } |
| |
| replaceRange(range: TextRange, replacement: string): string { |
| const sourceRange = this.toSourceRange(range); |
| return this.#value.substring(0, sourceRange.offset) + replacement + |
| this.#value.substring(sourceRange.offset + sourceRange.length); |
| } |
| |
| extract(range: TextRange): string { |
| const sourceRange = this.toSourceRange(range); |
| return this.#value.substr(sourceRange.offset, sourceRange.length); |
| } |
| } |
| export interface Position { |
| lineNumber: number; |
| columnNumber: number; |
| } |