Skip to content

Commit

Permalink
Render indent guides outside of the view line (#2192)
Browse files Browse the repository at this point in the history
  • Loading branch information
alexdima committed Jun 29, 2016
1 parent 1bfab84 commit 973c3bd
Show file tree
Hide file tree
Showing 10 changed files with 159 additions and 195 deletions.
2 changes: 2 additions & 0 deletions src/vs/editor/browser/view/viewImpl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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);
Expand Down
19 changes: 19 additions & 0 deletions src/vs/editor/browser/viewParts/indentGuides/indentGuides.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/*---------------------------------------------------------------------------------------------
* 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;
}
118 changes: 118 additions & 0 deletions src/vs/editor/browser/viewParts/indentGuides/indentGuides.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
/*---------------------------------------------------------------------------------------------
* 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];
}
}
8 changes: 1 addition & 7 deletions src/vs/editor/browser/viewParts/lines/viewLine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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;
}
Expand All @@ -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
);
}

Expand Down
3 changes: 1 addition & 2 deletions src/vs/editor/browser/widget/diffEditorWidget.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
14 changes: 0 additions & 14 deletions src/vs/editor/browser/widget/media/tokens.css
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

34 changes: 8 additions & 26 deletions src/vs/editor/common/viewLayout/viewLineParts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down Expand Up @@ -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) {
Expand All @@ -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
Expand All @@ -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);
Expand Down
11 changes: 3 additions & 8 deletions src/vs/editor/common/viewLayout/viewLineRenderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand All @@ -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) {
Expand All @@ -125,7 +120,7 @@ function renderLineActual(lineText: string, lineTextLength: number, tabSize: num
}

charOffsetInPart = 0;
if (partIsFixedWidth) {
if (parsRendersWhitespace) {

let partContentCnt = 0;
let partContent = '';
Expand All @@ -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--;
}
Expand All @@ -149,7 +144,7 @@ function renderLineActual(lineText: string, lineTextLength: number, tabSize: num
}
} else {
// must be _space
partContent += parsRendersWhitespace ? '&middot;' : '&nbsp;';
partContent += '&middot;';
partContentCnt++;
}

Expand Down
Loading

0 comments on commit 973c3bd

Please sign in to comment.