public static fromString(rawText: string): IRawTextSource { // Count the number of lines that end with \r\n let carriageReturnCnt = 0; let lastCarriageReturnIndex = -1; while ((lastCarriageReturnIndex = rawText.indexOf('\r', lastCarriageReturnIndex + 1)) !== -1) { carriageReturnCnt++; } const containsRTL = strings.containsRTL(rawText); const isBasicASCII = (containsRTL ? false : strings.isBasicASCII(rawText)); // Split the text into lines const lines = rawText.split(/\r\n|\r|\n/); // Remove the BOM (if present) let BOM = ''; if (strings.startsWithUTF8BOM(lines[0])) { BOM = strings.UTF8_BOM_CHARACTER; lines[0] = lines[0].substr(1); } return { BOM: BOM, lines: lines, containsRTL: containsRTL, isBasicASCII: isBasicASCII, totalCRCount: carriageReturnCnt }; }
public acceptChunk(chunk: string): void { if (chunk.length === 0) { return; } this.totalLength += chunk.length; this._updateCRCount(chunk); if (!this.containsRTL) { this.containsRTL = strings.containsRTL(chunk); } // Avoid dealing with a chunk that ends in \r (push the \r to the next chunk) if (this.leftoverEndsInCR) { chunk = '\r' + chunk; } if (chunk.charCodeAt(chunk.length - 1) === CharCode.CarriageReturn) { this.leftoverEndsInCR = true; chunk = chunk.substr(0, chunk.length - 1); } else { this.leftoverEndsInCR = false; } let lines = chunk.split(/\r\n|\r|\n/); if (lines.length === 1) { // no \r or \n encountered this.leftoverPrevChunk += lines[0]; return; } lines[0] = this.leftoverPrevChunk + lines[0]; this.lineBasedBuilder.acceptLines(lines.slice(0, lines.length - 1)); this.leftoverPrevChunk = lines[lines.length - 1]; }
function resolveRenderLineInput(input: RenderLineInput): ResolvedRenderLineInput { const useMonospaceOptimizations = input.useMonospaceOptimizations; const lineContent = input.lineContent; let isOverflowing: boolean; let len: number; if (input.stopRenderingLineAfter !== -1 && input.stopRenderingLineAfter < lineContent.length) { isOverflowing = true; len = input.stopRenderingLineAfter; } else { isOverflowing = false; len = lineContent.length; } let tokens = transformAndRemoveOverflowing(input.lineTokens, input.fauxIndentLength, len); if (input.renderWhitespace === RenderWhitespace.All || input.renderWhitespace === RenderWhitespace.Boundary) { tokens = _applyRenderWhitespace(lineContent, len, tokens, input.fauxIndentLength, input.tabSize, useMonospaceOptimizations, input.renderWhitespace === RenderWhitespace.Boundary); } let containsForeignElements = false; if (input.lineDecorations.length > 0) { for (let i = 0, len = input.lineDecorations.length; i < len; i++) { const lineDecoration = input.lineDecorations[i]; if (lineDecoration.insertsBeforeOrAfter) { containsForeignElements = true; break; } } tokens = _applyInlineDecorations(lineContent, len, tokens, input.lineDecorations); } let containsRTL = false; if (input.mightContainRTL) { containsRTL = strings.containsRTL(lineContent); } if (!containsRTL && !input.fontLigatures) { tokens = splitLargeTokens(lineContent, tokens); } return new ResolvedRenderLineInput( useMonospaceOptimizations, lineContent, len, isOverflowing, tokens, containsForeignElements, input.tabSize, containsRTL, input.spaceWidth, input.renderWhitespace, input.renderControlCharacters ); }
private _acceptChunk2(chunk: string): void { const lineStarts = createLineStarts(this._tmpLineStarts, chunk); this.chunks.push(new StringBuffer(chunk, lineStarts.lineStarts)); this.cr += lineStarts.cr; this.lf += lineStarts.lf; this.crlf += lineStarts.crlf; if (this.isBasicASCII) { this.isBasicASCII = lineStarts.isBasicASCII; } if (!this.isBasicASCII && !this.containsRTL) { // No need to check if is basic ASCII this.containsRTL = strings.containsRTL(chunk); } }
private _applyEdits(markersTracker: MarkersTracker, rawOperations: editorCommon.IIdentifiedSingleEditOperation[]): editorCommon.IIdentifiedSingleEditOperation[] { if (rawOperations.length === 0) { return []; } let mightContainRTL = this._mightContainRTL; let operations: IValidatedEditOperation[] = []; for (let i = 0; i < rawOperations.length; i++) { let op = rawOperations[i]; let validatedRange = this.validateRange(op.range); if (!mightContainRTL && op.text) { // check if the new inserted text contains RTL mightContainRTL = strings.containsRTL(op.text); } operations[i] = { sortIndex: i, identifier: op.identifier, range: validatedRange, rangeLength: this.getValueLengthInRange(validatedRange), lines: op.text ? op.text.split(/\r\n|\r|\n/) : null, forceMoveMarkers: op.forceMoveMarkers, isAutoWhitespaceEdit: op.isAutoWhitespaceEdit || false }; } // Sort operations ascending operations.sort(EditableTextModel._sortOpsAscending); for (let i = 0, count = operations.length - 1; i < count; i++) { let rangeEnd = operations[i].range.getEndPosition(); let nextRangeStart = operations[i + 1].range.getStartPosition(); if (nextRangeStart.isBefore(rangeEnd)) { // overlapping ranges throw new Error('Overlapping ranges are not allowed!'); } } operations = this._reduceOperations(operations); let editableRange = this.getEditableRange(); let editableRangeStart = editableRange.getStartPosition(); let editableRangeEnd = editableRange.getEndPosition(); for (let i = 0; i < operations.length; i++) { let operationRange = operations[i].range; if (!editableRangeStart.isBeforeOrEqual(operationRange.getStartPosition()) || !operationRange.getEndPosition().isBeforeOrEqual(editableRangeEnd)) { throw new Error('Editing outside of editable range not allowed!'); } } // Delta encode operations let reverseRanges = EditableTextModel._getInverseEditRanges(operations); let reverseOperations: editorCommon.IIdentifiedSingleEditOperation[] = []; let newTrimAutoWhitespaceCandidates: { lineNumber: number, oldContent: string }[] = []; for (let i = 0; i < operations.length; i++) { let op = operations[i]; let reverseRange = reverseRanges[i]; reverseOperations[i] = { identifier: op.identifier, range: reverseRange, text: this.getValueInRange(op.range), forceMoveMarkers: op.forceMoveMarkers }; if (this._options.trimAutoWhitespace && op.isAutoWhitespaceEdit && op.range.isEmpty()) { // Record already the future line numbers that might be auto whitespace removal candidates on next edit for (let lineNumber = reverseRange.startLineNumber; lineNumber <= reverseRange.endLineNumber; lineNumber++) { let currentLineContent = ''; if (lineNumber === reverseRange.startLineNumber) { currentLineContent = this.getLineContent(op.range.startLineNumber); if (strings.firstNonWhitespaceIndex(currentLineContent) !== -1) { continue; } } newTrimAutoWhitespaceCandidates.push({ lineNumber: lineNumber, oldContent: currentLineContent }); } } } this._mightContainRTL = mightContainRTL; this._doApplyEdits(markersTracker, operations); this._trimAutoWhitespaceLines = null; if (this._options.trimAutoWhitespace && newTrimAutoWhitespaceCandidates.length > 0) { // sort line numbers auto whitespace removal candidates for next edit descending newTrimAutoWhitespaceCandidates.sort((a, b) => b.lineNumber - a.lineNumber); this._trimAutoWhitespaceLines = []; for (let i = 0, len = newTrimAutoWhitespaceCandidates.length; i < len; i++) { let lineNumber = newTrimAutoWhitespaceCandidates[i].lineNumber; if (i > 0 && newTrimAutoWhitespaceCandidates[i - 1].lineNumber === lineNumber) { // Do not have the same line number twice continue; } let prevContent = newTrimAutoWhitespaceCandidates[i].oldContent; let lineContent = this.getLineContent(lineNumber); if (lineContent.length === 0 || lineContent === prevContent || strings.firstNonWhitespaceIndex(lineContent) !== -1) { continue; } this._trimAutoWhitespaceLines.push(lineNumber); } } return reverseOperations; }
applyEdits(rawOperations: IIdentifiedSingleEditOperation[], recordTrimAutoWhitespace: boolean): ApplyEditsResult { if (rawOperations.length === 0) { return new ApplyEditsResult([], [], []); } let mightContainRTL = this._mightContainRTL; let mightContainNonBasicASCII = this._mightContainNonBasicASCII; let canReduceOperations = true; let operations: IValidatedEditOperation[] = []; for (let i = 0; i < rawOperations.length; i++) { let op = rawOperations[i]; if (canReduceOperations && op._isTracked) { canReduceOperations = false; } let validatedRange = op.range; if (!mightContainRTL && op.text) { // check if the new inserted text contains RTL mightContainRTL = strings.containsRTL(op.text); } if (!mightContainNonBasicASCII && op.text) { mightContainNonBasicASCII = !strings.isBasicASCII(op.text); } operations[i] = { sortIndex: i, identifier: op.identifier || null, range: validatedRange, rangeOffset: this.getOffsetAt(validatedRange.startLineNumber, validatedRange.startColumn), rangeLength: this.getValueLengthInRange(validatedRange, EndOfLinePreference.TextDefined), lines: op.text ? op.text.split(/\r\n|\r|\n/) : null, forceMoveMarkers: op.forceMoveMarkers || false, isAutoWhitespaceEdit: op.isAutoWhitespaceEdit || false }; } // Sort operations ascending operations.sort(ChunksTextBuffer._sortOpsAscending); for (let i = 0, count = operations.length - 1; i < count; i++) { let rangeEnd = operations[i].range.getEndPosition(); let nextRangeStart = operations[i + 1].range.getStartPosition(); if (nextRangeStart.isBefore(rangeEnd)) { // overlapping ranges throw new Error('Overlapping ranges are not allowed!'); } } if (canReduceOperations) { operations = this._reduceOperations(operations); } // Delta encode operations let reverseRanges = ChunksTextBuffer._getInverseEditRanges(operations); let newTrimAutoWhitespaceCandidates: { lineNumber: number, oldContent: string }[] = []; for (let i = 0; i < operations.length; i++) { let op = operations[i]; let reverseRange = reverseRanges[i]; if (recordTrimAutoWhitespace && op.isAutoWhitespaceEdit && op.range.isEmpty()) { // Record already the future line numbers that might be auto whitespace removal candidates on next edit for (let lineNumber = reverseRange.startLineNumber; lineNumber <= reverseRange.endLineNumber; lineNumber++) { let currentLineContent = ''; if (lineNumber === reverseRange.startLineNumber) { currentLineContent = this.getLineContent(op.range.startLineNumber); if (strings.firstNonWhitespaceIndex(currentLineContent) !== -1) { continue; } } newTrimAutoWhitespaceCandidates.push({ lineNumber: lineNumber, oldContent: currentLineContent }); } } } let reverseOperations: IIdentifiedSingleEditOperation[] = []; for (let i = 0; i < operations.length; i++) { let op = operations[i]; let reverseRange = reverseRanges[i]; reverseOperations[i] = { identifier: op.identifier, range: reverseRange, text: this.getValueInRange(op.range, EndOfLinePreference.TextDefined), forceMoveMarkers: op.forceMoveMarkers }; } this._mightContainRTL = mightContainRTL; this._mightContainNonBasicASCII = mightContainNonBasicASCII; const contentChanges = this._doApplyEdits(operations); let trimAutoWhitespaceLineNumbers: number[] = null; if (recordTrimAutoWhitespace && newTrimAutoWhitespaceCandidates.length > 0) { // sort line numbers auto whitespace removal candidates for next edit descending newTrimAutoWhitespaceCandidates.sort((a, b) => b.lineNumber - a.lineNumber); trimAutoWhitespaceLineNumbers = []; for (let i = 0, len = newTrimAutoWhitespaceCandidates.length; i < len; i++) { let lineNumber = newTrimAutoWhitespaceCandidates[i].lineNumber; if (i > 0 && newTrimAutoWhitespaceCandidates[i - 1].lineNumber === lineNumber) { // Do not have the same line number twice continue; } let prevContent = newTrimAutoWhitespaceCandidates[i].oldContent; let lineContent = this.getLineContent(lineNumber); if (lineContent.length === 0 || lineContent === prevContent || strings.firstNonWhitespaceIndex(lineContent) !== -1) { continue; } trimAutoWhitespaceLineNumbers.push(lineNumber); } } return new ApplyEditsResult( reverseOperations, contentChanges, trimAutoWhitespaceLineNumbers ); }
public static containsRTL(lineContent: string, isBasicASCII: boolean, mightContainRTL: boolean): boolean { if (!isBasicASCII && mightContainRTL) { return strings.containsRTL(lineContent); } return false; }