Merge pull request #137508 from microsoft/hediet/unicode-highlighting
Implements highlighting of unicode characters.
This commit is contained in:
commit
3a33723d3f
File diff suppressed because one or more lines are too long
|
@ -640,6 +640,8 @@ export interface IEditorOptions {
|
|||
* Controls the behavior of editor guides.
|
||||
*/
|
||||
guides?: IGuidesOptions;
|
||||
|
||||
unicodeHighlight?: IUnicodeHighlightOptions;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -3245,6 +3247,120 @@ class EditorScrollbar extends BaseEditorOption<EditorOption.scrollbar, InternalE
|
|||
|
||||
//#endregion
|
||||
|
||||
//#region UnicodeHighlight
|
||||
|
||||
export type DeriveFromWorkspaceTrust = 'deriveFromWorkspaceTrust';
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export const deriveFromWorkspaceTrust: DeriveFromWorkspaceTrust = 'deriveFromWorkspaceTrust';
|
||||
|
||||
/**
|
||||
* Configuration options for unicode highlighting.
|
||||
*/
|
||||
export interface IUnicodeHighlightOptions {
|
||||
nonBasicASCII?: boolean | DeriveFromWorkspaceTrust;
|
||||
invisibleCharacters?: boolean | DeriveFromWorkspaceTrust;
|
||||
ambiguousCharacters?: boolean | DeriveFromWorkspaceTrust;
|
||||
includeComments?: boolean | DeriveFromWorkspaceTrust;
|
||||
/**
|
||||
* A list of allowed code points in a single string.
|
||||
*/
|
||||
allowedCharacters?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export type InternalUnicodeHighlightOptions = Required<Readonly<IUnicodeHighlightOptions>>;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export const unicodeHighlightConfigKeys = {
|
||||
allowedCharacters: 'editor.unicodeHighlight.allowedCharacters',
|
||||
invisibleCharacters: 'editor.unicodeHighlight.invisibleCharacters',
|
||||
nonBasicASCII: 'editor.unicodeHighlight.nonBasicASCII',
|
||||
ambiguousCharacters: 'editor.unicodeHighlight.ambiguousCharacters',
|
||||
includeComments: 'editor.unicodeHighlight.includeComments',
|
||||
};
|
||||
|
||||
class UnicodeHighlight extends BaseEditorOption<EditorOption.unicodeHighlighting, InternalUnicodeHighlightOptions> {
|
||||
constructor() {
|
||||
const defaults: InternalUnicodeHighlightOptions = {
|
||||
nonBasicASCII: deriveFromWorkspaceTrust,
|
||||
invisibleCharacters: deriveFromWorkspaceTrust,
|
||||
ambiguousCharacters: deriveFromWorkspaceTrust,
|
||||
includeComments: deriveFromWorkspaceTrust,
|
||||
allowedCharacters: '',
|
||||
};
|
||||
|
||||
super(
|
||||
EditorOption.unicodeHighlighting, 'unicodeHighlight', defaults,
|
||||
{
|
||||
[unicodeHighlightConfigKeys.nonBasicASCII]: {
|
||||
restricted: true,
|
||||
type: ['boolean', 'string'],
|
||||
enum: [true, false, deriveFromWorkspaceTrust],
|
||||
default: defaults.nonBasicASCII,
|
||||
description: nls.localize('unicodeHighlight.nonBasicASCII', "Controls whether all non-basic ASCII characters are highlighted. Only characters between U+0020 and U+007E, tab, line-feed and carriage-return are considered basic ASCII.")
|
||||
},
|
||||
[unicodeHighlightConfigKeys.invisibleCharacters]: {
|
||||
restricted: true,
|
||||
type: ['boolean', 'string'],
|
||||
enum: [true, false, deriveFromWorkspaceTrust],
|
||||
default: defaults.invisibleCharacters,
|
||||
description: nls.localize('unicodeHighlight.invisibleCharacters', "Controls whether characters that just reserve space or have no width at all are highlighted.")
|
||||
},
|
||||
[unicodeHighlightConfigKeys.ambiguousCharacters]: {
|
||||
restricted: true,
|
||||
type: ['boolean', 'string'],
|
||||
enum: [true, false, deriveFromWorkspaceTrust],
|
||||
default: defaults.ambiguousCharacters,
|
||||
description: nls.localize('unicodeHighlight.ambiguousCharacters', "Controls whether characters are highlighted that can be confused with basic ASCII characters, except those that are common in the current user locale.")
|
||||
},
|
||||
[unicodeHighlightConfigKeys.includeComments]: {
|
||||
restricted: true,
|
||||
type: ['boolean', 'string'],
|
||||
enum: [true, false, deriveFromWorkspaceTrust],
|
||||
default: defaults.includeComments,
|
||||
description: nls.localize('unicodeHighlight.includeComments', "Controls whether characters in comments should also be subject to unicode highlighting.")
|
||||
},
|
||||
[unicodeHighlightConfigKeys.allowedCharacters]: {
|
||||
restricted: true,
|
||||
type: 'string',
|
||||
default: defaults.allowedCharacters,
|
||||
description: nls.localize('unicodeHighlight.allowedCharacters', "Defines allowed characters that are not being highlighted.")
|
||||
},
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
public validate(_input: any): InternalUnicodeHighlightOptions {
|
||||
if (!_input || typeof _input !== 'object') {
|
||||
return this.defaultValue;
|
||||
}
|
||||
const input = _input as IUnicodeHighlightOptions;
|
||||
return {
|
||||
nonBasicASCII: primitiveSet<boolean | DeriveFromWorkspaceTrust>(input.nonBasicASCII, deriveFromWorkspaceTrust, [true, false, deriveFromWorkspaceTrust]),
|
||||
invisibleCharacters: primitiveSet<boolean | DeriveFromWorkspaceTrust>(input.invisibleCharacters, deriveFromWorkspaceTrust, [true, false, deriveFromWorkspaceTrust]),
|
||||
ambiguousCharacters: primitiveSet<boolean | DeriveFromWorkspaceTrust>(input.ambiguousCharacters, deriveFromWorkspaceTrust, [true, false, deriveFromWorkspaceTrust]),
|
||||
includeComments: primitiveSet<boolean | DeriveFromWorkspaceTrust>(input.includeComments, deriveFromWorkspaceTrust, [true, false, deriveFromWorkspaceTrust]),
|
||||
allowedCharacters: string(input.allowedCharacters, ''),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function string(value: unknown, defaultValue: string): string {
|
||||
if (typeof value !== 'string') {
|
||||
return defaultValue;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region inlineSuggest
|
||||
|
||||
export interface IInlineSuggestOptions {
|
||||
|
@ -4219,6 +4335,7 @@ export const enum EditorOption {
|
|||
suggestSelection,
|
||||
tabCompletion,
|
||||
tabIndex,
|
||||
unicodeHighlighting,
|
||||
unusualLineTerminators,
|
||||
useShadowDOM,
|
||||
useTabStops,
|
||||
|
@ -4807,6 +4924,7 @@ export const EditorOptions = {
|
|||
EditorOption.tabIndex, 'tabIndex',
|
||||
0, -1, Constants.MAX_SAFE_SMALL_INTEGER
|
||||
)),
|
||||
unicodeHighlight: register(new UnicodeHighlight()),
|
||||
unusualLineTerminators: register(new EditorStringEnumOption(
|
||||
EditorOption.unusualLineTerminators, 'unusualLineTerminators',
|
||||
'prompt' as 'auto' | 'off' | 'prompt',
|
||||
|
|
|
@ -168,6 +168,12 @@ export interface IModelDecorationOptions {
|
|||
* If set, text will be injected in the view before the range.
|
||||
*/
|
||||
before?: InjectedTextOptions | null;
|
||||
|
||||
/**
|
||||
* If set, this decoration will not be rendered for comment tokens.
|
||||
* @internal
|
||||
*/
|
||||
hideInCommentTokens?: boolean | null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -3044,6 +3044,8 @@ export class ModelDecorationOptions implements model.IModelDecorationOptions {
|
|||
readonly afterContentClassName: string | null;
|
||||
readonly after: ModelDecorationInjectedTextOptions | null;
|
||||
readonly before: ModelDecorationInjectedTextOptions | null;
|
||||
readonly hideInCommentTokens: boolean | null;
|
||||
|
||||
|
||||
private constructor(options: model.IModelDecorationOptions) {
|
||||
this.description = options.description;
|
||||
|
@ -3067,6 +3069,7 @@ export class ModelDecorationOptions implements model.IModelDecorationOptions {
|
|||
this.afterContentClassName = options.afterContentClassName ? cleanClassName(options.afterContentClassName) : null;
|
||||
this.after = options.after ? ModelDecorationInjectedTextOptions.from(options.after) : null;
|
||||
this.before = options.before ? ModelDecorationInjectedTextOptions.from(options.before) : null;
|
||||
this.hideInCommentTokens = options.hideInCommentTokens ?? false;
|
||||
}
|
||||
}
|
||||
ModelDecorationOptions.EMPTY = ModelDecorationOptions.register({ description: 'empty' });
|
||||
|
|
188
src/vs/editor/common/modes/unicodeTextModelHighlighter.ts
Normal file
188
src/vs/editor/common/modes/unicodeTextModelHighlighter.ts
Normal file
|
@ -0,0 +1,188 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { IRange, Range } from 'vs/editor/common/core/range';
|
||||
import { Searcher } from 'vs/editor/common/model/textModelSearch';
|
||||
import * as strings from 'vs/base/common/strings';
|
||||
|
||||
export class UnicodeTextModelHighlighter {
|
||||
public static computeUnicodeHighlights(model: IUnicodeCharacterSearcherTarget, options: UnicodeHighlighterOptions, range?: IRange): Range[] {
|
||||
const startLine = range ? range.startLineNumber : 1;
|
||||
const endLine = range ? range.endLineNumber : model.getLineCount();
|
||||
|
||||
const codePointHighlighter = new CodePointHighlighter(options);
|
||||
|
||||
const candidates = codePointHighlighter.getCandidateCodePoints();
|
||||
let regex: RegExp;
|
||||
if (candidates === 'allNonBasicAscii') {
|
||||
regex = new RegExp('[^\\t\\n\\r\\x20-\\x7E]', 'g');
|
||||
} else {
|
||||
regex = new RegExp(`${buildRegExpCharClassExpr(Array.from(candidates))}`, 'g');
|
||||
}
|
||||
|
||||
const searcher = new Searcher(null, regex);
|
||||
const result: Range[] = [];
|
||||
let m: RegExpExecArray | null;
|
||||
for (let lineNumber = startLine, lineCount = endLine; lineNumber <= lineCount; lineNumber++) {
|
||||
const lineContent = model.getLineContent(lineNumber);
|
||||
const lineLength = lineContent.length;
|
||||
|
||||
// Reset regex to search from the beginning
|
||||
searcher.reset(0);
|
||||
do {
|
||||
m = searcher.next(lineContent);
|
||||
if (m) {
|
||||
let startIndex = m.index;
|
||||
let endIndex = m.index + m[0].length;
|
||||
|
||||
// Extend range to entire code point
|
||||
if (startIndex > 0) {
|
||||
const charCodeBefore = lineContent.charCodeAt(startIndex - 1);
|
||||
if (strings.isHighSurrogate(charCodeBefore)) {
|
||||
startIndex--;
|
||||
}
|
||||
}
|
||||
if (endIndex + 1 < lineLength) {
|
||||
const charCodeBefore = lineContent.charCodeAt(endIndex - 1);
|
||||
if (strings.isHighSurrogate(charCodeBefore)) {
|
||||
endIndex++;
|
||||
}
|
||||
}
|
||||
const str = lineContent.substring(startIndex, endIndex);
|
||||
if (codePointHighlighter.shouldHighlightNonBasicASCII(str) !== SimpleHighlightReason.None) {
|
||||
result.push(new Range(lineNumber, startIndex + 1, lineNumber, endIndex + 1));
|
||||
|
||||
const maxResultLength = 1000;
|
||||
if (result.length > maxResultLength) {
|
||||
// TODO@hediet a message should be shown in this case
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} while (m);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static computeUnicodeHighlightReason(char: string, options: UnicodeHighlighterOptions): UnicodeHighlighterReason | null {
|
||||
const codePointHighlighter = new CodePointHighlighter(options);
|
||||
|
||||
const reason = codePointHighlighter.shouldHighlightNonBasicASCII(char);
|
||||
switch (reason) {
|
||||
case SimpleHighlightReason.None:
|
||||
return null;
|
||||
case SimpleHighlightReason.Invisible:
|
||||
return { kind: UnicodeHighlighterReasonKind.Invisible };
|
||||
|
||||
case SimpleHighlightReason.Ambiguous:
|
||||
const primaryConfusable = strings.AmbiguousCharacters.getPrimaryConfusable(char.codePointAt(0)!)!;
|
||||
return { kind: UnicodeHighlighterReasonKind.Ambiguous, confusableWith: String.fromCodePoint(primaryConfusable) };
|
||||
|
||||
case SimpleHighlightReason.NonBasicASCII:
|
||||
return { kind: UnicodeHighlighterReasonKind.NonBasicAscii };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function buildRegExpCharClassExpr(codePoints: number[], flags?: string): string {
|
||||
const src = `[${strings.escapeRegExpCharacters(
|
||||
codePoints.map((i) => String.fromCodePoint(i)).join('')
|
||||
)}]`;
|
||||
return src;
|
||||
}
|
||||
|
||||
export const enum UnicodeHighlighterReasonKind {
|
||||
Ambiguous, Invisible, NonBasicAscii
|
||||
}
|
||||
|
||||
export type UnicodeHighlighterReason = {
|
||||
kind: UnicodeHighlighterReasonKind.Ambiguous;
|
||||
confusableWith: string;
|
||||
} | {
|
||||
kind: UnicodeHighlighterReasonKind.Invisible;
|
||||
} | {
|
||||
kind: UnicodeHighlighterReasonKind.NonBasicAscii
|
||||
};
|
||||
|
||||
class CodePointHighlighter {
|
||||
private readonly allowedCodePoints: Set<number>;
|
||||
constructor(private readonly options: UnicodeHighlighterOptions) {
|
||||
this.allowedCodePoints = new Set(options.allowedCodePoints);
|
||||
}
|
||||
|
||||
public getCandidateCodePoints(): Set<number> | 'allNonBasicAscii' {
|
||||
if (this.options.nonBasicASCII) {
|
||||
return 'allNonBasicAscii';
|
||||
}
|
||||
|
||||
const set = new Set<number>();
|
||||
|
||||
if (this.options.invisibleCharacters) {
|
||||
for (const cp of strings.InvisibleCharacters.codePoints) {
|
||||
set.add(cp);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.options.ambiguousCharacters) {
|
||||
for (const cp of strings.AmbiguousCharacters.getPrimaryConfusableCodePoints()) {
|
||||
set.add(cp);
|
||||
}
|
||||
}
|
||||
|
||||
for (const cp of this.allowedCodePoints) {
|
||||
set.delete(cp);
|
||||
}
|
||||
|
||||
return set;
|
||||
}
|
||||
|
||||
public shouldHighlightNonBasicASCII(character: string): SimpleHighlightReason {
|
||||
const codePoint = character.codePointAt(0)!;
|
||||
|
||||
if (this.allowedCodePoints.has(codePoint)) {
|
||||
return SimpleHighlightReason.None;
|
||||
}
|
||||
|
||||
if (this.options.nonBasicASCII) {
|
||||
return SimpleHighlightReason.NonBasicASCII;
|
||||
}
|
||||
|
||||
if (this.options.invisibleCharacters) {
|
||||
const isAllowedInvisibleCharacter = character === ' ' || character === '\n' || character === '\t';
|
||||
// TODO check for emojis
|
||||
if (!isAllowedInvisibleCharacter && strings.InvisibleCharacters.isInvisibleCharacter(codePoint)) {
|
||||
return SimpleHighlightReason.Invisible;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.options.ambiguousCharacters) {
|
||||
if (strings.AmbiguousCharacters.isAmbiguous(codePoint)) {
|
||||
return SimpleHighlightReason.Ambiguous;
|
||||
}
|
||||
}
|
||||
|
||||
return SimpleHighlightReason.None;
|
||||
}
|
||||
}
|
||||
|
||||
const enum SimpleHighlightReason {
|
||||
None,
|
||||
NonBasicASCII,
|
||||
Invisible,
|
||||
Ambiguous
|
||||
}
|
||||
|
||||
export interface IUnicodeCharacterSearcherTarget {
|
||||
getLineCount(): number;
|
||||
getLineContent(lineNumber: number): string;
|
||||
}
|
||||
|
||||
export interface UnicodeHighlighterOptions {
|
||||
nonBasicASCII: boolean;
|
||||
ambiguousCharacters: boolean;
|
||||
invisibleCharacters: boolean;
|
||||
includeComments: boolean;
|
||||
allowedCodePoints: number[];
|
||||
}
|
|
@ -23,6 +23,7 @@ import { createMonacoBaseAPI } from 'vs/editor/common/standalone/standaloneBase'
|
|||
import * as types from 'vs/base/common/types';
|
||||
import { EditorWorkerHost } from 'vs/editor/common/services/editorWorkerServiceImpl';
|
||||
import { StopWatch } from 'vs/base/common/stopwatch';
|
||||
import { UnicodeTextModelHighlighter, UnicodeHighlighterOptions } from 'vs/editor/common/modes/unicodeTextModelHighlighter';
|
||||
|
||||
export interface IMirrorModel extends IMirrorTextModel {
|
||||
readonly uri: URI;
|
||||
|
@ -371,6 +372,14 @@ export class EditorSimpleWorker implements IRequestHandler, IDisposable {
|
|||
delete this._models[strURL];
|
||||
}
|
||||
|
||||
public async computeUnicodeHighlights(url: string, options: UnicodeHighlighterOptions, range?: IRange): Promise<IRange[]> {
|
||||
const model = this._getModel(url);
|
||||
if (!model) {
|
||||
return [];
|
||||
}
|
||||
return UnicodeTextModelHighlighter.computeUnicodeHighlights(model, options, range);
|
||||
}
|
||||
|
||||
// ---- BEGIN diff --------------------------------------------------------------------------
|
||||
|
||||
public async computeDiff(originalUrl: string, modifiedUrl: string, ignoreTrimWhitespace: boolean, maxComputationTime: number): Promise<IDiffComputationResult | null> {
|
||||
|
|
|
@ -7,6 +7,7 @@ import { URI } from 'vs/base/common/uri';
|
|||
import { IRange } from 'vs/editor/common/core/range';
|
||||
import { IChange, ILineChange } from 'vs/editor/common/editorCommon';
|
||||
import { IInplaceReplaceSupportResult, TextEdit } from 'vs/editor/common/modes';
|
||||
import { UnicodeHighlighterOptions } from 'vs/editor/common/modes/unicodeTextModelHighlighter';
|
||||
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||
|
||||
export const ID_EDITOR_WORKER_SERVICE = 'editorWorkerService';
|
||||
|
@ -21,6 +22,9 @@ export interface IDiffComputationResult {
|
|||
export interface IEditorWorkerService {
|
||||
readonly _serviceBrand: undefined;
|
||||
|
||||
canComputeUnicodeHighlights(uri: URI): boolean;
|
||||
computedUnicodeHighlights(uri: URI, options: UnicodeHighlighterOptions, range?: IRange): Promise<IRange[]>;
|
||||
|
||||
computeDiff(original: URI, modified: URI, ignoreTrimWhitespace: boolean, maxComputationTime: number): Promise<IDiffComputationResult | null>;
|
||||
|
||||
canComputeDirtyDiff(original: URI, modified: URI): boolean;
|
||||
|
|
|
@ -23,6 +23,7 @@ import { isNonEmptyArray } from 'vs/base/common/arrays';
|
|||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { StopWatch } from 'vs/base/common/stopwatch';
|
||||
import { canceled } from 'vs/base/common/errors';
|
||||
import { UnicodeHighlighterOptions } from 'vs/editor/common/modes/unicodeTextModelHighlighter';
|
||||
|
||||
/**
|
||||
* Stop syncing a model to the worker if it was not needed for 1 min.
|
||||
|
@ -81,6 +82,14 @@ export class EditorWorkerServiceImpl extends Disposable implements IEditorWorker
|
|||
super.dispose();
|
||||
}
|
||||
|
||||
public canComputeUnicodeHighlights(uri: URI): boolean {
|
||||
return canSyncModel(this._modelService, uri);
|
||||
}
|
||||
|
||||
public computedUnicodeHighlights(uri: URI, options: UnicodeHighlighterOptions, range?: IRange): Promise<IRange[]> {
|
||||
return this._workerManager.withWorker().then(client => client.computedUnicodeHighlights(uri, options, range));
|
||||
}
|
||||
|
||||
public computeDiff(original: URI, modified: URI, ignoreTrimWhitespace: boolean, maxComputationTime: number): Promise<IDiffComputationResult | null> {
|
||||
return this._workerManager.withWorker().then(client => client.computeDiff(original, modified, ignoreTrimWhitespace, maxComputationTime));
|
||||
}
|
||||
|
@ -466,6 +475,12 @@ export class EditorWorkerClient extends Disposable implements IEditorWorkerClien
|
|||
});
|
||||
}
|
||||
|
||||
public computedUnicodeHighlights(uri: URI, options: UnicodeHighlighterOptions, range?: IRange): Promise<IRange[]> {
|
||||
return this._withSyncedResources([uri]).then(proxy => {
|
||||
return proxy.computeUnicodeHighlights(uri.toString(), options, range);
|
||||
});
|
||||
}
|
||||
|
||||
public computeDiff(original: URI, modified: URI, ignoreTrimWhitespace: boolean, maxComputationTime: number): Promise<IDiffComputationResult | null> {
|
||||
return this._withSyncedResources([original, modified], /* forceLargeModels */true).then(proxy => {
|
||||
return proxy.computeDiff(original.toString(), modified.toString(), ignoreTrimWhitespace, maxComputationTime);
|
||||
|
|
|
@ -279,25 +279,26 @@ export enum EditorOption {
|
|||
suggestSelection = 109,
|
||||
tabCompletion = 110,
|
||||
tabIndex = 111,
|
||||
unusualLineTerminators = 112,
|
||||
useShadowDOM = 113,
|
||||
useTabStops = 114,
|
||||
wordSeparators = 115,
|
||||
wordWrap = 116,
|
||||
wordWrapBreakAfterCharacters = 117,
|
||||
wordWrapBreakBeforeCharacters = 118,
|
||||
wordWrapColumn = 119,
|
||||
wordWrapOverride1 = 120,
|
||||
wordWrapOverride2 = 121,
|
||||
wrappingIndent = 122,
|
||||
wrappingStrategy = 123,
|
||||
showDeprecated = 124,
|
||||
inlayHints = 125,
|
||||
editorClassName = 126,
|
||||
pixelRatio = 127,
|
||||
tabFocusMode = 128,
|
||||
layoutInfo = 129,
|
||||
wrappingInfo = 130
|
||||
unicodeHighlighting = 112,
|
||||
unusualLineTerminators = 113,
|
||||
useShadowDOM = 114,
|
||||
useTabStops = 115,
|
||||
wordSeparators = 116,
|
||||
wordWrap = 117,
|
||||
wordWrapBreakAfterCharacters = 118,
|
||||
wordWrapBreakBeforeCharacters = 119,
|
||||
wordWrapColumn = 120,
|
||||
wordWrapOverride1 = 121,
|
||||
wordWrapOverride2 = 122,
|
||||
wrappingIndent = 123,
|
||||
wrappingStrategy = 124,
|
||||
showDeprecated = 125,
|
||||
inlayHints = 126,
|
||||
editorClassName = 127,
|
||||
pixelRatio = 128,
|
||||
tabFocusMode = 129,
|
||||
layoutInfo = 130,
|
||||
wrappingInfo = 131
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -76,6 +76,8 @@ export const editorBracketPairGuideActiveBackground4 = registerColor('editorBrac
|
|||
export const editorBracketPairGuideActiveBackground5 = registerColor('editorBracketPairGuide.activeBackground5', { dark: '#00000000', light: '#00000000', hc: '#00000000' }, nls.localize('editorBracketPairGuide.activeBackground5', 'Background color of active bracket pair guides (5). Requires enabling bracket pair guides.'));
|
||||
export const editorBracketPairGuideActiveBackground6 = registerColor('editorBracketPairGuide.activeBackground6', { dark: '#00000000', light: '#00000000', hc: '#00000000' }, nls.localize('editorBracketPairGuide.activeBackground6', 'Background color of active bracket pair guides (6). Requires enabling bracket pair guides.'));
|
||||
|
||||
export const editorUnicodeHighlightBorder = registerColor('editorUnicodeHighlight.border', { dark: '#ff0000', light: '#ff0000', hc: '#ff0000' }, nls.localize('editorUnicodeHighlight.border', 'Border color used to highlight unicode characters.'));
|
||||
|
||||
|
||||
// contains all color rules that used to defined in editor/browser/widget/editor.css
|
||||
registerThemingParticipant((theme, collector) => {
|
||||
|
|
|
@ -11,6 +11,7 @@ import { IModelDecoration, ITextModel, PositionAffinity } from 'vs/editor/common
|
|||
import { IViewModelLines } from 'vs/editor/common/viewModel/viewModelLines';
|
||||
import { ICoordinatesConverter, InlineDecoration, InlineDecorationType, ViewModelDecoration } from 'vs/editor/common/viewModel/viewModel';
|
||||
import { filterValidationDecorations } from 'vs/editor/common/config/editorOptions';
|
||||
import { StandardTokenType } from 'vs/editor/common/modes';
|
||||
|
||||
export interface IDecorationsViewportData {
|
||||
/**
|
||||
|
@ -107,6 +108,7 @@ export class ViewModelDecorations implements IDisposable {
|
|||
|
||||
private _getDecorationsViewportData(viewportRange: Range): IDecorationsViewportData {
|
||||
const modelDecorations = this._linesCollection.getDecorationsInRange(viewportRange, this.editorId, filterValidationDecorations(this.configuration.options));
|
||||
|
||||
const startLineNumber = viewportRange.startLineNumber;
|
||||
const endLineNumber = viewportRange.endLineNumber;
|
||||
|
||||
|
@ -120,6 +122,18 @@ export class ViewModelDecorations implements IDisposable {
|
|||
let modelDecoration = modelDecorations[i];
|
||||
let decorationOptions = modelDecoration.options;
|
||||
|
||||
if (decorationOptions.hideInCommentTokens) {
|
||||
let allTokensComments = testTokensInRange(
|
||||
this.model,
|
||||
modelDecoration.range,
|
||||
(tokenType) => tokenType === StandardTokenType.Comment
|
||||
);
|
||||
|
||||
if (allTokensComments) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
let viewModelDecoration = this._getOrCreateViewModelDecoration(modelDecoration);
|
||||
let viewRange = viewModelDecoration.range;
|
||||
|
||||
|
@ -161,3 +175,33 @@ export class ViewModelDecorations implements IDisposable {
|
|||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls the callback for every token that intersects the range.
|
||||
* If the callback returns `false`, iteration stops and `false` is returned.
|
||||
* Otherwise, `true` is returned.
|
||||
*/
|
||||
function testTokensInRange(model: ITextModel, range: Range, callback: (tokenType: StandardTokenType) => boolean): boolean {
|
||||
for (let lineNumber = range.startLineNumber; lineNumber <= range.endLineNumber; lineNumber++) {
|
||||
const lineTokens = model.getLineTokens(lineNumber);
|
||||
const isFirstLine = lineNumber === range.startLineNumber;
|
||||
const isEndLine = lineNumber === range.endLineNumber;
|
||||
|
||||
let tokenIdx = isFirstLine ? lineTokens.findTokenIndexAtOffset(range.startColumn - 1) : 0;
|
||||
while (tokenIdx < lineTokens.getCount()) {
|
||||
if (isEndLine) {
|
||||
const startOffset = lineTokens.getStartOffset(tokenIdx);
|
||||
if (startOffset > range.endColumn - 1) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const callbackResult = callback(lineTokens.getStandardTokenType(tokenIdx));
|
||||
if (!callbackResult) {
|
||||
return false;
|
||||
}
|
||||
tokenIdx++;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -118,24 +118,35 @@ export class MarkdownHoverParticipant implements IEditorHoverParticipant<Markdow
|
|||
}
|
||||
|
||||
public renderHoverParts(hoverParts: MarkdownHover[], fragment: DocumentFragment, statusBar: IEditorHoverStatusBar): IDisposable {
|
||||
const disposables = new DisposableStore();
|
||||
for (const hoverPart of hoverParts) {
|
||||
for (const contents of hoverPart.contents) {
|
||||
if (isEmptyMarkdownString(contents)) {
|
||||
continue;
|
||||
}
|
||||
const markdownHoverElement = $('div.hover-row.markdown-hover');
|
||||
const hoverContentsElement = dom.append(markdownHoverElement, $('div.hover-contents'));
|
||||
const renderer = disposables.add(new MarkdownRenderer({ editor: this._editor }, this._modeService, this._openerService));
|
||||
disposables.add(renderer.onDidRenderAsync(() => {
|
||||
hoverContentsElement.className = 'hover-contents code-hover-contents';
|
||||
this._hover.onContentsChanged();
|
||||
}));
|
||||
const renderedContents = disposables.add(renderer.render(contents));
|
||||
hoverContentsElement.appendChild(renderedContents.element);
|
||||
fragment.appendChild(markdownHoverElement);
|
||||
}
|
||||
}
|
||||
return disposables;
|
||||
return renderMarkdownHovers(hoverParts, fragment, this._editor, this._hover, this._modeService, this._openerService);
|
||||
}
|
||||
}
|
||||
|
||||
export function renderMarkdownHovers(
|
||||
hoverParts: MarkdownHover[],
|
||||
fragment: DocumentFragment,
|
||||
editor: ICodeEditor,
|
||||
hover: IEditorHover,
|
||||
modeService: IModeService,
|
||||
openerService: IOpenerService,
|
||||
): IDisposable {
|
||||
const disposables = new DisposableStore();
|
||||
for (const hoverPart of hoverParts) {
|
||||
for (const contents of hoverPart.contents) {
|
||||
if (isEmptyMarkdownString(contents)) {
|
||||
continue;
|
||||
}
|
||||
const markdownHoverElement = $('div.hover-row.markdown-hover');
|
||||
const hoverContentsElement = dom.append(markdownHoverElement, $('div.hover-contents'));
|
||||
const renderer = disposables.add(new MarkdownRenderer({ editor }, modeService, openerService));
|
||||
disposables.add(renderer.onDidRenderAsync(() => {
|
||||
hoverContentsElement.className = 'hover-contents code-hover-contents';
|
||||
hover.onContentsChanged();
|
||||
}));
|
||||
const renderedContents = disposables.add(renderer.render(contents));
|
||||
hoverContentsElement.appendChild(renderedContents.element);
|
||||
fragment.appendChild(markdownHoverElement);
|
||||
}
|
||||
}
|
||||
return disposables;
|
||||
}
|
||||
|
|
|
@ -31,6 +31,7 @@ import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/c
|
|||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { Context as SuggestContext } from 'vs/editor/contrib/suggest/suggest';
|
||||
import { UnicodeHighlighterHoverParticipant } from 'vs/editor/contrib/unicodeHighlighter/unicodeHighlighter';
|
||||
|
||||
const $ = dom.$;
|
||||
|
||||
|
@ -223,6 +224,7 @@ export class ModesContentHoverWidget extends Widget implements IContentWidget, I
|
|||
instantiationService.createInstance(ColorHoverParticipant, editor, this),
|
||||
instantiationService.createInstance(MarkdownHoverParticipant, editor, this),
|
||||
instantiationService.createInstance(InlineCompletionsHoverParticipant, editor, this),
|
||||
instantiationService.createInstance(UnicodeHighlighterHoverParticipant, editor, this),
|
||||
instantiationService.createInstance(MarkerHoverParticipant, editor, this),
|
||||
];
|
||||
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
.monaco-editor .unicode-highlight {
|
||||
border: 1px solid var(--vscode-editorUnicodeHighlight-border);
|
||||
box-sizing: border-box;
|
||||
}
|
509
src/vs/editor/contrib/unicodeHighlighter/unicodeHighlighter.ts
Normal file
509
src/vs/editor/contrib/unicodeHighlighter/unicodeHighlighter.ts
Normal file
|
@ -0,0 +1,509 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { RunOnceScheduler } from 'vs/base/common/async';
|
||||
import { CharCode } from 'vs/base/common/charCode';
|
||||
import { IMarkdownString } from 'vs/base/common/htmlContent';
|
||||
import { Disposable, IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { InvisibleCharacters } from 'vs/base/common/strings';
|
||||
import 'vs/css!./unicodeHighlighter';
|
||||
import { IActiveCodeEditor, ICodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { EditorAction, registerEditorAction, registerEditorContribution, ServicesAccessor } from 'vs/editor/browser/editorExtensions';
|
||||
import { DeriveFromWorkspaceTrust, deriveFromWorkspaceTrust, EditorOption, InternalUnicodeHighlightOptions, unicodeHighlightConfigKeys } from 'vs/editor/common/config/editorOptions';
|
||||
import { Range } from 'vs/editor/common/core/range';
|
||||
import { IEditorContribution } from 'vs/editor/common/editorCommon';
|
||||
import { IModelDecoration, IModelDeltaDecoration, ITextModel, MinimapPosition, OverviewRulerLane, TrackedRangeStickiness } from 'vs/editor/common/model';
|
||||
import { ModelDecorationOptions } from 'vs/editor/common/model/textModel';
|
||||
import { UnicodeHighlighterOptions, UnicodeHighlighterReason, UnicodeHighlighterReasonKind, UnicodeTextModelHighlighter } from 'vs/editor/common/modes/unicodeTextModelHighlighter';
|
||||
import { IEditorWorkerService } from 'vs/editor/common/services/editorWorkerService';
|
||||
import { IModeService } from 'vs/editor/common/services/modeService';
|
||||
import { HoverAnchor, HoverAnchorType, IEditorHover, IEditorHoverParticipant, IEditorHoverStatusBar, IHoverPart } from 'vs/editor/contrib/hover/hoverTypes';
|
||||
import { MarkdownHover, renderMarkdownHovers } from 'vs/editor/contrib/hover/markdownHoverParticipant';
|
||||
import * as nls from 'vs/nls';
|
||||
import { ConfigurationTarget, IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
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 { themeColorFromId } from 'vs/platform/theme/common/themeService';
|
||||
import { IWorkspaceTrustManagementService } from 'vs/platform/workspace/common/workspaceTrust';
|
||||
|
||||
export class UnicodeHighlighter extends Disposable implements IEditorContribution {
|
||||
public static readonly ID = 'editor.contrib.unicodeHighlighter';
|
||||
|
||||
private _highlighter: DocumentUnicodeHighlighter | ViewportUnicodeHighlighter | null = null;
|
||||
private _options: InternalUnicodeHighlightOptions;
|
||||
|
||||
constructor(
|
||||
private readonly _editor: ICodeEditor,
|
||||
@IEditorWorkerService private readonly _editorWorkerService: IEditorWorkerService,
|
||||
@IWorkspaceTrustManagementService private readonly _workspaceTrustService: IWorkspaceTrustManagementService,
|
||||
) {
|
||||
super();
|
||||
|
||||
this._register(this._editor.onDidChangeModel(() => {
|
||||
this._updateHighlighter();
|
||||
}));
|
||||
|
||||
this._options = _editor.getOption(EditorOption.unicodeHighlighting);
|
||||
|
||||
this._register(_workspaceTrustService.onDidChangeTrust(e => {
|
||||
this._updateHighlighter();
|
||||
}));
|
||||
|
||||
this._register(_editor.onDidChangeConfiguration(e => {
|
||||
if (e.hasChanged(EditorOption.unicodeHighlighting)) {
|
||||
this._options = _editor.getOption(EditorOption.unicodeHighlighting);
|
||||
this._updateHighlighter();
|
||||
}
|
||||
}));
|
||||
|
||||
this._updateHighlighter();
|
||||
}
|
||||
|
||||
public override dispose(): void {
|
||||
if (this._highlighter) {
|
||||
this._highlighter.dispose();
|
||||
this._highlighter = null;
|
||||
}
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
private _updateHighlighter(): void {
|
||||
if (this._highlighter) {
|
||||
this._highlighter.dispose();
|
||||
this._highlighter = null;
|
||||
}
|
||||
if (!this._editor.hasModel()) {
|
||||
return;
|
||||
}
|
||||
const options = resolveOptions(this._workspaceTrustService.isWorkspaceTrusted(), this._options);
|
||||
|
||||
if (
|
||||
[
|
||||
options.nonBasicASCII,
|
||||
options.ambiguousCharacters,
|
||||
options.invisibleCharacters,
|
||||
].every((option) => option === false)
|
||||
) {
|
||||
// Don't do anything if the feature is fully disabled
|
||||
return;
|
||||
}
|
||||
|
||||
const highlightOptions: UnicodeHighlighterOptions = {
|
||||
nonBasicASCII: options.nonBasicASCII,
|
||||
ambiguousCharacters: options.ambiguousCharacters,
|
||||
invisibleCharacters: options.invisibleCharacters,
|
||||
includeComments: options.includeComments,
|
||||
allowedCodePoints: Array.from(options.allowedCharacters).map(c => c.codePointAt(0)!),
|
||||
};
|
||||
|
||||
if (this._editorWorkerService.canComputeUnicodeHighlights(this._editor.getModel().uri)) {
|
||||
this._highlighter = new DocumentUnicodeHighlighter(this._editor, highlightOptions, this._editorWorkerService);
|
||||
} else {
|
||||
this._highlighter = new ViewportUnicodeHighlighter(this._editor, highlightOptions);
|
||||
}
|
||||
}
|
||||
|
||||
public getDecorationInfo(decorationId: string): UnicodeHighlighterDecorationInfo | null {
|
||||
if (this._highlighter) {
|
||||
return this._highlighter.getDecorationInfo(decorationId);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export interface UnicodeHighlighterDecorationInfo {
|
||||
reason: UnicodeHighlighterReason;
|
||||
}
|
||||
|
||||
type RemoveDeriveFromWorkspaceTrust<T> = T extends DeriveFromWorkspaceTrust ? never : T;
|
||||
|
||||
function resolveOptions(_trusted: boolean, options: InternalUnicodeHighlightOptions): { [TKey in keyof InternalUnicodeHighlightOptions]: RemoveDeriveFromWorkspaceTrust<InternalUnicodeHighlightOptions[TKey]> } {
|
||||
/*
|
||||
// TODO@hediet enable some settings by default (depending on trust).
|
||||
// For now, make it opt in, so there is some time to test it without breaking anyone.
|
||||
|
||||
return {
|
||||
nonBasicASCII: options.nonBasicASCII !== deriveFromWorkspaceTrust ? options.nonBasicASCII : (trusted ? false : true),
|
||||
ambiguousCharacters: options.ambiguousCharacters !== deriveFromWorkspaceTrust ? options.ambiguousCharacters : (trusted ? false : true),
|
||||
invisibleCharacters: options.invisibleCharacters !== deriveFromWorkspaceTrust ? options.invisibleCharacters : (trusted ? true : true),
|
||||
includeComments: options.includeComments !== deriveFromWorkspaceTrust ? options.includeComments : (trusted ? true : false),
|
||||
allowedCharacters: options.allowedCharacters ?? [],
|
||||
};
|
||||
*/
|
||||
|
||||
return {
|
||||
nonBasicASCII: options.nonBasicASCII !== deriveFromWorkspaceTrust ? options.nonBasicASCII : false,
|
||||
ambiguousCharacters: options.ambiguousCharacters !== deriveFromWorkspaceTrust ? options.ambiguousCharacters : false,
|
||||
invisibleCharacters: options.invisibleCharacters !== deriveFromWorkspaceTrust ? options.invisibleCharacters : false,
|
||||
includeComments: options.includeComments !== deriveFromWorkspaceTrust ? options.includeComments : true,
|
||||
allowedCharacters: options.allowedCharacters ?? [],
|
||||
};
|
||||
}
|
||||
|
||||
class DocumentUnicodeHighlighter extends Disposable {
|
||||
private readonly _model: ITextModel = this._editor.getModel();
|
||||
private readonly _updateSoon: RunOnceScheduler;
|
||||
private _decorationIds = new Set<string>();
|
||||
|
||||
constructor(
|
||||
private readonly _editor: IActiveCodeEditor,
|
||||
private readonly _options: UnicodeHighlighterOptions,
|
||||
@IEditorWorkerService private readonly _editorWorkerService: IEditorWorkerService,
|
||||
) {
|
||||
super();
|
||||
this._updateSoon = this._register(new RunOnceScheduler(() => this._update(), 250));
|
||||
|
||||
this._register(this._editor.onDidChangeModelContent(() => {
|
||||
this._updateSoon.schedule();
|
||||
}));
|
||||
|
||||
this._updateSoon.schedule();
|
||||
}
|
||||
|
||||
public override dispose() {
|
||||
this._decorationIds = new Set(this._model.deltaDecorations(Array.from(this._decorationIds), []));
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
private _update(): void {
|
||||
if (!this._model.mightContainNonBasicASCII()) {
|
||||
this._decorationIds = new Set(this._editor.deltaDecorations(Array.from(this._decorationIds), []));
|
||||
return;
|
||||
}
|
||||
|
||||
const modelVersionId = this._model.getVersionId();
|
||||
this._editorWorkerService
|
||||
.computedUnicodeHighlights(this._model.uri, this._options)
|
||||
.then((ranges) => {
|
||||
if (this._model.getVersionId() !== modelVersionId) {
|
||||
// model changed in the meantime
|
||||
return;
|
||||
}
|
||||
const decorations: IModelDeltaDecoration[] = [];
|
||||
for (const range of ranges) {
|
||||
decorations.push({ range: range, options: this._options.includeComments ? DECORATION : DECORATION_HIDE_IN_COMMENTS });
|
||||
}
|
||||
this._decorationIds = new Set(this._editor.deltaDecorations(
|
||||
Array.from(this._decorationIds),
|
||||
decorations
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
public getDecorationInfo(decorationId: string): UnicodeHighlighterDecorationInfo | null {
|
||||
if (!this._decorationIds.has(decorationId)) {
|
||||
return null;
|
||||
}
|
||||
const range = this._editor.getModel().getDecorationRange(decorationId)!;
|
||||
const text = this._editor.getModel().getValueInRange(range);
|
||||
return {
|
||||
reason: computeReason(text, this._options)!,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
class ViewportUnicodeHighlighter extends Disposable {
|
||||
|
||||
private readonly _model: ITextModel = this._editor.getModel();
|
||||
private readonly _updateSoon: RunOnceScheduler;
|
||||
private _decorationIds = new Set<string>();
|
||||
|
||||
constructor(
|
||||
private readonly _editor: IActiveCodeEditor,
|
||||
private readonly _options: UnicodeHighlighterOptions
|
||||
) {
|
||||
super();
|
||||
|
||||
this._updateSoon = this._register(new RunOnceScheduler(() => this._update(), 250));
|
||||
|
||||
this._register(this._editor.onDidLayoutChange(() => {
|
||||
this._updateSoon.schedule();
|
||||
}));
|
||||
this._register(this._editor.onDidScrollChange(() => {
|
||||
this._updateSoon.schedule();
|
||||
}));
|
||||
this._register(this._editor.onDidChangeHiddenAreas(() => {
|
||||
this._updateSoon.schedule();
|
||||
}));
|
||||
this._register(this._editor.onDidChangeModelContent(() => {
|
||||
this._updateSoon.schedule();
|
||||
}));
|
||||
|
||||
this._updateSoon.schedule();
|
||||
}
|
||||
|
||||
public override dispose() {
|
||||
this._decorationIds = new Set(this._model.deltaDecorations(Array.from(this._decorationIds), []));
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
private _update(): void {
|
||||
if (!this._model.mightContainNonBasicASCII()) {
|
||||
this._decorationIds = new Set(this._editor.deltaDecorations(Array.from(this._decorationIds), []));
|
||||
return;
|
||||
}
|
||||
|
||||
const ranges = this._editor.getVisibleRanges();
|
||||
const decorations: IModelDeltaDecoration[] = [];
|
||||
for (const range of ranges) {
|
||||
const ranges = UnicodeTextModelHighlighter.computeUnicodeHighlights(this._model, this._options, range);
|
||||
for (const range of ranges) {
|
||||
decorations.push({ range, options: this._options.includeComments ? DECORATION : DECORATION_HIDE_IN_COMMENTS });
|
||||
}
|
||||
}
|
||||
this._decorationIds = new Set(this._editor.deltaDecorations(Array.from(this._decorationIds), decorations));
|
||||
}
|
||||
|
||||
public getDecorationInfo(decorationId: string): UnicodeHighlighterDecorationInfo | null {
|
||||
if (!this._decorationIds.has(decorationId)) {
|
||||
return null;
|
||||
}
|
||||
const range = this._editor.getModel().getDecorationRange(decorationId)!;
|
||||
const text = this._editor.getModel().getValueInRange(range);
|
||||
return {
|
||||
reason: computeReason(text, this._options)!,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export class UnicodeHighlighterHover implements IHoverPart {
|
||||
constructor(
|
||||
public readonly owner: IEditorHoverParticipant<UnicodeHighlighterHover>,
|
||||
public readonly range: Range,
|
||||
public readonly decoration: IModelDecoration
|
||||
) { }
|
||||
|
||||
public isValidForHoverAnchor(anchor: HoverAnchor): boolean {
|
||||
return (
|
||||
anchor.type === HoverAnchorType.Range
|
||||
&& this.range.startColumn <= anchor.range.startColumn
|
||||
&& this.range.endColumn >= anchor.range.endColumn
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export class UnicodeHighlighterHoverParticipant implements IEditorHoverParticipant<MarkdownHover> {
|
||||
|
||||
constructor(
|
||||
private readonly _editor: ICodeEditor,
|
||||
private readonly _hover: IEditorHover,
|
||||
@IModeService private readonly _modeService: IModeService,
|
||||
@IOpenerService private readonly _openerService: IOpenerService,
|
||||
) {
|
||||
}
|
||||
|
||||
computeSync(anchor: HoverAnchor, lineDecorations: IModelDecoration[]): MarkdownHover[] {
|
||||
if (!this._editor.hasModel() || anchor.type !== HoverAnchorType.Range) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const model = this._editor.getModel();
|
||||
|
||||
const unicodeHighlighter = this._editor.getContribution<UnicodeHighlighter>(UnicodeHighlighter.ID);
|
||||
|
||||
|
||||
const result: MarkdownHover[] = [];
|
||||
for (const d of lineDecorations) {
|
||||
|
||||
const highlightInfo = unicodeHighlighter.getDecorationInfo(d.id);
|
||||
if (!highlightInfo) {
|
||||
continue;
|
||||
}
|
||||
const char = model.getValueInRange(d.range);
|
||||
// text refers to a single character.
|
||||
const codePoint = char.codePointAt(0)!;
|
||||
|
||||
function formatCodePoint(codePoint: number) {
|
||||
let value = `\`U+${codePoint.toString(16).padStart(4, '0')}\``;
|
||||
if (!InvisibleCharacters.isInvisibleCharacter(codePoint)) {
|
||||
// Don't render any control characters or any invisible characters, as they cannot be seen anyways.
|
||||
value += ` "${`${renderCodePointAsInlineCode(codePoint)}`}"`;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
const codePointStr = formatCodePoint(codePoint);
|
||||
|
||||
let reason: string;
|
||||
switch (highlightInfo.reason.kind) {
|
||||
case UnicodeHighlighterReasonKind.Ambiguous:
|
||||
reason = nls.localize(
|
||||
'unicodeHighlight.characterIsAmbiguous',
|
||||
'The character {0} could be confused with the character {1}, which is more common in source code.',
|
||||
codePointStr,
|
||||
formatCodePoint(highlightInfo.reason.confusableWith.codePointAt(0)!)
|
||||
);
|
||||
break;
|
||||
|
||||
case UnicodeHighlighterReasonKind.Invisible:
|
||||
reason = nls.localize(
|
||||
'unicodeHighlight.characterIsInvisible',
|
||||
'The character {0} is invisible.',
|
||||
codePointStr
|
||||
);
|
||||
break;
|
||||
|
||||
case UnicodeHighlighterReasonKind.NonBasicAscii:
|
||||
reason = nls.localize(
|
||||
'unicodeHighlight.characterIsNonBasicAscii',
|
||||
'The character {0} is not a basic ASCII character.',
|
||||
codePoint
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
const adjustSettingsArgs: ShowExcludeOptionsArgs = {
|
||||
codePoint: codePoint,
|
||||
reason: highlightInfo.reason.kind,
|
||||
};
|
||||
|
||||
const adjustSettings = nls.localize('unicodeHighlight.adjustSettings', 'Adjust settings');
|
||||
const contents: Array<IMarkdownString> = [{
|
||||
value: `${reason} [${adjustSettings}](command:${ShowExcludeOptions.ID}?${encodeURIComponent(JSON.stringify(adjustSettingsArgs))})`,
|
||||
isTrusted: true,
|
||||
}];
|
||||
|
||||
result.push(new MarkdownHover(this, d.range, contents));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public renderHoverParts(hoverParts: MarkdownHover[], fragment: DocumentFragment, statusBar: IEditorHoverStatusBar): IDisposable {
|
||||
return renderMarkdownHovers(hoverParts, fragment, this._editor, this._hover, this._modeService, this._openerService);
|
||||
}
|
||||
}
|
||||
|
||||
function renderCodePointAsInlineCode(codePoint: number): string {
|
||||
if (codePoint === CharCode.BackTick) {
|
||||
return '`` ` ``';
|
||||
}
|
||||
return '`' + String.fromCodePoint(codePoint) + '`';
|
||||
}
|
||||
|
||||
function computeReason(char: string, options: UnicodeHighlighterOptions): UnicodeHighlighterReason | null {
|
||||
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
|
||||
});
|
||||
|
||||
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
|
||||
}
|
||||
});
|
||||
|
||||
interface ShowExcludeOptionsArgs {
|
||||
codePoint: number;
|
||||
reason: UnicodeHighlighterReason['kind'];
|
||||
}
|
||||
|
||||
export class ShowExcludeOptions extends EditorAction {
|
||||
public static ID = 'editor.action.unicodeHighlight.showExcludeOptions';
|
||||
constructor() {
|
||||
super({
|
||||
id: ShowExcludeOptions.ID,
|
||||
label: nls.localize('action.unicodeHighlight.showExcludeOptions', "Show Exclude Options"),
|
||||
alias: 'Show Exclude Options',
|
||||
precondition: undefined
|
||||
});
|
||||
}
|
||||
public async run(accessor: ServicesAccessor | undefined, editor: ICodeEditor, args: any): Promise<void> {
|
||||
const { codePoint, reason } = args as ShowExcludeOptionsArgs;
|
||||
|
||||
const char = String.fromCodePoint(codePoint);
|
||||
|
||||
const quickPickService = accessor!.get(IQuickInputService);
|
||||
const configurationService = accessor!.get(IConfigurationService);
|
||||
|
||||
interface ExtendedOptions extends IQuickPickItem {
|
||||
run(): Promise<void>;
|
||||
}
|
||||
|
||||
const options: ExtendedOptions[] = [
|
||||
{
|
||||
label: nls.localize('unicodeHighlight.excludeCharFromBeingHighlighted', 'Exclude {0} from being highlighted', `U+${codePoint.toString(16)} "${char}"`),
|
||||
run: async () => {
|
||||
const existingValue = configurationService.getValue(unicodeHighlightConfigKeys.allowedCharacters);
|
||||
let value: string;
|
||||
if (typeof existingValue === 'string') {
|
||||
value = existingValue;
|
||||
} else {
|
||||
value = '';
|
||||
}
|
||||
|
||||
value += char;
|
||||
await configurationService.updateValue(unicodeHighlightConfigKeys.allowedCharacters, value, ConfigurationTarget.USER);
|
||||
}
|
||||
},
|
||||
];
|
||||
|
||||
if (reason === UnicodeHighlighterReasonKind.Ambiguous) {
|
||||
options.push({
|
||||
label: nls.localize('unicodeHighlight.disableHighlightingOfAmbiguousCharacters', 'Disable highlighting of ambiguous characters'),
|
||||
run: async () => {
|
||||
await configurationService.updateValue(unicodeHighlightConfigKeys.ambiguousCharacters, false, ConfigurationTarget.USER);
|
||||
}
|
||||
});
|
||||
}
|
||||
else if (reason === UnicodeHighlighterReasonKind.Invisible) {
|
||||
options.push({
|
||||
label: nls.localize('unicodeHighlight.disableHighlightingOfInvisibleCharacters', 'Disable highlighting of invisible characters'),
|
||||
run: async () => {
|
||||
await configurationService.updateValue(unicodeHighlightConfigKeys.invisibleCharacters, false, ConfigurationTarget.USER);
|
||||
}
|
||||
});
|
||||
}
|
||||
else if (reason === UnicodeHighlighterReasonKind.NonBasicAscii) {
|
||||
options.push({
|
||||
label: nls.localize('unicodeHighlight.disableHighlightingOfNonBasicAsciiCharacters', 'Disable highlighting of non basic ASCII characters'),
|
||||
run: async () => {
|
||||
await configurationService.updateValue(unicodeHighlightConfigKeys.nonBasicASCII, false, ConfigurationTarget.USER);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
expectNever(reason);
|
||||
}
|
||||
|
||||
const result = await quickPickService.pick(
|
||||
options,
|
||||
{ title: nls.localize('unicodeHighlight.configureUnicodeHighlightOptions', 'Configure Unicode Highlight Options') }
|
||||
);
|
||||
|
||||
if (result) {
|
||||
await result.run();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function expectNever(value: never) {
|
||||
throw new Error(`Unexpected value: ${value}`);
|
||||
}
|
||||
|
||||
registerEditorAction(ShowExcludeOptions);
|
||||
registerEditorContribution(UnicodeHighlighter.ID, UnicodeHighlighter);
|
|
@ -43,6 +43,7 @@ import 'vs/editor/contrib/snippet/snippetController2';
|
|||
import 'vs/editor/contrib/suggest/suggestController';
|
||||
import 'vs/editor/contrib/tokenization/tokenization';
|
||||
import 'vs/editor/contrib/toggleTabFocusMode/toggleTabFocusMode';
|
||||
import 'vs/editor/contrib/unicodeHighlighter/unicodeHighlighter';
|
||||
import 'vs/editor/contrib/unusualLineTerminators/unusualLineTerminators';
|
||||
import 'vs/editor/contrib/viewportSemanticTokens/viewportSemanticTokens';
|
||||
import 'vs/editor/contrib/wordHighlighter/wordHighlighter';
|
||||
|
|
|
@ -47,6 +47,7 @@ import { ClassifiedEvent, StrictPropertyCheck, GDPRClassification } from 'vs/pla
|
|||
import { basename } from 'vs/base/common/resources';
|
||||
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { IWorkspaceTrustManagementService, IWorkspaceTrustTransitionParticipant, IWorkspaceTrustUriInfo } from 'vs/platform/workspace/common/workspaceTrust';
|
||||
|
||||
export class SimpleModel implements IResolvedTextEditorModel {
|
||||
|
||||
|
@ -798,3 +799,48 @@ export class SimpleLayoutService implements ILayoutService {
|
|||
|
||||
constructor(private _codeEditorService: ICodeEditorService, private _container: HTMLElement) { }
|
||||
}
|
||||
|
||||
export class SimpleWorkspaceTrustManagementService implements IWorkspaceTrustManagementService {
|
||||
_serviceBrand: undefined;
|
||||
|
||||
private _neverEmitter = new Emitter<never>();
|
||||
public readonly onDidChangeTrust: Event<boolean> = this._neverEmitter.event;
|
||||
onDidChangeTrustedFolders: Event<void> = this._neverEmitter.event;
|
||||
public readonly workspaceResolved = Promise.resolve();
|
||||
public readonly workspaceTrustInitialized = Promise.resolve();
|
||||
public readonly acceptsOutOfWorkspaceFiles = true;
|
||||
|
||||
isWorkspaceTrusted(): boolean {
|
||||
return true;
|
||||
}
|
||||
isWorkspaceTrustForced(): boolean {
|
||||
return false;
|
||||
}
|
||||
canSetParentFolderTrust(): boolean {
|
||||
return false;
|
||||
}
|
||||
async setParentFolderTrust(trusted: boolean): Promise<void> {
|
||||
// noop
|
||||
}
|
||||
canSetWorkspaceTrust(): boolean {
|
||||
return false;
|
||||
}
|
||||
async setWorkspaceTrust(trusted: boolean): Promise<void> {
|
||||
// noop
|
||||
}
|
||||
getUriTrustInfo(uri: URI): Promise<IWorkspaceTrustUriInfo> {
|
||||
throw new Error('Method not supported.');
|
||||
}
|
||||
async setUrisTrust(uri: URI[], trusted: boolean): Promise<void> {
|
||||
// noop
|
||||
}
|
||||
getTrustedUris(): URI[] {
|
||||
return [];
|
||||
}
|
||||
async setTrustedUris(uris: URI[]): Promise<void> {
|
||||
// noop
|
||||
}
|
||||
addWorkspaceTrustTransitionParticipant(participant: IWorkspaceTrustTransitionParticipant): IDisposable {
|
||||
throw new Error('Method not supported.');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ import { ModeServiceImpl } from 'vs/editor/common/services/modeServiceImpl';
|
|||
import { IModelService } from 'vs/editor/common/services/modelService';
|
||||
import { ModelServiceImpl } from 'vs/editor/common/services/modelServiceImpl';
|
||||
import { ITextResourceConfigurationService, ITextResourcePropertiesService } from 'vs/editor/common/services/textResourceConfigurationService';
|
||||
import { SimpleBulkEditService, SimpleConfigurationService, SimpleDialogService, SimpleNotificationService, SimpleEditorProgressService, SimpleResourceConfigurationService, SimpleResourcePropertiesService, SimpleUriLabelService, SimpleWorkspaceContextService, StandaloneCommandService, StandaloneKeybindingService, StandaloneTelemetryService, SimpleLayoutService } from 'vs/editor/standalone/browser/simpleServices';
|
||||
import { SimpleBulkEditService, SimpleConfigurationService, SimpleDialogService, SimpleNotificationService, SimpleEditorProgressService, SimpleResourceConfigurationService, SimpleResourcePropertiesService, SimpleUriLabelService, SimpleWorkspaceContextService, StandaloneCommandService, StandaloneKeybindingService, StandaloneTelemetryService, SimpleLayoutService, SimpleWorkspaceTrustManagementService } from 'vs/editor/standalone/browser/simpleServices';
|
||||
import { StandaloneCodeEditorServiceImpl } from 'vs/editor/standalone/browser/standaloneCodeServiceImpl';
|
||||
import { StandaloneThemeServiceImpl } from 'vs/editor/standalone/browser/standaloneThemeServiceImpl';
|
||||
import { IStandaloneThemeService } from 'vs/editor/standalone/common/standaloneThemeService';
|
||||
|
@ -55,6 +55,7 @@ import { UndoRedoService } from 'vs/platform/undoRedo/common/undoRedoService';
|
|||
import { StandaloneQuickInputServiceImpl } from 'vs/editor/standalone/browser/quickInput/standaloneQuickInputServiceImpl';
|
||||
import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput';
|
||||
import { ILanguageConfigurationService, LanguageConfigurationService } from 'vs/editor/common/modes/languageConfigurationRegistry';
|
||||
import { IWorkspaceTrustManagementService } from 'vs/platform/workspace/common/workspaceTrust';
|
||||
|
||||
export interface IEditorOverrideServices {
|
||||
[index: string]: any;
|
||||
|
@ -243,6 +244,8 @@ export class DynamicStandaloneServices extends Disposable {
|
|||
ensure(IMenuService, () => new MenuService(commandService));
|
||||
|
||||
ensure(IBulkEditService, () => new SimpleBulkEditService(StaticServices.modelService.get(IModelService)));
|
||||
|
||||
ensure(IWorkspaceTrustManagementService, () => new SimpleWorkspaceTrustManagementService());
|
||||
}
|
||||
|
||||
public get<T>(serviceId: ServiceIdentifier<T>): T {
|
||||
|
|
57
src/vs/monaco.d.ts
vendored
57
src/vs/monaco.d.ts
vendored
|
@ -3299,6 +3299,7 @@ declare namespace monaco.editor {
|
|||
* Controls the behavior of editor guides.
|
||||
*/
|
||||
guides?: IGuidesOptions;
|
||||
unicodeHighlight?: IUnicodeHighlightOptions;
|
||||
}
|
||||
|
||||
export interface IDiffEditorBaseOptions {
|
||||
|
@ -3862,6 +3863,22 @@ declare namespace monaco.editor {
|
|||
readonly scrollByPage: boolean;
|
||||
}
|
||||
|
||||
export type DeriveFromWorkspaceTrust = 'deriveFromWorkspaceTrust';
|
||||
|
||||
/**
|
||||
* Configuration options for unicode highlighting.
|
||||
*/
|
||||
export interface IUnicodeHighlightOptions {
|
||||
nonBasicASCII?: boolean | DeriveFromWorkspaceTrust;
|
||||
invisibleCharacters?: boolean | DeriveFromWorkspaceTrust;
|
||||
ambiguousCharacters?: boolean | DeriveFromWorkspaceTrust;
|
||||
includeComments?: boolean | DeriveFromWorkspaceTrust;
|
||||
/**
|
||||
* A list of allowed code points in a single string.
|
||||
*/
|
||||
allowedCharacters?: string;
|
||||
}
|
||||
|
||||
export interface IInlineSuggestOptions {
|
||||
/**
|
||||
* Enable or disable the rendering of automatic inline completions.
|
||||
|
@ -4216,25 +4233,26 @@ declare namespace monaco.editor {
|
|||
suggestSelection = 109,
|
||||
tabCompletion = 110,
|
||||
tabIndex = 111,
|
||||
unusualLineTerminators = 112,
|
||||
useShadowDOM = 113,
|
||||
useTabStops = 114,
|
||||
wordSeparators = 115,
|
||||
wordWrap = 116,
|
||||
wordWrapBreakAfterCharacters = 117,
|
||||
wordWrapBreakBeforeCharacters = 118,
|
||||
wordWrapColumn = 119,
|
||||
wordWrapOverride1 = 120,
|
||||
wordWrapOverride2 = 121,
|
||||
wrappingIndent = 122,
|
||||
wrappingStrategy = 123,
|
||||
showDeprecated = 124,
|
||||
inlayHints = 125,
|
||||
editorClassName = 126,
|
||||
pixelRatio = 127,
|
||||
tabFocusMode = 128,
|
||||
layoutInfo = 129,
|
||||
wrappingInfo = 130
|
||||
unicodeHighlighting = 112,
|
||||
unusualLineTerminators = 113,
|
||||
useShadowDOM = 114,
|
||||
useTabStops = 115,
|
||||
wordSeparators = 116,
|
||||
wordWrap = 117,
|
||||
wordWrapBreakAfterCharacters = 118,
|
||||
wordWrapBreakBeforeCharacters = 119,
|
||||
wordWrapColumn = 120,
|
||||
wordWrapOverride1 = 121,
|
||||
wordWrapOverride2 = 122,
|
||||
wrappingIndent = 123,
|
||||
wrappingStrategy = 124,
|
||||
showDeprecated = 125,
|
||||
inlayHints = 126,
|
||||
editorClassName = 127,
|
||||
pixelRatio = 128,
|
||||
tabFocusMode = 129,
|
||||
layoutInfo = 130,
|
||||
wrappingInfo = 131
|
||||
}
|
||||
|
||||
export const EditorOptions: {
|
||||
|
@ -4352,6 +4370,7 @@ declare namespace monaco.editor {
|
|||
suggestSelection: IEditorOption<EditorOption.suggestSelection, 'first' | 'recentlyUsed' | 'recentlyUsedByPrefix'>;
|
||||
tabCompletion: IEditorOption<EditorOption.tabCompletion, 'on' | 'off' | 'onlySnippets'>;
|
||||
tabIndex: IEditorOption<EditorOption.tabIndex, number>;
|
||||
unicodeHighlight: IEditorOption<EditorOption.unicodeHighlighting, Required<Readonly<IUnicodeHighlightOptions>>>;
|
||||
unusualLineTerminators: IEditorOption<EditorOption.unusualLineTerminators, 'auto' | 'off' | 'prompt'>;
|
||||
useShadowDOM: IEditorOption<EditorOption.useShadowDOM, boolean>;
|
||||
useTabStops: IEditorOption<EditorOption.useTabStops, boolean>;
|
||||
|
|
|
@ -499,6 +499,8 @@ export const overviewRulerFindMatchForeground = registerColor('editorOverviewRul
|
|||
|
||||
export const overviewRulerSelectionHighlightForeground = registerColor('editorOverviewRuler.selectionHighlightForeground', { dark: '#A0A0A0CC', light: '#A0A0A0CC', hc: '#A0A0A0CC' }, nls.localize('overviewRulerSelectionHighlightForeground', 'Overview ruler marker color for selection highlights. The color must not be opaque so as not to hide underlying decorations.'), true);
|
||||
|
||||
export const overviewRulerUnicodeHighlightForeground = registerColor('editorOverviewRuler.unicodeForeground', { dark: '#d186167e', light: '#d186167e', hc: '#AB5A00' }, nls.localize('overviewRulerUnicodeForeground', 'Overview ruler marker color for highlighted unicode characters. The color must not be opaque so as not to hide underlying decorations.'), true);
|
||||
|
||||
export const minimapFindMatch = registerColor('minimap.findMatchHighlight', { light: '#d18616', dark: '#d18616', hc: '#AB5A00' }, nls.localize('minimapFindMatchHighlight', 'Minimap marker color for find matches.'), true);
|
||||
export const minimapSelectionOccurrenceHighlight = registerColor('minimap.selectionOccurrenceHighlight', { light: '#c9c9c9', dark: '#676767', hc: '#ffffff' }, nls.localize('minimapSelectionOccurrenceHighlight', 'Minimap marker color for repeating editor selections.'), true);
|
||||
export const minimapSelection = registerColor('minimap.selectionHighlight', { light: '#ADD6FF', dark: '#264F78', hc: '#ffffff' }, nls.localize('minimapSelectionHighlight', 'Minimap marker color for the editor selection.'), true);
|
||||
|
@ -506,6 +508,7 @@ export const minimapError = registerColor('minimap.errorHighlight', { dark: new
|
|||
export const minimapWarning = registerColor('minimap.warningHighlight', { dark: editorWarningForeground, light: editorWarningForeground, hc: editorWarningBorder }, nls.localize('overviewRuleWarning', 'Minimap marker color for warnings.'));
|
||||
export const minimapBackground = registerColor('minimap.background', { dark: null, light: null, hc: null }, nls.localize('minimapBackground', "Minimap background color."));
|
||||
export const minimapForegroundOpacity = registerColor('minimap.foregroundOpacity', { dark: Color.fromHex('#000f'), light: Color.fromHex('#000f'), hc: Color.fromHex('#000f') }, nls.localize('minimapForegroundOpacity', 'Opacity of foreground elements rendered in the minimap. For example, "#000000c0" will render the elements with 75% opacity.'));
|
||||
export const minimapUnicodeHighlight = registerColor('minimap.unicodeHighlight', { light: '#d18616', dark: '#d18616', hc: '#AB5A00' }, nls.localize('minimapUnicodeHighlight', 'Minimap marker color for highlighted unicode characters.'));
|
||||
|
||||
export const minimapSliderBackground = registerColor('minimapSlider.background', { light: transparent(scrollbarSliderBackground, 0.5), dark: transparent(scrollbarSliderBackground, 0.5), hc: transparent(scrollbarSliderBackground, 0.5) }, nls.localize('minimapSliderBackground', "Minimap slider background color."));
|
||||
export const minimapSliderHoverBackground = registerColor('minimapSlider.hoverBackground', { light: transparent(scrollbarSliderHoverBackground, 0.5), dark: transparent(scrollbarSliderHoverBackground, 0.5), hc: transparent(scrollbarSliderHoverBackground, 0.5) }, nls.localize('minimapSliderHoverBackground', "Minimap slider background color when hovering."));
|
||||
|
|
|
@ -1864,6 +1864,8 @@ export class TestEditorWorkerService implements IEditorWorkerService {
|
|||
|
||||
declare readonly _serviceBrand: undefined;
|
||||
|
||||
canComputeUnicodeHighlights(uri: URI): boolean { return false; }
|
||||
async computedUnicodeHighlights(uri: URI): Promise<IRange[]> { return []; }
|
||||
async computeDiff(original: URI, modified: URI, ignoreTrimWhitespace: boolean, maxComputationTime: number): Promise<IDiffComputationResult | null> { return null; }
|
||||
canComputeDirtyDiff(original: URI, modified: URI): boolean { return false; }
|
||||
async computeDirtyDiff(original: URI, modified: URI, ignoreTrimWhitespace: boolean): Promise<IChange[] | null> { return null; }
|
||||
|
|
Loading…
Reference in a new issue