public static isHighSurrogate(model: ICursorSimpleModel, lineNumber: number, charOffset: number): boolean { let lineContent = model.getLineContent(lineNumber); if (charOffset < 0 || charOffset >= lineContent.length) { return false; } return strings.isHighSurrogate(lineContent.charCodeAt(charOffset)); }
/** * See https://github.com/Microsoft/vscode/issues/6885. * It appears that having very large spans causes very slow reading of character positions. * So here we try to avoid that. */ function splitLargeTokens(lineContent: string, tokens: LinePart[]): LinePart[] { let lastTokenEndIndex = 0; let result: LinePart[] = [], resultLen = 0; for (let i = 0, len = tokens.length; i < len; i++) { const token = tokens[i]; const tokenEndIndex = token.endIndex; let diff = (tokenEndIndex - lastTokenEndIndex); if (diff > Constants.LongToken) { const tokenType = token.type; const piecesCount = Math.ceil(diff / Constants.LongToken); for (let j = 1; j < piecesCount; j++) { let pieceEndIndex = lastTokenEndIndex + (j * Constants.LongToken); let lastCharInPiece = lineContent.charCodeAt(pieceEndIndex - 1); if (strings.isHighSurrogate(lastCharInPiece)) { // Don't cut in the middle of a surrogate pair pieceEndIndex--; } result[resultLen++] = new LinePart(pieceEndIndex, tokenType); } result[resultLen++] = new LinePart(tokenEndIndex, tokenType); } else { result[resultLen++] = token; } lastTokenEndIndex = tokenEndIndex; } return result; }
private _prepareRender(ctx: RenderingContext): ViewCursorRenderData { let textContent = ''; let textContentClassName = ''; if (this._cursorStyle === TextEditorCursorStyle.Line || this._cursorStyle === TextEditorCursorStyle.LineThin) { const visibleRange = ctx.visibleRangeForPosition(this._position); if (!visibleRange) { // Outside viewport return null; } let width: number; if (this._cursorStyle === TextEditorCursorStyle.Line) { width = dom.computeScreenAwareSize(this._lineCursorWidth > 0 ? this._lineCursorWidth : 2); if (width > 2) { const lineContent = this._context.model.getLineContent(this._position.lineNumber); textContent = lineContent.charAt(this._position.column - 1); } } else { width = dom.computeScreenAwareSize(1); } let left = visibleRange.left; if (width >= 2 && left >= 1) { // try to center cursor left -= 1; } const top = ctx.getVerticalOffsetForLineNumber(this._position.lineNumber) - ctx.bigNumbersDelta; return new ViewCursorRenderData(top, left, width, this._lineHeight, textContent, textContentClassName); } const visibleRangeForCharacter = ctx.linesVisibleRangesForRange(new Range(this._position.lineNumber, this._position.column, this._position.lineNumber, this._position.column + 1), false); if (!visibleRangeForCharacter || visibleRangeForCharacter.length === 0 || visibleRangeForCharacter[0].ranges.length === 0) { // Outside viewport return null; } const range = visibleRangeForCharacter[0].ranges[0]; const width = range.width < 1 ? this._typicalHalfwidthCharacterWidth : range.width; if (this._cursorStyle === TextEditorCursorStyle.Block) { const lineData = this._context.model.getViewLineData(this._position.lineNumber); textContent = lineData.content.charAt(this._position.column - 1); if (strings.isHighSurrogate(lineData.content.charCodeAt(this._position.column - 1))) { textContent += lineData.content.charAt(this._position.column); } const tokenIndex = lineData.tokens.findTokenIndexAtOffset(this._position.column - 1); textContentClassName = lineData.tokens.getClassName(tokenIndex); } let top = ctx.getVerticalOffsetForLineNumber(this._position.lineNumber) - ctx.bigNumbersDelta; let height = this._lineHeight; // Underline might interfere with clicking if (this._cursorStyle === TextEditorCursorStyle.Underline || this._cursorStyle === TextEditorCursorStyle.UnderlineThin) { top += this._lineHeight - 2; height = 2; } return new ViewCursorRenderData(top, range.left, width, height, textContent, textContentClassName); }
/** * Normalize line decorations. Overlapping decorations will generate multiple segments */ public static normalize(lineContent: string, lineDecorations: LineDecoration[]): DecorationSegment[] { if (lineDecorations.length === 0) { return []; } let result: DecorationSegment[] = []; let stack = new Stack(); let nextStartOffset = 0; for (let i = 0, len = lineDecorations.length; i < len; i++) { let d = lineDecorations[i]; let startColumn = d.startColumn; let endColumn = d.endColumn; let className = d.className; // If the position would end up in the middle of a high-low surrogate pair, we move it to before the pair if (startColumn > 1) { const charCodeBefore = lineContent.charCodeAt(startColumn - 2); if (strings.isHighSurrogate(charCodeBefore)) { startColumn--; } } if (endColumn > 1) { const charCodeBefore = lineContent.charCodeAt(endColumn - 2); if (strings.isHighSurrogate(charCodeBefore)) { endColumn--; } } let currentStartOffset = startColumn - 1; let currentEndOffset = endColumn - 2; nextStartOffset = stack.consumeLowerThan(currentStartOffset, nextStartOffset, result); if (stack.count === 0) { nextStartOffset = currentStartOffset; } stack.insert(currentEndOffset, className); } stack.consumeLowerThan(Constants.MAX_SAFE_SMALL_INTEGER, nextStartOffset, result); return result; }
public write1(charCode: number): void { const remainingSpace = this._capacity - this._bufferLength; if (remainingSpace <= 1) { if (remainingSpace === 0 || strings.isHighSurrogate(charCode)) { this._flushBuffer(); } } this._buffer[this._bufferLength++] = charCode; }
private positionRightOf(start: IPosition, model: ITextModel): Position { let column = start.column; let lineNumber = start.lineNumber; if (column < model.getLineMaxColumn(lineNumber)) { if (isHighSurrogate(model.getLineContent(lineNumber).charCodeAt(column - 1))) { // character after column is a high surrogate column = column + 2; } else { column = column + 1; } } else if (lineNumber < model.getLineCount()) { lineNumber = lineNumber + 1; column = 0; } return new Position(lineNumber, column); }
function isHighSurrogate(model: ICursorMoveHelperModel, lineNumber: number, column: number) { return strings.isHighSurrogate(model.getLineContent(lineNumber).charCodeAt(column - 1)); }