tokenization.ts 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  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 * as json from 'jsonc-parser';
  6. import { languages } from './fillers/monaco-editor-core';
  7. export function createTokenizationSupport(
  8. supportComments: boolean
  9. ): languages.TokensProvider {
  10. return {
  11. getInitialState: () => new JSONState(null, null),
  12. tokenize: (line, state, offsetDelta?, stopAtOffset?) =>
  13. tokenize(
  14. supportComments,
  15. line,
  16. <JSONState>state,
  17. offsetDelta,
  18. stopAtOffset
  19. )
  20. };
  21. }
  22. export const TOKEN_DELIM_OBJECT = 'delimiter.bracket.json';
  23. export const TOKEN_DELIM_ARRAY = 'delimiter.array.json';
  24. export const TOKEN_DELIM_COLON = 'delimiter.colon.json';
  25. export const TOKEN_DELIM_COMMA = 'delimiter.comma.json';
  26. export const TOKEN_VALUE_BOOLEAN = 'keyword.json';
  27. export const TOKEN_VALUE_NULL = 'keyword.json';
  28. export const TOKEN_VALUE_STRING = 'string.value.json';
  29. export const TOKEN_VALUE_NUMBER = 'number.json';
  30. export const TOKEN_PROPERTY_NAME = 'string.key.json';
  31. export const TOKEN_COMMENT_BLOCK = 'comment.block.json';
  32. export const TOKEN_COMMENT_LINE = 'comment.line.json';
  33. class JSONState implements languages.IState {
  34. private _state: languages.IState;
  35. public scanError: json.ScanError;
  36. constructor(state: languages.IState, scanError: json.ScanError) {
  37. this._state = state;
  38. this.scanError = scanError;
  39. }
  40. public clone(): JSONState {
  41. return new JSONState(this._state, this.scanError);
  42. }
  43. public equals(other: languages.IState): boolean {
  44. if (other === this) {
  45. return true;
  46. }
  47. if (!other || !(other instanceof JSONState)) {
  48. return false;
  49. }
  50. return this.scanError === (<JSONState>other).scanError;
  51. }
  52. public getStateData(): languages.IState {
  53. return this._state;
  54. }
  55. public setStateData(state: languages.IState): void {
  56. this._state = state;
  57. }
  58. }
  59. function tokenize(
  60. comments: boolean,
  61. line: string,
  62. state: JSONState,
  63. offsetDelta: number = 0,
  64. stopAtOffset?: number
  65. ): languages.ILineTokens {
  66. // handle multiline strings and block comments
  67. let numberOfInsertedCharacters = 0;
  68. let adjustOffset = false;
  69. switch (state.scanError) {
  70. case json.ScanError.UnexpectedEndOfString:
  71. line = '"' + line;
  72. numberOfInsertedCharacters = 1;
  73. break;
  74. case json.ScanError.UnexpectedEndOfComment:
  75. line = '/*' + line;
  76. numberOfInsertedCharacters = 2;
  77. break;
  78. }
  79. const scanner = json.createScanner(line);
  80. const ret: languages.ILineTokens = {
  81. tokens: <languages.IToken[]>[],
  82. endState: state.clone()
  83. };
  84. while (true) {
  85. let offset = offsetDelta + scanner.getPosition();
  86. let type = '';
  87. const kind = scanner.scan();
  88. if (kind === json.SyntaxKind.EOF) {
  89. break;
  90. }
  91. // Check that the scanner has advanced
  92. if (offset === offsetDelta + scanner.getPosition()) {
  93. throw new Error(
  94. 'Scanner did not advance, next 3 characters are: ' +
  95. line.substr(scanner.getPosition(), 3)
  96. );
  97. }
  98. // In case we inserted /* or " character, we need to
  99. // adjust the offset of all tokens (except the first)
  100. if (adjustOffset) {
  101. offset -= numberOfInsertedCharacters;
  102. }
  103. adjustOffset = numberOfInsertedCharacters > 0;
  104. // brackets and type
  105. switch (kind) {
  106. case json.SyntaxKind.OpenBraceToken:
  107. type = TOKEN_DELIM_OBJECT;
  108. break;
  109. case json.SyntaxKind.CloseBraceToken:
  110. type = TOKEN_DELIM_OBJECT;
  111. break;
  112. case json.SyntaxKind.OpenBracketToken:
  113. type = TOKEN_DELIM_ARRAY;
  114. break;
  115. case json.SyntaxKind.CloseBracketToken:
  116. type = TOKEN_DELIM_ARRAY;
  117. break;
  118. case json.SyntaxKind.ColonToken:
  119. for (let i = ret.tokens.length - 1; i >= 0; i--) {
  120. const token = ret.tokens[i];
  121. if (token.scopes === '' || token.scopes === TOKEN_COMMENT_BLOCK) {
  122. continue;
  123. }
  124. if (token.scopes === TOKEN_VALUE_STRING) {
  125. // !change previous token to property name!
  126. token.scopes = TOKEN_PROPERTY_NAME;
  127. }
  128. break;
  129. }
  130. type = TOKEN_DELIM_COLON;
  131. break;
  132. case json.SyntaxKind.CommaToken:
  133. type = TOKEN_DELIM_COMMA;
  134. break;
  135. case json.SyntaxKind.TrueKeyword:
  136. case json.SyntaxKind.FalseKeyword:
  137. type = TOKEN_VALUE_BOOLEAN;
  138. break;
  139. case json.SyntaxKind.NullKeyword:
  140. type = TOKEN_VALUE_NULL;
  141. break;
  142. case json.SyntaxKind.StringLiteral:
  143. type = TOKEN_VALUE_STRING;
  144. break;
  145. case json.SyntaxKind.NumericLiteral:
  146. type = TOKEN_VALUE_NUMBER;
  147. break;
  148. }
  149. // comments, iff enabled
  150. if (comments) {
  151. switch (kind) {
  152. case json.SyntaxKind.LineCommentTrivia:
  153. type = TOKEN_COMMENT_LINE;
  154. break;
  155. case json.SyntaxKind.BlockCommentTrivia:
  156. type = TOKEN_COMMENT_BLOCK;
  157. break;
  158. }
  159. }
  160. ret.endState = new JSONState(state.getStateData(), scanner.getTokenError());
  161. ret.tokens.push({
  162. startIndex: offset,
  163. scopes: type
  164. });
  165. }
  166. return ret;
  167. }