[themes] opt-in to semanticHighlighting
This commit is contained in:
parent
bf8fb5f814
commit
41a4adb47b
|
@ -434,5 +434,6 @@
|
||||||
"terminal.ansiBrightMagenta": "#d778ff",
|
"terminal.ansiBrightMagenta": "#d778ff",
|
||||||
"terminal.ansiBrightCyan": "#78ffff",
|
"terminal.ansiBrightCyan": "#78ffff",
|
||||||
"terminal.ansiBrightWhite": "#ffffff"
|
"terminal.ansiBrightWhite": "#ffffff"
|
||||||
}
|
},
|
||||||
|
"semanticHighlighting": true
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,5 +18,6 @@
|
||||||
"menu.foreground": "#CCCCCC",
|
"menu.foreground": "#CCCCCC",
|
||||||
"statusBarItem.remoteForeground": "#FFF",
|
"statusBarItem.remoteForeground": "#FFF",
|
||||||
"statusBarItem.remoteBackground": "#16825D"
|
"statusBarItem.remoteBackground": "#16825D"
|
||||||
}
|
},
|
||||||
|
"semanticHighlighting": true
|
||||||
}
|
}
|
|
@ -337,5 +337,6 @@
|
||||||
"foreground": "#569cd6"
|
"foreground": "#569cd6"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
|
"semanticHighlighting": true
|
||||||
}
|
}
|
|
@ -18,5 +18,6 @@
|
||||||
"settings.numberInputBorder": "#CECECE",
|
"settings.numberInputBorder": "#CECECE",
|
||||||
"statusBarItem.remoteForeground": "#FFF",
|
"statusBarItem.remoteForeground": "#FFF",
|
||||||
"statusBarItem.remoteBackground": "#16825D"
|
"statusBarItem.remoteBackground": "#16825D"
|
||||||
}
|
},
|
||||||
|
"semanticHighlighting": true
|
||||||
}
|
}
|
||||||
|
|
|
@ -394,5 +394,6 @@
|
||||||
"foreground": "#dc3958"
|
"foreground": "#dc3958"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
|
"semanticHighlighting": true
|
||||||
}
|
}
|
||||||
|
|
|
@ -572,5 +572,6 @@
|
||||||
"foreground": "#c7444a"
|
"foreground": "#c7444a"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
|
"semanticHighlighting": true
|
||||||
}
|
}
|
||||||
|
|
|
@ -476,5 +476,6 @@
|
||||||
"foreground": "#FD971F"
|
"foreground": "#FD971F"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
|
"semanticHighlighting": true
|
||||||
}
|
}
|
||||||
|
|
|
@ -494,5 +494,6 @@
|
||||||
"walkThrough.embeddedEditorBackground": "#00000014",
|
"walkThrough.embeddedEditorBackground": "#00000014",
|
||||||
"editorIndentGuide.background": "#aaaaaa60",
|
"editorIndentGuide.background": "#aaaaaa60",
|
||||||
"editorIndentGuide.activeBackground": "#777777b0"
|
"editorIndentGuide.activeBackground": "#777777b0"
|
||||||
}
|
},
|
||||||
|
"semanticHighlighting": true
|
||||||
}
|
}
|
||||||
|
|
|
@ -385,5 +385,6 @@
|
||||||
"foreground": "#ec0d1e"
|
"foreground": "#ec0d1e"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
|
"semanticHighlighting": true
|
||||||
}
|
}
|
||||||
|
|
|
@ -477,5 +477,6 @@
|
||||||
"terminal.ansiBrightMagenta": "#6c71c4",
|
"terminal.ansiBrightMagenta": "#6c71c4",
|
||||||
"terminal.ansiBrightCyan": "#93a1a1",
|
"terminal.ansiBrightCyan": "#93a1a1",
|
||||||
"terminal.ansiBrightWhite": "#fdf6e3"
|
"terminal.ansiBrightWhite": "#fdf6e3"
|
||||||
}
|
},
|
||||||
|
"semanticHighlighting": true
|
||||||
}
|
}
|
||||||
|
|
|
@ -484,5 +484,6 @@
|
||||||
|
|
||||||
// Interactive Playground
|
// Interactive Playground
|
||||||
"walkThrough.embeddedEditorBackground": "#00000014"
|
"walkThrough.embeddedEditorBackground": "#00000014"
|
||||||
}
|
},
|
||||||
|
"semanticHighlighting": true
|
||||||
}
|
}
|
||||||
|
|
|
@ -255,5 +255,6 @@
|
||||||
"foreground": "#b267e6"
|
"foreground": "#b267e6"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
|
"semanticHighlighting": true
|
||||||
}
|
}
|
||||||
|
|
|
@ -466,15 +466,16 @@ class SemanticColoringFeature extends Disposable {
|
||||||
|
|
||||||
private _watchers: Record<string, ModelSemanticColoring>;
|
private _watchers: Record<string, ModelSemanticColoring>;
|
||||||
private _semanticStyling: SemanticStyling;
|
private _semanticStyling: SemanticStyling;
|
||||||
private _configurationService: IConfigurationService;
|
|
||||||
|
|
||||||
constructor(modelService: IModelService, themeService: IThemeService, configurationService: IConfigurationService, logService: ILogService) {
|
constructor(modelService: IModelService, themeService: IThemeService, configurationService: IConfigurationService, logService: ILogService) {
|
||||||
super();
|
super();
|
||||||
this._configurationService = configurationService;
|
|
||||||
this._watchers = Object.create(null);
|
this._watchers = Object.create(null);
|
||||||
this._semanticStyling = this._register(new SemanticStyling(themeService, logService));
|
this._semanticStyling = this._register(new SemanticStyling(themeService, logService));
|
||||||
|
|
||||||
const isSemanticColoringEnabled = (model: ITextModel) => {
|
const isSemanticColoringEnabled = (model: ITextModel) => {
|
||||||
|
if (!themeService.getColorTheme().semanticHighlighting) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
const options = configurationService.getValue<IEditorSemanticHighlightingOptions>(SemanticColoringFeature.SETTING_ID, { overrideIdentifier: model.getLanguageIdentifier().language, resource: model.uri });
|
const options = configurationService.getValue<IEditorSemanticHighlightingOptions>(SemanticColoringFeature.SETTING_ID, { overrideIdentifier: model.getLanguageIdentifier().language, resource: model.uri });
|
||||||
return options && options.enabled;
|
return options && options.enabled;
|
||||||
};
|
};
|
||||||
|
@ -485,6 +486,20 @@ class SemanticColoringFeature extends Disposable {
|
||||||
modelSemanticColoring.dispose();
|
modelSemanticColoring.dispose();
|
||||||
delete this._watchers[model.uri.toString()];
|
delete this._watchers[model.uri.toString()];
|
||||||
};
|
};
|
||||||
|
const handleSettingOrThemeChange = () => {
|
||||||
|
for (let model of modelService.getModels()) {
|
||||||
|
const curr = this._watchers[model.uri.toString()];
|
||||||
|
if (isSemanticColoringEnabled(model)) {
|
||||||
|
if (!curr) {
|
||||||
|
register(model);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (curr) {
|
||||||
|
deregister(model, curr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
this._register(modelService.onModelAdded((model) => {
|
this._register(modelService.onModelAdded((model) => {
|
||||||
if (isSemanticColoringEnabled(model)) {
|
if (isSemanticColoringEnabled(model)) {
|
||||||
register(model);
|
register(model);
|
||||||
|
@ -496,22 +511,12 @@ class SemanticColoringFeature extends Disposable {
|
||||||
deregister(model, curr);
|
deregister(model, curr);
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
this._configurationService.onDidChangeConfiguration(e => {
|
this._register(configurationService.onDidChangeConfiguration(e => {
|
||||||
if (e.affectsConfiguration(SemanticColoringFeature.SETTING_ID)) {
|
if (e.affectsConfiguration(SemanticColoringFeature.SETTING_ID)) {
|
||||||
for (let model of modelService.getModels()) {
|
handleSettingOrThemeChange();
|
||||||
const curr = this._watchers[model.uri.toString()];
|
|
||||||
if (isSemanticColoringEnabled(model)) {
|
|
||||||
if (!curr) {
|
|
||||||
register(model);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (curr) {
|
|
||||||
deregister(model, curr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
}));
|
||||||
|
this._register(themeService.onDidColorThemeChange(handleSettingOrThemeChange));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -138,6 +138,8 @@ class StandaloneTheme implements IStandaloneTheme {
|
||||||
public get tokenColorMap(): string[] {
|
public get tokenColorMap(): string[] {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public readonly semanticHighlighting = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
function isBuiltinTheme(themeName: string): themeName is BuiltinTheme {
|
function isBuiltinTheme(themeName: string): themeName is BuiltinTheme {
|
||||||
|
|
|
@ -60,6 +60,8 @@ suite('TokenizationSupport2Adapter', () => {
|
||||||
return undefined;
|
return undefined;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
semanticHighlighting: false,
|
||||||
|
|
||||||
tokenColorMap: []
|
tokenColorMap: []
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -112,6 +112,11 @@ export interface IColorTheme {
|
||||||
* List of all colors used with tokens. <code>getTokenStyleMetadata</code> references the colors by index into this list.
|
* List of all colors used with tokens. <code>getTokenStyleMetadata</code> references the colors by index into this list.
|
||||||
*/
|
*/
|
||||||
readonly tokenColorMap: string[];
|
readonly tokenColorMap: string[];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines whether semantic highlighting should be enabled for the theme.
|
||||||
|
*/
|
||||||
|
readonly semanticHighlighting: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IFileIconTheme {
|
export interface IFileIconTheme {
|
||||||
|
|
|
@ -28,6 +28,8 @@ export class TestColorTheme implements IColorTheme {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
readonly semanticHighlighting = false;
|
||||||
|
|
||||||
get tokenColorMap(): string[] {
|
get tokenColorMap(): string[] {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
|
@ -260,6 +260,9 @@ class InspectEditorTokensWidget extends Disposable implements IContentWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
private _isSemanticColoringEnabled() {
|
private _isSemanticColoringEnabled() {
|
||||||
|
if (!this._themeService.getColorTheme().semanticHighlighting) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
const options = this._configurationService.getValue<IEditorSemanticHighlightingOptions>('editor.semanticHighlighting', { overrideIdentifier: this._model.getLanguageIdentifier().language, resource: this._model.uri });
|
const options = this._configurationService.getValue<IEditorSemanticHighlightingOptions>('editor.semanticHighlighting', { overrideIdentifier: this._model.getLanguageIdentifier().language, resource: this._model.uri });
|
||||||
return options && options.enabled;
|
return options && options.enabled;
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,8 +21,8 @@ function getMockTheme(type: ThemeType): IColorTheme {
|
||||||
getColor: (colorId: ColorIdentifier): Color | undefined => themingRegistry.resolveDefaultColor(colorId, theme),
|
getColor: (colorId: ColorIdentifier): Color | undefined => themingRegistry.resolveDefaultColor(colorId, theme),
|
||||||
defines: () => true,
|
defines: () => true,
|
||||||
getTokenStyleMetadata: () => undefined,
|
getTokenStyleMetadata: () => undefined,
|
||||||
tokenColorMap: []
|
tokenColorMap: [],
|
||||||
|
semanticHighlighting: false
|
||||||
};
|
};
|
||||||
return theme;
|
return theme;
|
||||||
}
|
}
|
||||||
|
|
|
@ -59,6 +59,9 @@ export class ColorThemeData implements IWorkbenchColorTheme {
|
||||||
watch?: boolean;
|
watch?: boolean;
|
||||||
extensionData?: ExtensionData;
|
extensionData?: ExtensionData;
|
||||||
|
|
||||||
|
private themeSemanticHighlighting: boolean;
|
||||||
|
private customSemanticHighlightSupport: boolean | undefined;
|
||||||
|
|
||||||
private themeTokenColors: ITextMateThemingRule[] = [];
|
private themeTokenColors: ITextMateThemingRule[] = [];
|
||||||
private customTokenColors: ITextMateThemingRule[] = [];
|
private customTokenColors: ITextMateThemingRule[] = [];
|
||||||
private colorMap: IColorMap = {};
|
private colorMap: IColorMap = {};
|
||||||
|
@ -78,6 +81,12 @@ export class ColorThemeData implements IWorkbenchColorTheme {
|
||||||
this.label = label;
|
this.label = label;
|
||||||
this.settingsId = settingsId;
|
this.settingsId = settingsId;
|
||||||
this.isLoaded = false;
|
this.isLoaded = false;
|
||||||
|
this.themeSemanticHighlighting = false;
|
||||||
|
this.customSemanticHighlightSupport = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
get semanticHighlighting(): boolean {
|
||||||
|
return this.customSemanticHighlightSupport !== undefined ? this.customSemanticHighlightSupport : this.themeSemanticHighlighting;
|
||||||
}
|
}
|
||||||
|
|
||||||
get tokenColors(): ITextMateThemingRule[] {
|
get tokenColors(): ITextMateThemingRule[] {
|
||||||
|
@ -360,6 +369,7 @@ export class ColorThemeData implements IWorkbenchColorTheme {
|
||||||
|
|
||||||
public setCustomTokenColors(customTokenColors: ITokenColorCustomizations) {
|
public setCustomTokenColors(customTokenColors: ITokenColorCustomizations) {
|
||||||
this.customTokenColors = [];
|
this.customTokenColors = [];
|
||||||
|
this.customSemanticHighlightSupport = undefined;
|
||||||
|
|
||||||
// first add the non-theme specific settings
|
// first add the non-theme specific settings
|
||||||
this.addCustomTokenColors(customTokenColors);
|
this.addCustomTokenColors(customTokenColors);
|
||||||
|
@ -411,6 +421,9 @@ export class ColorThemeData implements IWorkbenchColorTheme {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (customTokenColors.semanticHighlighting !== undefined) {
|
||||||
|
this.customSemanticHighlightSupport = customTokenColors.semanticHighlighting;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public ensureLoaded(extensionResourceLoaderService: IExtensionResourceLoaderService): Promise<void> {
|
public ensureLoaded(extensionResourceLoaderService: IExtensionResourceLoaderService): Promise<void> {
|
||||||
|
@ -431,13 +444,15 @@ export class ColorThemeData implements IWorkbenchColorTheme {
|
||||||
const result = {
|
const result = {
|
||||||
colors: {},
|
colors: {},
|
||||||
textMateRules: [],
|
textMateRules: [],
|
||||||
stylingRules: undefined
|
stylingRules: undefined,
|
||||||
|
semanticHighlighting: false
|
||||||
};
|
};
|
||||||
return _loadColorTheme(extensionResourceLoaderService, this.location, result).then(_ => {
|
return _loadColorTheme(extensionResourceLoaderService, this.location, result).then(_ => {
|
||||||
this.isLoaded = true;
|
this.isLoaded = true;
|
||||||
this.tokenStylingRules = result.stylingRules;
|
this.tokenStylingRules = result.stylingRules;
|
||||||
this.colorMap = result.colors;
|
this.colorMap = result.colors;
|
||||||
this.themeTokenColors = result.textMateRules;
|
this.themeTokenColors = result.textMateRules;
|
||||||
|
this.themeSemanticHighlighting = result.semanticHighlighting;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -562,7 +577,7 @@ function toCSSSelector(extensionId: string, path: string) {
|
||||||
return str;
|
return str;
|
||||||
}
|
}
|
||||||
|
|
||||||
function _loadColorTheme(extensionResourceLoaderService: IExtensionResourceLoaderService, themeLocation: URI, result: { textMateRules: ITextMateThemingRule[], colors: IColorMap, stylingRules: TokenStylingRule[] | undefined }): Promise<any> {
|
function _loadColorTheme(extensionResourceLoaderService: IExtensionResourceLoaderService, themeLocation: URI, result: { textMateRules: ITextMateThemingRule[], colors: IColorMap, stylingRules: TokenStylingRule[] | undefined, semanticHighlighting: boolean }): Promise<any> {
|
||||||
if (resources.extname(themeLocation) === '.json') {
|
if (resources.extname(themeLocation) === '.json') {
|
||||||
return extensionResourceLoaderService.readExtensionResource(themeLocation).then(content => {
|
return extensionResourceLoaderService.readExtensionResource(themeLocation).then(content => {
|
||||||
let errors: Json.ParseError[] = [];
|
let errors: Json.ParseError[] = [];
|
||||||
|
@ -581,6 +596,7 @@ function _loadColorTheme(extensionResourceLoaderService: IExtensionResourceLoade
|
||||||
convertSettings(contentValue.settings, result);
|
convertSettings(contentValue.settings, result);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
result.semanticHighlighting = result.semanticHighlighting || contentValue.semanticHighlighting;
|
||||||
let colors = contentValue.colors;
|
let colors = contentValue.colors;
|
||||||
if (colors) {
|
if (colors) {
|
||||||
if (typeof colors !== 'object') {
|
if (typeof colors !== 'object') {
|
||||||
|
@ -605,10 +621,10 @@ function _loadColorTheme(extensionResourceLoaderService: IExtensionResourceLoade
|
||||||
return Promise.reject(new Error(nls.localize({ key: 'error.invalidformat.tokenColors', comment: ['{0} will be replaced by a path. Values in quotes should not be translated.'] }, "Problem parsing color theme file: {0}. Property 'tokenColors' should be either an array specifying colors or a path to a TextMate theme file", themeLocation.toString())));
|
return Promise.reject(new Error(nls.localize({ key: 'error.invalidformat.tokenColors', comment: ['{0} will be replaced by a path. Values in quotes should not be translated.'] }, "Problem parsing color theme file: {0}. Property 'tokenColors' should be either an array specifying colors or a path to a TextMate theme file", themeLocation.toString())));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let tokenStylingRules = contentValue.tokenStylingRules;
|
// let tokenStylingRules = contentValue.tokenStylingRules;
|
||||||
if (tokenStylingRules && typeof tokenStylingRules === 'object') {
|
// if (tokenStylingRules && typeof tokenStylingRules === 'object') {
|
||||||
result.stylingRules = readCustomTokenStyleRules(tokenStylingRules, result.stylingRules);
|
// result.stylingRules = readCustomTokenStyleRules(tokenStylingRules, result.stylingRules);
|
||||||
}
|
// }
|
||||||
return null;
|
return null;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -222,6 +222,10 @@ const colorThemeSchema: IJSONSchema = {
|
||||||
$ref: textmateColorsSchemaId
|
$ref: textmateColorsSchemaId
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
semanticHighlighting: {
|
||||||
|
type: 'boolean',
|
||||||
|
description: nls.localize('schema.supportsSemanticHighlighting', 'Whether semantic highlighting should be enabled for this theme.')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -132,6 +132,10 @@ const tokenColorSchema: IJSONSchema = {
|
||||||
textMateRules: {
|
textMateRules: {
|
||||||
description: nls.localize('editorColors.textMateRules', 'Sets colors and styles using textmate theming rules (advanced).'),
|
description: nls.localize('editorColors.textMateRules', 'Sets colors and styles using textmate theming rules (advanced).'),
|
||||||
$ref: textmateColorsSchemaId
|
$ref: textmateColorsSchemaId
|
||||||
|
},
|
||||||
|
semanticHighlighting: {
|
||||||
|
description: nls.localize('editorColors.semanticHighlighting', 'Whether semantic highlighting should be enabled for this theme.'),
|
||||||
|
type: 'boolean'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -154,6 +158,7 @@ const tokenColorCustomizationConfiguration: IConfigurationNode = {
|
||||||
[ThemeSettings.TOKEN_COLOR_CUSTOMIZATIONS_EXPERIMENTAL]: experimentalTokenStylingCustomizationSchema
|
[ThemeSettings.TOKEN_COLOR_CUSTOMIZATIONS_EXPERIMENTAL]: experimentalTokenStylingCustomizationSchema
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
configurationRegistry.registerConfiguration(tokenColorCustomizationConfiguration);
|
configurationRegistry.registerConfiguration(tokenColorCustomizationConfiguration);
|
||||||
|
|
||||||
export function updateColorThemeConfigurationSchemas(themes: IWorkbenchColorTheme[]) {
|
export function updateColorThemeConfigurationSchemas(themes: IWorkbenchColorTheme[]) {
|
||||||
|
|
|
@ -97,7 +97,7 @@ export interface IColorCustomizations {
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ITokenColorCustomizations {
|
export interface ITokenColorCustomizations {
|
||||||
[groupIdOrThemeSettingsId: string]: string | ITokenColorizationSetting | ITokenColorCustomizations | undefined | ITextMateThemingRule[];
|
[groupIdOrThemeSettingsId: string]: string | ITokenColorizationSetting | ITokenColorCustomizations | undefined | ITextMateThemingRule[] | boolean;
|
||||||
comments?: string | ITokenColorizationSetting;
|
comments?: string | ITokenColorizationSetting;
|
||||||
strings?: string | ITokenColorizationSetting;
|
strings?: string | ITokenColorizationSetting;
|
||||||
numbers?: string | ITokenColorizationSetting;
|
numbers?: string | ITokenColorizationSetting;
|
||||||
|
@ -106,6 +106,7 @@ export interface ITokenColorCustomizations {
|
||||||
functions?: string | ITokenColorizationSetting;
|
functions?: string | ITokenColorizationSetting;
|
||||||
variables?: string | ITokenColorizationSetting;
|
variables?: string | ITokenColorizationSetting;
|
||||||
textMateRules?: ITextMateThemingRule[];
|
textMateRules?: ITextMateThemingRule[];
|
||||||
|
semanticHighlighting?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IExperimentalTokenStyleCustomizations {
|
export interface IExperimentalTokenStyleCustomizations {
|
||||||
|
|
Loading…
Reference in a new issue