You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

283 lines
11 KiB

8 years ago
  1. 'use strict';
  2. export * from './ui/config';
  3. import {
  4. ConfigurationChangeEvent,
  5. ConfigurationTarget,
  6. Event,
  7. EventEmitter,
  8. ExtensionContext,
  9. Uri,
  10. workspace
  11. } from 'vscode';
  12. import { CommandContext, extensionId, setCommandContext } from './constants';
  13. import { Container } from './container';
  14. import { clearGravatarCache } from './git/gitService';
  15. import { Functions } from './system';
  16. import { Config, KeyMap } from './ui/config';
  17. const emptyConfig: any = new Proxy<any>({} as Config, {
  18. get(target, propKey, receiver) {
  19. return emptyConfig;
  20. }
  21. });
  22. export class Configuration {
  23. static configure(context: ExtensionContext) {
  24. context.subscriptions.push(
  25. workspace.onDidChangeConfiguration(configuration.onConfigurationChanged, configuration)
  26. );
  27. }
  28. private _onDidChange = new EventEmitter<ConfigurationChangeEvent>();
  29. get onDidChange(): Event<ConfigurationChangeEvent> {
  30. return this._onDidChange.event;
  31. }
  32. private readonly _configAffectedByMode: string[];
  33. constructor() {
  34. this._configAffectedByMode = [
  35. `gitlens.${this.name('mode').value}`,
  36. `gitlens.${this.name('modes').value}`,
  37. `gitlens.${this.name('codeLens').value}`,
  38. `gitlens.${this.name('currentLine').value}`,
  39. `gitlens.${this.name('fileHistoryExplorer').value}`,
  40. `gitlens.${this.name('hovers').value}`,
  41. `gitlens.${this.name('lineHistoryExplorer').value}`,
  42. `gitlens.${this.name('repositoriesExplorer').value}`,
  43. `gitlens.${this.name('statusBar').value}`
  44. ];
  45. }
  46. private onConfigurationChanged(e: ConfigurationChangeEvent) {
  47. if (!e.affectsConfiguration(extensionId, null!)) return;
  48. Container.resetConfig();
  49. if (configuration.changed(e, configuration.name('defaultGravatarsStyle').value)) {
  50. clearGravatarCache();
  51. }
  52. const section = configuration.name('keymap').value;
  53. if (configuration.changed(e, section)) {
  54. setCommandContext(CommandContext.KeyMap, this.get<KeyMap>(section));
  55. }
  56. if (
  57. configuration.changed(e, configuration.name('mode').value) ||
  58. configuration.changed(e, configuration.name('modes').value)
  59. ) {
  60. const original = e.affectsConfiguration;
  61. e = {
  62. ...e,
  63. affectsConfiguration: (section: string, resource?: Uri) => {
  64. if (this._configAffectedByMode.some(n => section.startsWith(n))) {
  65. return true;
  66. }
  67. return original(section, resource);
  68. }
  69. } as ConfigurationChangeEvent;
  70. }
  71. this._onDidChange.fire(e);
  72. }
  73. readonly initializingChangeEvent: ConfigurationChangeEvent = {
  74. affectsConfiguration: (section: string, resource?: Uri) => false
  75. };
  76. get<T>(section?: string, resource?: Uri | null, defaultValue?: T) {
  77. return defaultValue === undefined
  78. ? workspace
  79. .getConfiguration(section === undefined ? undefined : extensionId, resource!)
  80. .get<T>(section === undefined ? extensionId : section)!
  81. : workspace
  82. .getConfiguration(section === undefined ? undefined : extensionId, resource!)
  83. .get<T>(section === undefined ? extensionId : section, defaultValue)!;
  84. }
  85. changed(e: ConfigurationChangeEvent, section: string, resource?: Uri | null) {
  86. return e.affectsConfiguration(`${extensionId}.${section}`, resource!);
  87. }
  88. initializing(e: ConfigurationChangeEvent) {
  89. return e === this.initializingChangeEvent;
  90. }
  91. inspect(section?: string, resource?: Uri | null) {
  92. return workspace
  93. .getConfiguration(section === undefined ? undefined : extensionId, resource!)
  94. .inspect(section === undefined ? extensionId : section);
  95. }
  96. async migrate<TFrom, TTo>(
  97. from: string,
  98. to: string,
  99. options: { fallbackValue?: TTo; migrationFn?(value: TFrom): TTo } = {}
  100. ): Promise<boolean> {
  101. const inspection = configuration.inspect(from);
  102. if (inspection === undefined) return false;
  103. let migrated = false;
  104. if (inspection.globalValue !== undefined) {
  105. await this.update(
  106. to,
  107. options.migrationFn ? options.migrationFn(inspection.globalValue as TFrom) : inspection.globalValue,
  108. ConfigurationTarget.Global
  109. );
  110. migrated = true;
  111. // Can't delete the old setting currently because it errors with `Unable to write to User Settings because <setting name> is not a registered configuration`
  112. // if (from !== to) {
  113. // try {
  114. // await this.update(from, undefined, ConfigurationTarget.Global);
  115. // }
  116. // catch { }
  117. // }
  118. }
  119. if (inspection.workspaceValue !== undefined) {
  120. await this.update(
  121. to,
  122. options.migrationFn
  123. ? options.migrationFn(inspection.workspaceValue as TFrom)
  124. : inspection.workspaceValue,
  125. ConfigurationTarget.Workspace
  126. );
  127. migrated = true;
  128. // Can't delete the old setting currently because it errors with `Unable to write to User Settings because <setting name> is not a registered configuration`
  129. // if (from !== to) {
  130. // try {
  131. // await this.update(from, undefined, ConfigurationTarget.Workspace);
  132. // }
  133. // catch { }
  134. // }
  135. }
  136. if (inspection.workspaceFolderValue !== undefined) {
  137. await this.update(
  138. to,
  139. options.migrationFn
  140. ? options.migrationFn(inspection.workspaceFolderValue as TFrom)
  141. : inspection.workspaceFolderValue,
  142. ConfigurationTarget.WorkspaceFolder
  143. );
  144. migrated = true;
  145. // Can't delete the old setting currently because it errors with `Unable to write to User Settings because <setting name> is not a registered configuration`
  146. // if (from !== to) {
  147. // try {
  148. // await this.update(from, undefined, ConfigurationTarget.WorkspaceFolder);
  149. // }
  150. // catch { }
  151. // }
  152. }
  153. if (!migrated && options.fallbackValue !== undefined) {
  154. await this.update(to, options.fallbackValue, ConfigurationTarget.Global);
  155. migrated = true;
  156. }
  157. return migrated;
  158. }
  159. async migrateIfMissing<TFrom, TTo>(from: string, to: string, options: { migrationFn?(value: TFrom): TTo } = {}) {
  160. const fromInspection = configuration.inspect(from);
  161. if (fromInspection === undefined) return;
  162. const toInspection = configuration.inspect(to);
  163. if (fromInspection.globalValue !== undefined) {
  164. if (toInspection === undefined || toInspection.globalValue === undefined) {
  165. await this.update(
  166. to,
  167. options.migrationFn
  168. ? options.migrationFn(fromInspection.globalValue as TFrom)
  169. : fromInspection.globalValue,
  170. ConfigurationTarget.Global
  171. );
  172. // Can't delete the old setting currently because it errors with `Unable to write to User Settings because <setting name> is not a registered configuration`
  173. // if (from !== to) {
  174. // try {
  175. // await this.update(from, undefined, ConfigurationTarget.Global);
  176. // }
  177. // catch { }
  178. // }
  179. }
  180. }
  181. if (fromInspection.workspaceValue !== undefined) {
  182. if (toInspection === undefined || toInspection.workspaceValue === undefined) {
  183. await this.update(
  184. to,
  185. options.migrationFn
  186. ? options.migrationFn(fromInspection.workspaceValue as TFrom)
  187. : fromInspection.workspaceValue,
  188. ConfigurationTarget.Workspace
  189. );
  190. // Can't delete the old setting currently because it errors with `Unable to write to User Settings because <setting name> is not a registered configuration`
  191. // if (from !== to) {
  192. // try {
  193. // await this.update(from, undefined, ConfigurationTarget.Workspace);
  194. // }
  195. // catch { }
  196. // }
  197. }
  198. }
  199. if (fromInspection.workspaceFolderValue !== undefined) {
  200. if (toInspection === undefined || toInspection.workspaceFolderValue === undefined) {
  201. await this.update(
  202. to,
  203. options.migrationFn
  204. ? options.migrationFn(fromInspection.workspaceFolderValue as TFrom)
  205. : fromInspection.workspaceFolderValue,
  206. ConfigurationTarget.WorkspaceFolder
  207. );
  208. // Can't delete the old setting currently because it errors with `Unable to write to User Settings because <setting name> is not a registered configuration`
  209. // if (from !== to) {
  210. // try {
  211. // await this.update(from, undefined, ConfigurationTarget.WorkspaceFolder);
  212. // }
  213. // catch { }
  214. // }
  215. }
  216. }
  217. }
  218. name<K extends keyof Config>(name: K) {
  219. return Functions.propOf(emptyConfig as Config, name);
  220. }
  221. update(section: string, value: any, target: ConfigurationTarget, resource?: Uri | null) {
  222. return workspace
  223. .getConfiguration(extensionId, target === ConfigurationTarget.Global ? undefined : resource!)
  224. .update(section, value, target);
  225. }
  226. async updateEffective(section: string, value: any, resource: Uri | null = null) {
  227. const inspect = await configuration.inspect(section, resource)!;
  228. if (inspect.workspaceFolderValue !== undefined) {
  229. if (value === inspect.workspaceFolderValue) return;
  230. return await configuration.update(section, value, ConfigurationTarget.WorkspaceFolder, resource);
  231. }
  232. if (inspect.workspaceValue !== undefined) {
  233. if (value === inspect.workspaceValue) return;
  234. return await configuration.update(section, value, ConfigurationTarget.Workspace);
  235. }
  236. if (inspect.globalValue === value || (inspect.globalValue === undefined && value === inspect.defaultValue)) {
  237. return;
  238. }
  239. return await configuration.update(
  240. section,
  241. value === inspect.defaultValue ? undefined : value,
  242. ConfigurationTarget.Global
  243. );
  244. }
  245. }
  246. export const configuration = new Configuration();