4a518566dae8401dea725419f3e60867f4547c72f61e5acd41206eff2194757b8d4d69684c30d908b40cf0dafd53f008fa990d2f7b03062e0d94f71031b833 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. /*---------------------------------------------------------------------------------------------
  2. * Copyright (c) Microsoft Corporation. All rights reserved.
  3. * Licensed under the MIT License. See License.txt in the project root for license information.
  4. *--------------------------------------------------------------------------------------------*/
  5. var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
  6. function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
  7. return new (P || (P = Promise))(function (resolve, reject) {
  8. function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
  9. function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
  10. function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
  11. step((generator = generator.apply(thisArg, _arguments || [])).next());
  12. });
  13. };
  14. import { ThrottledDelayer } from '../../../common/async.js';
  15. import { Emitter, Event } from '../../../common/event.js';
  16. import { Disposable } from '../../../common/lifecycle.js';
  17. import { isUndefinedOrNull } from '../../../common/types.js';
  18. export var StorageState;
  19. (function (StorageState) {
  20. StorageState[StorageState["None"] = 0] = "None";
  21. StorageState[StorageState["Initialized"] = 1] = "Initialized";
  22. StorageState[StorageState["Closed"] = 2] = "Closed";
  23. })(StorageState || (StorageState = {}));
  24. export class Storage extends Disposable {
  25. constructor(database, options = Object.create(null)) {
  26. super();
  27. this.database = database;
  28. this.options = options;
  29. this._onDidChangeStorage = this._register(new Emitter());
  30. this.onDidChangeStorage = this._onDidChangeStorage.event;
  31. this.state = StorageState.None;
  32. this.cache = new Map();
  33. this.flushDelayer = new ThrottledDelayer(Storage.DEFAULT_FLUSH_DELAY);
  34. this.pendingDeletes = new Set();
  35. this.pendingInserts = new Map();
  36. this.whenFlushedCallbacks = [];
  37. this.registerListeners();
  38. }
  39. registerListeners() {
  40. this._register(this.database.onDidChangeItemsExternal(e => this.onDidChangeItemsExternal(e)));
  41. }
  42. onDidChangeItemsExternal(e) {
  43. var _a, _b;
  44. // items that change external require us to update our
  45. // caches with the values. we just accept the value and
  46. // emit an event if there is a change.
  47. (_a = e.changed) === null || _a === void 0 ? void 0 : _a.forEach((value, key) => this.accept(key, value));
  48. (_b = e.deleted) === null || _b === void 0 ? void 0 : _b.forEach(key => this.accept(key, undefined));
  49. }
  50. accept(key, value) {
  51. if (this.state === StorageState.Closed) {
  52. return; // Return early if we are already closed
  53. }
  54. let changed = false;
  55. // Item got removed, check for deletion
  56. if (isUndefinedOrNull(value)) {
  57. changed = this.cache.delete(key);
  58. }
  59. // Item got updated, check for change
  60. else {
  61. const currentValue = this.cache.get(key);
  62. if (currentValue !== value) {
  63. this.cache.set(key, value);
  64. changed = true;
  65. }
  66. }
  67. // Signal to outside listeners
  68. if (changed) {
  69. this._onDidChangeStorage.fire(key);
  70. }
  71. }
  72. get(key, fallbackValue) {
  73. const value = this.cache.get(key);
  74. if (isUndefinedOrNull(value)) {
  75. return fallbackValue;
  76. }
  77. return value;
  78. }
  79. getBoolean(key, fallbackValue) {
  80. const value = this.get(key);
  81. if (isUndefinedOrNull(value)) {
  82. return fallbackValue;
  83. }
  84. return value === 'true';
  85. }
  86. getNumber(key, fallbackValue) {
  87. const value = this.get(key);
  88. if (isUndefinedOrNull(value)) {
  89. return fallbackValue;
  90. }
  91. return parseInt(value, 10);
  92. }
  93. set(key, value) {
  94. return __awaiter(this, void 0, void 0, function* () {
  95. if (this.state === StorageState.Closed) {
  96. return; // Return early if we are already closed
  97. }
  98. // We remove the key for undefined/null values
  99. if (isUndefinedOrNull(value)) {
  100. return this.delete(key);
  101. }
  102. // Otherwise, convert to String and store
  103. const valueStr = String(value);
  104. // Return early if value already set
  105. const currentValue = this.cache.get(key);
  106. if (currentValue === valueStr) {
  107. return;
  108. }
  109. // Update in cache and pending
  110. this.cache.set(key, valueStr);
  111. this.pendingInserts.set(key, valueStr);
  112. this.pendingDeletes.delete(key);
  113. // Event
  114. this._onDidChangeStorage.fire(key);
  115. // Accumulate work by scheduling after timeout
  116. return this.doFlush();
  117. });
  118. }
  119. delete(key) {
  120. return __awaiter(this, void 0, void 0, function* () {
  121. if (this.state === StorageState.Closed) {
  122. return; // Return early if we are already closed
  123. }
  124. // Remove from cache and add to pending
  125. const wasDeleted = this.cache.delete(key);
  126. if (!wasDeleted) {
  127. return; // Return early if value already deleted
  128. }
  129. if (!this.pendingDeletes.has(key)) {
  130. this.pendingDeletes.add(key);
  131. }
  132. this.pendingInserts.delete(key);
  133. // Event
  134. this._onDidChangeStorage.fire(key);
  135. // Accumulate work by scheduling after timeout
  136. return this.doFlush();
  137. });
  138. }
  139. get hasPending() {
  140. return this.pendingInserts.size > 0 || this.pendingDeletes.size > 0;
  141. }
  142. flushPending() {
  143. return __awaiter(this, void 0, void 0, function* () {
  144. if (!this.hasPending) {
  145. return; // return early if nothing to do
  146. }
  147. // Get pending data
  148. const updateRequest = { insert: this.pendingInserts, delete: this.pendingDeletes };
  149. // Reset pending data for next run
  150. this.pendingDeletes = new Set();
  151. this.pendingInserts = new Map();
  152. // Update in storage and release any
  153. // waiters we have once done
  154. return this.database.updateItems(updateRequest).finally(() => {
  155. var _a;
  156. if (!this.hasPending) {
  157. while (this.whenFlushedCallbacks.length) {
  158. (_a = this.whenFlushedCallbacks.pop()) === null || _a === void 0 ? void 0 : _a();
  159. }
  160. }
  161. });
  162. });
  163. }
  164. doFlush(delay) {
  165. return __awaiter(this, void 0, void 0, function* () {
  166. return this.flushDelayer.trigger(() => this.flushPending(), delay);
  167. });
  168. }
  169. dispose() {
  170. this.flushDelayer.dispose();
  171. super.dispose();
  172. }
  173. }
  174. Storage.DEFAULT_FLUSH_DELAY = 100;
  175. export class InMemoryStorageDatabase {
  176. constructor() {
  177. this.onDidChangeItemsExternal = Event.None;
  178. this.items = new Map();
  179. }
  180. updateItems(request) {
  181. var _a, _b;
  182. return __awaiter(this, void 0, void 0, function* () {
  183. (_a = request.insert) === null || _a === void 0 ? void 0 : _a.forEach((value, key) => this.items.set(key, value));
  184. (_b = request.delete) === null || _b === void 0 ? void 0 : _b.forEach(key => this.items.delete(key));
  185. });
  186. }
  187. }