modelServiceImpl.js 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744
  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 __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
  6. var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
  7. if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
  8. else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
  9. return c > 3 && r && Object.defineProperty(target, key, r), r;
  10. };
  11. var __param = (this && this.__param) || function (paramIndex, decorator) {
  12. return function (target, key) { decorator(target, key, paramIndex); }
  13. };
  14. import { Emitter } from '../../../base/common/event.js';
  15. import { Disposable, DisposableStore, dispose } from '../../../base/common/lifecycle.js';
  16. import * as platform from '../../../base/common/platform.js';
  17. import * as errors from '../../../base/common/errors.js';
  18. import { EDITOR_MODEL_DEFAULTS } from '../config/editorOptions.js';
  19. import { TextModel } from '../model/textModel.js';
  20. import { DocumentSemanticTokensProviderRegistry } from '../modes.js';
  21. import { PLAINTEXT_MODE_ID } from '../modes/modesRegistry.js';
  22. import { IModeService } from './modeService.js';
  23. import { ITextResourcePropertiesService } from './textResourceConfigurationService.js';
  24. import { IConfigurationService } from '../../../platform/configuration/common/configuration.js';
  25. import { RunOnceScheduler } from '../../../base/common/async.js';
  26. import { CancellationTokenSource } from '../../../base/common/cancellation.js';
  27. import { IThemeService } from '../../../platform/theme/common/themeService.js';
  28. import { ILogService } from '../../../platform/log/common/log.js';
  29. import { IUndoRedoService } from '../../../platform/undoRedo/common/undoRedo.js';
  30. import { StringSHA1 } from '../../../base/common/hash.js';
  31. import { isEditStackElement } from '../model/editStack.js';
  32. import { Schemas } from '../../../base/common/network.js';
  33. import { SemanticTokensProviderStyling, toMultilineTokens2 } from './semanticTokensProviderStyling.js';
  34. import { getDocumentSemanticTokens, hasDocumentSemanticTokensProvider, isSemanticTokens, isSemanticTokensEdits } from './getSemanticTokens.js';
  35. import { equals } from '../../../base/common/objects.js';
  36. import { ILanguageConfigurationService } from '../modes/languageConfigurationRegistry.js';
  37. function MODEL_ID(resource) {
  38. return resource.toString();
  39. }
  40. function computeModelSha1(model) {
  41. // compute the sha1
  42. const shaComputer = new StringSHA1();
  43. const snapshot = model.createSnapshot();
  44. let text;
  45. while ((text = snapshot.read())) {
  46. shaComputer.update(text);
  47. }
  48. return shaComputer.digest();
  49. }
  50. class ModelData {
  51. constructor(model, onWillDispose, onDidChangeLanguage) {
  52. this._modelEventListeners = new DisposableStore();
  53. this.model = model;
  54. this._languageSelection = null;
  55. this._languageSelectionListener = null;
  56. this._modelEventListeners.add(model.onWillDispose(() => onWillDispose(model)));
  57. this._modelEventListeners.add(model.onDidChangeLanguage((e) => onDidChangeLanguage(model, e)));
  58. }
  59. _disposeLanguageSelection() {
  60. if (this._languageSelectionListener) {
  61. this._languageSelectionListener.dispose();
  62. this._languageSelectionListener = null;
  63. }
  64. }
  65. dispose() {
  66. this._modelEventListeners.dispose();
  67. this._disposeLanguageSelection();
  68. }
  69. setLanguage(languageSelection) {
  70. this._disposeLanguageSelection();
  71. this._languageSelection = languageSelection;
  72. this._languageSelectionListener = this._languageSelection.onDidChange(() => this.model.setMode(languageSelection.languageId));
  73. this.model.setMode(languageSelection.languageId);
  74. }
  75. }
  76. const DEFAULT_EOL = (platform.isLinux || platform.isMacintosh) ? 1 /* LF */ : 2 /* CRLF */;
  77. class DisposedModelInfo {
  78. constructor(uri, initialUndoRedoSnapshot, time, sharesUndoRedoStack, heapSize, sha1, versionId, alternativeVersionId) {
  79. this.uri = uri;
  80. this.initialUndoRedoSnapshot = initialUndoRedoSnapshot;
  81. this.time = time;
  82. this.sharesUndoRedoStack = sharesUndoRedoStack;
  83. this.heapSize = heapSize;
  84. this.sha1 = sha1;
  85. this.versionId = versionId;
  86. this.alternativeVersionId = alternativeVersionId;
  87. }
  88. }
  89. let ModelServiceImpl = class ModelServiceImpl extends Disposable {
  90. constructor(_configurationService, _resourcePropertiesService, _themeService, _logService, _undoRedoService, _modeService, _languageConfigurationService) {
  91. super();
  92. this._configurationService = _configurationService;
  93. this._resourcePropertiesService = _resourcePropertiesService;
  94. this._themeService = _themeService;
  95. this._logService = _logService;
  96. this._undoRedoService = _undoRedoService;
  97. this._modeService = _modeService;
  98. this._languageConfigurationService = _languageConfigurationService;
  99. this._onModelAdded = this._register(new Emitter());
  100. this.onModelAdded = this._onModelAdded.event;
  101. this._onModelRemoved = this._register(new Emitter());
  102. this.onModelRemoved = this._onModelRemoved.event;
  103. this._onModelModeChanged = this._register(new Emitter());
  104. this.onModelModeChanged = this._onModelModeChanged.event;
  105. this._modelCreationOptionsByLanguageAndResource = Object.create(null);
  106. this._models = {};
  107. this._disposedModels = new Map();
  108. this._disposedModelsHeapSize = 0;
  109. this._semanticStyling = this._register(new SemanticStyling(this._themeService, this._modeService, this._logService));
  110. this._register(this._configurationService.onDidChangeConfiguration(() => this._updateModelOptions()));
  111. this._updateModelOptions();
  112. this._register(new SemanticColoringFeature(this, this._themeService, this._configurationService, this._semanticStyling));
  113. }
  114. static _readModelOptions(config, isForSimpleWidget) {
  115. var _a;
  116. let tabSize = EDITOR_MODEL_DEFAULTS.tabSize;
  117. if (config.editor && typeof config.editor.tabSize !== 'undefined') {
  118. const parsedTabSize = parseInt(config.editor.tabSize, 10);
  119. if (!isNaN(parsedTabSize)) {
  120. tabSize = parsedTabSize;
  121. }
  122. if (tabSize < 1) {
  123. tabSize = 1;
  124. }
  125. }
  126. let indentSize = tabSize;
  127. if (config.editor && typeof config.editor.indentSize !== 'undefined' && config.editor.indentSize !== 'tabSize') {
  128. const parsedIndentSize = parseInt(config.editor.indentSize, 10);
  129. if (!isNaN(parsedIndentSize)) {
  130. indentSize = parsedIndentSize;
  131. }
  132. if (indentSize < 1) {
  133. indentSize = 1;
  134. }
  135. }
  136. let insertSpaces = EDITOR_MODEL_DEFAULTS.insertSpaces;
  137. if (config.editor && typeof config.editor.insertSpaces !== 'undefined') {
  138. insertSpaces = (config.editor.insertSpaces === 'false' ? false : Boolean(config.editor.insertSpaces));
  139. }
  140. let newDefaultEOL = DEFAULT_EOL;
  141. const eol = config.eol;
  142. if (eol === '\r\n') {
  143. newDefaultEOL = 2 /* CRLF */;
  144. }
  145. else if (eol === '\n') {
  146. newDefaultEOL = 1 /* LF */;
  147. }
  148. let trimAutoWhitespace = EDITOR_MODEL_DEFAULTS.trimAutoWhitespace;
  149. if (config.editor && typeof config.editor.trimAutoWhitespace !== 'undefined') {
  150. trimAutoWhitespace = (config.editor.trimAutoWhitespace === 'false' ? false : Boolean(config.editor.trimAutoWhitespace));
  151. }
  152. let detectIndentation = EDITOR_MODEL_DEFAULTS.detectIndentation;
  153. if (config.editor && typeof config.editor.detectIndentation !== 'undefined') {
  154. detectIndentation = (config.editor.detectIndentation === 'false' ? false : Boolean(config.editor.detectIndentation));
  155. }
  156. let largeFileOptimizations = EDITOR_MODEL_DEFAULTS.largeFileOptimizations;
  157. if (config.editor && typeof config.editor.largeFileOptimizations !== 'undefined') {
  158. largeFileOptimizations = (config.editor.largeFileOptimizations === 'false' ? false : Boolean(config.editor.largeFileOptimizations));
  159. }
  160. let bracketPairColorizationOptions = EDITOR_MODEL_DEFAULTS.bracketPairColorizationOptions;
  161. if (((_a = config.editor) === null || _a === void 0 ? void 0 : _a.bracketPairColorization) && typeof config.editor.bracketPairColorization === 'object') {
  162. bracketPairColorizationOptions = {
  163. enabled: !!config.editor.bracketPairColorization.enabled
  164. };
  165. }
  166. return {
  167. isForSimpleWidget: isForSimpleWidget,
  168. tabSize: tabSize,
  169. indentSize: indentSize,
  170. insertSpaces: insertSpaces,
  171. detectIndentation: detectIndentation,
  172. defaultEOL: newDefaultEOL,
  173. trimAutoWhitespace: trimAutoWhitespace,
  174. largeFileOptimizations: largeFileOptimizations,
  175. bracketPairColorizationOptions
  176. };
  177. }
  178. _getEOL(resource, language) {
  179. if (resource) {
  180. return this._resourcePropertiesService.getEOL(resource, language);
  181. }
  182. const eol = this._configurationService.getValue('files.eol', { overrideIdentifier: language });
  183. if (eol && typeof eol === 'string' && eol !== 'auto') {
  184. return eol;
  185. }
  186. return platform.OS === 3 /* Linux */ || platform.OS === 2 /* Macintosh */ ? '\n' : '\r\n';
  187. }
  188. _shouldRestoreUndoStack() {
  189. const result = this._configurationService.getValue('files.restoreUndoStack');
  190. if (typeof result === 'boolean') {
  191. return result;
  192. }
  193. return true;
  194. }
  195. getCreationOptions(language, resource, isForSimpleWidget) {
  196. let creationOptions = this._modelCreationOptionsByLanguageAndResource[language + resource];
  197. if (!creationOptions) {
  198. const editor = this._configurationService.getValue('editor', { overrideIdentifier: language, resource });
  199. const eol = this._getEOL(resource, language);
  200. creationOptions = ModelServiceImpl._readModelOptions({ editor, eol }, isForSimpleWidget);
  201. this._modelCreationOptionsByLanguageAndResource[language + resource] = creationOptions;
  202. }
  203. return creationOptions;
  204. }
  205. _updateModelOptions() {
  206. const oldOptionsByLanguageAndResource = this._modelCreationOptionsByLanguageAndResource;
  207. this._modelCreationOptionsByLanguageAndResource = Object.create(null);
  208. // Update options on all models
  209. const keys = Object.keys(this._models);
  210. for (let i = 0, len = keys.length; i < len; i++) {
  211. const modelId = keys[i];
  212. const modelData = this._models[modelId];
  213. const language = modelData.model.getLanguageId();
  214. const uri = modelData.model.uri;
  215. const oldOptions = oldOptionsByLanguageAndResource[language + uri];
  216. const newOptions = this.getCreationOptions(language, uri, modelData.model.isForSimpleWidget);
  217. ModelServiceImpl._setModelOptionsForModel(modelData.model, newOptions, oldOptions);
  218. }
  219. }
  220. static _setModelOptionsForModel(model, newOptions, currentOptions) {
  221. if (currentOptions && currentOptions.defaultEOL !== newOptions.defaultEOL && model.getLineCount() === 1) {
  222. model.setEOL(newOptions.defaultEOL === 1 /* LF */ ? 0 /* LF */ : 1 /* CRLF */);
  223. }
  224. if (currentOptions
  225. && (currentOptions.detectIndentation === newOptions.detectIndentation)
  226. && (currentOptions.insertSpaces === newOptions.insertSpaces)
  227. && (currentOptions.tabSize === newOptions.tabSize)
  228. && (currentOptions.indentSize === newOptions.indentSize)
  229. && (currentOptions.trimAutoWhitespace === newOptions.trimAutoWhitespace)
  230. && equals(currentOptions.bracketPairColorizationOptions, newOptions.bracketPairColorizationOptions)) {
  231. // Same indent opts, no need to touch the model
  232. return;
  233. }
  234. if (newOptions.detectIndentation) {
  235. model.detectIndentation(newOptions.insertSpaces, newOptions.tabSize);
  236. model.updateOptions({
  237. trimAutoWhitespace: newOptions.trimAutoWhitespace,
  238. bracketColorizationOptions: newOptions.bracketPairColorizationOptions
  239. });
  240. }
  241. else {
  242. model.updateOptions({
  243. insertSpaces: newOptions.insertSpaces,
  244. tabSize: newOptions.tabSize,
  245. indentSize: newOptions.indentSize,
  246. trimAutoWhitespace: newOptions.trimAutoWhitespace,
  247. bracketColorizationOptions: newOptions.bracketPairColorizationOptions
  248. });
  249. }
  250. }
  251. // --- begin IModelService
  252. _insertDisposedModel(disposedModelData) {
  253. this._disposedModels.set(MODEL_ID(disposedModelData.uri), disposedModelData);
  254. this._disposedModelsHeapSize += disposedModelData.heapSize;
  255. }
  256. _removeDisposedModel(resource) {
  257. const disposedModelData = this._disposedModels.get(MODEL_ID(resource));
  258. if (disposedModelData) {
  259. this._disposedModelsHeapSize -= disposedModelData.heapSize;
  260. }
  261. this._disposedModels.delete(MODEL_ID(resource));
  262. return disposedModelData;
  263. }
  264. _ensureDisposedModelsHeapSize(maxModelsHeapSize) {
  265. if (this._disposedModelsHeapSize > maxModelsHeapSize) {
  266. // we must remove some old undo stack elements to free up some memory
  267. const disposedModels = [];
  268. this._disposedModels.forEach(entry => {
  269. if (!entry.sharesUndoRedoStack) {
  270. disposedModels.push(entry);
  271. }
  272. });
  273. disposedModels.sort((a, b) => a.time - b.time);
  274. while (disposedModels.length > 0 && this._disposedModelsHeapSize > maxModelsHeapSize) {
  275. const disposedModel = disposedModels.shift();
  276. this._removeDisposedModel(disposedModel.uri);
  277. if (disposedModel.initialUndoRedoSnapshot !== null) {
  278. this._undoRedoService.restoreSnapshot(disposedModel.initialUndoRedoSnapshot);
  279. }
  280. }
  281. }
  282. }
  283. _createModelData(value, languageId, resource, isForSimpleWidget) {
  284. // create & save the model
  285. const options = this.getCreationOptions(languageId, resource, isForSimpleWidget);
  286. const model = new TextModel(value, options, languageId, resource, this._undoRedoService, this._modeService, this._languageConfigurationService);
  287. if (resource && this._disposedModels.has(MODEL_ID(resource))) {
  288. const disposedModelData = this._removeDisposedModel(resource);
  289. const elements = this._undoRedoService.getElements(resource);
  290. const sha1IsEqual = (computeModelSha1(model) === disposedModelData.sha1);
  291. if (sha1IsEqual || disposedModelData.sharesUndoRedoStack) {
  292. for (const element of elements.past) {
  293. if (isEditStackElement(element) && element.matchesResource(resource)) {
  294. element.setModel(model);
  295. }
  296. }
  297. for (const element of elements.future) {
  298. if (isEditStackElement(element) && element.matchesResource(resource)) {
  299. element.setModel(model);
  300. }
  301. }
  302. this._undoRedoService.setElementsValidFlag(resource, true, (element) => (isEditStackElement(element) && element.matchesResource(resource)));
  303. if (sha1IsEqual) {
  304. model._overwriteVersionId(disposedModelData.versionId);
  305. model._overwriteAlternativeVersionId(disposedModelData.alternativeVersionId);
  306. model._overwriteInitialUndoRedoSnapshot(disposedModelData.initialUndoRedoSnapshot);
  307. }
  308. }
  309. else {
  310. if (disposedModelData.initialUndoRedoSnapshot !== null) {
  311. this._undoRedoService.restoreSnapshot(disposedModelData.initialUndoRedoSnapshot);
  312. }
  313. }
  314. }
  315. const modelId = MODEL_ID(model.uri);
  316. if (this._models[modelId]) {
  317. // There already exists a model with this id => this is a programmer error
  318. throw new Error('ModelService: Cannot add model because it already exists!');
  319. }
  320. const modelData = new ModelData(model, (model) => this._onWillDispose(model), (model, e) => this._onDidChangeLanguage(model, e));
  321. this._models[modelId] = modelData;
  322. return modelData;
  323. }
  324. createModel(value, languageSelection, resource, isForSimpleWidget = false) {
  325. let modelData;
  326. if (languageSelection) {
  327. modelData = this._createModelData(value, languageSelection.languageId, resource, isForSimpleWidget);
  328. this.setMode(modelData.model, languageSelection);
  329. }
  330. else {
  331. modelData = this._createModelData(value, PLAINTEXT_MODE_ID, resource, isForSimpleWidget);
  332. }
  333. this._onModelAdded.fire(modelData.model);
  334. return modelData.model;
  335. }
  336. setMode(model, languageSelection) {
  337. if (!languageSelection) {
  338. return;
  339. }
  340. const modelData = this._models[MODEL_ID(model.uri)];
  341. if (!modelData) {
  342. return;
  343. }
  344. modelData.setLanguage(languageSelection);
  345. }
  346. getModels() {
  347. const ret = [];
  348. const keys = Object.keys(this._models);
  349. for (let i = 0, len = keys.length; i < len; i++) {
  350. const modelId = keys[i];
  351. ret.push(this._models[modelId].model);
  352. }
  353. return ret;
  354. }
  355. getModel(resource) {
  356. const modelId = MODEL_ID(resource);
  357. const modelData = this._models[modelId];
  358. if (!modelData) {
  359. return null;
  360. }
  361. return modelData.model;
  362. }
  363. getSemanticTokensProviderStyling(provider) {
  364. return this._semanticStyling.get(provider);
  365. }
  366. // --- end IModelService
  367. _schemaShouldMaintainUndoRedoElements(resource) {
  368. return (resource.scheme === Schemas.file
  369. || resource.scheme === Schemas.vscodeRemote
  370. || resource.scheme === Schemas.userData
  371. || resource.scheme === Schemas.vscodeNotebookCell
  372. || resource.scheme === 'fake-fs' // for tests
  373. );
  374. }
  375. _onWillDispose(model) {
  376. const modelId = MODEL_ID(model.uri);
  377. const modelData = this._models[modelId];
  378. const sharesUndoRedoStack = (this._undoRedoService.getUriComparisonKey(model.uri) !== model.uri.toString());
  379. let maintainUndoRedoStack = false;
  380. let heapSize = 0;
  381. if (sharesUndoRedoStack || (this._shouldRestoreUndoStack() && this._schemaShouldMaintainUndoRedoElements(model.uri))) {
  382. const elements = this._undoRedoService.getElements(model.uri);
  383. if (elements.past.length > 0 || elements.future.length > 0) {
  384. for (const element of elements.past) {
  385. if (isEditStackElement(element) && element.matchesResource(model.uri)) {
  386. maintainUndoRedoStack = true;
  387. heapSize += element.heapSize(model.uri);
  388. element.setModel(model.uri); // remove reference from text buffer instance
  389. }
  390. }
  391. for (const element of elements.future) {
  392. if (isEditStackElement(element) && element.matchesResource(model.uri)) {
  393. maintainUndoRedoStack = true;
  394. heapSize += element.heapSize(model.uri);
  395. element.setModel(model.uri); // remove reference from text buffer instance
  396. }
  397. }
  398. }
  399. }
  400. const maxMemory = ModelServiceImpl.MAX_MEMORY_FOR_CLOSED_FILES_UNDO_STACK;
  401. if (!maintainUndoRedoStack) {
  402. if (!sharesUndoRedoStack) {
  403. const initialUndoRedoSnapshot = modelData.model.getInitialUndoRedoSnapshot();
  404. if (initialUndoRedoSnapshot !== null) {
  405. this._undoRedoService.restoreSnapshot(initialUndoRedoSnapshot);
  406. }
  407. }
  408. }
  409. else if (!sharesUndoRedoStack && heapSize > maxMemory) {
  410. // the undo stack for this file would never fit in the configured memory, so don't bother with it.
  411. const initialUndoRedoSnapshot = modelData.model.getInitialUndoRedoSnapshot();
  412. if (initialUndoRedoSnapshot !== null) {
  413. this._undoRedoService.restoreSnapshot(initialUndoRedoSnapshot);
  414. }
  415. }
  416. else {
  417. this._ensureDisposedModelsHeapSize(maxMemory - heapSize);
  418. // We only invalidate the elements, but they remain in the undo-redo service.
  419. this._undoRedoService.setElementsValidFlag(model.uri, false, (element) => (isEditStackElement(element) && element.matchesResource(model.uri)));
  420. this._insertDisposedModel(new DisposedModelInfo(model.uri, modelData.model.getInitialUndoRedoSnapshot(), Date.now(), sharesUndoRedoStack, heapSize, computeModelSha1(model), model.getVersionId(), model.getAlternativeVersionId()));
  421. }
  422. delete this._models[modelId];
  423. modelData.dispose();
  424. // clean up cache
  425. delete this._modelCreationOptionsByLanguageAndResource[model.getLanguageId() + model.uri];
  426. this._onModelRemoved.fire(model);
  427. }
  428. _onDidChangeLanguage(model, e) {
  429. const oldModeId = e.oldLanguage;
  430. const newModeId = model.getLanguageId();
  431. const oldOptions = this.getCreationOptions(oldModeId, model.uri, model.isForSimpleWidget);
  432. const newOptions = this.getCreationOptions(newModeId, model.uri, model.isForSimpleWidget);
  433. ModelServiceImpl._setModelOptionsForModel(model, newOptions, oldOptions);
  434. this._onModelModeChanged.fire({ model, oldModeId });
  435. }
  436. };
  437. ModelServiceImpl.MAX_MEMORY_FOR_CLOSED_FILES_UNDO_STACK = 20 * 1024 * 1024;
  438. ModelServiceImpl = __decorate([
  439. __param(0, IConfigurationService),
  440. __param(1, ITextResourcePropertiesService),
  441. __param(2, IThemeService),
  442. __param(3, ILogService),
  443. __param(4, IUndoRedoService),
  444. __param(5, IModeService),
  445. __param(6, ILanguageConfigurationService)
  446. ], ModelServiceImpl);
  447. export { ModelServiceImpl };
  448. export const SEMANTIC_HIGHLIGHTING_SETTING_ID = 'editor.semanticHighlighting';
  449. export function isSemanticColoringEnabled(model, themeService, configurationService) {
  450. var _a;
  451. const setting = (_a = configurationService.getValue(SEMANTIC_HIGHLIGHTING_SETTING_ID, { overrideIdentifier: model.getLanguageId(), resource: model.uri })) === null || _a === void 0 ? void 0 : _a.enabled;
  452. if (typeof setting === 'boolean') {
  453. return setting;
  454. }
  455. return themeService.getColorTheme().semanticHighlighting;
  456. }
  457. class SemanticColoringFeature extends Disposable {
  458. constructor(modelService, themeService, configurationService, semanticStyling) {
  459. super();
  460. this._watchers = Object.create(null);
  461. this._semanticStyling = semanticStyling;
  462. const register = (model) => {
  463. this._watchers[model.uri.toString()] = new ModelSemanticColoring(model, themeService, this._semanticStyling);
  464. };
  465. const deregister = (model, modelSemanticColoring) => {
  466. modelSemanticColoring.dispose();
  467. delete this._watchers[model.uri.toString()];
  468. };
  469. const handleSettingOrThemeChange = () => {
  470. for (let model of modelService.getModels()) {
  471. const curr = this._watchers[model.uri.toString()];
  472. if (isSemanticColoringEnabled(model, themeService, configurationService)) {
  473. if (!curr) {
  474. register(model);
  475. }
  476. }
  477. else {
  478. if (curr) {
  479. deregister(model, curr);
  480. }
  481. }
  482. }
  483. };
  484. this._register(modelService.onModelAdded((model) => {
  485. if (isSemanticColoringEnabled(model, themeService, configurationService)) {
  486. register(model);
  487. }
  488. }));
  489. this._register(modelService.onModelRemoved((model) => {
  490. const curr = this._watchers[model.uri.toString()];
  491. if (curr) {
  492. deregister(model, curr);
  493. }
  494. }));
  495. this._register(configurationService.onDidChangeConfiguration(e => {
  496. if (e.affectsConfiguration(SEMANTIC_HIGHLIGHTING_SETTING_ID)) {
  497. handleSettingOrThemeChange();
  498. }
  499. }));
  500. this._register(themeService.onDidColorThemeChange(handleSettingOrThemeChange));
  501. }
  502. }
  503. class SemanticStyling extends Disposable {
  504. constructor(_themeService, _modeService, _logService) {
  505. super();
  506. this._themeService = _themeService;
  507. this._modeService = _modeService;
  508. this._logService = _logService;
  509. this._caches = new WeakMap();
  510. this._register(this._themeService.onDidColorThemeChange(() => {
  511. this._caches = new WeakMap();
  512. }));
  513. }
  514. get(provider) {
  515. if (!this._caches.has(provider)) {
  516. this._caches.set(provider, new SemanticTokensProviderStyling(provider.getLegend(), this._themeService, this._modeService, this._logService));
  517. }
  518. return this._caches.get(provider);
  519. }
  520. }
  521. class SemanticTokensResponse {
  522. constructor(provider, resultId, data) {
  523. this.provider = provider;
  524. this.resultId = resultId;
  525. this.data = data;
  526. }
  527. dispose() {
  528. this.provider.releaseDocumentSemanticTokens(this.resultId);
  529. }
  530. }
  531. export class ModelSemanticColoring extends Disposable {
  532. constructor(model, themeService, stylingProvider) {
  533. super();
  534. this._isDisposed = false;
  535. this._model = model;
  536. this._semanticStyling = stylingProvider;
  537. this._fetchDocumentSemanticTokens = this._register(new RunOnceScheduler(() => this._fetchDocumentSemanticTokensNow(), ModelSemanticColoring.FETCH_DOCUMENT_SEMANTIC_TOKENS_DELAY));
  538. this._currentDocumentResponse = null;
  539. this._currentDocumentRequestCancellationTokenSource = null;
  540. this._documentProvidersChangeListeners = [];
  541. this._register(this._model.onDidChangeContent(() => {
  542. if (!this._fetchDocumentSemanticTokens.isScheduled()) {
  543. this._fetchDocumentSemanticTokens.schedule();
  544. }
  545. }));
  546. this._register(this._model.onDidChangeLanguage(() => {
  547. // clear any outstanding state
  548. if (this._currentDocumentResponse) {
  549. this._currentDocumentResponse.dispose();
  550. this._currentDocumentResponse = null;
  551. }
  552. if (this._currentDocumentRequestCancellationTokenSource) {
  553. this._currentDocumentRequestCancellationTokenSource.cancel();
  554. this._currentDocumentRequestCancellationTokenSource = null;
  555. }
  556. this._setDocumentSemanticTokens(null, null, null, []);
  557. this._fetchDocumentSemanticTokens.schedule(0);
  558. }));
  559. const bindDocumentChangeListeners = () => {
  560. dispose(this._documentProvidersChangeListeners);
  561. this._documentProvidersChangeListeners = [];
  562. for (const provider of DocumentSemanticTokensProviderRegistry.all(model)) {
  563. if (typeof provider.onDidChange === 'function') {
  564. this._documentProvidersChangeListeners.push(provider.onDidChange(() => this._fetchDocumentSemanticTokens.schedule(0)));
  565. }
  566. }
  567. };
  568. bindDocumentChangeListeners();
  569. this._register(DocumentSemanticTokensProviderRegistry.onDidChange(() => {
  570. bindDocumentChangeListeners();
  571. this._fetchDocumentSemanticTokens.schedule();
  572. }));
  573. this._register(themeService.onDidColorThemeChange(_ => {
  574. // clear out existing tokens
  575. this._setDocumentSemanticTokens(null, null, null, []);
  576. this._fetchDocumentSemanticTokens.schedule();
  577. }));
  578. this._fetchDocumentSemanticTokens.schedule(0);
  579. }
  580. dispose() {
  581. if (this._currentDocumentResponse) {
  582. this._currentDocumentResponse.dispose();
  583. this._currentDocumentResponse = null;
  584. }
  585. if (this._currentDocumentRequestCancellationTokenSource) {
  586. this._currentDocumentRequestCancellationTokenSource.cancel();
  587. this._currentDocumentRequestCancellationTokenSource = null;
  588. }
  589. this._setDocumentSemanticTokens(null, null, null, []);
  590. this._isDisposed = true;
  591. super.dispose();
  592. }
  593. _fetchDocumentSemanticTokensNow() {
  594. if (this._currentDocumentRequestCancellationTokenSource) {
  595. // there is already a request running, let it finish...
  596. return;
  597. }
  598. if (!hasDocumentSemanticTokensProvider(this._model)) {
  599. // there is no provider
  600. if (this._currentDocumentResponse) {
  601. // there are semantic tokens set
  602. this._model.setSemanticTokens(null, false);
  603. }
  604. return;
  605. }
  606. const cancellationTokenSource = new CancellationTokenSource();
  607. const lastProvider = this._currentDocumentResponse ? this._currentDocumentResponse.provider : null;
  608. const lastResultId = this._currentDocumentResponse ? this._currentDocumentResponse.resultId || null : null;
  609. const request = getDocumentSemanticTokens(this._model, lastProvider, lastResultId, cancellationTokenSource.token);
  610. this._currentDocumentRequestCancellationTokenSource = cancellationTokenSource;
  611. const pendingChanges = [];
  612. const contentChangeListener = this._model.onDidChangeContent((e) => {
  613. pendingChanges.push(e);
  614. });
  615. request.then((res) => {
  616. this._currentDocumentRequestCancellationTokenSource = null;
  617. contentChangeListener.dispose();
  618. if (!res) {
  619. this._setDocumentSemanticTokens(null, null, null, pendingChanges);
  620. }
  621. else {
  622. const { provider, tokens } = res;
  623. const styling = this._semanticStyling.get(provider);
  624. this._setDocumentSemanticTokens(provider, tokens || null, styling, pendingChanges);
  625. }
  626. }, (err) => {
  627. const isExpectedError = err && (errors.isPromiseCanceledError(err) || (typeof err.message === 'string' && err.message.indexOf('busy') !== -1));
  628. if (!isExpectedError) {
  629. errors.onUnexpectedError(err);
  630. }
  631. // Semantic tokens eats up all errors and considers errors to mean that the result is temporarily not available
  632. // The API does not have a special error kind to express this...
  633. this._currentDocumentRequestCancellationTokenSource = null;
  634. contentChangeListener.dispose();
  635. if (pendingChanges.length > 0) {
  636. // More changes occurred while the request was running
  637. if (!this._fetchDocumentSemanticTokens.isScheduled()) {
  638. this._fetchDocumentSemanticTokens.schedule();
  639. }
  640. }
  641. });
  642. }
  643. static _copy(src, srcOffset, dest, destOffset, length) {
  644. for (let i = 0; i < length; i++) {
  645. dest[destOffset + i] = src[srcOffset + i];
  646. }
  647. }
  648. _setDocumentSemanticTokens(provider, tokens, styling, pendingChanges) {
  649. const currentResponse = this._currentDocumentResponse;
  650. const rescheduleIfNeeded = () => {
  651. if (pendingChanges.length > 0 && !this._fetchDocumentSemanticTokens.isScheduled()) {
  652. this._fetchDocumentSemanticTokens.schedule();
  653. }
  654. };
  655. if (this._currentDocumentResponse) {
  656. this._currentDocumentResponse.dispose();
  657. this._currentDocumentResponse = null;
  658. }
  659. if (this._isDisposed) {
  660. // disposed!
  661. if (provider && tokens) {
  662. provider.releaseDocumentSemanticTokens(tokens.resultId);
  663. }
  664. return;
  665. }
  666. if (!provider || !styling) {
  667. this._model.setSemanticTokens(null, false);
  668. return;
  669. }
  670. if (!tokens) {
  671. this._model.setSemanticTokens(null, true);
  672. rescheduleIfNeeded();
  673. return;
  674. }
  675. if (isSemanticTokensEdits(tokens)) {
  676. if (!currentResponse) {
  677. // not possible!
  678. this._model.setSemanticTokens(null, true);
  679. return;
  680. }
  681. if (tokens.edits.length === 0) {
  682. // nothing to do!
  683. tokens = {
  684. resultId: tokens.resultId,
  685. data: currentResponse.data
  686. };
  687. }
  688. else {
  689. let deltaLength = 0;
  690. for (const edit of tokens.edits) {
  691. deltaLength += (edit.data ? edit.data.length : 0) - edit.deleteCount;
  692. }
  693. const srcData = currentResponse.data;
  694. const destData = new Uint32Array(srcData.length + deltaLength);
  695. let srcLastStart = srcData.length;
  696. let destLastStart = destData.length;
  697. for (let i = tokens.edits.length - 1; i >= 0; i--) {
  698. const edit = tokens.edits[i];
  699. const copyCount = srcLastStart - (edit.start + edit.deleteCount);
  700. if (copyCount > 0) {
  701. ModelSemanticColoring._copy(srcData, srcLastStart - copyCount, destData, destLastStart - copyCount, copyCount);
  702. destLastStart -= copyCount;
  703. }
  704. if (edit.data) {
  705. ModelSemanticColoring._copy(edit.data, 0, destData, destLastStart - edit.data.length, edit.data.length);
  706. destLastStart -= edit.data.length;
  707. }
  708. srcLastStart = edit.start;
  709. }
  710. if (srcLastStart > 0) {
  711. ModelSemanticColoring._copy(srcData, 0, destData, 0, srcLastStart);
  712. }
  713. tokens = {
  714. resultId: tokens.resultId,
  715. data: destData
  716. };
  717. }
  718. }
  719. if (isSemanticTokens(tokens)) {
  720. this._currentDocumentResponse = new SemanticTokensResponse(provider, tokens.resultId, tokens.data);
  721. const result = toMultilineTokens2(tokens, styling, this._model.getLanguageId());
  722. // Adjust incoming semantic tokens
  723. if (pendingChanges.length > 0) {
  724. // More changes occurred while the request was running
  725. // We need to:
  726. // 1. Adjust incoming semantic tokens
  727. // 2. Request them again
  728. for (const change of pendingChanges) {
  729. for (const area of result) {
  730. for (const singleChange of change.changes) {
  731. area.applyEdit(singleChange.range, singleChange.text);
  732. }
  733. }
  734. }
  735. }
  736. this._model.setSemanticTokens(result, true);
  737. }
  738. else {
  739. this._model.setSemanticTokens(null, true);
  740. }
  741. rescheduleIfNeeded();
  742. }
  743. }
  744. ModelSemanticColoring.FETCH_DOCUMENT_SEMANTIC_TOKENS_DELAY = 300;