提交 973c3bda 编写于 作者: A Alex Dima

Render indent guides outside of the view line (#2192)

上级 1bfab84d
......@@ -28,6 +28,7 @@ import {CurrentLineHighlightOverlay} from 'vs/editor/browser/viewParts/currentLi
import {DecorationsOverlay} from 'vs/editor/browser/viewParts/decorations/decorations';
import {GlyphMarginOverlay} from 'vs/editor/browser/viewParts/glyphMargin/glyphMargin';
import {LineNumbersOverlay} from 'vs/editor/browser/viewParts/lineNumbers/lineNumbers';
import {IndentGuidesOverlay} from 'vs/editor/browser/viewParts/indentGuides/indentGuides';
import {ViewLines} from 'vs/editor/browser/viewParts/lines/viewLines';
import {LinesDecorationsOverlay} from 'vs/editor/browser/viewParts/linesDecorations/linesDecorations';
import {ViewOverlayWidgets} from 'vs/editor/browser/viewParts/overlayWidgets/overlayWidgets';
......@@ -235,6 +236,7 @@ export class View extends ViewEventHandler implements editorBrowser.IView, IDisp
contentViewOverlays.addDynamicOverlay(new CurrentLineHighlightOverlay(this._context, this.layoutProvider));
contentViewOverlays.addDynamicOverlay(new SelectionsOverlay(this._context));
contentViewOverlays.addDynamicOverlay(new DecorationsOverlay(this._context));
contentViewOverlays.addDynamicOverlay(new IndentGuidesOverlay(this._context));
var marginViewOverlays = new MarginViewOverlays(this._context, this.layoutProvider);
this.viewParts.push(marginViewOverlays);
......
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
/*
Keeping name short for faster parsing.
cigr = core ident guides rendering (div)
*/
.monaco-editor .lines-content .cigr {
position: absolute;
background: lightgray;
width: 1px;
}
.monaco-editor.vs-dark .lines-content .cigr,
.monaco-editor.hc-black .lines-content .cigr {
background: #353535;
}
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import 'vs/css!./indentGuides';
import * as editorCommon from 'vs/editor/common/editorCommon';
import {DynamicViewOverlay} from 'vs/editor/browser/view/dynamicViewOverlay';
import {ViewContext} from 'vs/editor/common/view/viewContext';
import {IRenderingContext} from 'vs/editor/common/view/renderingContext';
export class IndentGuidesOverlay extends DynamicViewOverlay {
private _context:ViewContext;
private _lineHeight: number;
private _spaceWidth: number;
private _renderResult: string[];
private _enabled: boolean;
constructor(context:ViewContext) {
super();
this._context = context;
this._lineHeight = this._context.configuration.editor.lineHeight;
this._spaceWidth = this._context.configuration.editor.fontInfo.spaceWidth;
this._enabled = this._context.configuration.editor.viewInfo.indentGuides;
this._renderResult = null;
this._context.addEventHandler(this);
}
public dispose(): void {
this._context.removeEventHandler(this);
this._context = null;
this._renderResult = null;
}
// --- begin event handlers
public onModelFlushed(): boolean {
return true;
}
public onModelLinesDeleted(e:editorCommon.IViewLinesDeletedEvent): boolean {
return true;
}
public onModelLineChanged(e:editorCommon.IViewLineChangedEvent): boolean {
return true;
}
public onModelLinesInserted(e:editorCommon.IViewLinesInsertedEvent): boolean {
return true;
}
public onConfigurationChanged(e:editorCommon.IConfigurationChangedEvent): boolean {
if (e.lineHeight) {
this._lineHeight = this._context.configuration.editor.lineHeight;
}
if (e.fontInfo) {
this._spaceWidth = this._context.configuration.editor.fontInfo.spaceWidth;
}
if (e.viewInfo.indentGuides) {
this._enabled = this._context.configuration.editor.viewInfo.indentGuides;
}
return true;
}
public onLayoutChanged(layoutInfo:editorCommon.EditorLayoutInfo): boolean {
return true;
}
public onScrollChanged(e:editorCommon.IScrollEvent): boolean {
return e.scrollTopChanged;// || e.scrollWidthChanged;
}
public onZonesChanged(): boolean {
return true;
}
// --- end event handlers
public prepareRender(ctx:IRenderingContext): void {
if (!this.shouldRender()) {
throw new Error('I did not ask to render!');
}
if (!this._enabled) {
this._renderResult = null;
return;
}
const visibleStartLineNumber = ctx.visibleRange.startLineNumber;
const visibleEndLineNumber = ctx.visibleRange.endLineNumber;
const tabSize = this._context.model.getTabSize();
const tabWidth = tabSize * this._spaceWidth;
const lineHeight = this._lineHeight;
let output:string[] = [];
for (let lineNumber = visibleStartLineNumber; lineNumber <= visibleEndLineNumber; lineNumber++) {
let lineIndex = lineNumber - visibleStartLineNumber;
let indent = this._context.model.getLineIndentGuide(lineNumber);
let result = '';
let left = 0;
for (let i = 0; i < indent; i++) {
result += `<div class="cigr" style="left:${left}px;height:${lineHeight}px;"></div>`;
left += tabWidth;
}
output[lineIndex] = result;
}
this._renderResult = output;
}
public render(startLineNumber:number, lineNumber:number): string {
if (!this._renderResult) {
return '';
}
let lineIndex = lineNumber - startLineNumber;
if (lineIndex < 0 || lineIndex >= this._renderResult.length) {
throw new Error('Unexpected render request');
}
return this._renderResult[lineIndex];
}
}
......@@ -21,7 +21,6 @@ export class ViewLine implements IVisibleLineData {
protected _context:ViewContext;
private _renderWhitespace: boolean;
private _renderControlCharacters: boolean;
private _indentGuides: boolean;
private _spaceWidth: number;
private _lineHeight: number;
private _stopRenderingLineAfter: number;
......@@ -41,7 +40,6 @@ export class ViewLine implements IVisibleLineData {
this._context = context;
this._renderWhitespace = this._context.configuration.editor.viewInfo.renderWhitespace;
this._renderControlCharacters = this._context.configuration.editor.viewInfo.renderControlCharacters;
this._indentGuides = this._context.configuration.editor.viewInfo.indentGuides;
this._spaceWidth = this._context.configuration.editor.fontInfo.spaceWidth;
this._lineHeight = this._context.configuration.editor.lineHeight;
this._stopRenderingLineAfter = this._context.configuration.editor.viewInfo.stopRenderingLineAfter;
......@@ -91,9 +89,6 @@ export class ViewLine implements IVisibleLineData {
if (e.viewInfo.renderControlCharacters) {
this._renderControlCharacters = this._context.configuration.editor.viewInfo.renderControlCharacters;
}
if (e.viewInfo.indentGuides) {
this._indentGuides = this._context.configuration.editor.viewInfo.indentGuides;
}
if (e.fontInfo) {
this._spaceWidth = this._context.configuration.editor.fontInfo.spaceWidth;
}
......@@ -118,8 +113,7 @@ export class ViewLine implements IVisibleLineData {
this._context.model.getTabSize(),
this._context.model.getLineTokens(lineNumber),
inlineDecorations,
this._renderWhitespace,
this._indentGuides ? this._context.model.getLineIndentGuide(lineNumber) : 0
this._renderWhitespace
);
}
......
......@@ -1836,8 +1836,7 @@ class InlineViewZonesComputer extends ViewZonesComputer {
let lineContent = originalModel.getLineContent(lineNumber);
let lineTokens = new ViewLineTokens([new ViewLineToken(0, '')], 0, lineContent.length);
let indentGuides = config.viewInfo.indentGuides ? originalModel.getLineIndentGuide(lineNumber) : 0;
let parts = createLineParts(lineNumber, 1, lineContent, tabSize, lineTokens, decorations, config.viewInfo.renderWhitespace, indentGuides);
let parts = createLineParts(lineNumber, 1, lineContent, tabSize, lineTokens, decorations, config.viewInfo.renderWhitespace);
let r = renderLine(new RenderLineInput(
lineContent,
......
......@@ -36,17 +36,3 @@
.monaco-editor .markup.bold { font-weight: bold; }
.monaco-editor .markup.italic { font-style: italic; }
.monaco-editor .markup.underline { text-decoration: underline; }
/* -------------------- Indent Guides -------------------- */
.monaco-editor .token.indent-guide {
display:inline-block;
border-left: 1px solid lightgray;
box-sizing: border-box;
}
.monaco-editor.vs-dark .token.indent-guide,
.monaco-editor.hc-black .token.indent-guide {
border-left: 1px solid #353535;
}
......@@ -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, indentGuides:number): LineParts {
if (indentGuides || renderWhitespace) {
export function createLineParts(lineNumber:number, minLineColumn:number, lineContent:string, tabSize:number, lineTokens:ViewLineTokens, rawLineDecorations:InlineDecoration[], renderWhitespace:boolean): LineParts {
if (renderWhitespace) {
let oldLength = rawLineDecorations.length;
rawLineDecorations = insertCustomLineDecorations(indentGuides, renderWhitespace, lineNumber, lineContent, tabSize, lineTokens.getFauxIndentLength(), rawLineDecorations);
rawLineDecorations = insertWhitespaceLineDecorations(lineNumber, lineContent, tabSize, lineTokens.getFauxIndentLength(), rawLineDecorations);
if (rawLineDecorations.length !== oldLength) {
rawLineDecorations.sort(cmpLineDecorations);
}
......@@ -100,17 +100,13 @@ function insertOneCustomLineDecoration(dest:InlineDecoration[], lineNumber:numbe
dest.push(new InlineDecoration(new Range(lineNumber, startColumn, lineNumber, endColumn), className));
}
function insertCustomLineDecorations(indentGuides:number, renderWhitespace:boolean, lineNumber:number, lineContent: string, tabSize:number, fauxIndentLength: number, rawLineDecorations: InlineDecoration[]): InlineDecoration[] {
if (indentGuides === 0 && !renderWhitespace) {
return rawLineDecorations;
}
function insertWhitespaceLineDecorations(lineNumber:number, lineContent: string, tabSize:number, fauxIndentLength: number, rawLineDecorations: InlineDecoration[]): InlineDecoration[] {
let lineLength = lineContent.length;
if (lineLength === fauxIndentLength) {
return rawLineDecorations;
}
let firstChar = indentGuides ? lineContent.charCodeAt(0) : lineContent.charCodeAt(fauxIndentLength);
let firstChar = lineContent.charCodeAt(fauxIndentLength);
let lastChar = lineContent.charCodeAt(lineLength - 1);
if (firstChar !== _tab && firstChar !== _space && lastChar !== _tab && lastChar !== _space) {
......@@ -134,26 +130,12 @@ function insertCustomLineDecorations(indentGuides:number, renderWhitespace:boole
if (fauxIndentLength > 0) {
// add faux indent state
sm_endIndex.push(fauxIndentLength - 1);
sm_decoration.push(indentGuides ? 'indent-guide' : null);
sm_decoration.push(null);
}
if (firstNonWhitespaceIndex > fauxIndentLength) {
// add leading whitespace state
sm_endIndex.push(firstNonWhitespaceIndex - 1);
let leadingClassName:string = null;
if (fauxIndentLength > 0) {
leadingClassName = (renderWhitespace ? 'leading whitespace' : null);
} else {
if (indentGuides && renderWhitespace) {
leadingClassName = 'leading whitespace indent-guide';
} else if (indentGuides) {
leadingClassName = 'indent-guide';
} else {
leadingClassName = 'leading whitespace';
}
}
sm_decoration.push(leadingClassName);
sm_decoration.push('leading whitespace');
}
// add content state
......@@ -162,7 +144,7 @@ function insertCustomLineDecorations(indentGuides:number, renderWhitespace:boole
// add trailing whitespace state
sm_endIndex.push(lineLength - 1);
sm_decoration.push(renderWhitespace ? 'trailing whitespace' : null);
sm_decoration.push('trailing whitespace');
// add dummy state to avoid array length checks
sm_endIndex.push(lineLength);
......
......@@ -89,10 +89,6 @@ function isWhitespace(type:string): boolean {
return (type.indexOf('whitespace') >= 0);
}
function isIndentGuide(type:string): boolean {
return (type.indexOf('indent-guide') >= 0);
}
function isControlCharacter(characterCode: number): boolean {
return characterCode < 32;
}
......@@ -116,7 +112,6 @@ function renderLineActual(lineText: string, lineTextLength: number, tabSize: num
let part = actualLineParts[partIndex];
let parsRendersWhitespace = (renderWhitespace && isWhitespace(part.type));
let partIsFixedWidth = parsRendersWhitespace || isIndentGuide(part.type);
let toCharIndex = lineTextLength;
if (partIndex + 1 < partIndexLen) {
......@@ -125,7 +120,7 @@ function renderLineActual(lineText: string, lineTextLength: number, tabSize: num
}
charOffsetInPart = 0;
if (partIsFixedWidth) {
if (parsRendersWhitespace) {
let partContentCnt = 0;
let partContent = '';
......@@ -138,7 +133,7 @@ function renderLineActual(lineText: string, lineTextLength: number, tabSize: num
tabsCharDelta += insertSpacesCount - 1;
charOffsetInPart += insertSpacesCount - 1;
if (insertSpacesCount > 0) {
partContent += parsRendersWhitespace ? '&rarr;' : '&nbsp;';
partContent += '&rarr;';
partContentCnt++;
insertSpacesCount--;
}
......@@ -149,7 +144,7 @@ function renderLineActual(lineText: string, lineTextLength: number, tabSize: num
}
} else {
// must be _space
partContent += parsRendersWhitespace ? '&middot;' : '&nbsp;';
partContent += '&middot;';
partContentCnt++;
}
......
......@@ -55,8 +55,8 @@ suite('Editor ViewLayout - ViewLineParts', () => {
]);
});
function testCreateLineParts(lineContent: string, tokens: ViewLineToken[], fauxIndentLength: number, renderWhitespace:boolean, indentGuides:number, expected:ViewLineToken[]): void {
let lineParts = createLineParts(1, 1, lineContent, 4, new ViewLineTokens(tokens, fauxIndentLength, lineContent.length), [], renderWhitespace, indentGuides);
function testCreateLineParts(lineContent: string, tokens: ViewLineToken[], fauxIndentLength: number, renderWhitespace:boolean, expected:ViewLineToken[]): void {
let lineParts = createLineParts(1, 1, lineContent, 4, new ViewLineTokens(tokens, fauxIndentLength, lineContent.length), [], renderWhitespace);
let actual = lineParts.getParts();
assert.deepEqual(actual, expected);
......@@ -70,7 +70,6 @@ suite('Editor ViewLayout - ViewLineParts', () => {
],
0,
false,
0,
[
new ViewLineToken(0, '')
]
......@@ -85,7 +84,6 @@ suite('Editor ViewLayout - ViewLineParts', () => {
],
0,
false,
0,
[
new ViewLineToken(0, 'a'),
new ViewLineToken(6, 'b')
......@@ -102,7 +100,6 @@ suite('Editor ViewLayout - ViewLineParts', () => {
],
0,
true,
0,
[
new ViewLineToken(0, ' leading whitespace'),
new ViewLineToken(4, 'a'),
......@@ -121,7 +118,6 @@ suite('Editor ViewLayout - ViewLineParts', () => {
],
0,
true,
0,
[
new ViewLineToken(0, ' leading whitespace'),
new ViewLineToken(4, ' leading whitespace'),
......@@ -142,7 +138,6 @@ suite('Editor ViewLayout - ViewLineParts', () => {
],
0,
true,
0,
[
new ViewLineToken(0, ' leading whitespace'),
new ViewLineToken(1, ' leading whitespace'),
......@@ -162,7 +157,6 @@ suite('Editor ViewLayout - ViewLineParts', () => {
],
0,
true,
0,
[
new ViewLineToken(0, ' leading whitespace'),
new ViewLineToken(3, ' leading whitespace'),
......@@ -176,84 +170,7 @@ suite('Editor ViewLayout - ViewLineParts', () => {
]
);
});
test('createLineParts render indent guides - 4 leading spaces', () => {
testCreateLineParts(
' Hello world! ',
[
new ViewLineToken(0, ''),
new ViewLineToken(4, 'a'),
new ViewLineToken(6, 'b')
],
0,
false,
1,
[
new ViewLineToken(0, ' indent-guide'),
new ViewLineToken(4, 'a'),
new ViewLineToken(6, 'b')
]
);
});
test('createLineParts render indent guides - 8 leading spaces', () => {
testCreateLineParts(
' Hello world! ',
[
new ViewLineToken(0, ''),
new ViewLineToken(8, 'a'),
new ViewLineToken(10, 'b')
],
0,
false,
2,
[
new ViewLineToken(0, ' indent-guide'),
new ViewLineToken(4, ' indent-guide'),
new ViewLineToken(8, 'a'),
new ViewLineToken(10, 'b')
]
);
});
test('createLineParts render indent guides - 2 leading tabs', () => {
testCreateLineParts(
'\t\tHello world!\t',
[
new ViewLineToken(0, ''),
new ViewLineToken(2, 'a'),
new ViewLineToken(4, 'b')
],
0,
false,
2,
[
new ViewLineToken(0, ' indent-guide'),
new ViewLineToken(1, ' indent-guide'),
new ViewLineToken(2, 'a'),
new ViewLineToken(4, 'b'),
]
);
});
test('createLineParts render indent guides - mixed leading spaces and tabs', () => {
testCreateLineParts(
' \t\t Hello world! \t \t \t ',
[
new ViewLineToken(0, ''),
new ViewLineToken(6, 'a'),
new ViewLineToken(8, 'b')
],
0,
false,
2,
[
new ViewLineToken(0, ' indent-guide'),
new ViewLineToken(3, ' indent-guide'),
new ViewLineToken(4, ' indent-guide'),
new ViewLineToken(6, 'a'),
new ViewLineToken(8, 'b'),
]
);
});
test('createLineParts render whitespace and indent guides - mixed leading spaces and tabs', () => {
test('createLineParts render whitespace - mixed leading spaces and tabs', () => {
testCreateLineParts(
' \t\t Hello world! \t \t \t ',
[
......@@ -263,11 +180,10 @@ suite('Editor ViewLayout - ViewLineParts', () => {
],
0,
true,
2,
[
new ViewLineToken(0, ' leading whitespace indent-guide'),
new ViewLineToken(3, ' leading whitespace indent-guide'),
new ViewLineToken(4, ' leading whitespace indent-guide'),
new ViewLineToken(0, ' leading whitespace'),
new ViewLineToken(3, ' leading whitespace'),
new ViewLineToken(4, ' leading whitespace'),
new ViewLineToken(6, 'a'),
new ViewLineToken(8, 'b'),
new ViewLineToken(18, 'b trailing whitespace'),
......@@ -288,7 +204,6 @@ suite('Editor ViewLayout - ViewLineParts', () => {
],
2,
true,
0,
[
new ViewLineToken(0, ''),
new ViewLineToken(2, ' leading whitespace'),
......@@ -302,52 +217,6 @@ suite('Editor ViewLayout - ViewLineParts', () => {
);
});
test('createLineParts render indent guides uses faux indent', () => {
testCreateLineParts(
'\t\t Hello world! \t \t \t ',
[
new ViewLineToken(0, ''),
new ViewLineToken(4, 'a'),
new ViewLineToken(6, 'b')
],
2,
false,
2,
[
new ViewLineToken(0, ' indent-guide'),
new ViewLineToken(1, ' indent-guide'),
new ViewLineToken(2, ''),
new ViewLineToken(4, 'a'),
new ViewLineToken(6, 'b')
]
);
});
test('createLineParts render whitespace and indent guides for line with faux indent', () => {
testCreateLineParts(
'\t\t Hello world! \t \t \t ',
[
new ViewLineToken(0, ''),
new ViewLineToken(4, 'a'),
new ViewLineToken(6, 'b')
],
2,
true,
2,
[
new ViewLineToken(0, ' indent-guide'),
new ViewLineToken(1, ' indent-guide'),
new ViewLineToken(2, ' leading whitespace'),
new ViewLineToken(4, 'a'),
new ViewLineToken(6, 'b'),
new ViewLineToken(16, 'b trailing whitespace'),
new ViewLineToken(18, 'b trailing whitespace'),
new ViewLineToken(21, 'b trailing whitespace'),
new ViewLineToken(25, 'b trailing whitespace'),
]
);
});
test('ViewLineParts', () => {
assert.deepEqual(LineDecorationsNormalizer.normalize(1, 1, [
......
......@@ -382,7 +382,7 @@ function _processThemeObject(themeId: string, themeDocument: ThemeDocument): str
if (editorSettings.invisibles) {
let invisibles = new Color(editorSettings.invisibles);
cssRules.push(`.monaco-editor.${themeSelector} .token.whitespace { color: ${invisibles} !important; }`);
cssRules.push(`.monaco-editor.${themeSelector} .token.indent-guide { border-left: 1px solid ${invisibles}; }`);
cssRules.push(`.monaco-editor.${themeSelector} .lines-content .cigr { background: ${invisibles}; }`);
}
return cssRules.join('\n');
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册