Adds option to include strings when highlighting unicode characters (default true).

This commit is contained in:
Henning Dieterichs 2021-12-15 17:45:26 +01:00
parent 87bf1d8065
commit 36fdeed3b5
No known key found for this signature in database
GPG key ID: 771381EFFDB9EC06
7 changed files with 102 additions and 59 deletions

View file

@ -3300,6 +3300,8 @@ export interface IUnicodeHighlightOptions {
invisibleCharacters?: boolean;
ambiguousCharacters?: boolean;
includeComments?: boolean | InUntrustedWorkspace;
includeStrings?: boolean | InUntrustedWorkspace;
/**
* A map of allowed characters (true: allowed).
*/
@ -3320,6 +3322,7 @@ export const unicodeHighlightConfigKeys = {
nonBasicASCII: 'editor.unicodeHighlight.nonBasicASCII',
ambiguousCharacters: 'editor.unicodeHighlight.ambiguousCharacters',
includeComments: 'editor.unicodeHighlight.includeComments',
includeStrings: 'editor.unicodeHighlight.includeStrings',
};
class UnicodeHighlight extends BaseEditorOption<EditorOption.unicodeHighlighting, InternalUnicodeHighlightOptions> {
@ -3329,6 +3332,7 @@ class UnicodeHighlight extends BaseEditorOption<EditorOption.unicodeHighlighting
invisibleCharacters: true,
ambiguousCharacters: true,
includeComments: inUntrustedWorkspace,
includeStrings: true,
allowedCharacters: {},
};
@ -3361,6 +3365,13 @@ class UnicodeHighlight extends BaseEditorOption<EditorOption.unicodeHighlighting
default: defaults.includeComments,
description: nls.localize('unicodeHighlight.includeComments', "Controls whether characters in comments should also be subject to unicode highlighting.")
},
[unicodeHighlightConfigKeys.includeStrings]: {
restricted: true,
type: ['boolean', 'string'],
enum: [true, false, inUntrustedWorkspace],
default: defaults.includeStrings,
description: nls.localize('unicodeHighlight.includeStrings', "Controls whether characters in strings should also be subject to unicode highlighting.")
},
[unicodeHighlightConfigKeys.allowedCharacters]: {
restricted: true,
type: 'object',
@ -3401,6 +3412,7 @@ class UnicodeHighlight extends BaseEditorOption<EditorOption.unicodeHighlighting
invisibleCharacters: boolean(input.invisibleCharacters, this.defaultValue.invisibleCharacters),
ambiguousCharacters: boolean(input.ambiguousCharacters, this.defaultValue.ambiguousCharacters),
includeComments: primitiveSet<boolean | InUntrustedWorkspace>(input.includeComments, inUntrustedWorkspace, [true, false, inUntrustedWorkspace]),
includeStrings: primitiveSet<boolean | InUntrustedWorkspace>(input.includeStrings, inUntrustedWorkspace, [true, false, inUntrustedWorkspace]),
allowedCharacters: this.validateAllowedCharacters(_input.allowedCharacters, this.defaultValue.allowedCharacters),
};
}

View file

