77aa597c6bf16e29834a35e991535b1a7f96de98052d995ae1deca5f36cc74c1d7a951221aae878709d35253057fc7bb55ebf4a73f16f1cdc37479bd36e410 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. import { Emitter, PauseableEmitter } from '../../../base/common/event.js';
  2. import { Disposable } from '../../../base/common/lifecycle.js';
  3. import { isUndefinedOrNull } from '../../../base/common/types.js';
  4. import { InMemoryStorageDatabase, Storage } from '../../../base/parts/storage/common/storage.js';
  5. import { createDecorator } from '../../instantiation/common/instantiation.js';
  6. const TARGET_KEY = '__$__targetStorageMarker';
  7. export const IStorageService = createDecorator('storageService');
  8. export var WillSaveStateReason;
  9. (function (WillSaveStateReason) {
  10. /**
  11. * No specific reason to save state.
  12. */
  13. WillSaveStateReason[WillSaveStateReason["NONE"] = 0] = "NONE";
  14. /**
  15. * A hint that the workbench is about to shutdown.
  16. */
  17. WillSaveStateReason[WillSaveStateReason["SHUTDOWN"] = 1] = "SHUTDOWN";
  18. })(WillSaveStateReason || (WillSaveStateReason = {}));
  19. export class AbstractStorageService extends Disposable {
  20. constructor(options = { flushInterval: AbstractStorageService.DEFAULT_FLUSH_INTERVAL }) {
  21. super();
  22. this.options = options;
  23. this._onDidChangeValue = this._register(new PauseableEmitter());
  24. this.onDidChangeValue = this._onDidChangeValue.event;
  25. this._onDidChangeTarget = this._register(new PauseableEmitter());
  26. this._onWillSaveState = this._register(new Emitter());
  27. this.onWillSaveState = this._onWillSaveState.event;
  28. this._workspaceKeyTargets = undefined;
  29. this._profileKeyTargets = undefined;
  30. this._applicationKeyTargets = undefined;
  31. }
  32. emitDidChangeValue(scope, key) {
  33. // Specially handle `TARGET_KEY`
  34. if (key === TARGET_KEY) {
  35. // Clear our cached version which is now out of date
  36. switch (scope) {
  37. case -1 /* StorageScope.APPLICATION */:
  38. this._applicationKeyTargets = undefined;
  39. break;
  40. case 0 /* StorageScope.PROFILE */:
  41. this._profileKeyTargets = undefined;
  42. break;
  43. case 1 /* StorageScope.WORKSPACE */:
  44. this._workspaceKeyTargets = undefined;
  45. break;
  46. }
  47. // Emit as `didChangeTarget` event
  48. this._onDidChangeTarget.fire({ scope });
  49. }
  50. // Emit any other key to outside
  51. else {
  52. this._onDidChangeValue.fire({ scope, key, target: this.getKeyTargets(scope)[key] });
  53. }
  54. }
  55. get(key, scope, fallbackValue) {
  56. var _a;
  57. return (_a = this.getStorage(scope)) === null || _a === void 0 ? void 0 : _a.get(key, fallbackValue);
  58. }
  59. getBoolean(key, scope, fallbackValue) {
  60. var _a;
  61. return (_a = this.getStorage(scope)) === null || _a === void 0 ? void 0 : _a.getBoolean(key, fallbackValue);
  62. }
  63. getNumber(key, scope, fallbackValue) {
  64. var _a;
  65. return (_a = this.getStorage(scope)) === null || _a === void 0 ? void 0 : _a.getNumber(key, fallbackValue);
  66. }
  67. store(key, value, scope, target) {
  68. // We remove the key for undefined/null values
  69. if (isUndefinedOrNull(value)) {
  70. this.remove(key, scope);
  71. return;
  72. }
  73. // Update our datastructures but send events only after
  74. this.withPausedEmitters(() => {
  75. var _a;
  76. // Update key-target map
  77. this.updateKeyTarget(key, scope, target);
  78. // Store actual value
  79. (_a = this.getStorage(scope)) === null || _a === void 0 ? void 0 : _a.set(key, value);
  80. });
  81. }
  82. remove(key, scope) {
  83. // Update our datastructures but send events only after
  84. this.withPausedEmitters(() => {
  85. var _a;
  86. // Update key-target map
  87. this.updateKeyTarget(key, scope, undefined);
  88. // Remove actual key
  89. (_a = this.getStorage(scope)) === null || _a === void 0 ? void 0 : _a.delete(key);
  90. });
  91. }
  92. withPausedEmitters(fn) {
  93. // Pause emitters
  94. this._onDidChangeValue.pause();
  95. this._onDidChangeTarget.pause();
  96. try {
  97. fn();
  98. }
  99. finally {
  100. // Resume emitters
  101. this._onDidChangeValue.resume();
  102. this._onDidChangeTarget.resume();
  103. }
  104. }
  105. updateKeyTarget(key, scope, target) {
  106. var _a, _b;
  107. // Add
  108. const keyTargets = this.getKeyTargets(scope);
  109. if (typeof target === 'number') {
  110. if (keyTargets[key] !== target) {
  111. keyTargets[key] = target;
  112. (_a = this.getStorage(scope)) === null || _a === void 0 ? void 0 : _a.set(TARGET_KEY, JSON.stringify(keyTargets));
  113. }
  114. }
  115. // Remove
  116. else {
  117. if (typeof keyTargets[key] === 'number') {
  118. delete keyTargets[key];
  119. (_b = this.getStorage(scope)) === null || _b === void 0 ? void 0 : _b.set(TARGET_KEY, JSON.stringify(keyTargets));
  120. }
  121. }
  122. }
  123. get workspaceKeyTargets() {
  124. if (!this._workspaceKeyTargets) {
  125. this._workspaceKeyTargets = this.loadKeyTargets(1 /* StorageScope.WORKSPACE */);
  126. }
  127. return this._workspaceKeyTargets;
  128. }
  129. get profileKeyTargets() {
  130. if (!this._profileKeyTargets) {
  131. this._profileKeyTargets = this.loadKeyTargets(0 /* StorageScope.PROFILE */);
  132. }
  133. return this._profileKeyTargets;
  134. }
  135. get applicationKeyTargets() {
  136. if (!this._applicationKeyTargets) {
  137. this._applicationKeyTargets = this.loadKeyTargets(-1 /* StorageScope.APPLICATION */);
  138. }
  139. return this._applicationKeyTargets;
  140. }
  141. getKeyTargets(scope) {
  142. switch (scope) {
  143. case -1 /* StorageScope.APPLICATION */:
  144. return this.applicationKeyTargets;
  145. case 0 /* StorageScope.PROFILE */:
  146. return this.profileKeyTargets;
  147. default:
  148. return this.workspaceKeyTargets;
  149. }
  150. }
  151. loadKeyTargets(scope) {
  152. const keysRaw = this.get(TARGET_KEY, scope);
  153. if (keysRaw) {
  154. try {
  155. return JSON.parse(keysRaw);
  156. }
  157. catch (error) {
  158. // Fail gracefully
  159. }
  160. }
  161. return Object.create(null);
  162. }
  163. }
  164. AbstractStorageService.DEFAULT_FLUSH_INTERVAL = 60 * 1000; // every minute
  165. export class InMemoryStorageService extends AbstractStorageService {
  166. constructor() {
  167. super();
  168. this.applicationStorage = this._register(new Storage(new InMemoryStorageDatabase()));
  169. this.profileStorage = this._register(new Storage(new InMemoryStorageDatabase()));
  170. this.workspaceStorage = this._register(new Storage(new InMemoryStorageDatabase()));
  171. this._register(this.workspaceStorage.onDidChangeStorage(key => this.emitDidChangeValue(1 /* StorageScope.WORKSPACE */, key)));
  172. this._register(this.profileStorage.onDidChangeStorage(key => this.emitDidChangeValue(0 /* StorageScope.PROFILE */, key)));
  173. this._register(this.applicationStorage.onDidChangeStorage(key => this.emitDidChangeValue(-1 /* StorageScope.APPLICATION */, key)));
  174. }
  175. getStorage(scope) {
  176. switch (scope) {
  177. case -1 /* StorageScope.APPLICATION */:
  178. return this.applicationStorage;
  179. case 0 /* StorageScope.PROFILE */:
  180. return this.profileStorage;
  181. default:
  182. return this.workspaceStorage;
  183. }
  184. }
  185. }