Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix caching issue for VS Code Remote Containers #649

Merged
merged 9 commits into from Feb 28, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions .eslintrc.js
Expand Up @@ -40,6 +40,7 @@ module.exports = {
"@typescript-eslint/type-annotation-spacing": "error",
"camelcase": "error",
"eol-last": "error",
"prefer-const": "error",
"eqeqeq": [
"error",
"smart"
Expand Down
2 changes: 1 addition & 1 deletion .vscode/launch.json
Expand Up @@ -26,7 +26,7 @@
"runtimeExecutable": "${execPath}",
"args": [
"--extensionDevelopmentPath=${workspaceFolder}",
"--extensionTestsPath=${workspaceFolder}/out/test/suite/index"
"--extensionTestsPath=${workspaceFolder}/out/test/spec/index"
],
"outFiles": [
"${workspaceFolder}/out/test/**/*.js"
Expand Down
3 changes: 2 additions & 1 deletion package.json
Expand Up @@ -31,7 +31,8 @@
"customization"
],
"extensionKind": [
"ui"
"ui",
"workspace"
],
"activationEvents": [
"*"
Expand Down
2 changes: 1 addition & 1 deletion src/commands/iconPacks.ts
Expand Up @@ -52,7 +52,7 @@ const getActiveIconPack = (): string => {
/** Get all packs that can be used in this icon theme. */
export const getAllIconPacks = () => {
const packs: string[] = [];
for (let item in IconPack) {
for (const item in IconPack) {
if (isNaN(Number(item))) {
packs.push(IconPack[item].toLowerCase());
}
Expand Down
21 changes: 9 additions & 12 deletions src/helpers/changeDetection.ts
Expand Up @@ -6,12 +6,7 @@ import { getObjectPropertyValue, setObjectPropertyValue } from './objects';

/** Compare the workspace and the user configurations with the current setup of the icons. */
export const detectConfigChanges = () => {
const configs = Object.keys(getConfigProperties())
.map(c => c.split('.').slice(1).join('.'))
// remove configurable notification messages
.filter((c) => !/show(Welcome|Update|Reload)Message/g.test(c));

const changes = compareConfigs(configs);
const changes = compareConfigs();

// if there's nothing to update
if (Object.keys(changes.updatedConfigs).length === 0) return;
Expand All @@ -21,9 +16,7 @@ export const detectConfigChanges = () => {
createIconFile(changes.updatedConfigs, changes.updatedJSONConfig);

// check if a reload of the editor is required
const configRequiresReload = ['opacity', 'saturation', 'folders.color'];
const reloadRequired = configRequiresReload.some(r => getObjectPropertyValue(changes.updatedConfigs, r) !== undefined);
if (!versioning.checkVersionSupport('1.31.0') || reloadRequired) {
if (!versioning.checkVersionSupport('1.31.0')) {
promptToReload();
}
} catch (error) {
Expand All @@ -34,15 +27,19 @@ export const detectConfigChanges = () => {
/**
* Compares a specific configuration in the settings with a current configuration state.
* The current configuration state is read from the icons json file.
* @param configs List of configuration names
* @returns List of configurations that needs to be updated.
*/
const compareConfigs = (configs: string[]): { updatedConfigs: IconJsonOptions; updatedJSONConfig: IconJsonOptions } => {
const compareConfigs = (): { updatedConfigs: IconJsonOptions; updatedJSONConfig: IconJsonOptions } => {
const configs = Object.keys(getConfigProperties())
.map(c => c.split('.').slice(1).join('.'))
// remove configurable notification messages
.filter((c) => !/show(Welcome|Update|Reload)Message/g.test(c));

const json = getMaterialIconsJSON();
return configs.reduce((result, configName) => {
try {
const themeConfig = getThemeConfig(configName);
const configValue = themeConfig.globalValue !== undefined ? themeConfig.globalValue : themeConfig.defaultValue;
const configValue = themeConfig.globalValue ?? themeConfig.defaultValue;
const currentState = getObjectPropertyValue(json.options, configName);

if (JSON.stringify(configValue) !== JSON.stringify(currentState)) {
Expand Down
34 changes: 34 additions & 0 deletions src/helpers/fileConfig.ts
@@ -0,0 +1,34 @@
import { getDefaultIconOptions } from '../icons';
import { IconJsonOptions } from '../models';

/**
* Generate a config string that is appended to each icon file name.
* @param config Icon Configuration object
*/
export const getFileConfigString = (options: IconJsonOptions) => {
try {
const defaults = getDefaultIconOptions();
let fileConfigString = '';
if (options.saturation !== defaults.saturation ||
options.opacity !== defaults.opacity ||
options.folders.color !== defaults.folders.color
) {
fileConfigString += `~${getHash(JSON.stringify(options))}`;
}
return fileConfigString;

} catch (error) {
console.error(error);
}
};

const getHash = (value: string) => {
let hash = 0, i, chr;
if (value.length === 0) return hash;
for (i = 0; i < value.length; i++) {
chr = value.charCodeAt(i);
hash = ((hash << 5) - hash) + chr;
hash |= 0; // Convert to 32bit integer
}
return hash;
};
35 changes: 14 additions & 21 deletions src/helpers/objects.ts
@@ -1,32 +1,30 @@
/**
* Get the nested properties of an object
* This solution is lighter than the lodash get-version and works fine for the translations.
* This solution is lighter than the lodash get-version.
* Source: http://stackoverflow.com/a/6491621/6942210
*/
export const getObjectPropertyValue = (obj: Object, path: string) => {
// convert indexes to properties
path = path.replace(/\[(\w+)\]/g, '.$1');

// strip a leading dot
path = path.replace(/^\./, '');

// separate paths in array
let pathArray = path.split('.');
const pathArray = path
.replace(/\[(\w+)\]/g, '.$1') // convert indexes to properties
.replace(/^\./, '') // strip a leading dot
.split('.'); // separate paths in array

/** Avoid errors in the getValue function. */
const isObject = (object) => {
return object === Object(object);
};

let result = JSON.parse(JSON.stringify(obj));

for (let i = 0; i < pathArray.length; ++i) {
let k = pathArray[i];
if (isObject(obj) && k in obj) {
obj = obj[k];
const k = pathArray[i];
if (isObject(result) && k in result) {
result = result[k];
} else {
return;
}
}
return obj;
return result;
};

/**
Expand All @@ -36,19 +34,14 @@ export const getObjectPropertyValue = (obj: Object, path: string) => {
* @param value Value to be set for the given property
* Source: https://stackoverflow.com/a/13719799/6942210
*/
export const setObjectPropertyValue = (obj: Object, path, value) => {
export const setObjectPropertyValue = (obj: Object, path: string | string[], value: any) => {
if (typeof path === 'string') {
path = path.split('.');
}

if (path.length > 1) {
let e = path.shift();
setObjectPropertyValue(obj[e] =
Object.prototype.toString.call(obj[e]) === '[object Object]'
? obj[e]
: {},
path,
value);
const e = path.shift();
setObjectPropertyValue(obj[e] = Object.prototype.toString.call(obj[e]) === '[object Object]' ? obj[e] : {}, path, value);
} else {
obj[path[0]] = value;
}
Expand Down
20 changes: 11 additions & 9 deletions src/icons/generator/fileGenerator.ts
@@ -1,25 +1,26 @@
import * as merge from 'lodash.merge';
import { getFileConfigString } from '../../helpers/fileConfig';
import { FileIcon, FileIcons, IconAssociations, IconConfiguration, IconJsonOptions } from '../../models/index';
import { highContrastVersion, iconFolderPath, lightVersion, wildcardPattern } from './constants';

/**
* Get all file icons that can be used in this theme.
*/
export const getFileIconDefinitions = (fileIcons: FileIcons, config: IconConfiguration, options: IconJsonOptions): IconConfiguration => {
export const loadFileIconDefinitions = (fileIcons: FileIcons, config: IconConfiguration, options: IconJsonOptions): IconConfiguration => {
config = merge({}, config);
const enabledIcons = disableIconsByPack(fileIcons, options.activeIconPack);
const customIcons = getCustomIcons(options.files.associations);
const allFileIcons = [...enabledIcons, ...customIcons];

allFileIcons.forEach(icon => {
if (icon.disabled) return;
config = merge({}, config, setIconDefinition(icon.name));
config = merge({}, config, setIconDefinition(config, icon.name));

if (icon.light) {
config = merge({}, config, setIconDefinition(icon.name, lightVersion));
config = merge({}, config, setIconDefinition(config, icon.name, lightVersion));
}
if (icon.highContrast) {
config = merge({}, config, setIconDefinition(icon.name, highContrastVersion));
config = merge({}, config, setIconDefinition(config, icon.name, highContrastVersion));
}

if (icon.fileExtensions) {
Expand All @@ -31,16 +32,16 @@ export const getFileIconDefinitions = (fileIcons: FileIcons, config: IconConfigu
});

// set default file icon
config = merge({}, config, setIconDefinition(fileIcons.defaultIcon.name));
config = merge({}, config, setIconDefinition(config, fileIcons.defaultIcon.name));
config.file = fileIcons.defaultIcon.name;

if (fileIcons.defaultIcon.light) {
config = merge({}, config, setIconDefinition(fileIcons.defaultIcon.name, lightVersion));
config = merge({}, config, setIconDefinition(config, fileIcons.defaultIcon.name, lightVersion));
config.light.file = fileIcons.defaultIcon.name + lightVersion;
}

if (fileIcons.defaultIcon.highContrast) {
config = merge({}, config, setIconDefinition(fileIcons.defaultIcon.name, highContrastVersion));
config = merge({}, config, setIconDefinition(config, fileIcons.defaultIcon.name, highContrastVersion));
config.highContrast.file = fileIcons.defaultIcon.name + highContrastVersion;
}

Expand Down Expand Up @@ -87,10 +88,11 @@ const disableIconsByPack = (fileIcons: FileIcons, activatedIconPack: string): Fi
});
};

const setIconDefinition = (iconName: string, appendix: string = '') => {
const setIconDefinition = (config: IconConfiguration, iconName: string, appendix: string = '') => {
const obj = { iconDefinitions: {} };
const fileConfig = getFileConfigString(config.options);
obj.iconDefinitions[`${iconName}${appendix}`] = {
iconPath: `${iconFolderPath}${iconName}${appendix}.svg`
iconPath: `${iconFolderPath}${iconName}${appendix}${fileConfig}.svg`
};
return obj;
};
Expand Down
8 changes: 5 additions & 3 deletions src/icons/generator/folderGenerator.ts
@@ -1,13 +1,14 @@
import * as fs from 'fs';
import * as merge from 'lodash.merge';
import * as path from 'path';
import { getFileConfigString } from '../../helpers/fileConfig';
import { DefaultIcon, FolderIcon, FolderTheme, IconAssociations, IconConfiguration, IconJsonOptions } from '../../models/index';
import { highContrastVersion, iconFolderPath, lightVersion, openedFolder } from './constants';

/**
* Get the folder icon definitions as object.
*/
export const getFolderIconDefinitions = (folderThemes: FolderTheme[], config: IconConfiguration, options: IconJsonOptions): IconConfiguration => {
export const loadFolderIconDefinitions = (folderThemes: FolderTheme[], config: IconConfiguration, options: IconJsonOptions): IconConfiguration => {
config = merge({}, config);
config.hidesExplorerArrows = options.hidesExplorerArrows;
const activeTheme = getEnabledFolderTheme(folderThemes, options.folders.theme);
Expand Down Expand Up @@ -87,11 +88,12 @@ const setIconDefinitions = (config: IconConfiguration, icon: FolderIcon | Defaul

const createIconDefinitions = (config: IconConfiguration, iconName: string, appendix: string = '') => {
config = merge({}, config);
const fileConfig = getFileConfigString(config.options);
config.iconDefinitions[iconName + appendix] = {
iconPath: `${iconFolderPath}${iconName}${appendix}.svg`
iconPath: `${iconFolderPath}${iconName}${appendix}${fileConfig}.svg`
};
config.iconDefinitions[`${iconName}${openedFolder}${appendix}`] = {
iconPath: `${iconFolderPath}${iconName}${openedFolder}${appendix}.svg`
iconPath: `${iconFolderPath}${iconName}${openedFolder}${appendix}${fileConfig}.svg`
};
return config;
};
Expand Down
63 changes: 47 additions & 16 deletions src/icons/generator/jsonGenerator.ts
@@ -1,21 +1,22 @@
import * as fs from 'fs';
import * as merge from 'lodash.merge';
import * as path from 'path';
import * as fs from 'fs';
import { getFileConfigString } from '../../helpers/fileConfig';
import { IconConfiguration, IconJsonOptions } from '../../models/index';
import { fileIcons } from '../fileIcons';
import { folderIcons } from '../folderIcons';
import { languageIcons } from '../languageIcons';
import { iconJsonName } from './constants';
import { generateFolderIcons, getFileIconDefinitions, getFolderIconDefinitions, getLanguageIconDefinitions, setIconOpacity, setIconSaturation, validateHEXColorCode, validateOpacityValue, validateSaturationValue } from './index';
import { generateFolderIcons, loadFileIconDefinitions, loadFolderIconDefinitions, loadLanguageIconDefinitions, setIconOpacity, setIconSaturation, validateHEXColorCode, validateOpacityValue, validateSaturationValue } from './index';

/**
* Generate the complete icon configuration object that can be written as JSON file.
*/
export const generateIconConfigurationObject = (options: IconJsonOptions): IconConfiguration => {
const iconConfig = merge({}, new IconConfiguration(), { options });
const languageIconDefinitions = getLanguageIconDefinitions(languageIcons, iconConfig, options);
const fileIconDefinitions = getFileIconDefinitions(fileIcons, iconConfig, options);
const folderIconDefinitions = getFolderIconDefinitions(folderIcons, iconConfig, options);
const languageIconDefinitions = loadLanguageIconDefinitions(languageIcons, iconConfig, options);
const fileIconDefinitions = loadFileIconDefinitions(fileIcons, iconConfig, options);
const folderIconDefinitions = loadFolderIconDefinitions(folderIcons, iconConfig, options);

return merge({}, languageIconDefinitions, fileIconDefinitions, folderIconDefinitions);
};
Expand Down Expand Up @@ -49,17 +50,6 @@ export const createIconFile = (updatedConfigs?: IconJsonOptions, updatedJSONConf
}
}

try {
let iconsPath = __dirname;
if (path.basename(__dirname) !== 'dist') {
// executed via script
iconsPath = path.join(__dirname, '..', '..', '..', 'dist');
}
fs.writeFileSync(path.join(iconsPath, iconJsonName), JSON.stringify(json, undefined, 2), 'utf-8');
} catch (error) {
throw Error(error);
}

try {
if (!updatedConfigs || (updatedConfigs.folders || {}).color) {
// if updatedConfigs do not exist (because of initial setup)
Expand All @@ -73,6 +63,23 @@ export const createIconFile = (updatedConfigs?: IconJsonOptions, updatedJSONConf
if (!updatedConfigs || updatedConfigs.saturation !== undefined) {
setIconSaturation(options.saturation);
}
let iconJsonPath = __dirname;
// if executed via script
if (path.basename(__dirname) !== 'dist') {
iconJsonPath = path.join(__dirname, '..', '..', '..', 'dist');
}
renameIconFiles(iconJsonPath, options);
} catch (error) {
throw Error(error);
}

try {
let iconJsonPath = __dirname;
// if executed via script
if (path.basename(__dirname) !== 'dist') {
iconJsonPath = path.join(__dirname, '..', '..', '..', 'dist');
}
fs.writeFileSync(path.join(iconJsonPath, iconJsonName), JSON.stringify(json, undefined, 2), 'utf-8');
} catch (error) {
throw Error(error);
}
Expand All @@ -96,3 +103,27 @@ export const getDefaultIconOptions = (): IconJsonOptions => ({
files: { associations: {} },
languages: { associations: {} },
});

/**
* Rename all icon files according their respective config
* @param iconJsonPath Path of icon json folder
* @param options Icon Json Options
*/
const renameIconFiles = (iconJsonPath: string, options: IconJsonOptions) => {
fs.readdirSync(path.join(iconJsonPath, '..', 'icons'))
.filter(f => f.match(/\.svg/gi))
.forEach(f => {
const filePath = path.join(iconJsonPath, '..', 'icons', f);
const fileConfig = getFileConfigString(options);

// append file config to file name
const newFilePath = path.join(iconJsonPath, '..', 'icons', f.replace(/(^[^\.~]+)(.*)\.svg/, `$1${fileConfig}.svg`));

// if generated files are already in place, do not overwrite them
if (filePath !== newFilePath && fs.existsSync(newFilePath)) {
fs.unlinkSync(filePath);
} else {
fs.renameSync(filePath, newFilePath);
}
});
};