@ -175,6 +175,12 @@ export interface IModelDecorationOptions {
* @internal
*/
hideInCommentTokens?: boolean | null;
/**
* If set, this decoration will not be rendered for string tokens.
* @internal
*/
hideInStringTokens?: boolean | null;
}
/**

View file

@ -2535,6 +2535,7 @@ export class ModelDecorationOptions implements model.IModelDecorationOptions {
readonly after: ModelDecorationInjectedTextOptions | null;
readonly before: ModelDecorationInjectedTextOptions | null;
readonly hideInCommentTokens: boolean | null;
readonly hideInStringTokens: boolean | null;
private constructor(options: model.IModelDecorationOptions) {
@ -2560,6 +2561,7 @@ export class ModelDecorationOptions implements model.IModelDecorationOptions {
this.after = options.after ? ModelDecorationInjectedTextOptions.from(options.after) : null;
this.before = options.before ? ModelDecorationInjectedTextOptions.from(options.before) : null;
this.hideInCommentTokens = options.hideInCommentTokens ?? false;
this.hideInStringTokens = options.hideInStringTokens ?? false;
}
}
ModelDecorationOptions.EMPTY = ModelDecorationOptions.register({ description: 'empty' });

View file

@ -211,5 +211,6 @@ export interface UnicodeHighlighterOptions {
ambiguousCharacters: boolean;
invisibleCharacters: boolean;
includeComments: boolean;
includeStrings: boolean;
allowedCodePoints: number[];
}

View file

@ -169,20 +169,33 @@ export class ViewModelDecorations implements IDisposable {
}
export function isModelDecorationVisible(model: ITextModel, decoration: IModelDecoration): boolean {
if (decoration.options.hideInCommentTokens) {
const allTokensAreComments = testTokensInRange(
model,
decoration.range,
(tokenType) => tokenType === StandardTokenType.Comment
);
if (allTokensAreComments) {
return false;
}
if (decoration.options.hideInCommentTokens && isModelDecorationInComment(model, decoration)) {
return false;
}
if (decoration.options.hideInStringTokens && isModelDecorationInString(model, decoration)) {
return false;
}
return true;
}
export function isModelDecorationInComment(model: ITextModel, decoration: IModelDecoration): boolean {
return testTokensInRange(
model,
decoration.range,
(tokenType) => tokenType === StandardTokenType.Comment
);
}
export function isModelDecorationInString(model: ITextModel, decoration: IModelDecoration): boolean {
return testTokensInRange(
model,
decoration.range,
(tokenType) => tokenType === StandardTokenType.String
);
}
/**
* Calls the callback for every token that intersects the range.
* If the callback returns `false`, iteration stops and `false` is returned.

View file

@ -20,7 +20,7 @@ import { ModelDecorationOptions } from 'vs/editor/common/model/textModel';
import { UnicodeHighlighterOptions, UnicodeHighlighterReason, UnicodeHighlighterReasonKind, UnicodeTextModelHighlighter } from 'vs/editor/common/modes/unicodeTextModelHighlighter';
import { IEditorWorkerService, IUnicodeHighlightsResult } from 'vs/editor/common/services/editorWorkerService';
import { ILanguageService } from 'vs/editor/common/services/languageService';
import { isModelDecorationVisible } from 'vs/editor/common/viewModel/viewModelDecorations';
import { isModelDecorationInComment, isModelDecorationInString, isModelDecorationVisible } from 'vs/editor/common/viewModel/viewModelDecorations';
import { HoverAnchor, HoverAnchorType, IEditorHover, IEditorHoverParticipant, IEditorHoverStatusBar, IHoverPart } from 'vs/editor/contrib/hover/hoverTypes';
import { MarkdownHover, renderMarkdownHovers } from 'vs/editor/contrib/hover/markdownHoverParticipant';
import { BannerController } from 'vs/editor/contrib/unicodeHighlighter/bannerController';
@ -29,7 +29,7 @@ import { ConfigurationTarget, IConfigurationService } from 'vs/platform/configur
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IOpenerService } from 'vs/platform/opener/common/opener';
import { IQuickInputService, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput';
import { minimapFindMatch, minimapUnicodeHighlight, overviewRulerFindMatchForeground, overviewRulerUnicodeHighlightForeground } from 'vs/platform/theme/common/colorRegistry';
import { minimapUnicodeHighlight, overviewRulerUnicodeHighlightForeground } from 'vs/platform/theme/common/colorRegistry';
import { registerIcon } from 'vs/platform/theme/common/iconRegistry';
import { themeColorFromId } from 'vs/platform/theme/common/themeService';
import { IWorkspaceTrustManagementService } from 'vs/platform/workspace/common/workspaceTrust';
@ -160,6 +160,7 @@ export class UnicodeHighlighter extends Disposable implements IEditorContributio
ambiguousCharacters: options.ambiguousCharacters,
invisibleCharacters: options.invisibleCharacters,
includeComments: options.includeComments,
includeStrings: options.includeStrings,
allowedCodePoints: Object.keys(options.allowedCharacters).map(c => c.codePointAt(0)!),
};
@ -180,6 +181,8 @@ export class UnicodeHighlighter extends Disposable implements IEditorContributio
export interface UnicodeHighlighterDecorationInfo {
reason: UnicodeHighlighterReason;
inComment: boolean;
inString: boolean;
}
type RemoveTrueIfUntrusted<T> = T extends InUntrustedWorkspace ? never : T;
@ -191,6 +194,7 @@ function resolveOptions(trusted: boolean, options: InternalUnicodeHighlightOptio
ambiguousCharacters: options.ambiguousCharacters,
invisibleCharacters: options.invisibleCharacters,
includeComments: options.includeComments === inUntrustedWorkspace ? !trusted : options.includeComments,
includeStrings: options.includeStrings === inUntrustedWorkspace ? !trusted : options.includeStrings,
allowedCharacters: options.allowedCharacters ?? {},
};
}
@ -242,7 +246,7 @@ class DocumentUnicodeHighlighter extends Disposable {
// Don't show decoration if there are too many.
// In this case, a banner is shown.
for (const range of info.ranges) {
decorations.push({ range: range, options: this._options.includeComments ? DECORATION : DECORATION_HIDE_IN_COMMENTS });
decorations.push({ range: range, options: Decorations.instance.getDecoration(!this._options.includeComments, !this._options.includeStrings) });
}
}
this._decorationIds = new Set(this._editor.deltaDecorations(
@ -258,21 +262,22 @@ class DocumentUnicodeHighlighter extends Disposable {
}
const model = this._editor.getModel();
const range = model.getDecorationRange(decorationId)!;
const decoration = {
range: range,
options: Decorations.instance.getDecoration(!this._options.includeComments, !this._options.includeStrings),
id: decorationId,
ownerId: 0,
};
if (
!isModelDecorationVisible(model, {
range: range,
options: this._options.includeComments
? DECORATION
: DECORATION_HIDE_IN_COMMENTS,
id: decorationId,
ownerId: 0,
})
!isModelDecorationVisible(model, decoration)
) {
return null;
}
const text = model.getValueInRange(range);
return {
reason: computeReason(text, this._options)!,
inComment: isModelDecorationInComment(model, decoration),
inString: isModelDecorationInString(model, decoration),
};
}
}
@ -343,7 +348,7 @@ class ViewportUnicodeHighlighter extends Disposable {
// Don't show decorations if there are too many.
// A banner will be shown instead.
for (const range of totalResult.ranges) {
decorations.push({ range, options: this._options.includeComments ? DECORATION : DECORATION_HIDE_IN_COMMENTS });
decorations.push({ range, options: Decorations.instance.getDecoration(!this._options.includeComments, !this._options.includeStrings) });
}
}
this._updateState(totalResult);
@ -358,20 +363,19 @@ class ViewportUnicodeHighlighter extends Disposable {
const model = this._editor.getModel();
const range = model.getDecorationRange(decorationId)!;
const text = model.getValueInRange(range);
if (
!isModelDecorationVisible(model, {
range: range,
options: this._options.includeComments
? DECORATION
: DECORATION_HIDE_IN_COMMENTS,
id: decorationId,
ownerId: 0,
})
) {
const decoration = {
range: range,
options: Decorations.instance.getDecoration(!this._options.includeComments, !this._options.includeStrings),
id: decorationId,
ownerId: 0,
};
if (!isModelDecorationVisible(model, decoration)) {
return null;
}
return {
reason: computeReason(text, this._options)!,
inComment: isModelDecorationInComment(model, decoration),
inString: isModelDecorationInString(model, decoration),
};
}
}
@ -468,6 +472,8 @@ export class UnicodeHighlighterHoverParticipant implements IEditorHoverParticipa
const adjustSettingsArgs: ShowExcludeOptionsArgs = {
codePoint: codePoint,
reason: highlightInfo.reason.kind,
inComment: highlightInfo.inComment,
inString: highlightInfo.inString,
};
const adjustSettings = nls.localize('unicodeHighlight.adjustSettings', 'Adjust settings');
@ -497,36 +503,36 @@ function computeReason(char: string, options: UnicodeHighlighterOptions): Unicod
return UnicodeTextModelHighlighter.computeUnicodeHighlightReason(char, options);
}
const DECORATION_HIDE_IN_COMMENTS = ModelDecorationOptions.register({
description: 'unicode-highlight',
stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges,
className: 'unicode-highlight',
showIfCollapsed: true,
overviewRuler: {
color: themeColorFromId(overviewRulerUnicodeHighlightForeground),
position: OverviewRulerLane.Center
},
minimap: {
color: themeColorFromId(minimapUnicodeHighlight),
position: MinimapPosition.Inline
},
hideInCommentTokens: true
});
class Decorations {
public static readonly instance = new Decorations();
const DECORATION = ModelDecorationOptions.register({
description: 'unicode-highlight',
stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges,
className: 'unicode-highlight',
showIfCollapsed: true,
overviewRuler: {
color: themeColorFromId(overviewRulerFindMatchForeground),
position: OverviewRulerLane.Center
},
minimap: {
color: themeColorFromId(minimapFindMatch),
position: MinimapPosition.Inline
private readonly map = new Map<string, ModelDecorationOptions>();
getDecoration(hideInComments: boolean, hideInStrings: boolean): ModelDecorationOptions {
const key = `${hideInComments}${hideInStrings}`;
let options = this.map.get(key);
if (!options) {
options = ModelDecorationOptions.createDynamic({
description: 'unicode-highlight',
stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges,
className: 'unicode-highlight',
showIfCollapsed: true,
overviewRuler: {
color: themeColorFromId(overviewRulerUnicodeHighlightForeground),
position: OverviewRulerLane.Center
},
minimap: {
color: themeColorFromId(minimapUnicodeHighlight),
position: MinimapPosition.Inline
},
hideInCommentTokens: hideInComments,
hideInStringTokens: hideInStrings,
});
this.map.set(key, options);
}
return options;
}
});
}
interface IDisableUnicodeHighlightAction {
shortLabel: string;
@ -607,6 +613,8 @@ export class DisableHighlightingOfNonBasicAsciiCharactersAction extends EditorAc
interface ShowExcludeOptionsArgs {
codePoint: number;
reason: UnicodeHighlighterReason['kind'];
inComment: boolean;
inString: boolean;
}
export class ShowExcludeOptions extends EditorAction {

1
src/vs/monaco.d.ts vendored
View file

@ -3883,6 +3883,7 @@ declare namespace monaco.editor {
invisibleCharacters?: boolean;
ambiguousCharacters?: boolean;
includeComments?: boolean | InUntrustedWorkspace;
includeStrings?: boolean | InUntrustedWorkspace;
/**
* A map of allowed characters (true: allowed).
*/