提交 2dab253a 编写于 作者: M marktrz

Implements whitespace rendering with modes 'boundary' and 'all'.

上级 3939e4d3
......@@ -98,7 +98,7 @@ export class Colorizer {
tabSize,
0,
-1,
false,
'none',
false,
tokens
));
......@@ -143,7 +143,7 @@ function actualColorize(lines:string[], mode:IMode, tabSize:number): IActualColo
tabSize,
0,
-1,
false,
'none',
false,
tokenizeResult.tokens.map(t => new ViewLineToken(t.startIndex, t.type))
));
......
......@@ -19,7 +19,7 @@ import {InlineDecoration} from 'vs/editor/common/viewModel/viewModel';
export class ViewLine implements IVisibleLineData {
protected _context:ViewContext;
private _renderWhitespace: boolean;
private _renderWhitespace: 'none' | 'boundary' | 'all';
private _renderControlCharacters: boolean;
private _spaceWidth: number;
private _lineHeight: number;
......
......@@ -244,7 +244,7 @@ class InternalEditorOptionsHelper {
scrollBeyondLastLine: toBoolean(opts.scrollBeyondLastLine),
editorClassName: editorClassName,
stopRenderingLineAfter: stopRenderingLineAfter,
renderWhitespace: toBoolean(opts.renderWhitespace),
renderWhitespace: opts.renderWhitespace,
renderControlCharacters: toBoolean(opts.renderControlCharacters),
renderIndentGuides: toBoolean(opts.renderIndentGuides),
renderLineHighlight: toBoolean(opts.renderLineHighlight),
......@@ -756,9 +756,10 @@ let editorConfiguration:IConfigurationNode = {
'description': nls.localize('hideCursorInOverviewRuler', "Controls if the cursor should be hidden in the overview ruler.")
},
'editor.renderWhitespace': {
'type': 'boolean',
'type': 'string',
'enum': ['none', 'boundary', 'all'],
default: DefaultConfig.editor.renderWhitespace,
description: nls.localize('renderWhitespace', "Controls whether the editor should render whitespace characters")
description: nls.localize('renderWhitespace', "Controls how the editor should render whitespace characters, posibilties are 'none', 'boundary', and 'all'. The 'boundary' option does not render single spaces between words.")
},
'editor.renderControlCharacters': {
'type': 'boolean',
......
......@@ -90,7 +90,7 @@ class ConfigClass implements IConfiguration {
codeLens: true,
referenceInfos: true,
folding: true,
renderWhitespace: false,
renderWhitespace: 'none',
renderControlCharacters: false,
renderIndentGuides: false,
renderLineHighlight: true,
......
......@@ -423,10 +423,10 @@ export interface IEditorOptions {
*/
folding?: boolean;
/**
* Enable rendering of leading whitespace.
* Defaults to false.
* Enable rendering of whitespace.
* Defaults to none.
*/
renderWhitespace?: boolean;
renderWhitespace?: 'none' | 'boundary' | 'all';
/**
* Enable rendering of control characters.
* Defaults to false.
......@@ -637,7 +637,7 @@ export class InternalEditorViewOptions {
scrollBeyondLastLine:boolean;
editorClassName: string;
stopRenderingLineAfter: number;
renderWhitespace: boolean;
renderWhitespace: 'none' | 'boundary' | 'all';
renderControlCharacters: boolean;
renderIndentGuides: boolean;
renderLineHighlight: boolean;
......@@ -665,7 +665,7 @@ export class InternalEditorViewOptions {
scrollBeyondLastLine:boolean;
editorClassName: string;
stopRenderingLineAfter: number;
renderWhitespace: boolean;
renderWhitespace: 'none' | 'boundary' | 'all';
renderControlCharacters: boolean;
renderIndentGuides: boolean;
renderLineHighlight: boolean;
......@@ -689,7 +689,7 @@ export class InternalEditorViewOptions {
this.scrollBeyondLastLine = Boolean(source.scrollBeyondLastLine);
this.editorClassName = String(source.editorClassName);
this.stopRenderingLineAfter = source.stopRenderingLineAfter|0;
this.renderWhitespace = Boolean(source.renderWhitespace);
this.renderWhitespace = source.renderWhitespace;
this.renderControlCharacters = Boolean(source.renderControlCharacters);
this.renderIndentGuides = Boolean(source.renderIndentGuides);
this.renderLineHighlight = Boolean(source.renderLineHighlight);
......
......@@ -14,10 +14,10 @@ function cmpLineDecorations(a:InlineDecoration, b:InlineDecoration): number {
return Range.compareRangesUsingStarts(a.range, b.range);
}
export function createLineParts(lineNumber:number, minLineColumn:number, lineContent:string, tabSize:number, lineTokens:ViewLineTokens, rawLineDecorations:InlineDecoration[], renderWhitespace:boolean): LineParts {
if (renderWhitespace) {
export function createLineParts(lineNumber:number, minLineColumn:number, lineContent:string, tabSize:number, lineTokens:ViewLineTokens, rawLineDecorations:InlineDecoration[], renderWhitespace:'none' | 'boundary' | 'all'): LineParts {
if (renderWhitespace !== 'none') {
let oldLength = rawLineDecorations.length;
rawLineDecorations = insertWhitespaceLineDecorations(lineNumber, lineContent, tabSize, lineTokens.getFauxIndentLength(), rawLineDecorations);
rawLineDecorations = insertWhitespaceLineDecorations(lineNumber, lineContent, tabSize, lineTokens.getFauxIndentLength(), renderWhitespace, rawLineDecorations);
if (rawLineDecorations.length !== oldLength) {
rawLineDecorations.sort(cmpLineDecorations);
}
......@@ -100,20 +100,12 @@ function insertOneCustomLineDecoration(dest:InlineDecoration[], lineNumber:numbe
dest.push(new InlineDecoration(new Range(lineNumber, startColumn, lineNumber, endColumn), className));
}
function insertWhitespaceLineDecorations(lineNumber:number, lineContent: string, tabSize:number, fauxIndentLength: number, rawLineDecorations: InlineDecoration[]): InlineDecoration[] {
function insertWhitespaceLineDecorations(lineNumber:number, lineContent: string, tabSize:number, fauxIndentLength: number, renderWhitespace: 'none' | 'boundary' | 'all', rawLineDecorations: InlineDecoration[]): InlineDecoration[] {
let lineLength = lineContent.length;
if (lineLength === fauxIndentLength) {
return rawLineDecorations;
}
let firstChar = lineContent.charCodeAt(fauxIndentLength);
let lastChar = lineContent.charCodeAt(lineLength - 1);
if (firstChar !== _tab && firstChar !== _space && lastChar !== _tab && lastChar !== _space) {
// This line contains no leading nor trailing whitespace => fast path
return rawLineDecorations;
}
let firstNonWhitespaceIndex = strings.firstNonWhitespaceIndex(lineContent);
let lastNonWhitespaceIndex: number;
if (firstNonWhitespaceIndex === -1) {
......@@ -138,6 +130,33 @@ function insertWhitespaceLineDecorations(lineNumber:number, lineContent: string,
sm_decoration.push('leading whitespace');
}
let startOfWhitespace = -1;
let hasTab = false;
for (let i = firstNonWhitespaceIndex; i <= lastNonWhitespaceIndex; ++i) {
let currentCharIsTab = lineContent.charCodeAt(i) === _tab;
if (currentCharIsTab || lineContent.charCodeAt(i) === _space) {
if (currentCharIsTab) {
hasTab = true;
}
if (startOfWhitespace === -1) {
startOfWhitespace = i;
}
} else if (startOfWhitespace !== -1) {
if (renderWhitespace === 'all' || renderWhitespace === 'boundary' && (hasTab || i - startOfWhitespace >= 2)) {
sm_endIndex.push(startOfWhitespace - 1);
sm_decoration.push(null);
sm_endIndex.push(i - 1);
sm_decoration.push('embedded whitespace');
}
startOfWhitespace = -1;
hasTab = false;
}
}
// add content state
sm_endIndex.push(lastNonWhitespaceIndex);
sm_decoration.push(null);
......
......@@ -13,7 +13,7 @@ export class RenderLineInput {
tabSize: number;
spaceWidth: number;
stopRenderingLineAfter: number;
renderWhitespace: boolean;
renderWhitespace: 'none' | 'boundary' | 'all';
renderControlCharacters: boolean;
parts: ViewLineToken[];
......@@ -22,7 +22,7 @@ export class RenderLineInput {
tabSize: number,
spaceWidth: number,
stopRenderingLineAfter: number,
renderWhitespace: boolean,
renderWhitespace: 'none' | 'boundary' | 'all',
renderControlCharacters: boolean,
parts: ViewLineToken[]
) {
......@@ -96,7 +96,7 @@ function controlCharacterToPrintable(characterCode: number): string {
return String.fromCharCode(_controlCharacterSequenceConversionStart + characterCode);
}
function renderLineActual(lineText: string, lineTextLength: number, tabSize: number, spaceWidth: number, actualLineParts: ViewLineToken[], renderWhitespace: boolean, renderControlCharacters: boolean, charBreakIndex: number): RenderLineOutput {
function renderLineActual(lineText: string, lineTextLength: number, tabSize: number, spaceWidth: number, actualLineParts: ViewLineToken[], renderWhitespace: 'none' | 'boundary' | 'all', renderControlCharacters: boolean, charBreakIndex: number): RenderLineOutput {
lineTextLength = +lineTextLength;
tabSize = +tabSize;
charBreakIndex = +charBreakIndex;
......@@ -111,7 +111,7 @@ function renderLineActual(lineText: string, lineTextLength: number, tabSize: num
for (let partIndex = 0, partIndexLen = actualLineParts.length; partIndex < partIndexLen; partIndex++) {
let part = actualLineParts[partIndex];
let parsRendersWhitespace = (renderWhitespace && isWhitespace(part.type));
let parsRendersWhitespace = (renderWhitespace !== 'none' && isWhitespace(part.type));
let toCharIndex = lineTextLength;
if (partIndex + 1 < partIndexLen) {
......
......@@ -55,7 +55,7 @@ suite('Editor ViewLayout - ViewLineParts', () => {
]);
});
function testCreateLineParts(lineContent: string, tokens: ViewLineToken[], fauxIndentLength: number, renderWhitespace:boolean, expected:ViewLineToken[]): void {
function testCreateLineParts(lineContent: string, tokens: ViewLineToken[], fauxIndentLength: number, renderWhitespace:'none' | 'boundary' | 'all', expected:ViewLineToken[]): void {
let lineParts = createLineParts(1, 1, lineContent, 4, new ViewLineTokens(tokens, fauxIndentLength, lineContent.length), [], renderWhitespace);
let actual = lineParts.getParts();
......@@ -69,7 +69,7 @@ suite('Editor ViewLayout - ViewLineParts', () => {
new ViewLineToken(0, '')
],
0,
false,
'none',
[
new ViewLineToken(0, '')
]
......@@ -83,7 +83,7 @@ suite('Editor ViewLayout - ViewLineParts', () => {
new ViewLineToken(6, 'b')
],
0,
false,
'none',
[
new ViewLineToken(0, 'a'),
new ViewLineToken(6, 'b')
......@@ -99,7 +99,7 @@ suite('Editor ViewLayout - ViewLineParts', () => {
new ViewLineToken(6, 'b')
],
0,
true,
'boundary',
[
new ViewLineToken(0, ' leading whitespace'),
new ViewLineToken(4, 'a'),
......@@ -117,7 +117,7 @@ suite('Editor ViewLayout - ViewLineParts', () => {
new ViewLineToken(10, 'b')
],
0,
true,
'boundary',
[
new ViewLineToken(0, ' leading whitespace'),
new ViewLineToken(4, ' leading whitespace'),
......@@ -137,7 +137,7 @@ suite('Editor ViewLayout - ViewLineParts', () => {
new ViewLineToken(4, 'b')
],
0,
true,
'boundary',
[
new ViewLineToken(0, ' leading whitespace'),
new ViewLineToken(1, ' leading whitespace'),
......@@ -156,7 +156,7 @@ suite('Editor ViewLayout - ViewLineParts', () => {
new ViewLineToken(8, 'b')
],
0,
true,
'boundary',
[
new ViewLineToken(0, ' leading whitespace'),
new ViewLineToken(3, ' leading whitespace'),
......@@ -179,7 +179,7 @@ suite('Editor ViewLayout - ViewLineParts', () => {
new ViewLineToken(8, 'b')
],
0,
true,
'boundary',
[
new ViewLineToken(0, ' leading whitespace'),
new ViewLineToken(3, ' leading whitespace'),
......@@ -203,7 +203,7 @@ suite('Editor ViewLayout - ViewLineParts', () => {
new ViewLineToken(6, 'b')
],
2,
true,
'boundary',
[
new ViewLineToken(0, ''),
new ViewLineToken(2, ' leading whitespace'),
......@@ -217,6 +217,49 @@ suite('Editor ViewLayout - ViewLineParts', () => {
);
});
test('createLineParts render whitespace in middle but not for one space', () => {
testCreateLineParts(
'it it it it',
[
new ViewLineToken(0, ''),
new ViewLineToken(6, 'a'),
new ViewLineToken(7, 'b')
],
0,
'boundary',
[
new ViewLineToken(0, ''),
new ViewLineToken(2, ' embedded whitespace'),
new ViewLineToken(4, ''),
new ViewLineToken(6, 'a'),
new ViewLineToken(7, 'b'),
new ViewLineToken(9, 'b embedded whitespace'),
new ViewLineToken(11, 'b'),
]
);
});
test('createLineParts render whitespace for all in middle', () => {
testCreateLineParts(
' Hello world!\t',
[
new ViewLineToken(0, ''),
new ViewLineToken(4, 'a'),
new ViewLineToken(6, 'b')
],
0,
'all',
[
new ViewLineToken(0, ' leading whitespace'),
new ViewLineToken(1, ''),
new ViewLineToken(4, 'a'),
new ViewLineToken(6, 'b embedded whitespace'),
new ViewLineToken(7, 'b'),
new ViewLineToken(13, 'b trailing whitespace'),
]
);
});
test('ViewLineParts', () => {
assert.deepEqual(LineDecorationsNormalizer.normalize(1, 1, [
......@@ -287,7 +330,7 @@ suite('Editor ViewLayout - ViewLineParts', () => {
});
function createTestGetColumnOfLinePartOffset(lineContent:string, tabSize:number, parts:ViewLineToken[]): (partIndex:number, partLength:number, offset:number, expected:number)=>void {
let renderLineOutput = renderLine(new RenderLineInput(lineContent, tabSize, 10, -1, false, false, parts));
let renderLineOutput = renderLine(new RenderLineInput(lineContent, tabSize, 10, -1, 'none', false, parts));
return (partIndex:number, partLength:number, offset:number, expected:number) => {
let actual = getColumnOfLinePartOffset(-1, parts, lineContent.length + 1, renderLineOutput.charOffsetInPart, partIndex, partLength, offset);
......
......@@ -20,7 +20,7 @@ suite('viewLineRenderer.renderLine', () => {
tabSize,
0,
-1,
false,
'none',
false,
[createPart(0, '')]
));
......@@ -62,7 +62,7 @@ suite('viewLineRenderer.renderLine', () => {
tabSize,
0,
-1,
false,
'none',
false,
parts
));
......@@ -93,7 +93,7 @@ suite('viewLineRenderer.renderLine', () => {
4,
10,
6,
true,
'boundary',
false,
[
createPart( 0, '0'),
......@@ -182,7 +182,7 @@ suite('viewLineRenderer.renderLine', () => {
4,
10,
-1,
true,
'boundary',
false,
lineParts
));
......@@ -237,7 +237,7 @@ suite('viewLineRenderer.renderLine', () => {
4,
10,
-1,
false,
'none',
false,
lineParts
));
......@@ -292,7 +292,7 @@ suite('viewLineRenderer.renderLine', () => {
4,
10,
-1,
false,
'none',
false,
lineParts
));
......
......@@ -1272,10 +1272,10 @@ declare module monaco.editor {
*/
folding?: boolean;
/**
* Enable rendering of leading whitespace.
* Defaults to false.
* Enable rendering of whitespace.
* Defaults to none.
*/
renderWhitespace?: boolean;
renderWhitespace?: 'none' | 'boundary' | 'all';
/**
* Enable rendering of control characters.
* Defaults to false.
......@@ -1385,7 +1385,7 @@ declare module monaco.editor {
scrollBeyondLastLine: boolean;
editorClassName: string;
stopRenderingLineAfter: number;
renderWhitespace: boolean;
renderWhitespace: 'none' | 'boundary' | 'all';
renderControlCharacters: boolean;
renderIndentGuides: boolean;
renderLineHighlight: boolean;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册