Fixes #34497: Count unicode points for the status bar

上级 e1a25446
......@@ -499,6 +499,17 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE
return CursorColumns.visibleColumnFromColumn(this._modelData.model.getLineContent(position.lineNumber), position.column, tabSize) + 1;
}
public getStatusbarColumn(rawPosition: IPosition): number {
if (!this._modelData) {
return rawPosition.column;
}
const position = this._modelData.model.validatePosition(rawPosition);
const tabSize = this._modelData.model.getOptions().tabSize;
return CursorColumns.toStatusbarColumn(this._modelData.model.getLineContent(position.lineNumber), position.column, tabSize);
}
public getPosition(): Position | null {
if (!this._modelData) {
return null;
......
......@@ -738,6 +738,10 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE
return this.modifiedEditor.getVisibleColumnFromPosition(position);
}
public getStatusbarColumn(position: IPosition): number {
return this.modifiedEditor.getStatusbarColumn(position);
}
public getPosition(): Position | null {
return this.modifiedEditor.getPosition();
}
......
......@@ -549,6 +549,29 @@ export class CursorColumns {
return result;
}
public static toStatusbarColumn(lineContent: string, column: number, tabSize: number): number {
let endOffset = lineContent.length;
if (endOffset > column - 1) {
endOffset = column - 1;
}
let result = 0;
for (let i = 0; i < endOffset; i++) {
let charCode = lineContent.charCodeAt(i);
if (charCode === CharCode.Tab) {
result = this.nextRenderTabStop(result, tabSize);
} else {
if (strings.isHighSurrogate(charCode)) {
result = result + 1;
i = i + 1;
} else {
result = result + 1;
}
}
}
return result + 1;
}
public static visibleColumnFromColumn2(config: CursorConfiguration, model: ICursorSimpleModel, position: Position): number {
return this.visibleColumnFromColumn(model.getLineContent(position.lineNumber), position.column, config.tabSize);
}
......
......@@ -73,13 +73,6 @@ export class Selection extends Range {
this.positionColumn = positionColumn;
}
/**
* Clone this selection.
*/
public clone(): Selection {
return new Selection(this.selectionStartLineNumber, this.selectionStartColumn, this.positionLineNumber, this.positionColumn);
}
/**
* Transform to a human-readable representation.
*/
......
......@@ -315,6 +315,12 @@ export interface IEditor {
*/
getVisibleColumnFromPosition(position: IPosition): number;
/**
* Given a position, returns a column number that takes tab-widths into account.
* @internal
*/
getStatusbarColumn(position: IPosition): number;
/**
* Returns the primary position of the cursor.
*/
......
......@@ -608,6 +608,12 @@ export interface ITextModel {
*/
getValueLengthInRange(range: IRange): number;
/**
* Get the character count of text in a certain range.
* @param range The range describing what text length to get.
*/
getCharacterCountInRange(range: IRange): number;
/**
* Splits characters in two buckets. First bucket (A) is of characters that
* sit in lines with length < `LONG_LINE_BOUNDARY`. Second bucket (B) is of
......@@ -1203,6 +1209,7 @@ export interface ITextBuffer {
getValueInRange(range: Range, eol: EndOfLinePreference): string;
createSnapshot(preserveBOM: boolean): ITextSnapshot;
getValueLengthInRange(range: Range, eol: EndOfLinePreference): number;
getCharacterCountInRange(range: Range, eol: EndOfLinePreference): number;
getLength(): number;
getLineCount(): number;
getLinesContent(): string[];
......
......@@ -106,6 +106,37 @@ export class PieceTreeTextBuffer implements ITextBuffer {
return endOffset - startOffset;
}
public getCharacterCountInRange(range: Range, eol: EndOfLinePreference = EndOfLinePreference.TextDefined): number {
if (this._mightContainNonBasicASCII) {
// we must count by iterating
let result = 0;
const fromLineNumber = range.startLineNumber;
const toLineNumber = range.endLineNumber;
for (let lineNumber = fromLineNumber; lineNumber <= toLineNumber; lineNumber++) {
const lineContent = this.getLineContent(lineNumber);
const fromOffset = (lineNumber === fromLineNumber ? range.startColumn - 1 : 0);
const toOffset = (lineNumber === toLineNumber ? range.endColumn - 1 : lineContent.length);
for (let offset = fromOffset; offset < toOffset; offset++) {
if (strings.isHighSurrogate(lineContent.charCodeAt(offset))) {
result = result + 1;
offset = offset + 1;
} else {
result = result + 1;
}
}
}
result += this._getEndOfLine(eol).length * (toLineNumber - fromLineNumber);
return result;
}
return this.getValueLengthInRange(range, eol);
}
public getLength(): number {
return this._pieceTree.getLength();
}
......
......@@ -726,6 +726,11 @@ export class TextModel extends Disposable implements model.ITextModel {
return this._buffer.getValueLengthInRange(this.validateRange(rawRange), eol);
}
public getCharacterCountInRange(rawRange: IRange, eol: model.EndOfLinePreference = model.EndOfLinePreference.TextDefined): number {
this._assertNotDisposed();
return this._buffer.getCharacterCountInRange(this.validateRange(rawRange), eol);
}
public getLineCount(): number {
this._assertNotDisposed();
return this._buffer.getLineCount();
......
......@@ -173,4 +173,60 @@ suite('CursorMove', () => {
testColumnFromVisibleColumn('📚az', 4, 3, 4);
testColumnFromVisibleColumn('📚az', 4, 4, 5);
});
});
\ No newline at end of file
test('toStatusbarColumn', () => {
function t(text: string, tabSize: number, column: number, expected: number): void {
assert.equal(CursorColumns.toStatusbarColumn(text, column, tabSize), expected, `<<t('${text}', ${tabSize}, ${column}, ${expected})>>`);
}
t(' spaces', 4, 1, 1);
t(' spaces', 4, 2, 2);
t(' spaces', 4, 3, 3);
t(' spaces', 4, 4, 4);
t(' spaces', 4, 5, 5);
t(' spaces', 4, 6, 6);
t(' spaces', 4, 7, 7);
t(' spaces', 4, 8, 8);
t(' spaces', 4, 9, 9);
t(' spaces', 4, 10, 10);
t(' spaces', 4, 11, 11);
t('\ttab', 4, 1, 1);
t('\ttab', 4, 2, 5);
t('\ttab', 4, 3, 6);
t('\ttab', 4, 4, 7);
t('\ttab', 4, 5, 8);
t('𐌀𐌁𐌂𐌃𐌄𐌅𐌆', 4, 1, 1);
t('𐌀𐌁𐌂𐌃𐌄𐌅𐌆', 4, 2, 2);
t('𐌀𐌁𐌂𐌃𐌄𐌅𐌆', 4, 3, 2);
t('𐌀𐌁𐌂𐌃𐌄𐌅𐌆', 4, 4, 3);
t('𐌀𐌁𐌂𐌃𐌄𐌅𐌆', 4, 5, 3);
t('𐌀𐌁𐌂𐌃𐌄𐌅𐌆', 4, 6, 4);
t('𐌀𐌁𐌂𐌃𐌄𐌅𐌆', 4, 7, 4);
t('𐌀𐌁𐌂𐌃𐌄𐌅𐌆', 4, 8, 5);
t('𐌀𐌁𐌂𐌃𐌄𐌅𐌆', 4, 9, 5);
t('𐌀𐌁𐌂𐌃𐌄𐌅𐌆', 4, 10, 6);
t('𐌀𐌁𐌂𐌃𐌄𐌅𐌆', 4, 11, 6);
t('𐌀𐌁𐌂𐌃𐌄𐌅𐌆', 4, 12, 7);
t('𐌀𐌁𐌂𐌃𐌄𐌅𐌆', 4, 13, 7);
t('𐌀𐌁𐌂𐌃𐌄𐌅𐌆', 4, 14, 8);
t('𐌀𐌁𐌂𐌃𐌄𐌅𐌆', 4, 15, 8);
t('🎈🎈🎈🎈', 4, 1, 1);
t('🎈🎈🎈🎈', 4, 2, 2);
t('🎈🎈🎈🎈', 4, 3, 2);
t('🎈🎈🎈🎈', 4, 4, 3);
t('🎈🎈🎈🎈', 4, 5, 3);
t('🎈🎈🎈🎈', 4, 6, 4);
t('🎈🎈🎈🎈', 4, 7, 4);
t('🎈🎈🎈🎈', 4, 8, 5);
t('🎈🎈🎈🎈', 4, 9, 5);
t('何何何何', 4, 1, 1);
t('何何何何', 4, 2, 2);
t('何何何何', 4, 3, 3);
t('何何何何', 4, 4, 4);
});
});
......@@ -726,10 +726,6 @@ declare namespace monaco {
*/
readonly positionColumn: number;
constructor(selectionStartLineNumber: number, selectionStartColumn: number, positionLineNumber: number, positionColumn: number);
/**
* Clone this selection.
*/
clone(): Selection;
/**
* Transform to a human-readable representation.
*/
......@@ -1555,6 +1551,11 @@ declare namespace monaco.editor {
* @return The text length.
*/
getValueLengthInRange(range: IRange): number;
/**
* Get the character count of text in a certain range.
* @param range The range describing what text length to get.
*/
getCharacterCountInRange(range: IRange): number;
/**
* Get the number of lines in the model.
*/
......
......@@ -738,7 +738,7 @@ export class EditorStatus extends Disposable implements IWorkbenchContribution {
info.charactersSelected = 0;
}
info.charactersSelected += textModel.getValueLengthInRange(selection);
info.charactersSelected += textModel.getCharacterCountInRange(selection);
});
}
......@@ -746,12 +746,11 @@ export class EditorStatus extends Disposable implements IWorkbenchContribution {
if (info.selections.length === 1) {
const editorPosition = editorWidget.getPosition();
let selectionClone = info.selections[0].clone(); // do not modify the original position we got from the editor
selectionClone = new Selection(
selectionClone.selectionStartLineNumber,
selectionClone.selectionStartColumn,
selectionClone.positionLineNumber,
editorPosition ? editorWidget.getVisibleColumnFromPosition(editorPosition) : selectionClone.positionColumn
let selectionClone = new Selection(
info.selections[0].selectionStartLineNumber,
info.selections[0].selectionStartColumn,
info.selections[0].positionLineNumber,
editorPosition ? editorWidget.getStatusbarColumn(editorPosition) : info.selections[0].positionColumn
);
info.selections[0] = selectionClone;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册