未验证 提交 39d5332d 编写于 作者: H Henning Dieterichs 提交者: GitHub

Merge pull request #138042 from microsoft/hediet/allowedCharactersRecord

allowed characters: string -> record
......@@ -112,12 +112,15 @@ class EditorConfiguration2 {
}
private static _deepEquals<T>(a: T, b: T): boolean {
if (typeof a !== 'object' || typeof b !== 'object') {
return (a === b);
if (typeof a !== 'object' || typeof b !== 'object' || !a || !b) {
return a === b;
}
if (Array.isArray(a) || Array.isArray(b)) {
return (Array.isArray(a) && Array.isArray(b) ? arrays.equals(a, b) : false);
}
if (Object.keys(a).length !== Object.keys(b).length) {
return false;
}
for (let key in a) {
if (!EditorConfiguration2._deepEquals(a[key], b[key])) {
return false;
......@@ -138,6 +141,22 @@ class EditorConfiguration2 {
}
return (somethingChanged ? new ConfigurationChangedEvent(result) : null);
}
/**
* Returns true if something changed.
* Modifies `options`.
*/
public static applyUpdate(options: IEditorOptions, update: Readonly<IEditorOptions>): boolean {
let changed = false;
for (const editorOption of editorOptionsRegistry) {
if (update.hasOwnProperty(editorOption.name)) {
const result = editorOption.applyUpdate((options as any)[editorOption.name], (update as any)[editorOption.name]);
(options as any)[editorOption.name] = result.newValue;
changed = changed || result.didChange;
}
}
return changed;
}
}
/**
......@@ -382,43 +401,17 @@ export abstract class CommonEditorConfiguration extends Disposable implements IC
return EditorConfiguration2.computeOptions(this._validatedOptions, env);
}
private static _subsetEquals(base: { [key: string]: any }, subset: { [key: string]: any }): boolean {
for (const key in subset) {
if (hasOwnProperty.call(subset, key)) {
const subsetValue = subset[key];
const baseValue = base[key];
if (baseValue === subsetValue) {
continue;
}
if (Array.isArray(baseValue) && Array.isArray(subsetValue)) {
if (!arrays.equals(baseValue, subsetValue)) {
return false;
}
continue;
}
if (baseValue && typeof baseValue === 'object' && subsetValue && typeof subsetValue === 'object') {
if (!this._subsetEquals(baseValue, subsetValue)) {
return false;
}
continue;
}
return false;
}
}
return true;
}
public updateOptions(_newOptions: Readonly<IEditorOptions>): void {
if (typeof _newOptions === 'undefined') {
return;
}
const newOptions = deepCloneAndMigrateOptions(_newOptions);
if (CommonEditorConfiguration._subsetEquals(this._rawOptions, newOptions)) {
const didChange = EditorConfiguration2.applyUpdate(this._rawOptions, newOptions);
if (!didChange) {
return;
}
this._rawOptions = objects.mixin(this._rawOptions, newOptions || {});
this._readOptions = EditorConfiguration2.readOptions(this._rawOptions);
this._validatedOptions = EditorConfiguration2.validateOptions(this._readOptions);
......
......@@ -12,6 +12,8 @@ import { USUAL_WORD_SEPARATORS } from 'vs/editor/common/model/wordHelper';
import { AccessibilitySupport } from 'vs/platform/accessibility/common/accessibility';
import { IConfigurationPropertySchema } from 'vs/platform/configuration/common/configurationRegistry';
import { IJSONSchema } from 'vs/base/common/jsonSchema';
import * as arrays from 'vs/base/common/arrays';
import * as objects from 'vs/base/common/objects';
//#region typed options
......@@ -806,6 +808,11 @@ export interface IEditorOption<K1 extends EditorOption, V> {
* @internal
*/
compute(env: IEnvironmentalOptions, options: IComputedEditorOptions, value: V): V;
/**
* Might modify `value`.
*/
applyUpdate(value: V, update: V): ApplyUpdateResult<V>;
}
type PossibleKeyName0<V> = { [K in keyof IEditorOptions]: IEditorOptions[K] extends V | undefined ? K : never }[keyof IEditorOptions];
......@@ -828,6 +835,10 @@ abstract class BaseEditorOption<K1 extends EditorOption, V> implements IEditorOp
this.schema = schema;
}
public applyUpdate(value: V, update: V): ApplyUpdateResult<V> {
return applyUpdate(value, update);
}
public abstract validate(input: any): V;
public compute(env: IEnvironmentalOptions, options: IComputedEditorOptions, value: V): V {
......@@ -835,6 +846,34 @@ abstract class BaseEditorOption<K1 extends EditorOption, V> implements IEditorOp
}
}
export class ApplyUpdateResult<T> {
constructor(
public readonly newValue: T,
public readonly didChange: boolean
) { }
}
function applyUpdate<T>(value: T, update: T): ApplyUpdateResult<T> {
if (typeof value !== 'object' || typeof update !== 'object' || !value || !update) {
return new ApplyUpdateResult(update, value !== update);
}
if (Array.isArray(value) || Array.isArray(update)) {
const arrayEquals = Array.isArray(value) && Array.isArray(update) && arrays.equals(value, update);
return new ApplyUpdateResult(update, arrayEquals);
}
let didChange = false;
for (let key in update) {
if ((update as T & object).hasOwnProperty(key)) {
const result = applyUpdate(value[key], update[key]);
if (result.didChange) {
value[key] = result.newValue;
didChange = true;
}
}
}
return new ApplyUpdateResult(value, didChange);
}
/**
* @internal
*/
......@@ -853,6 +892,10 @@ abstract class ComputedEditorOption<K1 extends EditorOption, V> implements IEdit
this.deps = deps;
}
public applyUpdate(value: V, update: V): ApplyUpdateResult<V> {
return applyUpdate(value, update);
}
public validate(input: any): V {
return this.defaultValue;
}
......@@ -874,6 +917,10 @@ class SimpleEditorOption<K1 extends EditorOption, V> implements IEditorOption<K1
this.schema = schema;
}
public applyUpdate(value: V, update: V): ApplyUpdateResult<V> {
return applyUpdate(value, update);
}
public validate(input: any): V {
if (typeof input === 'undefined') {
return this.defaultValue;
......@@ -3265,9 +3312,9 @@ export interface IUnicodeHighlightOptions {
ambiguousCharacters?: boolean;
includeComments?: boolean | InUntrustedWorkspace;
/**
* A list of allowed code points in a single string.
* A map of allowed characters (true: allowed).
*/
allowedCharacters?: string;
allowedCharacters?: Record<string, true>;
}
/**
......@@ -3293,7 +3340,7 @@ class UnicodeHighlight extends BaseEditorOption<EditorOption.unicodeHighlighting
invisibleCharacters: true,
ambiguousCharacters: true,
includeComments: inUntrustedWorkspace,
allowedCharacters: '',
allowedCharacters: {},
};
super(
......@@ -3327,14 +3374,34 @@ class UnicodeHighlight extends BaseEditorOption<EditorOption.unicodeHighlighting
},
[unicodeHighlightConfigKeys.allowedCharacters]: {
restricted: true,
type: 'string',
type: 'object',
default: defaults.allowedCharacters,
description: nls.localize('unicodeHighlight.allowedCharacters', "Defines allowed characters that are not being highlighted.")
description: nls.localize('unicodeHighlight.allowedCharacters', "Defines allowed characters that are not being highlighted."),
additionalProperties: {
type: 'boolean'
}
},
}
);
}
public override applyUpdate(value: Required<Readonly<IUnicodeHighlightOptions>>, update: Required<Readonly<IUnicodeHighlightOptions>>): ApplyUpdateResult<Required<Readonly<IUnicodeHighlightOptions>>> {
let didChange = false;
if (update.allowedCharacters) {
// Treat allowedCharacters atomically
if (!objects.equals(value.allowedCharacters, update.allowedCharacters)) {
value = { ...value, allowedCharacters: update.allowedCharacters };
didChange = true;
}
}
const result = super.applyUpdate(value, update);
if (didChange) {
return new ApplyUpdateResult(result.newValue, true);
}
return result;
}
public validate(_input: any): InternalUnicodeHighlightOptions {
if (!_input || typeof _input !== 'object') {
return this.defaultValue;
......@@ -3345,16 +3412,22 @@ class UnicodeHighlight extends BaseEditorOption<EditorOption.unicodeHighlighting
invisibleCharacters: boolean(input.invisibleCharacters, this.defaultValue.invisibleCharacters),
ambiguousCharacters: boolean(input.ambiguousCharacters, this.defaultValue.ambiguousCharacters),
includeComments: primitiveSet<boolean | InUntrustedWorkspace>(input.includeComments, inUntrustedWorkspace, [true, false, inUntrustedWorkspace]),
allowedCharacters: string(input.allowedCharacters, ''),
allowedCharacters: this.validateAllowedCharacters(_input.allowedCharacters, this.defaultValue.allowedCharacters),
};
}
}
function string(value: unknown, defaultValue: string): string {
if (typeof value !== 'string') {
return defaultValue;
private validateAllowedCharacters(map: unknown, defaultValue: Record<string, true>): Record<string, true> {
if ((typeof map !== 'object') || !map) {
return defaultValue;
}
const result: Record<string, true> = {};
for (const [key, value] of Object.entries(map)) {
if (value === true) {
result[key] = true;
}
}
return result;
}
return value;
}
//#endregion
......
......@@ -160,7 +160,7 @@ export class UnicodeHighlighter extends Disposable implements IEditorContributio
ambiguousCharacters: options.ambiguousCharacters,
invisibleCharacters: options.invisibleCharacters,
includeComments: options.includeComments,
allowedCodePoints: Array.from(options.allowedCharacters).map(c => c.codePointAt(0)!),
allowedCodePoints: Object.keys(options.allowedCharacters).map(c => c.codePointAt(0)!),
};
if (this._editorWorkerService.canComputeUnicodeHighlights(this._editor.getModel().uri)) {
......@@ -191,7 +191,7 @@ function resolveOptions(trusted: boolean, options: InternalUnicodeHighlightOptio
ambiguousCharacters: options.ambiguousCharacters,
invisibleCharacters: options.invisibleCharacters,
includeComments: options.includeComments === inUntrustedWorkspace ? !trusted : options.includeComments,
allowedCharacters: options.allowedCharacters ?? [],
allowedCharacters: options.allowedCharacters ?? {},
};
}
......@@ -640,18 +640,7 @@ export class ShowExcludeOptions extends EditorAction {
const options: ExtendedOptions[] = [
{
label: getExcludeCharFromBeingHighlightedLabel(codePoint),
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);
}
run: () => excludeCharFromBeingHighlighted(configurationService, [codePoint])
},
];
......@@ -681,6 +670,23 @@ export class ShowExcludeOptions extends EditorAction {
}
}
async function excludeCharFromBeingHighlighted(configurationService: IConfigurationService, charCodes: number[]) {
const existingValue = configurationService.getValue(unicodeHighlightConfigKeys.allowedCharacters);
let value: Record<string, boolean>;
if ((typeof existingValue === 'object') && existingValue) {
value = existingValue as any;
} else {
value = {};
}
for (const charCode of charCodes) {
value[String.fromCodePoint(charCode)] = true;
}
await configurationService.updateValue(unicodeHighlightConfigKeys.allowedCharacters, value, ConfigurationTarget.USER);
}
function expectNever(value: never) {
throw new Error(`Unexpected value: ${value}`);
}
......
......@@ -10,7 +10,7 @@ import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
import { OpenerService } from 'vs/editor/browser/services/openerService';
import { DiffNavigator, IDiffNavigator } from 'vs/editor/browser/widget/diffNavigator';
import { EditorOptions, ConfigurationChangedEvent } from 'vs/editor/common/config/editorOptions';
import { EditorOptions, ConfigurationChangedEvent, ApplyUpdateResult } from 'vs/editor/common/config/editorOptions';
import { BareFontInfo, FontInfo } from 'vs/editor/common/config/fontInfo';
import { Token } from 'vs/editor/common/core/token';
import { IEditor, EditorType } from 'vs/editor/common/editorCommon';
......@@ -393,6 +393,7 @@ export function createMonacoEditorAPI(): typeof monaco.editor {
FontInfo: <any>FontInfo,
TextModelResolvedOptions: <any>TextModelResolvedOptions,
FindMatch: <any>FindMatch,
ApplyUpdateResult: <any>ApplyUpdateResult,
// vars
EditorType: EditorType,
......
......@@ -3378,6 +3378,16 @@ declare namespace monaco.editor {
readonly id: K1;
readonly name: string;
defaultValue: V;
/**
* Might modify `value`.
*/
applyUpdate(value: V, update: V): ApplyUpdateResult<V>;
}
export class ApplyUpdateResult<T> {
readonly newValue: T;
readonly didChange: boolean;
constructor(newValue: T, didChange: boolean);
}
/**
......@@ -3874,9 +3884,9 @@ declare namespace monaco.editor {
ambiguousCharacters?: boolean;
includeComments?: boolean | InUntrustedWorkspace;
/**
* A list of allowed code points in a single string.
* A map of allowed characters (true: allowed).
*/
allowedCharacters?: string;
allowedCharacters?: Record<string, true>;
}
export interface IInlineSuggestOptions {
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册