From 92b4c0bdafaa859621539602a0952b8edcc591c0 Mon Sep 17 00:00:00 2001 From: Thomas Smith Date: Sun, 1 Oct 2023 15:17:09 -0400 Subject: [PATCH] [JavaScript] Clean up imports (#3851) * [JavaScript] Clean up imports * [JavaScript] Add named context and bailout for `from` This commit... 1. adds a named context `import-export-from-name` 2. adds a bailout in case `from` source literal is missing, so following statements don't break highlighting. * [JavaScript] Add named context for assert/with objects This commit... 1. adds named context for optional object-literals in export/import statements. 2. adds a test case to verify bailout of incomplete statements. * [JavaScript] Add named context for default exports This commit... 1. adds named context for export-default expressions. 2. adds a test case to verify bailout of incomplete statements. --------- Co-authored-by: deathaxe --- JavaScript/JavaScript.sublime-syntax | 79 ++++++++----------- JavaScript/TypeScript.sublime-syntax | 30 ++++--- .../tests/syntax_test_js_import_export.js | 41 +++++++++- JavaScript/tests/syntax_test_typescript.ts | 8 ++ 4 files changed, 97 insertions(+), 61 deletions(-) diff --git a/JavaScript/JavaScript.sublime-syntax b/JavaScript/JavaScript.sublime-syntax index cc39aeaa29..7661d8222b 100644 --- a/JavaScript/JavaScript.sublime-syntax +++ b/JavaScript/JavaScript.sublime-syntax @@ -248,7 +248,7 @@ contexts: set: - import-meta - expect-semicolon - - import-assert + - import-export-assert - import-string-or-items - import-check-branch @@ -281,7 +281,11 @@ contexts: import-export-from: - match: 'from{{identifier_break}}' scope: keyword.control.import-export.js - set: literal-string + set: import-export-from-name + - include: else-pop + + import-export-from-name: + - include: literal-string - include: else-pop import-string-or-items: @@ -290,49 +294,49 @@ contexts: set: - import-export-from - import-list - - import-export-alias - - import-item + - import-export-item import-list: - match: ',' scope: punctuation.separator.comma.js - push: - - import-export-alias - - import-item + push: import-export-item - include: else-pop - import-item: + import-export-item: - match: '\{' scope: punctuation.section.block.begin.js - set: import-brace + set: import-export-brace - match: '{{non_reserved_identifier}}' scope: variable.other.readwrite.js pop: 1 - match: '\*' - scope: constant.other.js - pop: 1 + scope: constant.other.wildcard.asterisk.js + set: import-export-alias - include: else-pop - import-brace: + import-export-brace: - meta_scope: meta.block.js - include: comma-separator - match: '\}' scope: punctuation.section.block.end.js pop: 1 - - match: '{{identifier_name}}' - scope: variable.other.readwrite.js - push: import-export-alias + - match: (?={{identifier_start}}|'|") + push: + - import-export-alias + - import-export-name - match: '\*' - scope: constant.other.js + scope: constant.other.wildcard.asterisk.js push: import-export-alias - include: else-pop - import-assert: - - match: assert{{identifier_break}} + import-export-assert: + - match: (?:assert|with){{identifier_break}} scope: keyword.control.import-export.js - set: - - include: object-literal - - include: else-pop + set: import-export-object + - include: else-pop + + import-export-object: + - include: object-literal - include: else-pop export-statement: @@ -350,39 +354,20 @@ contexts: export-body: - include: declaration - - match: 'default{{identifier_break}}' + - match: default{{identifier_break}} scope: keyword.control.import-export.js - set: - - include: declaration - - match: (?=\S) - set: expression-statement + set: export-default - match: (?=\S) set: - expect-semicolon + - import-export-assert - import-export-from - - export-item - - export-item: - - match: '\{' - scope: punctuation.section.block.begin.js - set: export-brace - - match: '\*' - scope: constant.other.js - set: import-export-alias - - include: else-pop + - import-export-item - export-brace: - - meta_scope: meta.block.js - - include: comma-separator - - match: '\}' - scope: punctuation.section.block.end.js - pop: 1 - - match: (?={{identifier_start}}|'|") - push: - - import-export-alias - - import-export-name - - include: else-pop + export-default: + - include: declaration + - include: expression-statement statements: - match: '\)|\}|\]' diff --git a/JavaScript/TypeScript.sublime-syntax b/JavaScript/TypeScript.sublime-syntax index 38014a2f5e..cf57481d38 100644 --- a/JavaScript/TypeScript.sublime-syntax +++ b/JavaScript/TypeScript.sublime-syntax @@ -100,7 +100,7 @@ contexts: set: - expect-semicolon - import-export-from - - export-item + - import-export-item - match: (?=\S) fail: ts-export-type-from @@ -180,8 +180,18 @@ contexts: - ts-type-parameter-list ts-import-type: - - match: type{{identifier_break}} - scope: keyword.control.import-export.js + - match: (?=type{{identifier_break}}) + pop: 1 + branch_point: ts-import-type + branch: + - - match: type{{identifier_break}} + scope: keyword.control.import-export.js + set: + - match: (?=,|from{{identifier_break}}) + fail: ts-import-type + - include: else-pop + - immediately-pop + - include: else-pop import-statement: @@ -190,20 +200,20 @@ contexts: set: - import-meta - expect-semicolon - - import-assert + - import-export-assert - import-string-or-items - ts-import-type - import-check-branch - import-brace: - - meta_prepend: true - - match: type{{identifier_break}} - scope: keyword.control.import-export.js - - export-brace: + import-export-brace: - meta_prepend: true - match: type{{identifier_break}} scope: keyword.control.import-export.js + - match: (?={{identifier_start}}|'|") + push: + - import-export-alias + - import-export-name + - ts-import-type ts-type-declaration: - match: type{{identifier_break}} diff --git a/JavaScript/tests/syntax_test_js_import_export.js b/JavaScript/tests/syntax_test_js_import_export.js index 685faf6a65..2e30337ea9 100644 --- a/JavaScript/tests/syntax_test_js_import_export.js +++ b/JavaScript/tests/syntax_test_js_import_export.js @@ -21,13 +21,17 @@ import thing, {identifier as otherIdentifier}, * as otherName from "otherplace"; // ^ variable.other.readwrite // ^ keyword.control.import-export // ^ variable.other.readwrite -// ^ constant.other.js +// ^ constant.other.wildcard.asterisk // ^ keyword.control.import-export import 'module'; // ^^^^^^^^^^^^^ meta.import +import foo from 'bar' assert // incomplete! +//^^^^^^^^^^^^^^^^^^^^^^^^^^^ meta.import.js + import foo from 'bar' assert { type: "json" }; +// <- meta.import.js keyword.control.import-export.js //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ meta.import.js //^^^^ keyword.control.import-export.js // ^^^ variable.other.readwrite.js @@ -46,6 +50,10 @@ import foo from 'bar' assert { type: "json" }; // ^ punctuation.section.mapping.end.js // ^ punctuation.terminator.statement.js + +import foo from 'bar' assert { type: "json" }; +// ^^^^^^ meta.import keyword.control.import-export + // Better highlighting while typing. import import; @@ -111,7 +119,14 @@ export class Foo {}; // ^^^^^^^^^^^^ meta.class // ^ punctuation.terminator.statement.empty +export default +// <- meta.export.js keyword.control.import-export.js +//^^^^^^^^^^^^^ meta.export.js +//^^^^ keyword.control.import-export.js +// ^^^^^^^ keyword.control.import-export.js + export default expression; +// <- meta.export.js keyword.control.import-export.js //^^^^^^^^^^^^^^^^^^^^^^^^ meta.export //^ keyword.control.import-export // ^ keyword.control.import-export @@ -157,10 +172,16 @@ export { name1 as default }; // ^ keyword.control.import-export // ^ keyword.control.import-export +export * from // incomplete, missing source! +//^^^^^^^^^^^^ meta.export.js +//^^^^ keyword.control.import-export.js +// ^ constant.other.wildcard.asterisk.js +// ^^^^ keyword.control.import-export.js + export * from "./othermod"; //^^^^^^^^^^^^^^^^^^^^^^^^^ meta.export //^ keyword.control.import-export -// ^ constant.other +// ^ constant.other.wildcard.asterisk // ^ keyword.control.import-export export { name1, name2 } from "./othermod"; @@ -210,14 +231,26 @@ export { member as let from; // ^^^^ variable.other.readwrite.js +import from from // incomplete, missing source! +// <- meta.import.js keyword.control.import-export.js +// ^^^^ variable.other.readwrite.js + import from from "./othermod"; +// <- meta.import.js keyword.control.import-export.js // ^^^^ variable.other.readwrite.js import { from } from "./othermod"; // ^^^^ variable.other.readwrite.js -export { from } from "./othermod"; -// ^^^^ variable.other.readwrite.js +export {} from "./othermod" with {}; +//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ meta.export +//^^^^ keyword.control.import-export +// ^^ meta.block +// ^^^^ keyword.control.import-export +// ^^^^^^^^^^^^ meta.string string.quoted.double +// ^^^^ keyword.control.import-export +// ^^ meta.mapping +// ^ punctuation.terminator.statement export default$ // ^^^^^^^^ - keyword diff --git a/JavaScript/tests/syntax_test_typescript.ts b/JavaScript/tests/syntax_test_typescript.ts index 4754b15de6..36afc9d3d8 100644 --- a/JavaScript/tests/syntax_test_typescript.ts +++ b/JavaScript/tests/syntax_test_typescript.ts @@ -109,6 +109,14 @@ import foo; // ^^^^ keyword.control.import-export // ^^^^^^^^^^^ meta.string string.quoted.single + import type from 'somewhere'; +// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ meta.import +// ^^^^^^ keyword.control.import-export +// ^^^^ variable.other.readwrite +// ^^^^ keyword.control.import-export +// ^^^^^^^^^^^ meta.string string.quoted.single +// ^ punctuation.terminator.statement + import type { U, V } from 'somewhere'; // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ meta.import // ^^^^^^ keyword.control.import-export