text-editor-registry-spec.js 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649
  1. const TextEditorRegistry = require('../src/text-editor-registry');
  2. const TextEditor = require('../src/text-editor');
  3. const TextBuffer = require('text-buffer');
  4. const { Point, Range } = TextBuffer;
  5. const dedent = require('dedent');
  6. const NullGrammar = require('../src/null-grammar');
  7. describe('TextEditorRegistry', function() {
  8. let registry, editor, initialPackageActivation;
  9. beforeEach(function() {
  10. initialPackageActivation = Promise.resolve();
  11. registry = new TextEditorRegistry({
  12. assert: atom.assert,
  13. config: atom.config,
  14. grammarRegistry: atom.grammars,
  15. packageManager: {
  16. getActivatePromise() {
  17. return initialPackageActivation;
  18. }
  19. }
  20. });
  21. editor = new TextEditor({ autoHeight: false });
  22. expect(
  23. atom.grammars.assignLanguageMode(editor, 'text.plain.null-grammar')
  24. ).toBe(true);
  25. });
  26. afterEach(function() {
  27. registry.destroy();
  28. });
  29. describe('.add', function() {
  30. it('adds an editor to the list of registered editors', function() {
  31. registry.add(editor);
  32. expect(editor.registered).toBe(true);
  33. expect(registry.editors.size).toBe(1);
  34. expect(registry.editors.has(editor)).toBe(true);
  35. });
  36. it('returns a Disposable that can unregister the editor', function() {
  37. const disposable = registry.add(editor);
  38. expect(registry.editors.size).toBe(1);
  39. disposable.dispose();
  40. expect(registry.editors.size).toBe(0);
  41. expect(editor.registered).toBe(false);
  42. expect(retainedEditorCount(registry)).toBe(0);
  43. });
  44. });
  45. describe('.observe', function() {
  46. it('calls the callback for current and future editors until unsubscribed', function() {
  47. const spy = jasmine.createSpy();
  48. const [editor1, editor2, editor3] = [{}, {}, {}];
  49. registry.add(editor1);
  50. const subscription = registry.observe(spy);
  51. expect(spy.calls.length).toBe(1);
  52. registry.add(editor2);
  53. expect(spy.calls.length).toBe(2);
  54. expect(spy.argsForCall[0][0]).toBe(editor1);
  55. expect(spy.argsForCall[1][0]).toBe(editor2);
  56. subscription.dispose();
  57. registry.add(editor3);
  58. expect(spy.calls.length).toBe(2);
  59. });
  60. });
  61. describe('.build', function() {
  62. it('constructs a TextEditor with the right parameters based on its path and text', function() {
  63. atom.config.set('editor.tabLength', 8, { scope: '.source.js' });
  64. const languageMode = {
  65. grammar: NullGrammar,
  66. onDidChangeHighlighting: jasmine.createSpy()
  67. };
  68. const buffer = new TextBuffer({ filePath: 'test.js' });
  69. buffer.setLanguageMode(languageMode);
  70. const editor = registry.build({
  71. buffer
  72. });
  73. expect(editor.getTabLength()).toBe(8);
  74. expect(editor.getGrammar()).toEqual(NullGrammar);
  75. expect(languageMode.onDidChangeHighlighting.calls.length).toBe(1);
  76. });
  77. });
  78. describe('.getActiveTextEditor', function() {
  79. it('gets the currently focused text editor', function() {
  80. const disposable = registry.add(editor);
  81. var editorElement = editor.getElement();
  82. jasmine.attachToDOM(editorElement);
  83. editorElement.focus();
  84. expect(registry.getActiveTextEditor()).toBe(editor);
  85. disposable.dispose();
  86. });
  87. });
  88. describe('.maintainConfig(editor)', function() {
  89. it('does not update the editor when config settings change for unrelated scope selectors', async function() {
  90. await atom.packages.activatePackage('language-javascript');
  91. const editor2 = new TextEditor();
  92. atom.grammars.assignLanguageMode(editor2, 'source.js');
  93. registry.maintainConfig(editor);
  94. registry.maintainConfig(editor2);
  95. await initialPackageActivation;
  96. expect(editor.getRootScopeDescriptor().getScopesArray()).toEqual([
  97. 'text.plain.null-grammar'
  98. ]);
  99. expect(editor2.getRootScopeDescriptor().getScopesArray()).toEqual([
  100. 'source.js'
  101. ]);
  102. expect(editor.getEncoding()).toBe('utf8');
  103. expect(editor2.getEncoding()).toBe('utf8');
  104. atom.config.set('core.fileEncoding', 'utf16le', {
  105. scopeSelector: '.text.plain.null-grammar'
  106. });
  107. atom.config.set('core.fileEncoding', 'utf16be', {
  108. scopeSelector: '.source.js'
  109. });
  110. expect(editor.getEncoding()).toBe('utf16le');
  111. expect(editor2.getEncoding()).toBe('utf16be');
  112. });
  113. it('does not update the editor before the initial packages have loaded', async function() {
  114. let resolveActivatePromise;
  115. initialPackageActivation = new Promise(resolve => {
  116. resolveActivatePromise = resolve;
  117. });
  118. atom.config.set('core.fileEncoding', 'utf16le');
  119. registry.maintainConfig(editor);
  120. await Promise.resolve();
  121. expect(editor.getEncoding()).toBe('utf8');
  122. atom.config.set('core.fileEncoding', 'utf16be');
  123. await Promise.resolve();
  124. expect(editor.getEncoding()).toBe('utf8');
  125. resolveActivatePromise();
  126. await initialPackageActivation;
  127. expect(editor.getEncoding()).toBe('utf16be');
  128. });
  129. it("updates the editor's settings when its grammar changes", async function() {
  130. await atom.packages.activatePackage('language-javascript');
  131. registry.maintainConfig(editor);
  132. await initialPackageActivation;
  133. atom.config.set('core.fileEncoding', 'utf16be', {
  134. scopeSelector: '.source.js'
  135. });
  136. expect(editor.getEncoding()).toBe('utf8');
  137. atom.config.set('core.fileEncoding', 'utf16le', {
  138. scopeSelector: '.source.js'
  139. });
  140. expect(editor.getEncoding()).toBe('utf8');
  141. atom.grammars.assignLanguageMode(editor, 'source.js');
  142. await initialPackageActivation;
  143. expect(editor.getEncoding()).toBe('utf16le');
  144. atom.config.set('core.fileEncoding', 'utf16be', {
  145. scopeSelector: '.source.js'
  146. });
  147. expect(editor.getEncoding()).toBe('utf16be');
  148. atom.grammars.assignLanguageMode(editor, 'text.plain.null-grammar');
  149. await initialPackageActivation;
  150. expect(editor.getEncoding()).toBe('utf8');
  151. });
  152. it("preserves editor settings that haven't changed between previous and current language modes", async function() {
  153. await atom.packages.activatePackage('language-javascript');
  154. registry.maintainConfig(editor);
  155. await initialPackageActivation;
  156. expect(editor.getEncoding()).toBe('utf8');
  157. editor.setEncoding('utf16le');
  158. expect(editor.getEncoding()).toBe('utf16le');
  159. expect(editor.isSoftWrapped()).toBe(false);
  160. editor.setSoftWrapped(true);
  161. expect(editor.isSoftWrapped()).toBe(true);
  162. atom.grammars.assignLanguageMode(editor, 'source.js');
  163. await initialPackageActivation;
  164. expect(editor.getEncoding()).toBe('utf16le');
  165. expect(editor.isSoftWrapped()).toBe(true);
  166. });
  167. it('updates editor settings that have changed between previous and current language modes', async function() {
  168. await atom.packages.activatePackage('language-javascript');
  169. registry.maintainConfig(editor);
  170. await initialPackageActivation;
  171. expect(editor.getEncoding()).toBe('utf8');
  172. atom.config.set('core.fileEncoding', 'utf16be', {
  173. scopeSelector: '.text.plain.null-grammar'
  174. });
  175. atom.config.set('core.fileEncoding', 'utf16le', {
  176. scopeSelector: '.source.js'
  177. });
  178. expect(editor.getEncoding()).toBe('utf16be');
  179. editor.setEncoding('utf8');
  180. expect(editor.getEncoding()).toBe('utf8');
  181. atom.grammars.assignLanguageMode(editor, 'source.js');
  182. await initialPackageActivation;
  183. expect(editor.getEncoding()).toBe('utf16le');
  184. });
  185. it("returns a disposable that can be used to stop the registry from updating the editor's config", async function() {
  186. await atom.packages.activatePackage('language-javascript');
  187. const previousSubscriptionCount = getSubscriptionCount(editor);
  188. const disposable = registry.maintainConfig(editor);
  189. await initialPackageActivation;
  190. expect(getSubscriptionCount(editor)).toBeGreaterThan(
  191. previousSubscriptionCount
  192. );
  193. expect(registry.editorsWithMaintainedConfig.size).toBe(1);
  194. atom.config.set('core.fileEncoding', 'utf16be');
  195. expect(editor.getEncoding()).toBe('utf16be');
  196. atom.config.set('core.fileEncoding', 'utf8');
  197. expect(editor.getEncoding()).toBe('utf8');
  198. disposable.dispose();
  199. atom.config.set('core.fileEncoding', 'utf16be');
  200. expect(editor.getEncoding()).toBe('utf8');
  201. expect(getSubscriptionCount(editor)).toBe(previousSubscriptionCount);
  202. expect(retainedEditorCount(registry)).toBe(0);
  203. });
  204. it('sets the encoding based on the config', async function() {
  205. editor.update({ encoding: 'utf8' });
  206. expect(editor.getEncoding()).toBe('utf8');
  207. atom.config.set('core.fileEncoding', 'utf16le');
  208. registry.maintainConfig(editor);
  209. await initialPackageActivation;
  210. expect(editor.getEncoding()).toBe('utf16le');
  211. atom.config.set('core.fileEncoding', 'utf8');
  212. expect(editor.getEncoding()).toBe('utf8');
  213. });
  214. it('sets the tab length based on the config', async function() {
  215. editor.update({ tabLength: 4 });
  216. expect(editor.getTabLength()).toBe(4);
  217. atom.config.set('editor.tabLength', 8);
  218. registry.maintainConfig(editor);
  219. await initialPackageActivation;
  220. expect(editor.getTabLength()).toBe(8);
  221. atom.config.set('editor.tabLength', 4);
  222. expect(editor.getTabLength()).toBe(4);
  223. });
  224. it('enables soft tabs when the tabType config setting is "soft"', async function() {
  225. atom.config.set('editor.tabType', 'soft');
  226. registry.maintainConfig(editor);
  227. await initialPackageActivation;
  228. expect(editor.getSoftTabs()).toBe(true);
  229. });
  230. it('disables soft tabs when the tabType config setting is "hard"', async function() {
  231. atom.config.set('editor.tabType', 'hard');
  232. registry.maintainConfig(editor);
  233. await initialPackageActivation;
  234. expect(editor.getSoftTabs()).toBe(false);
  235. });
  236. describe('when the "tabType" config setting is "auto"', function() {
  237. it("enables or disables soft tabs based on the editor's content", async function() {
  238. await initialPackageActivation;
  239. await atom.packages.activatePackage('language-javascript');
  240. atom.grammars.assignLanguageMode(editor, 'source.js');
  241. atom.config.set('editor.tabType', 'auto');
  242. await initialPackageActivation;
  243. editor.setText(dedent`
  244. {
  245. hello;
  246. }
  247. `);
  248. let disposable = registry.maintainConfig(editor);
  249. expect(editor.getSoftTabs()).toBe(true);
  250. /* eslint-disable no-tabs */
  251. editor.setText(dedent`
  252. {
  253. hello;
  254. }
  255. `);
  256. /* eslint-enable no-tabs */
  257. disposable.dispose();
  258. disposable = registry.maintainConfig(editor);
  259. expect(editor.getSoftTabs()).toBe(false);
  260. editor.setTextInBufferRange(
  261. new Range(Point.ZERO, Point.ZERO),
  262. dedent`
  263. /*
  264. * Comment with a leading space.
  265. */
  266. ` + '\n'
  267. );
  268. disposable.dispose();
  269. disposable = registry.maintainConfig(editor);
  270. expect(editor.getSoftTabs()).toBe(false);
  271. /* eslint-disable no-tabs */
  272. editor.setText(dedent`
  273. /*
  274. * Comment with a leading space.
  275. */
  276. {
  277. hello;
  278. }
  279. `);
  280. /* eslint-enable no-tabs */
  281. disposable.dispose();
  282. disposable = registry.maintainConfig(editor);
  283. expect(editor.getSoftTabs()).toBe(false);
  284. editor.setText(dedent`
  285. /*
  286. * Comment with a leading space.
  287. */
  288. {
  289. hello;
  290. }
  291. `);
  292. disposable.dispose();
  293. disposable = registry.maintainConfig(editor);
  294. expect(editor.getSoftTabs()).toBe(true);
  295. });
  296. });
  297. describe('when the "tabType" config setting is "auto"', function() {
  298. it('enables or disables soft tabs based on the "softTabs" config setting', async function() {
  299. registry.maintainConfig(editor);
  300. await initialPackageActivation;
  301. editor.setText('abc\ndef');
  302. atom.config.set('editor.softTabs', true);
  303. atom.config.set('editor.tabType', 'auto');
  304. expect(editor.getSoftTabs()).toBe(true);
  305. atom.config.set('editor.softTabs', false);
  306. expect(editor.getSoftTabs()).toBe(false);
  307. });
  308. });
  309. it('enables or disables soft tabs based on the config', async function() {
  310. editor.update({ softTabs: true });
  311. expect(editor.getSoftTabs()).toBe(true);
  312. atom.config.set('editor.tabType', 'hard');
  313. registry.maintainConfig(editor);
  314. await initialPackageActivation;
  315. expect(editor.getSoftTabs()).toBe(false);
  316. atom.config.set('editor.tabType', 'soft');
  317. expect(editor.getSoftTabs()).toBe(true);
  318. atom.config.set('editor.tabType', 'auto');
  319. atom.config.set('editor.softTabs', true);
  320. expect(editor.getSoftTabs()).toBe(true);
  321. });
  322. it('enables or disables atomic soft tabs based on the config', async function() {
  323. editor.update({ atomicSoftTabs: true });
  324. expect(editor.hasAtomicSoftTabs()).toBe(true);
  325. atom.config.set('editor.atomicSoftTabs', false);
  326. registry.maintainConfig(editor);
  327. await initialPackageActivation;
  328. expect(editor.hasAtomicSoftTabs()).toBe(false);
  329. atom.config.set('editor.atomicSoftTabs', true);
  330. expect(editor.hasAtomicSoftTabs()).toBe(true);
  331. });
  332. it('enables or disables cursor on selection visibility based on the config', async function() {
  333. editor.update({ showCursorOnSelection: true });
  334. expect(editor.getShowCursorOnSelection()).toBe(true);
  335. atom.config.set('editor.showCursorOnSelection', false);
  336. registry.maintainConfig(editor);
  337. await initialPackageActivation;
  338. expect(editor.getShowCursorOnSelection()).toBe(false);
  339. atom.config.set('editor.showCursorOnSelection', true);
  340. expect(editor.getShowCursorOnSelection()).toBe(true);
  341. });
  342. it('enables or disables line numbers based on the config', async function() {
  343. editor.update({ showLineNumbers: true });
  344. expect(editor.showLineNumbers).toBe(true);
  345. atom.config.set('editor.showLineNumbers', false);
  346. registry.maintainConfig(editor);
  347. await initialPackageActivation;
  348. expect(editor.showLineNumbers).toBe(false);
  349. atom.config.set('editor.showLineNumbers', true);
  350. expect(editor.showLineNumbers).toBe(true);
  351. });
  352. it('sets the invisibles based on the config', async function() {
  353. const invisibles1 = { tab: 'a', cr: false, eol: false, space: false };
  354. const invisibles2 = { tab: 'b', cr: false, eol: false, space: false };
  355. editor.update({
  356. showInvisibles: true,
  357. invisibles: invisibles1
  358. });
  359. expect(editor.getInvisibles()).toEqual(invisibles1);
  360. atom.config.set('editor.showInvisibles', true);
  361. atom.config.set('editor.invisibles', invisibles2);
  362. registry.maintainConfig(editor);
  363. await initialPackageActivation;
  364. expect(editor.getInvisibles()).toEqual(invisibles2);
  365. atom.config.set('editor.invisibles', invisibles1);
  366. expect(editor.getInvisibles()).toEqual(invisibles1);
  367. atom.config.set('editor.showInvisibles', false);
  368. expect(editor.getInvisibles()).toEqual({});
  369. });
  370. it('enables or disables the indent guide based on the config', async function() {
  371. editor.update({ showIndentGuide: true });
  372. expect(editor.doesShowIndentGuide()).toBe(true);
  373. atom.config.set('editor.showIndentGuide', false);
  374. registry.maintainConfig(editor);
  375. await initialPackageActivation;
  376. expect(editor.doesShowIndentGuide()).toBe(false);
  377. atom.config.set('editor.showIndentGuide', true);
  378. expect(editor.doesShowIndentGuide()).toBe(true);
  379. });
  380. it('enables or disables soft wrap based on the config', async function() {
  381. editor.update({ softWrapped: true });
  382. expect(editor.isSoftWrapped()).toBe(true);
  383. atom.config.set('editor.softWrap', false);
  384. registry.maintainConfig(editor);
  385. await initialPackageActivation;
  386. expect(editor.isSoftWrapped()).toBe(false);
  387. atom.config.set('editor.softWrap', true);
  388. expect(editor.isSoftWrapped()).toBe(true);
  389. });
  390. it('sets the soft wrap indent length based on the config', async function() {
  391. editor.update({ softWrapHangingIndentLength: 4 });
  392. expect(editor.getSoftWrapHangingIndentLength()).toBe(4);
  393. atom.config.set('editor.softWrapHangingIndent', 2);
  394. registry.maintainConfig(editor);
  395. await initialPackageActivation;
  396. expect(editor.getSoftWrapHangingIndentLength()).toBe(2);
  397. atom.config.set('editor.softWrapHangingIndent', 4);
  398. expect(editor.getSoftWrapHangingIndentLength()).toBe(4);
  399. });
  400. it('enables or disables preferred line length-based soft wrap based on the config', async function() {
  401. editor.update({
  402. softWrapped: true,
  403. preferredLineLength: 80,
  404. editorWidthInChars: 120,
  405. softWrapAtPreferredLineLength: true
  406. });
  407. expect(editor.getSoftWrapColumn()).toBe(80);
  408. atom.config.set('editor.softWrap', true);
  409. atom.config.set('editor.softWrapAtPreferredLineLength', false);
  410. registry.maintainConfig(editor);
  411. await initialPackageActivation;
  412. expect(editor.getSoftWrapColumn()).toBe(120);
  413. atom.config.set('editor.softWrapAtPreferredLineLength', true);
  414. expect(editor.getSoftWrapColumn()).toBe(80);
  415. });
  416. it('allows for custom definition of maximum soft wrap based on config', async function() {
  417. editor.update({
  418. softWrapped: false,
  419. maxScreenLineLength: 1500
  420. });
  421. expect(editor.getSoftWrapColumn()).toBe(1500);
  422. atom.config.set('editor.softWrap', false);
  423. atom.config.set('editor.maxScreenLineLength', 500);
  424. registry.maintainConfig(editor);
  425. await initialPackageActivation;
  426. expect(editor.getSoftWrapColumn()).toBe(500);
  427. });
  428. it('sets the preferred line length based on the config', async function() {
  429. editor.update({ preferredLineLength: 80 });
  430. expect(editor.getPreferredLineLength()).toBe(80);
  431. atom.config.set('editor.preferredLineLength', 110);
  432. registry.maintainConfig(editor);
  433. await initialPackageActivation;
  434. expect(editor.getPreferredLineLength()).toBe(110);
  435. atom.config.set('editor.preferredLineLength', 80);
  436. expect(editor.getPreferredLineLength()).toBe(80);
  437. });
  438. it('enables or disables auto-indent based on the config', async function() {
  439. editor.update({ autoIndent: true });
  440. expect(editor.shouldAutoIndent()).toBe(true);
  441. atom.config.set('editor.autoIndent', false);
  442. registry.maintainConfig(editor);
  443. await initialPackageActivation;
  444. expect(editor.shouldAutoIndent()).toBe(false);
  445. atom.config.set('editor.autoIndent', true);
  446. expect(editor.shouldAutoIndent()).toBe(true);
  447. });
  448. it('enables or disables auto-indent-on-paste based on the config', async function() {
  449. editor.update({ autoIndentOnPaste: true });
  450. expect(editor.shouldAutoIndentOnPaste()).toBe(true);
  451. atom.config.set('editor.autoIndentOnPaste', false);
  452. registry.maintainConfig(editor);
  453. await initialPackageActivation;
  454. expect(editor.shouldAutoIndentOnPaste()).toBe(false);
  455. atom.config.set('editor.autoIndentOnPaste', true);
  456. expect(editor.shouldAutoIndentOnPaste()).toBe(true);
  457. });
  458. it('enables or disables scrolling past the end of the buffer based on the config', async function() {
  459. editor.update({ scrollPastEnd: true });
  460. expect(editor.getScrollPastEnd()).toBe(true);
  461. atom.config.set('editor.scrollPastEnd', false);
  462. registry.maintainConfig(editor);
  463. await initialPackageActivation;
  464. expect(editor.getScrollPastEnd()).toBe(false);
  465. atom.config.set('editor.scrollPastEnd', true);
  466. expect(editor.getScrollPastEnd()).toBe(true);
  467. });
  468. it('sets the undo grouping interval based on the config', async function() {
  469. editor.update({ undoGroupingInterval: 300 });
  470. expect(editor.getUndoGroupingInterval()).toBe(300);
  471. atom.config.set('editor.undoGroupingInterval', 600);
  472. registry.maintainConfig(editor);
  473. await initialPackageActivation;
  474. expect(editor.getUndoGroupingInterval()).toBe(600);
  475. atom.config.set('editor.undoGroupingInterval', 300);
  476. expect(editor.getUndoGroupingInterval()).toBe(300);
  477. });
  478. it('sets the scroll sensitivity based on the config', async function() {
  479. editor.update({ scrollSensitivity: 50 });
  480. expect(editor.getScrollSensitivity()).toBe(50);
  481. atom.config.set('editor.scrollSensitivity', 60);
  482. registry.maintainConfig(editor);
  483. await initialPackageActivation;
  484. expect(editor.getScrollSensitivity()).toBe(60);
  485. atom.config.set('editor.scrollSensitivity', 70);
  486. expect(editor.getScrollSensitivity()).toBe(70);
  487. });
  488. describe('when called twice with a given editor', function() {
  489. it('does nothing the second time', async function() {
  490. editor.update({ scrollSensitivity: 50 });
  491. const disposable1 = registry.maintainConfig(editor);
  492. const disposable2 = registry.maintainConfig(editor);
  493. await initialPackageActivation;
  494. atom.config.set('editor.scrollSensitivity', 60);
  495. expect(editor.getScrollSensitivity()).toBe(60);
  496. disposable2.dispose();
  497. atom.config.set('editor.scrollSensitivity', 70);
  498. expect(editor.getScrollSensitivity()).toBe(70);
  499. disposable1.dispose();
  500. atom.config.set('editor.scrollSensitivity', 80);
  501. expect(editor.getScrollSensitivity()).toBe(70);
  502. });
  503. });
  504. });
  505. });
  506. function getSubscriptionCount(editor) {
  507. return (
  508. editor.emitter.getTotalListenerCount() +
  509. editor.tokenizedBuffer.emitter.getTotalListenerCount() +
  510. editor.buffer.emitter.getTotalListenerCount() +
  511. editor.displayLayer.emitter.getTotalListenerCount()
  512. );
  513. }
  514. function retainedEditorCount(registry) {
  515. const editors = new Set();
  516. registry.editors.forEach(e => editors.add(e));
  517. registry.editorsWithMaintainedConfig.forEach(e => editors.add(e));
  518. registry.editorsWithMaintainedGrammar.forEach(e => editors.add(e));
  519. return editors.size;
  520. }