提交 5a46bd04 编写于 作者: A Alex Dima

Towards adding indent guides

上级 cd12a7e3
......@@ -9,7 +9,7 @@ import {TPromise} from 'vs/base/common/winjs.base';
import {ViewLineToken, IModel} from 'vs/editor/common/editorCommon';
import {ILineTokens, IMode} from 'vs/editor/common/modes';
import {IModeService} from 'vs/editor/common/services/modeService';
import {IRenderLineOutput, renderLine, RenderLineInput} from 'vs/editor/common/viewLayout/viewLineRenderer';
import {RenderLineOutput, renderLine, RenderLineInput} from 'vs/editor/common/viewLayout/viewLineRenderer';
import * as TokensBinaryEncoding from 'vs/editor/common/model/tokensBinaryEncoding';
export interface IColorizerOptions {
......@@ -126,7 +126,7 @@ function actualColorize(lines:string[], mode:IMode, tabSize:number): IActualColo
length:number,
line: string,
tokenizeResult: ILineTokens,
renderResult: IRenderLineOutput,
renderResult: RenderLineOutput,
retokenize: TPromise<void>[] = [];
for (i = 0, length = lines.length; i < length; i++) {
......
......@@ -7,7 +7,7 @@
import * as browser from 'vs/base/browser/browser';
import {FastDomNode, createFastDomNode} from 'vs/base/browser/styleMutator';
import {HorizontalRange, IConfigurationChangedEvent, IModelDecoration} from 'vs/editor/common/editorCommon';
import {ILineParts, createLineParts, getColumnOfLinePartOffset} from 'vs/editor/common/viewLayout/viewLineParts';
import {LineParts, createLineParts, getColumnOfLinePartOffset} from 'vs/editor/common/viewLayout/viewLineParts';
import {renderLine, RenderLineInput} from 'vs/editor/common/viewLayout/viewLineRenderer';
import {ClassNames, IViewContext} from 'vs/editor/browser/editorBrowser';
import {IVisibleLineData} from 'vs/editor/browser/view/viewLayer';
......@@ -24,7 +24,7 @@ export class ViewLine implements IVisibleLineData {
private _domNode: FastDomNode;
private _lineParts: ILineParts;
private _lineParts: LineParts;
private _isInvalid: boolean;
private _isMaybeInvalid: boolean;
......@@ -99,7 +99,7 @@ export class ViewLine implements IVisibleLineData {
}
public shouldUpdateHTML(startLineNumber:number, lineNumber:number, inlineDecorations:IModelDecoration[]): boolean {
let newLineParts:ILineParts = null;
let newLineParts:LineParts = null;
if (this._isMaybeInvalid || this._isInvalid) {
// Compute new line parts only if there is some evidence that something might have changed
......@@ -158,7 +158,7 @@ export class ViewLine implements IVisibleLineData {
// --- end IVisibleLineData
private _render(lineNumber:number, lineParts:ILineParts): string {
private _render(lineNumber:number, lineParts:LineParts): string {
this._cachedWidth = -1;
......@@ -346,7 +346,7 @@ class WebKitViewLine extends ViewLine {
}
function findIndexInArrayWithMax(lineParts:ILineParts, desiredIndex: number, maxResult:number): number {
function findIndexInArrayWithMax(lineParts:LineParts, desiredIndex: number, maxResult:number): number {
let r = lineParts.findIndexOfOffset(desiredIndex);
return r <= maxResult ? r : maxResult;
}
......
......@@ -14,7 +14,12 @@
}
.monaco-editor .token.whitespace { display:inline-block; }
/*.monaco-editor .token.leading.whitespace { border-left:1px solid lightgrey; }*/
.monaco-editor .token.indent-guide {
display:inline-block;
border-left: 1px solid lightgray;
box-sizing: border-box;
}
.monaco-editor.enable-ligatures {
-webkit-font-feature-settings: "liga" on, "calt" on;
......
......@@ -9,32 +9,25 @@ import {Arrays} from 'vs/editor/common/core/arrays';
import {ViewLineToken, IEditorRange, ViewLineTokens} from 'vs/editor/common/editorCommon';
import {Range} from 'vs/editor/common/core/range';
export interface ILineParts {
getParts(): ViewLineToken[];
equals(other:ILineParts): boolean;
findIndexOfOffset(offset:number): number;
}
const INDENT_GUIDES = false;
function cmpLineDecorations(a:ILineDecoration, b:ILineDecoration): number {
return Range.compareRangesUsingStarts(a.range, b.range);
}
export function createLineParts(lineNumber:number, minLineColumn:number, lineContent:string, tabSize:number, lineTokens:ViewLineTokens, rawLineDecorations:ILineDecoration[], renderWhitespace:boolean): ILineParts {
if (renderWhitespace) {
export function createLineParts(lineNumber:number, minLineColumn:number, lineContent:string, tabSize:number, lineTokens:ViewLineTokens, rawLineDecorations:ILineDecoration[], renderWhitespace:boolean): LineParts {
if (INDENT_GUIDES || renderWhitespace) {
let oldLength = rawLineDecorations.length;
rawLineDecorations = insertWhitespace(lineNumber, lineContent, tabSize, lineTokens.getFauxIndentLength(), rawLineDecorations);
rawLineDecorations = insertWhitespace(INDENT_GUIDES, renderWhitespace, lineNumber, lineContent, tabSize, lineTokens.getFauxIndentLength(), rawLineDecorations);
if (rawLineDecorations.length !== oldLength) {
rawLineDecorations.sort(cmpLineDecorations);
}
}
if (rawLineDecorations.length > 0) {
return new ViewLineParts(lineNumber, minLineColumn, lineTokens, lineContent, rawLineDecorations);
return createViewLineParts(lineNumber, minLineColumn, lineTokens, lineContent, rawLineDecorations);
} else {
return new FastViewLineParts(lineTokens, lineContent);
return createFastViewLineParts(lineTokens, lineContent);
}
}
......@@ -113,7 +106,7 @@ function insertOneWhitespace(dest:ILineDecoration[], lineNumber:number, startCol
});
}
function insertWhitespace(lineNumber:number, lineContent: string, tabSize:number, fauxIndentLength: number, rawLineDecorations: ILineDecoration[]): ILineDecoration[] {
function insertWhitespace(renderIndentGuides:boolean, renderWhitespace:boolean, lineNumber:number, lineContent: string, tabSize:number, fauxIndentLength: number, rawLineDecorations: ILineDecoration[]): ILineDecoration[] {
let lineLength = lineContent.length;
if (lineLength === fauxIndentLength) {
return rawLineDecorations;
......@@ -140,6 +133,20 @@ function insertWhitespace(lineNumber:number, lineContent: string, tabSize:number
let result = rawLineDecorations.slice(0);
let tmpIndent = 0;
let whitespaceStartColumn = fauxIndentLength + 1;
let leadingClassName = '';
if (renderIndentGuides) {
leadingClassName += ' indent-guide';
}
if (renderWhitespace) {
leadingClassName += ' leading whitespace';
}
let trailingClassName = '';
if (renderWhitespace) {
trailingClassName += ' trailing whitespace';
}
for (let i = fauxIndentLength; i < lineLength; i++) {
let chCode = lineContent.charCodeAt(i);
......@@ -153,7 +160,7 @@ function insertWhitespace(lineNumber:number, lineContent: string, tabSize:number
// in leading whitespace
// push for every indent or when the end of the leading whitespace is reached
if (tmpIndent >= tabSize || i === firstNonWhitespaceIndex - 1) {
insertOneWhitespace(result, lineNumber, whitespaceStartColumn, i + 2, 'leading whitespace');
insertOneWhitespace(result, lineNumber, whitespaceStartColumn, i + 2, leadingClassName);
whitespaceStartColumn = i + 2;
tmpIndent = 0;
}
......@@ -164,7 +171,7 @@ function insertWhitespace(lineNumber:number, lineContent: string, tabSize:number
// in trailing whitespace
// push for every indent or when the end of the string is reached
if (tmpIndent >= tabSize || i === lineLength - 1) {
insertOneWhitespace(result, lineNumber, whitespaceStartColumn, i + 2, 'trailing whitespace');
insertOneWhitespace(result, lineNumber, whitespaceStartColumn, i + 2, trailingClassName);
whitespaceStartColumn = i + 2;
tmpIndent = 0;
}
......@@ -180,119 +187,77 @@ function insertWhitespace(lineNumber:number, lineContent: string, tabSize:number
return result;
}
export class FastViewLineParts implements ILineParts {
export class LineParts {
_linePartsTrait: void;
private _parts: ViewLineToken[];
private lineTokens: ViewLineTokens;
private parts: ViewLineToken[];
constructor(lineTokens:ViewLineTokens, lineContent:string) {
this.lineTokens = lineTokens;
this.parts = lineTokens.getTokens();
this.parts = trimEmptyTrailingPart(this.parts, lineContent);
constructor(parts: ViewLineToken[]) {
this._parts = parts;
}
public getParts(): ViewLineToken[]{
return this.parts;
public getParts(): ViewLineToken[] {
return this._parts;
}
public equals(other:ILineParts): boolean {
if (other instanceof FastViewLineParts) {
var otherFastLineParts = <FastViewLineParts>other;
return this.lineTokens.equals(otherFastLineParts.lineTokens);
}
return false;
public equals(other:LineParts): boolean {
return ViewLineToken.equalsArray(this._parts, other._parts);
}
public findIndexOfOffset(offset:number): number {
return Arrays.findIndexInSegmentsArray(this.parts, offset);
return Arrays.findIndexInSegmentsArray(this._parts, offset);
}
}
export class ViewLineParts implements ILineParts {
private parts:ViewLineToken[];
private lastPartIndex:number;
private lastEndOffset:number;
constructor(lineNumber:number, minLineColumn:number, lineTokens:ViewLineTokens, lineContent:string, rawLineDecorations:ILineDecoration[]) {
// lineDecorations might overlap on top of each other, so they need to be normalized
var lineDecorations = LineDecorationsNormalizer.normalize(lineNumber, minLineColumn, rawLineDecorations),
lineDecorationsIndex = 0,
lineDecorationsLength = lineDecorations.length;
var actualLineTokens = lineTokens.getTokens(),
nextStartOffset:number,
currentTokenEndOffset:number,
currentTokenClassName:string;
function createFastViewLineParts(lineTokens:ViewLineTokens, lineContent:string): LineParts {
let parts = lineTokens.getTokens();
parts = trimEmptyTrailingPart(parts, lineContent);
return new LineParts(parts);
}
var parts:ViewLineToken[] = [];
function createViewLineParts(lineNumber:number, minLineColumn:number, lineTokens:ViewLineTokens, lineContent:string, rawLineDecorations:ILineDecoration[]): LineParts {
// lineDecorations might overlap on top of each other, so they need to be normalized
var lineDecorations = LineDecorationsNormalizer.normalize(lineNumber, minLineColumn, rawLineDecorations),
lineDecorationsIndex = 0,
lineDecorationsLength = lineDecorations.length;
for (var i = 0, len = actualLineTokens.length; i < len; i++) {
nextStartOffset = actualLineTokens[i].startIndex;
currentTokenEndOffset = (i + 1 < len ? actualLineTokens[i + 1].startIndex : lineTokens.getTextLength());
currentTokenClassName = actualLineTokens[i].type;
var actualLineTokens = lineTokens.getTokens(),
nextStartOffset:number,
currentTokenEndOffset:number,
currentTokenClassName:string;
while (lineDecorationsIndex < lineDecorationsLength && lineDecorations[lineDecorationsIndex].startOffset < currentTokenEndOffset) {
if (lineDecorations[lineDecorationsIndex].startOffset > nextStartOffset) {
// the first decorations starts after the token
parts.push(new ViewLineToken(nextStartOffset, currentTokenClassName));
nextStartOffset = lineDecorations[lineDecorationsIndex].startOffset;
}
var parts:ViewLineToken[] = [];
parts.push(new ViewLineToken(nextStartOffset, currentTokenClassName + ' ' + lineDecorations[lineDecorationsIndex].className));
if (lineDecorations[lineDecorationsIndex].endOffset >= currentTokenEndOffset) {
// this decoration goes on to the next token
nextStartOffset = currentTokenEndOffset;
break;
} else {
// this decorations stops inside this token
nextStartOffset = lineDecorations[lineDecorationsIndex].endOffset + 1;
lineDecorationsIndex++;
}
}
for (var i = 0, len = actualLineTokens.length; i < len; i++) {
nextStartOffset = actualLineTokens[i].startIndex;
currentTokenEndOffset = (i + 1 < len ? actualLineTokens[i + 1].startIndex : lineTokens.getTextLength());
currentTokenClassName = actualLineTokens[i].type;
if (nextStartOffset < currentTokenEndOffset) {
while (lineDecorationsIndex < lineDecorationsLength && lineDecorations[lineDecorationsIndex].startOffset < currentTokenEndOffset) {
if (lineDecorations[lineDecorationsIndex].startOffset > nextStartOffset) {
// the first decorations starts after the token
parts.push(new ViewLineToken(nextStartOffset, currentTokenClassName));
nextStartOffset = lineDecorations[lineDecorationsIndex].startOffset;
}
}
this.parts = parts;
this.lastPartIndex = parts.length - 1;
this.lastEndOffset = currentTokenEndOffset;
}
public getParts(): ViewLineToken[] {
return this.parts;
}
parts.push(new ViewLineToken(nextStartOffset, currentTokenClassName + ' ' + lineDecorations[lineDecorationsIndex].className));
public equals(other:ILineParts): boolean {
if (other instanceof ViewLineParts) {
var otherSimpleLineParts = <ViewLineParts>other;
if (this.lastPartIndex !== otherSimpleLineParts.lastPartIndex) {
return false;
}
if (this.lastEndOffset !== otherSimpleLineParts.lastEndOffset) {
return false;
}
for (var i = 0, len = this.parts.length; i < len; i++) {
if (this.parts[i].startIndex !== otherSimpleLineParts.parts[i].startIndex) {
return false;
}
if (this.parts[i].type !== otherSimpleLineParts.parts[i].type) {
return false;
}
if (lineDecorations[lineDecorationsIndex].endOffset >= currentTokenEndOffset) {
// this decoration goes on to the next token
nextStartOffset = currentTokenEndOffset;
break;
} else {
// this decorations stops inside this token
nextStartOffset = lineDecorations[lineDecorationsIndex].endOffset + 1;
lineDecorationsIndex++;
}
return true;
}
return false;
}
public findIndexOfOffset(offset:number): number {
return Arrays.findIndexInSegmentsArray(this.parts, offset);
if (nextStartOffset < currentTokenEndOffset) {
parts.push(new ViewLineToken(nextStartOffset, currentTokenClassName));
}
}
return new LineParts(parts);
}
export class DecorationSegment {
......
......@@ -33,10 +33,17 @@ export class RenderLineInput {
}
}
export interface IRenderLineOutput {
export class RenderLineOutput {
_renderLineOutputTrait: void;
charOffsetInPart: number[];
lastRenderedPartIndex: number;
output: string;
constructor(charOffsetInPart: number[], lastRenderedPartIndex: number, output: string) {
this.charOffsetInPart = charOffsetInPart;
this.lastRenderedPartIndex = lastRenderedPartIndex;
this.output = output;
}
}
const _space = ' '.charCodeAt(0);
......@@ -48,7 +55,7 @@ const _carriageReturn = '\r'.charCodeAt(0);
const _lineSeparator = '\u2028'.charCodeAt(0); //http://www.fileformat.info/info/unicode/char/2028/index.htm
const _bom = 65279;
export function renderLine(input:RenderLineInput): IRenderLineOutput {
export function renderLine(input:RenderLineInput): RenderLineOutput {
const lineText = input.lineContent;
const lineTextLength = lineText.length;
const tabSize = input.tabSize;
......@@ -58,12 +65,12 @@ export function renderLine(input:RenderLineInput): IRenderLineOutput {
const charBreakIndex = (input.stopRenderingLineAfter === -1 ? lineTextLength : input.stopRenderingLineAfter - 1);
if (lineTextLength === 0) {
return {
charOffsetInPart: [],
lastRenderedPartIndex: 0,
return new RenderLineOutput(
[],
0,
// This is basically for IE's hit test to work
output: '<span><span>&nbsp;</span></span>'
};
'<span><span>&nbsp;</span></span>'
);
}
if (actualLineParts.length === 0) {
......@@ -78,7 +85,7 @@ function isWhitespace(type:string): boolean {
return WHITESPACE_TOKEN_TEST.test(type);
}
function renderLineActual(lineText:string, lineTextLength:number, tabSize:number, spaceWidth:number, actualLineParts:ViewLineToken[], renderWhitespace:boolean, charBreakIndex:number): IRenderLineOutput {
function renderLineActual(lineText:string, lineTextLength:number, tabSize:number, spaceWidth:number, actualLineParts:ViewLineToken[], renderWhitespace:boolean, charBreakIndex:number): RenderLineOutput {
lineTextLength = +lineTextLength;
tabSize = +tabSize;
charBreakIndex = +charBreakIndex;
......@@ -140,11 +147,11 @@ function renderLineActual(lineText:string, lineTextLength:number, tabSize:number
out += partContent;
out += '&hellip;</span></span>';
charOffsetInPartArr[charIndex] = charOffsetInPart;
return {
charOffsetInPart: charOffsetInPartArr,
lastRenderedPartIndex: partIndex,
output: out
};
return new RenderLineOutput(
charOffsetInPartArr,
partIndex,
out
);
}
}
out += '<span class="token '+part.type+'" style="width:'+(spaceWidth * partContentCnt)+'px">';
......@@ -213,11 +220,11 @@ function renderLineActual(lineText:string, lineTextLength:number, tabSize:number
if (charIndex >= charBreakIndex) {
out += '&hellip;</span></span>';
charOffsetInPartArr[charIndex] = charOffsetInPart;
return {
charOffsetInPart: charOffsetInPartArr,
lastRenderedPartIndex: partIndex,
output: out
};
return new RenderLineOutput(
charOffsetInPartArr,
partIndex,
out
);
}
}
......@@ -231,9 +238,9 @@ function renderLineActual(lineText:string, lineTextLength:number, tabSize:number
// text range at the end of the span, insteaf of at the beginning of next span
charOffsetInPartArr.push(charOffsetInPart);
return {
charOffsetInPart: charOffsetInPartArr,
lastRenderedPartIndex: actualLineParts.length - 1,
output: out
};
return new RenderLineOutput(
charOffsetInPartArr,
actualLineParts.length - 1,
out
);
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册