提交 ebf17943 编写于 作者: T tamas.kiss

Add an option to display control characters

Resolves #7331
上级 eca3662d
......@@ -538,6 +538,8 @@ export class VSCodeMenu {
const toggleWordWrap = this.createMenuItem(nls.localize({ key: 'miToggleWordWrap', comment: ['&& denotes a mnemonic'] }, "Toggle &&Word Wrap"), 'editor.action.toggleWordWrap');
const toggleRenderWhitespace = this.createMenuItem(nls.localize({ key: 'miToggleRenderWhitespace', comment: ['&& denotes a mnemonic'] }, "Toggle &&Render Whitespace"), 'editor.action.toggleRenderWhitespace');
const toggleRenderControlCharacters = this.createMenuItem(nls.localize({ key: 'miToggleRenderControlCharacters', comment: ['&& denotes a mnemonic'] }, "Toggle &&Control Characters"), 'editor.action.toggleRenderControlCharacters');
let zoomIn = this.createMenuItem(nls.localize({ key: 'miZoomIn', comment: ['&& denotes a mnemonic'] }, "&&Zoom In"), 'workbench.action.zoomIn');
let zoomOut = this.createMenuItem(nls.localize({ key: 'miZoomOut', comment: ['&& denotes a mnemonic'] }, "Zoom O&&ut"), 'workbench.action.zoomOut');
......@@ -559,6 +561,7 @@ export class VSCodeMenu {
__separator__(),
toggleWordWrap,
toggleRenderWhitespace,
toggleRenderControlCharacters,
__separator__(),
zoomIn,
zoomOut,
......
......@@ -99,6 +99,7 @@ export class Colorizer {
0,
-1,
false,
false,
tokens
));
return renderResult.output;
......@@ -143,6 +144,7 @@ function actualColorize(lines:string[], mode:IMode, tabSize:number): IActualColo
0,
-1,
false,
false,
tokenizeResult.tokens.map(t => new ViewLineToken(t.startIndex, t.type))
));
......
......@@ -20,6 +20,7 @@ export class ViewLine implements IVisibleLineData {
protected _context:ViewContext;
private _renderWhitespace: boolean;
private _renderControlCharacters: boolean;
private _indentGuides: boolean;
private _spaceWidth: number;
private _lineHeight: number;
......@@ -39,6 +40,7 @@ export class ViewLine implements IVisibleLineData {
constructor(context:ViewContext) {
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;
......@@ -86,6 +88,9 @@ export class ViewLine implements IVisibleLineData {
if (e.viewInfo.renderWhitespace) {
this._renderWhitespace = this._context.configuration.editor.viewInfo.renderWhitespace;
}
if (e.viewInfo.renderControlCharacters) {
this._renderControlCharacters = this._context.configuration.editor.viewInfo.renderControlCharacters;
}
if (e.viewInfo.indentGuides) {
this._indentGuides = this._context.configuration.editor.viewInfo.indentGuides;
}
......@@ -172,6 +177,7 @@ export class ViewLine implements IVisibleLineData {
this._spaceWidth,
this._stopRenderingLineAfter,
this._renderWhitespace,
this._renderControlCharacters,
lineParts.getParts()
));
......
......@@ -1836,6 +1836,7 @@ class InlineViewZonesComputer extends ViewZonesComputer {
config.fontInfo.spaceWidth,
config.viewInfo.stopRenderingLineAfter,
config.viewInfo.renderWhitespace,
config.viewInfo.renderControlCharacters,
parts.getParts()
));
......
......@@ -173,6 +173,7 @@ class InternalEditorOptionsHelper {
editorClassName: editorClassName,
stopRenderingLineAfter: stopRenderingLineAfter,
renderWhitespace: toBoolean(opts.renderWhitespace),
renderControlCharacters: toBoolean(opts.renderControlCharacters),
indentGuides: toBoolean(opts.indentGuides),
scrollbar: scrollbar,
});
......@@ -659,6 +660,11 @@ let editorConfiguration:IConfigurationNode = {
default: DefaultConfig.editor.renderWhitespace,
description: nls.localize('renderWhitespace', "Controls whether the editor should render whitespace characters")
},
'editor.renderControlCharacters': {
'type': 'boolean',
default: DefaultConfig.editor.renderControlCharacters,
description: nls.localize('renderControlCharacters', "Controls whether the editor should render control characters")
},
// 'editor.indentGuides': {
// 'type': 'boolean',
// default: DefaultConfig.editor.indentGuides,
......
......@@ -88,6 +88,7 @@ class ConfigClass implements IConfiguration {
referenceInfos: true,
folding: true,
renderWhitespace: false,
renderControlCharacters: false,
indentGuides: false,
useTabStops: true,
......
......@@ -415,6 +415,11 @@ export interface IEditorOptions {
* Defaults to false.
*/
renderWhitespace?: boolean;
/**
* Enable rendering of control characters.
* Defaults to false.
*/
renderControlCharacters?: boolean;
/**
* Enable rendering of indent guides.
* Defaults to true.
......@@ -611,6 +616,7 @@ export class InternalEditorViewOptions {
editorClassName: string;
stopRenderingLineAfter: number;
renderWhitespace: boolean;
renderControlCharacters: boolean;
indentGuides: boolean;
scrollbar:InternalEditorScrollbarOptions;
......@@ -636,6 +642,7 @@ export class InternalEditorViewOptions {
editorClassName: string;
stopRenderingLineAfter: number;
renderWhitespace: boolean;
renderControlCharacters: boolean;
indentGuides: boolean;
scrollbar:InternalEditorScrollbarOptions;
}) {
......@@ -657,6 +664,7 @@ export class InternalEditorViewOptions {
this.editorClassName = String(source.editorClassName);
this.stopRenderingLineAfter = source.stopRenderingLineAfter|0;
this.renderWhitespace = Boolean(source.renderWhitespace);
this.renderControlCharacters = Boolean(source.renderControlCharacters);
this.indentGuides = Boolean(source.indentGuides);
this.scrollbar = source.scrollbar.clone();
}
......@@ -712,6 +720,7 @@ export class InternalEditorViewOptions {
&& this.editorClassName === other.editorClassName
&& this.stopRenderingLineAfter === other.stopRenderingLineAfter
&& this.renderWhitespace === other.renderWhitespace
&& this.renderControlCharacters === other.renderControlCharacters
&& this.indentGuides === other.indentGuides
&& this.scrollbar.equals(other.scrollbar)
);
......@@ -740,6 +749,7 @@ export class InternalEditorViewOptions {
editorClassName: this.editorClassName !== newOpts.editorClassName,
stopRenderingLineAfter: this.stopRenderingLineAfter !== newOpts.stopRenderingLineAfter,
renderWhitespace: this.renderWhitespace !== newOpts.renderWhitespace,
renderControlCharacters: this.renderControlCharacters !== newOpts.renderControlCharacters,
indentGuides: this.indentGuides !== newOpts.indentGuides,
scrollbar: (!this.scrollbar.equals(newOpts.scrollbar)),
};
......@@ -772,6 +782,7 @@ export interface IViewConfigurationChangedEvent {
editorClassName: boolean;
stopRenderingLineAfter: boolean;
renderWhitespace: boolean;
renderControlCharacters: boolean;
indentGuides: boolean;
scrollbar: boolean;
}
......
......@@ -14,6 +14,7 @@ export class RenderLineInput {
spaceWidth: number;
stopRenderingLineAfter: number;
renderWhitespace: boolean;
renderControlCharacters: boolean;
parts: ViewLineToken[];
constructor(
......@@ -22,6 +23,7 @@ export class RenderLineInput {
spaceWidth: number,
stopRenderingLineAfter: number,
renderWhitespace: boolean,
renderControlCharacters: boolean,
parts: ViewLineToken[]
) {
this.lineContent = lineContent;
......@@ -29,6 +31,7 @@ export class RenderLineInput {
this.spaceWidth = spaceWidth;
this.stopRenderingLineAfter = stopRenderingLineAfter;
this.renderWhitespace = renderWhitespace;
this.renderControlCharacters = renderControlCharacters;
this.parts = parts;
}
}
......@@ -52,6 +55,7 @@ const _lowerThan = '<'.charCodeAt(0);
const _greaterThan = '>'.charCodeAt(0);
const _ampersand = '&'.charCodeAt(0);
const _carriageReturn = '\r'.charCodeAt(0);
const _controlCharacterSequenceConversionStart = 9216;
const _lineSeparator = '\u2028'.charCodeAt(0); //http://www.fileformat.info/info/unicode/char/2028/index.htm
const _bom = 65279;
......@@ -62,6 +66,7 @@ export function renderLine(input:RenderLineInput): RenderLineOutput {
const spaceWidth = input.spaceWidth;
const actualLineParts = input.parts;
const renderWhitespace = input.renderWhitespace;
const renderControlCharacters = input.renderControlCharacters;
const charBreakIndex = (input.stopRenderingLineAfter === -1 ? lineTextLength : input.stopRenderingLineAfter - 1);
if (lineTextLength === 0) {
......@@ -77,7 +82,7 @@ export function renderLine(input:RenderLineInput): RenderLineOutput {
throw new Error('Cannot render non empty line without line parts!');
}
return renderLineActual(lineText, lineTextLength, tabSize, spaceWidth, actualLineParts.slice(0), renderWhitespace, charBreakIndex);
return renderLineActual(lineText, lineTextLength, tabSize, spaceWidth, actualLineParts.slice(0), renderWhitespace, renderControlCharacters, charBreakIndex);
}
function isWhitespace(type:string): boolean {
......@@ -88,7 +93,14 @@ function isIndentGuide(type:string): boolean {
return (type.indexOf('indent-guide') >= 0);
}
function renderLineActual(lineText:string, lineTextLength:number, tabSize:number, spaceWidth:number, actualLineParts:ViewLineToken[], renderWhitespace:boolean, charBreakIndex:number): RenderLineOutput {
function isControlCharacter(characterCode: number): boolean {
return characterCode < 32;
}
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 {
lineTextLength = +lineTextLength;
tabSize = +tabSize;
charBreakIndex = +charBreakIndex;
......@@ -209,7 +221,12 @@ function renderLineActual(lineText:string, lineTextLength:number, tabSize:number
break;
default:
out += lineText.charAt(charIndex);
let characterCode = lineText.charCodeAt(charIndex);
if (renderControlCharacters && isControlCharacter(characterCode)) {
out += controlCharacterToPrintable(characterCode);
} else {
out += lineText.charAt(charIndex);
}
}
charOffsetInPart ++;
......
......@@ -168,6 +168,22 @@ export class ToggleRenderWhitespaceAction extends EditorAction {
}
}
export class ToggleRenderControlCharacterAction extends EditorAction {
static ID = 'editor.action.toggleRenderControlCharacter';
constructor(descriptor: IEditorActionDescriptorData, editor: ICommonCodeEditor) {
super(descriptor, editor, Behaviour.TextFocus);
}
public run(): TPromise<boolean> {
this.editor.updateOptions({
renderControlCharacters: !this.editor.getConfiguration().viewInfo.renderControlCharacters
});
return TPromise.as(true);
}
}
// register actions
CommonEditorRegistry.registerEditorAction(new EditorActionDescriptor(IndentationToSpacesAction, IndentationToSpacesAction.ID, nls.localize('indentationToSpaces', "Convert Indentation to Spaces"), void 0, 'Convert Indentation to Spaces'));
CommonEditorRegistry.registerEditorAction(new EditorActionDescriptor(IndentationToTabsAction, IndentationToTabsAction.ID, nls.localize('indentationToTabs', "Convert Indentation to Tabs"), void 0, 'Convert Indentation to Tabs'));
......@@ -175,3 +191,4 @@ CommonEditorRegistry.registerEditorAction(new EditorActionDescriptor(IndentUsing
CommonEditorRegistry.registerEditorAction(new EditorActionDescriptor(IndentUsingTabs, IndentUsingTabs.ID, nls.localize('indentUsingTabs', "Indent Using Tabs"), void 0, 'Indent Using Tabs'));
CommonEditorRegistry.registerEditorAction(new EditorActionDescriptor(DetectIndentation, DetectIndentation.ID, nls.localize('detectIndentation', "Detect Indentation from Content"), void 0, 'Detect Indentation from Content'));
CommonEditorRegistry.registerEditorAction(new EditorActionDescriptor(ToggleRenderWhitespaceAction, ToggleRenderWhitespaceAction.ID, nls.localize('toggleRenderWhitespace', "Toggle Render Whitespace"), void 0, 'Toggle Render Whitespace'));
CommonEditorRegistry.registerEditorAction(new EditorActionDescriptor(ToggleRenderControlCharacterAction, ToggleRenderControlCharacterAction.ID, nls.localize('toggleRenderControlCharacters', "Toggle Control Characters"), void 0, 'Toggle Render Control Characters'));
......@@ -418,7 +418,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, parts));
let renderLineOutput = renderLine(new RenderLineInput(lineContent, tabSize, 10, -1, false, false, parts));
return (partIndex:number, partLength:number, offset:number, expected:number) => {
let actual = getColumnOfLinePartOffset(-1, parts, lineContent.length + 1, renderLineOutput.charOffsetInPart, partIndex, partLength, offset);
......
......@@ -21,6 +21,7 @@ suite('viewLineRenderer.renderLine', () => {
0,
-1,
false,
false,
[createPart(0, '')]
));
......@@ -62,6 +63,7 @@ suite('viewLineRenderer.renderLine', () => {
0,
-1,
false,
false,
parts
));
......@@ -92,6 +94,7 @@ suite('viewLineRenderer.renderLine', () => {
10,
6,
true,
false,
[
createPart( 0, '0'),
createPart( 1, '1'),
......@@ -180,6 +183,7 @@ suite('viewLineRenderer.renderLine', () => {
10,
-1,
true,
false,
lineParts
));
......@@ -234,6 +238,7 @@ suite('viewLineRenderer.renderLine', () => {
10,
-1,
false,
false,
lineParts
));
......@@ -288,6 +293,7 @@ suite('viewLineRenderer.renderLine', () => {
10,
-1,
false,
false,
lineParts
));
......
......@@ -1242,6 +1242,11 @@ declare module monaco.editor {
* Defaults to false.
*/
renderWhitespace?: boolean;
/**
* Enable rendering of control characters.
* Defaults to false.
*/
renderControlCharacters?: boolean;
/**
* Enable rendering of indent guides.
* Defaults to true.
......@@ -1337,6 +1342,7 @@ declare module monaco.editor {
editorClassName: string;
stopRenderingLineAfter: number;
renderWhitespace: boolean;
renderControlCharacters: boolean;
indentGuides: boolean;
scrollbar: InternalEditorScrollbarOptions;
}
......@@ -1360,6 +1366,7 @@ declare module monaco.editor {
editorClassName: boolean;
stopRenderingLineAfter: boolean;
renderWhitespace: boolean;
renderControlCharacters: boolean;
indentGuides: boolean;
scrollbar: boolean;
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册