25개 이상의 토픽을 선택하실 수 없습니다. Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

244 lines
7.6 KiB

  1. import type { ConfigurationChangeEvent } from 'vscode';
  2. import { ConfigurationTarget, Disposable } from 'vscode';
  3. import type { Container } from '../container';
  4. import { CommitFormatter } from '../git/formatters/commitFormatter';
  5. import { GitCommit, GitCommitIdentity } from '../git/models/commit';
  6. import { GitFileChange, GitFileIndexStatus } from '../git/models/file';
  7. import { PullRequest, PullRequestState } from '../git/models/pullRequest';
  8. import { configuration } from '../system/configuration';
  9. import { Logger } from '../system/logger';
  10. import type { CustomConfigPath, IpcMessage } from './protocol';
  11. import {
  12. DidChangeConfigurationNotificationType,
  13. DidGenerateConfigurationPreviewNotificationType,
  14. GenerateConfigurationPreviewCommandType,
  15. isCustomConfigKey,
  16. onIpc,
  17. UpdateConfigurationCommandType,
  18. } from './protocol';
  19. import type { WebviewController, WebviewProvider } from './webviewController';
  20. import type { WebviewIds, WebviewViewIds } from './webviewsController';
  21. export abstract class WebviewProviderWithConfigBase<State> implements WebviewProvider<State> {
  22. private readonly _disposable: Disposable;
  23. constructor(
  24. readonly container: Container,
  25. readonly id: `gitlens.${WebviewIds}` | `gitlens.views.${WebviewViewIds}`,
  26. readonly host: WebviewController<State>,
  27. ) {
  28. this._disposable = Disposable.from(
  29. configuration.onDidChange(this.onConfigurationChanged, this),
  30. configuration.onDidChangeAny(this.onAnyConfigurationChanged, this),
  31. );
  32. }
  33. dispose() {
  34. this._disposable.dispose();
  35. }
  36. onActiveChanged(active: boolean): void {
  37. // Anytime the webview becomes active, make sure it has the most up-to-date config
  38. if (active) {
  39. void this.notifyDidChangeConfiguration();
  40. }
  41. }
  42. onMessageReceived(e: IpcMessage): void {
  43. if (e == null) return;
  44. switch (e.method) {
  45. case UpdateConfigurationCommandType.method:
  46. Logger.debug(`Webview(${this.id}).onMessageReceived: method=${e.method}`);
  47. onIpc(UpdateConfigurationCommandType, e, async params => {
  48. const target =
  49. params.scope === 'workspace' ? ConfigurationTarget.Workspace : ConfigurationTarget.Global;
  50. let key: keyof typeof params.changes;
  51. for (key in params.changes) {
  52. let value = params.changes[key];
  53. if (isCustomConfigKey(key)) {
  54. const customSetting = this.customSettings.get(key);
  55. if (customSetting != null) {
  56. if (typeof value === 'boolean') {
  57. await customSetting.update(value);
  58. } else {
  59. debugger;
  60. }
  61. }
  62. continue;
  63. }
  64. const inspect = configuration.inspect(key)!;
  65. if (value != null) {
  66. if (params.scope === 'workspace') {
  67. if (value === inspect.workspaceValue) continue;
  68. } else {
  69. if (value === inspect.globalValue && value !== inspect.defaultValue) continue;
  70. if (value === inspect.defaultValue) {
  71. value = undefined;
  72. }
  73. }
  74. }
  75. await configuration.update(key as any, value, target);
  76. }
  77. for (const key of params.removes) {
  78. await configuration.update(key as any, undefined, target);
  79. }
  80. });
  81. break;
  82. case GenerateConfigurationPreviewCommandType.method:
  83. Logger.debug(`Webview(${this.id}).onMessageReceived: method=${e.method}`);
  84. onIpc(GenerateConfigurationPreviewCommandType, e, async params => {
  85. switch (params.type) {
  86. case 'commit':
  87. case 'commit-uncommitted': {
  88. const commit = new GitCommit(
  89. this.container,
  90. '~/code/eamodio/vscode-gitlens-demo',
  91. 'fe26af408293cba5b4bfd77306e1ac9ff7ccaef8',
  92. new GitCommitIdentity('You', 'eamodio@gmail.com', new Date('2016-11-12T20:41:00.000Z')),
  93. new GitCommitIdentity('You', 'eamodio@gmail.com', new Date('2020-11-01T06:57:21.000Z')),
  94. params.type === 'commit-uncommitted' ? 'Uncommitted changes' : 'Supercharged',
  95. ['3ac1d3f51d7cf5f438cc69f25f6740536ad80fef'],
  96. params.type === 'commit-uncommitted' ? 'Uncommitted changes' : 'Supercharged',
  97. new GitFileChange(
  98. '~/code/eamodio/vscode-gitlens-demo',
  99. 'code.ts',
  100. GitFileIndexStatus.Modified,
  101. ),
  102. undefined,
  103. [],
  104. );
  105. let includePullRequest = false;
  106. switch (params.key) {
  107. case configuration.name('currentLine.format'):
  108. includePullRequest = configuration.get('currentLine.pullRequests.enabled');
  109. break;
  110. case configuration.name('statusBar.format'):
  111. includePullRequest = configuration.get('statusBar.pullRequests.enabled');
  112. break;
  113. }
  114. let pr: PullRequest | undefined;
  115. if (includePullRequest) {
  116. pr = new PullRequest(
  117. { id: 'github', name: 'GitHub', domain: 'github.com', icon: 'github' },
  118. {
  119. name: 'Eric Amodio',
  120. avatarUrl: 'https://avatars1.githubusercontent.com/u/641685?s=32&v=4',
  121. url: 'https://github.com/eamodio',
  122. },
  123. '1',
  124. 'Supercharged',
  125. 'https://github.com/gitkraken/vscode-gitlens/pulls/1',
  126. PullRequestState.Merged,
  127. new Date('Sat, 12 Nov 2016 19:41:00 GMT'),
  128. undefined,
  129. new Date('Sat, 12 Nov 2016 20:41:00 GMT'),
  130. );
  131. }
  132. let preview;
  133. try {
  134. preview = CommitFormatter.fromTemplate(params.format, commit, {
  135. dateFormat: configuration.get('defaultDateFormat'),
  136. pullRequestOrRemote: pr,
  137. messageTruncateAtNewLine: true,
  138. });
  139. } catch {
  140. preview = 'Invalid format';
  141. }
  142. await this.host.notify(
  143. DidGenerateConfigurationPreviewNotificationType,
  144. { preview: preview },
  145. e.completionId,
  146. );
  147. }
  148. }
  149. });
  150. break;
  151. }
  152. }
  153. private onAnyConfigurationChanged(e: ConfigurationChangeEvent) {
  154. let notify = false;
  155. for (const setting of this.customSettings.values()) {
  156. if (e.affectsConfiguration(setting.name)) {
  157. notify = true;
  158. break;
  159. }
  160. }
  161. if (!notify) return;
  162. void this.notifyDidChangeConfiguration();
  163. }
  164. private onConfigurationChanged(_e: ConfigurationChangeEvent) {
  165. void this.notifyDidChangeConfiguration();
  166. }
  167. private _customSettings: Map<CustomConfigPath, CustomSetting> | undefined;
  168. private get customSettings() {
  169. if (this._customSettings == null) {
  170. this._customSettings = new Map<CustomConfigPath, CustomSetting>([
  171. [
  172. 'rebaseEditor.enabled',
  173. {
  174. name: 'workbench.editorAssociations',
  175. enabled: () => this.container.rebaseEditor.enabled,
  176. update: this.container.rebaseEditor.setEnabled,
  177. },
  178. ],
  179. [
  180. 'currentLine.useUncommittedChangesFormat',
  181. {
  182. name: 'currentLine.useUncommittedChangesFormat',
  183. enabled: () => configuration.get('currentLine.uncommittedChangesFormat') != null,
  184. update: async enabled =>
  185. configuration.updateEffective(
  186. 'currentLine.uncommittedChangesFormat',
  187. // eslint-disable-next-line no-template-curly-in-string
  188. enabled ? '✏️ ${ago}' : null,
  189. ),
  190. },
  191. ],
  192. ]);
  193. }
  194. return this._customSettings;
  195. }
  196. protected getCustomSettings(): Record<string, boolean> {
  197. const customSettings: Record<string, boolean> = Object.create(null);
  198. for (const [key, setting] of this.customSettings) {
  199. customSettings[key] = setting.enabled();
  200. }
  201. return customSettings;
  202. }
  203. private notifyDidChangeConfiguration() {
  204. // Make sure to get the raw config, not from the container which has the modes mixed in
  205. return this.host.notify(DidChangeConfigurationNotificationType, {
  206. config: configuration.getAll(true),
  207. customSettings: this.getCustomSettings(),
  208. });
  209. }
  210. }
  211. interface CustomSetting {
  212. name: string;
  213. enabled: () => boolean;
  214. update: (enabled: boolean) => Promise<void>;
  215. }