提交 af7765c0 编写于 作者: S Stepan Repin

Add 'trailing' option to render whitespace

上级 6d913c4c
...@@ -72,7 +72,7 @@ export class DomReadingContext { ...@@ -72,7 +72,7 @@ export class DomReadingContext {
export class ViewLineOptions { export class ViewLineOptions {
public readonly themeType: ThemeType; public readonly themeType: ThemeType;
public readonly renderWhitespace: 'none' | 'boundary' | 'selection' | 'all'; public readonly renderWhitespace: 'none' | 'boundary' | 'selection' | 'trailing' | 'all';
public readonly renderControlCharacters: boolean; public readonly renderControlCharacters: boolean;
public readonly spaceWidth: number; public readonly spaceWidth: number;
public readonly middotWidth: number; public readonly middotWidth: number;
......
...@@ -529,7 +529,7 @@ export interface IEditorOptions { ...@@ -529,7 +529,7 @@ export interface IEditorOptions {
* Enable rendering of whitespace. * Enable rendering of whitespace.
* Defaults to none. * Defaults to none.
*/ */
renderWhitespace?: 'none' | 'boundary' | 'selection' | 'all'; renderWhitespace?: 'none' | 'boundary' | 'selection' | 'trailing' | 'all';
/** /**
* Enable rendering of control characters. * Enable rendering of control characters.
* Defaults to false. * Defaults to false.
...@@ -4037,13 +4037,14 @@ export const EditorOptions = { ...@@ -4037,13 +4037,14 @@ export const EditorOptions = {
)), )),
renderWhitespace: register(new EditorStringEnumOption( renderWhitespace: register(new EditorStringEnumOption(
EditorOption.renderWhitespace, 'renderWhitespace', EditorOption.renderWhitespace, 'renderWhitespace',
'selection' as 'selection' | 'none' | 'boundary' | 'all', 'selection' as 'selection' | 'none' | 'boundary' | 'trailing' | 'all',
['none', 'boundary', 'selection', 'all'] as const, ['none', 'boundary', 'selection', 'trailing', 'all'] as const,
{ {
enumDescriptions: [ enumDescriptions: [
'', '',
nls.localize('renderWhitespace.boundary', "Render whitespace characters except for single spaces between words."), nls.localize('renderWhitespace.boundary', "Render whitespace characters except for single spaces between words."),
nls.localize('renderWhitespace.selection', "Render whitespace characters only on selected text."), nls.localize('renderWhitespace.selection', "Render whitespace characters only on selected text."),
nls.localize('renderWhitespace.trailing', "Render only trailing whitespace characters"),
'' ''
], ],
description: nls.localize('renderWhitespace', "Controls how the editor should render whitespace characters.") description: nls.localize('renderWhitespace', "Controls how the editor should render whitespace characters.")
......
...@@ -14,7 +14,8 @@ export const enum RenderWhitespace { ...@@ -14,7 +14,8 @@ export const enum RenderWhitespace {
None = 0, None = 0,
Boundary = 1, Boundary = 1,
Selection = 2, Selection = 2,
All = 3 Trailing = 3,
All = 4
} }
export const enum LinePartMetadata { export const enum LinePartMetadata {
...@@ -113,7 +114,7 @@ export class RenderLineInput { ...@@ -113,7 +114,7 @@ export class RenderLineInput {
middotWidth: number, middotWidth: number,
wsmiddotWidth: number, wsmiddotWidth: number,
stopRenderingLineAfter: number, stopRenderingLineAfter: number,
renderWhitespace: 'none' | 'boundary' | 'selection' | 'all', renderWhitespace: 'none' | 'boundary' | 'selection' | 'trailing' | 'all',
renderControlCharacters: boolean, renderControlCharacters: boolean,
fontLigatures: boolean, fontLigatures: boolean,
selectionsOnLine: LineRange[] | null selectionsOnLine: LineRange[] | null
...@@ -138,6 +139,8 @@ export class RenderLineInput { ...@@ -138,6 +139,8 @@ export class RenderLineInput {
? RenderWhitespace.Boundary ? RenderWhitespace.Boundary
: renderWhitespace === 'selection' : renderWhitespace === 'selection'
? RenderWhitespace.Selection ? RenderWhitespace.Selection
: renderWhitespace === 'trailing'
? RenderWhitespace.Trailing
: RenderWhitespace.None : RenderWhitespace.None
); );
this.renderControlCharacters = renderControlCharacters; this.renderControlCharacters = renderControlCharacters;
...@@ -435,7 +438,11 @@ function resolveRenderLineInput(input: RenderLineInput): ResolvedRenderLineInput ...@@ -435,7 +438,11 @@ function resolveRenderLineInput(input: RenderLineInput): ResolvedRenderLineInput
} }
let tokens = transformAndRemoveOverflowing(input.lineTokens, input.fauxIndentLength, len); let tokens = transformAndRemoveOverflowing(input.lineTokens, input.fauxIndentLength, len);
if (input.renderWhitespace === RenderWhitespace.All || input.renderWhitespace === RenderWhitespace.Boundary || (input.renderWhitespace === RenderWhitespace.Selection && !!input.selectionsOnLine)) { if (input.renderWhitespace === RenderWhitespace.All ||
input.renderWhitespace === RenderWhitespace.Boundary ||
(input.renderWhitespace === RenderWhitespace.Selection && !!input.selectionsOnLine) ||
input.renderWhitespace === RenderWhitespace.Trailing) {
tokens = _applyRenderWhitespace(input, lineContent, len, tokens); tokens = _applyRenderWhitespace(input, lineContent, len, tokens);
} }
let containsForeignElements = ForeignElementType.None; let containsForeignElements = ForeignElementType.None;
...@@ -592,6 +599,7 @@ function _applyRenderWhitespace(input: RenderLineInput, lineContent: string, len ...@@ -592,6 +599,7 @@ function _applyRenderWhitespace(input: RenderLineInput, lineContent: string, len
const useMonospaceOptimizations = input.useMonospaceOptimizations; const useMonospaceOptimizations = input.useMonospaceOptimizations;
const selections = input.selectionsOnLine; const selections = input.selectionsOnLine;
const onlyBoundary = (input.renderWhitespace === RenderWhitespace.Boundary); const onlyBoundary = (input.renderWhitespace === RenderWhitespace.Boundary);
const onlyTrailing = (input.renderWhitespace === RenderWhitespace.Trailing);
const generateLinePartForEachWhitespace = (input.renderSpaceWidth !== input.spaceWidth); const generateLinePartForEachWhitespace = (input.renderSpaceWidth !== input.spaceWidth);
let result: LinePart[] = [], resultLen = 0; let result: LinePart[] = [], resultLen = 0;
...@@ -600,10 +608,11 @@ function _applyRenderWhitespace(input: RenderLineInput, lineContent: string, len ...@@ -600,10 +608,11 @@ function _applyRenderWhitespace(input: RenderLineInput, lineContent: string, len
let tokenEndIndex = tokens[tokenIndex].endIndex; let tokenEndIndex = tokens[tokenIndex].endIndex;
const tokensLength = tokens.length; const tokensLength = tokens.length;
let lineIsEmptyOrWhitespace = false;
let firstNonWhitespaceIndex = strings.firstNonWhitespaceIndex(lineContent); let firstNonWhitespaceIndex = strings.firstNonWhitespaceIndex(lineContent);
let lastNonWhitespaceIndex: number; let lastNonWhitespaceIndex: number;
if (firstNonWhitespaceIndex === -1) { if (firstNonWhitespaceIndex === -1) {
// The entire line is whitespace lineIsEmptyOrWhitespace = true;
firstNonWhitespaceIndex = len; firstNonWhitespaceIndex = len;
lastNonWhitespaceIndex = len; lastNonWhitespaceIndex = len;
} else { } else {
...@@ -651,6 +660,11 @@ function _applyRenderWhitespace(input: RenderLineInput, lineContent: string, len ...@@ -651,6 +660,11 @@ function _applyRenderWhitespace(input: RenderLineInput, lineContent: string, len
isInWhitespace = !!currentSelection && currentSelection.startOffset <= charIndex && currentSelection.endOffset > charIndex; isInWhitespace = !!currentSelection && currentSelection.startOffset <= charIndex && currentSelection.endOffset > charIndex;
} }
// If rendering only trailing whitespace, check that the charIndex points to trailing whitespace.
if (isInWhitespace && onlyTrailing) {
isInWhitespace = lineIsEmptyOrWhitespace || charIndex > lastNonWhitespaceIndex;
}
if (wasInWhitespace) { if (wasInWhitespace) {
// was in whitespace token // was in whitespace token
if (!isInWhitespace || (!useMonospaceOptimizations && tmpIndent >= tabSize)) { if (!isInWhitespace || (!useMonospaceOptimizations && tmpIndent >= tabSize)) {
......
...@@ -869,7 +869,7 @@ suite('viewLineRenderer.renderLine', () => { ...@@ -869,7 +869,7 @@ suite('viewLineRenderer.renderLine', () => {
suite('viewLineRenderer.renderLine 2', () => { suite('viewLineRenderer.renderLine 2', () => {
function testCreateLineParts(fontIsMonospace: boolean, lineContent: string, tokens: ViewLineToken[], fauxIndentLength: number, renderWhitespace: 'none' | 'boundary' | 'selection' | 'all', selections: LineRange[] | null, expected: string): void { function testCreateLineParts(fontIsMonospace: boolean, lineContent: string, tokens: ViewLineToken[], fauxIndentLength: number, renderWhitespace: 'none' | 'boundary' | 'selection' | 'trailing' | 'all', selections: LineRange[] | null, expected: string): void {
let actual = renderViewLine(new RenderLineInput( let actual = renderViewLine(new RenderLineInput(
fontIsMonospace, fontIsMonospace,
true, true,
...@@ -1355,6 +1355,95 @@ suite('viewLineRenderer.renderLine 2', () => { ...@@ -1355,6 +1355,95 @@ suite('viewLineRenderer.renderLine 2', () => {
); );
}); });
test('createLineParts render whitespace for trailing with leading, inner, and without trailing whitespace', () => {
testCreateLineParts(
false,
' Hello world!',
[
createPart(4, 0),
createPart(6, 1),
createPart(14, 2)
],
0,
'trailing',
null,
[
'<span>',
'<span class="mtk0">\u00a0Hel</span>',
'<span class="mtk1">lo</span>',
'<span class="mtk2">\u00a0world!</span>',
'</span>',
].join('')
);
});
test('createLineParts render whitespace for trailing with leading, inner, and trailing whitespace', () => {
testCreateLineParts(
false,
' Hello world! \t',
[
createPart(4, 0),
createPart(6, 1),
createPart(15, 2)
],
0,
'trailing',
null,
[
'<span>',
'<span class="mtk0">\u00a0Hel</span>',
'<span class="mtk1">lo</span>',
'<span class="mtk2">\u00a0world!</span>',
'<span class="mtkz" style="width:30px">\u00b7\u2192\u00a0</span>',
'</span>',
].join('')
);
});
test('createLineParts render whitespace for trailing with 8 leading and 8 trailing whitespaces', () => {
testCreateLineParts(
false,
' Hello world! ',
[
createPart(8, 1),
createPart(10, 2),
createPart(28, 3)
],
0,
'trailing',
null,
[
'<span>',
'<span class="mtk1">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0</span>',
'<span class="mtk2">He</span>',
'<span class="mtk3">llo\u00a0world!</span>',
'<span class="mtkz" style="width:40px">\u00b7\u00b7\u00b7\u00b7</span>',
'<span class="mtkz" style="width:40px">\u00b7\u00b7\u00b7\u00b7</span>',
'</span>',
].join('')
);
});
test('createLineParts render whitespace for trailing with line containing only whitespaces', () => {
testCreateLineParts(
false,
' \t ',
[
createPart(2, 0),
createPart(3, 1),
],
0,
'trailing',
null,
[
'<span>',
'<span class="mtkz" style="width:40px">\u00b7\u2192\u00a0\u00a0</span>',
'<span class="mtkz" style="width:10px">\u00b7</span>',
'</span>',
].join('')
);
});
test('createLineParts can handle unsorted inline decorations', () => { test('createLineParts can handle unsorted inline decorations', () => {
let actual = renderViewLine(new RenderLineInput( let actual = renderViewLine(new RenderLineInput(
false, false,
......
...@@ -3068,7 +3068,7 @@ declare namespace monaco.editor { ...@@ -3068,7 +3068,7 @@ declare namespace monaco.editor {
* Enable rendering of whitespace. * Enable rendering of whitespace.
* Defaults to none. * Defaults to none.
*/ */
renderWhitespace?: 'none' | 'boundary' | 'selection' | 'all'; renderWhitespace?: 'none' | 'boundary' | 'selection' | 'trailing' | 'all';
/** /**
* Enable rendering of control characters. * Enable rendering of control characters.
* Defaults to false. * Defaults to false.
...@@ -4048,7 +4048,7 @@ declare namespace monaco.editor { ...@@ -4048,7 +4048,7 @@ declare namespace monaco.editor {
renderLineHighlight: IEditorOption<EditorOption.renderLineHighlight, 'all' | 'line' | 'none' | 'gutter'>; renderLineHighlight: IEditorOption<EditorOption.renderLineHighlight, 'all' | 'line' | 'none' | 'gutter'>;
renderLineHighlightOnlyWhenFocus: IEditorOption<EditorOption.renderLineHighlightOnlyWhenFocus, boolean>; renderLineHighlightOnlyWhenFocus: IEditorOption<EditorOption.renderLineHighlightOnlyWhenFocus, boolean>;
renderValidationDecorations: IEditorOption<EditorOption.renderValidationDecorations, 'on' | 'off' | 'editable'>; renderValidationDecorations: IEditorOption<EditorOption.renderValidationDecorations, 'on' | 'off' | 'editable'>;
renderWhitespace: IEditorOption<EditorOption.renderWhitespace, 'all' | 'none' | 'boundary' | 'selection'>; renderWhitespace: IEditorOption<EditorOption.renderWhitespace, 'all' | 'none' | 'boundary' | 'selection' | 'trailing'>;
revealHorizontalRightPadding: IEditorOption<EditorOption.revealHorizontalRightPadding, number>; revealHorizontalRightPadding: IEditorOption<EditorOption.revealHorizontalRightPadding, number>;
roundedSelection: IEditorOption<EditorOption.roundedSelection, boolean>; roundedSelection: IEditorOption<EditorOption.roundedSelection, boolean>;
rulers: IEditorOption<EditorOption.rulers, {}>; rulers: IEditorOption<EditorOption.rulers, {}>;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册