Skip to content

Commit b9f5b9e

Browse files
committed
Ensure @rematch and nextEmbedded can be used together in a Monarch grammar.
Similar to the repro case I created for microsoft#90266, I created a local playground with the following: ```js const DEMO_LANG_ID = "demo"; const SQL_QUERY_START = "(SELECT|INSERT|UPDATE|DELETE|CREATE|REPLACE|ALTER|WITH)"; const languageConfiguration = { tokenizer: { root: [ [ `(\"\"\")${SQL_QUERY_START}`, [ { "token": "string.quote", }, { token: "@rematch", next: "@endStringWithSQL", nextEmbedded: "sql", }, ] ], [ /(""")$/, [ { token: "string.quote", next: "@maybeStringIsSQL", }, ] ], ], maybeStringIsSQL: [ [ /(.*)/, { cases: { [`${SQL_QUERY_START}\\b.*`]: { token: "@rematch", next: "@endStringWithSQL", nextEmbedded: "sql", }, "@default": { token: "@rematch", switchTo: "@endDblDocString", }, } } ], ], endDblDocString: [ [ "[^\"]+", "string" ], [ "\\\\\"", "string" ], [ "\"\"\"", "string", "@popall" ], [ "\"", "string" ] ], endStringWithSQL: [ [ /"""/, { token: "string.quote", next: "@popall", nextEmbedded: "@pop", }, ] ], }, }; monaco.languages.register({ id: DEMO_LANG_ID, extensions: ['.example'], }); monaco.languages.setMonarchTokensProvider(DEMO_LANG_ID, languageConfiguration); const value = `\ mysql_query("""SELECT * FROM table_name WHERE ds = '<DATEID>'""") mysql_query(""" SELECT * FROM table_name WHERE ds = '<DATEID>' """) `; var editor = monaco.editor.create(document.getElementById("container"), { value, language: DEMO_LANG_ID, lineNumbers: "off", roundedSelection: false, scrollBeyondLastLine: false, readOnly: false, theme: "vs-dark", }); ``` Without my change to monarchLexer.ts, I get this error in the console: > errors.ts:26 Uncaught Error: demo: cannot pop embedded mode if not inside one But with my change, I see a Python-esque multiline string literal with SQL syntax highlighting within it.
1 parent bdd729a commit b9f5b9e

1 file changed

Lines changed: 25 additions & 15 deletions

File tree

src/vs/editor/standalone/common/monarch/monarchLexer.ts

Lines changed: 25 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -740,6 +740,24 @@ export class MonarchTokenizer implements modes.ITokenizationSupport {
740740
throw monarchCommon.createError(this._lexer, 'lexer rule has no well-defined action in rule: ' + this._safeRuleName(rule));
741741
}
742742

743+
const computeNewStateForEmbeddedMode = (enteringEmbeddedMode: string) => {
744+
// substitute language alias to known modes to support syntax highlighting
745+
let enteringEmbeddedModeId = this._modeService.getModeIdForLanguageName(enteringEmbeddedMode);
746+
if (enteringEmbeddedModeId) {
747+
enteringEmbeddedMode = enteringEmbeddedModeId;
748+
}
749+
750+
const embeddedModeData = this._getNestedEmbeddedModeData(enteringEmbeddedMode);
751+
752+
if (pos < lineLength) {
753+
// there is content from the embedded mode on this line
754+
const restOfLine = line.substr(pos);
755+
return this._nestedTokenize(restOfLine, MonarchLineStateFactory.create(stack, embeddedModeData), offsetDelta + pos, tokensCollector);
756+
} else {
757+
return MonarchLineStateFactory.create(stack, embeddedModeData);
758+
}
759+
};
760+
743761
// is the result a group match?
744762
if (Array.isArray(result)) {
745763
if (groupMatching && groupMatching.groups.length > 0) {
@@ -780,6 +798,12 @@ export class MonarchTokenizer implements modes.ITokenizationSupport {
780798
matched = ''; // better set the next state too..
781799
matches = null;
782800
result = '';
801+
802+
// Even though `@rematch` was specified, if `nextEmbedded` also specified,
803+
// a state transition should occur.
804+
if (enteringEmbeddedMode !== null) {
805+
return computeNewStateForEmbeddedMode(enteringEmbeddedMode);
806+
}
783807
}
784808

785809
// check progress
@@ -810,21 +834,7 @@ export class MonarchTokenizer implements modes.ITokenizationSupport {
810834
}
811835

812836
if (enteringEmbeddedMode !== null) {
813-
// substitute language alias to known modes to support syntax highlighting
814-
let enteringEmbeddedModeId = this._modeService.getModeIdForLanguageName(enteringEmbeddedMode);
815-
if (enteringEmbeddedModeId) {
816-
enteringEmbeddedMode = enteringEmbeddedModeId;
817-
}
818-
819-
let embeddedModeData = this._getNestedEmbeddedModeData(enteringEmbeddedMode);
820-
821-
if (pos < lineLength) {
822-
// there is content from the embedded mode on this line
823-
let restOfLine = line.substr(pos);
824-
return this._nestedTokenize(restOfLine, MonarchLineStateFactory.create(stack, embeddedModeData), offsetDelta + pos, tokensCollector);
825-
} else {
826-
return MonarchLineStateFactory.create(stack, embeddedModeData);
827-
}
837+
return computeNewStateForEmbeddedMode(enteringEmbeddedMode);
828838
}
829839
}
830840

0 commit comments

Comments
 (0)