f66a8394dae7e3581e5a5d84849addea33c6208bf12cea1e496e235563f6cae600c1e19d7da72891808abae0207f96d2478efd2c39abd05f005e3785b992f5 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122
  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 { CachedFunction } from '../../../../base/common/cache.js';
  6. import { BugIndicatingError } from '../../../../base/common/errors.js';
  7. /**
  8. * Captures all bracket related configurations for a single language.
  9. * Immutable.
  10. */
  11. export class LanguageBracketsConfiguration {
  12. constructor(languageId, config) {
  13. this.languageId = languageId;
  14. let brackets;
  15. // Prefer colorized bracket pairs, as they are more accurate.
  16. // TODO@hediet: Deprecate `colorizedBracketPairs` and increase accuracy for brackets.
  17. if (config.colorizedBracketPairs) {
  18. brackets = filterValidBrackets(config.colorizedBracketPairs.map(b => [b[0], b[1]]));
  19. }
  20. else if (config.brackets) {
  21. brackets = filterValidBrackets(config.brackets
  22. .map((b) => [b[0], b[1]])
  23. // Many languages set < ... > as bracket pair, even though they also use it as comparison operator.
  24. // This leads to problems when colorizing this bracket, so we exclude it by default.
  25. // Languages can still override this by configuring `colorizedBracketPairs`
  26. // https://github.com/microsoft/vscode/issues/132476
  27. .filter((p) => !(p[0] === '<' && p[1] === '>')));
  28. }
  29. else {
  30. brackets = [];
  31. }
  32. const openingBracketInfos = new CachedFunction((bracket) => {
  33. const closing = new Set();
  34. return {
  35. info: new OpeningBracketKind(this, bracket, closing),
  36. closing,
  37. };
  38. });
  39. const closingBracketInfos = new CachedFunction((bracket) => {
  40. const opening = new Set();
  41. return {
  42. info: new ClosingBracketKind(this, bracket, opening),
  43. opening,
  44. };
  45. });
  46. for (const [open, close] of brackets) {
  47. const opening = openingBracketInfos.get(open);
  48. const closing = closingBracketInfos.get(close);
  49. opening.closing.add(closing.info);
  50. closing.opening.add(opening.info);
  51. }
  52. this._openingBrackets = new Map([...openingBracketInfos.cachedValues].map(([k, v]) => [k, v.info]));
  53. this._closingBrackets = new Map([...closingBracketInfos.cachedValues].map(([k, v]) => [k, v.info]));
  54. }
  55. /**
  56. * No two brackets have the same bracket text.
  57. */
  58. get openingBrackets() {
  59. return [...this._openingBrackets.values()];
  60. }
  61. /**
  62. * No two brackets have the same bracket text.
  63. */
  64. get closingBrackets() {
  65. return [...this._closingBrackets.values()];
  66. }
  67. getOpeningBracketInfo(bracketText) {
  68. return this._openingBrackets.get(bracketText);
  69. }
  70. getClosingBracketInfo(bracketText) {
  71. return this._closingBrackets.get(bracketText);
  72. }
  73. getBracketInfo(bracketText) {
  74. return this.getOpeningBracketInfo(bracketText) || this.getClosingBracketInfo(bracketText);
  75. }
  76. }
  77. function filterValidBrackets(bracketPairs) {
  78. return bracketPairs.filter(([open, close]) => open !== '' && close !== '');
  79. }
  80. export class BracketKindBase {
  81. constructor(config, bracketText) {
  82. this.config = config;
  83. this.bracketText = bracketText;
  84. }
  85. get languageId() {
  86. return this.config.languageId;
  87. }
  88. }
  89. export class OpeningBracketKind extends BracketKindBase {
  90. constructor(config, bracketText, openedBrackets) {
  91. super(config, bracketText);
  92. this.openedBrackets = openedBrackets;
  93. this.isOpeningBracket = true;
  94. }
  95. }
  96. export class ClosingBracketKind extends BracketKindBase {
  97. constructor(config, bracketText,
  98. /**
  99. * Non empty array of all opening brackets this bracket closes.
  100. */
  101. closedBrackets) {
  102. super(config, bracketText);
  103. this.closedBrackets = closedBrackets;
  104. this.isOpeningBracket = false;
  105. }
  106. /**
  107. * Checks if this bracket closes the given other bracket.
  108. * Brackets from other language configuration can be used (they will always return false).
  109. * If other is a bracket with the same language id, they have to be from the same configuration.
  110. */
  111. closes(other) {
  112. if (other.languageId === this.languageId) {
  113. if (other['config'] !== this.config) {
  114. throw new BugIndicatingError('Brackets from different language configuration cannot be used.');
  115. }
  116. }
  117. return this.closedBrackets.has(other);
  118. }
  119. getClosedBrackets() {
  120. return [...this.closedBrackets];
  121. }
  122. }