Merge pull request #137508 from microsoft/hediet/unicode-highlighting

Implements highlighting of unicode characters.
This commit is contained in:
Henning Dieterichs 2021-11-22 21:56:51 +01:00 committed by GitHub
commit 3a33723d3f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
21 changed files with 1135 additions and 58 deletions

File diff suppressed because one or more lines are too long

View file

@ -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',

View file

@ -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;
}
/**

View file

@ -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' });

View 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[];
}

View file

@ -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> {

View file

@ -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;

View file

@ -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);

View file

@ -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
}
/**

View file

@ -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) => {

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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),
];

View file

@ -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;
}

View 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);

View file

@ -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';

View file

@ -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.');
}
}

View file

@ -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
View file

@ -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>;

View file

@ -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."));

View file

@ -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; }