2c6137d5cf2c1267ff1057698c0a2ac1919c3e9d5fa3303c7e0b435ab164ac49471fd40d828558887e9a4566474da79d0f844ec204138ec43ad5206fdbdf60 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109
  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. import { Emitter } from '../../../base/common/event.js';
  6. import { Disposable } from '../../../base/common/lifecycle.js';
  7. import { LanguagesRegistry } from './languagesRegistry.js';
  8. import { firstOrDefault } from '../../../base/common/arrays.js';
  9. import { TokenizationRegistry } from '../languages.js';
  10. import { PLAINTEXT_LANGUAGE_ID } from '../languages/modesRegistry.js';
  11. export class LanguageService extends Disposable {
  12. constructor(warnOnOverwrite = false) {
  13. super();
  14. this._onDidEncounterLanguage = this._register(new Emitter());
  15. this.onDidEncounterLanguage = this._onDidEncounterLanguage.event;
  16. this._onDidChange = this._register(new Emitter({ leakWarningThreshold: 200 /* https://github.com/microsoft/vscode/issues/119968 */ }));
  17. this.onDidChange = this._onDidChange.event;
  18. LanguageService.instanceCount++;
  19. this._encounteredLanguages = new Set();
  20. this._registry = this._register(new LanguagesRegistry(true, warnOnOverwrite));
  21. this.languageIdCodec = this._registry.languageIdCodec;
  22. this._register(this._registry.onDidChange(() => this._onDidChange.fire()));
  23. }
  24. dispose() {
  25. LanguageService.instanceCount--;
  26. super.dispose();
  27. }
  28. isRegisteredLanguageId(languageId) {
  29. return this._registry.isRegisteredLanguageId(languageId);
  30. }
  31. getLanguageIdByLanguageName(languageName) {
  32. return this._registry.getLanguageIdByLanguageName(languageName);
  33. }
  34. getLanguageIdByMimeType(mimeType) {
  35. return this._registry.getLanguageIdByMimeType(mimeType);
  36. }
  37. guessLanguageIdByFilepathOrFirstLine(resource, firstLine) {
  38. const languageIds = this._registry.guessLanguageIdByFilepathOrFirstLine(resource, firstLine);
  39. return firstOrDefault(languageIds, null);
  40. }
  41. createById(languageId) {
  42. return new LanguageSelection(this.onDidChange, () => {
  43. return this._createAndGetLanguageIdentifier(languageId);
  44. });
  45. }
  46. createByFilepathOrFirstLine(resource, firstLine) {
  47. return new LanguageSelection(this.onDidChange, () => {
  48. const languageId = this.guessLanguageIdByFilepathOrFirstLine(resource, firstLine);
  49. return this._createAndGetLanguageIdentifier(languageId);
  50. });
  51. }
  52. _createAndGetLanguageIdentifier(languageId) {
  53. if (!languageId || !this.isRegisteredLanguageId(languageId)) {
  54. // Fall back to plain text if language is unknown
  55. languageId = PLAINTEXT_LANGUAGE_ID;
  56. }
  57. if (!this._encounteredLanguages.has(languageId)) {
  58. this._encounteredLanguages.add(languageId);
  59. // Ensure tokenizers are created
  60. TokenizationRegistry.getOrCreate(languageId);
  61. // Fire event
  62. this._onDidEncounterLanguage.fire(languageId);
  63. }
  64. return languageId;
  65. }
  66. }
  67. LanguageService.instanceCount = 0;
  68. class LanguageSelection {
  69. constructor(_onDidChangeLanguages, _selector) {
  70. this._onDidChangeLanguages = _onDidChangeLanguages;
  71. this._selector = _selector;
  72. this._listener = null;
  73. this._emitter = null;
  74. this.languageId = this._selector();
  75. }
  76. _dispose() {
  77. if (this._listener) {
  78. this._listener.dispose();
  79. this._listener = null;
  80. }
  81. if (this._emitter) {
  82. this._emitter.dispose();
  83. this._emitter = null;
  84. }
  85. }
  86. get onDidChange() {
  87. if (!this._listener) {
  88. this._listener = this._onDidChangeLanguages(() => this._evaluate());
  89. }
  90. if (!this._emitter) {
  91. this._emitter = new Emitter({
  92. onLastListenerRemove: () => {
  93. this._dispose();
  94. }
  95. });
  96. }
  97. return this._emitter.event;
  98. }
  99. _evaluate() {
  100. var _a;
  101. const languageId = this._selector();
  102. if (languageId === this.languageId) {
  103. // no change
  104. return;
  105. }
  106. this.languageId = languageId;
  107. (_a = this._emitter) === null || _a === void 0 ? void 0 : _a.fire(this.languageId);
  108. }
  109. }