From 21c8ca40558b55ea588d6fd6efc5ddbb8517e5d6 Mon Sep 17 00:00:00 2001 From: deathaxe Date: Sat, 24 Aug 2024 14:29:48 +0200 Subject: [PATCH 01/52] Base CoffeeScript Literate on bundled Markdown --- .github/workflows/syntax-tests.yml | 6 +- CoffeeScript Literate.sublime-syntax | 514 +++------------------------ tests/syntax_test_scope.litcoffee | 33 +- 3 files changed, 64 insertions(+), 489 deletions(-) diff --git a/.github/workflows/syntax-tests.yml b/.github/workflows/syntax-tests.yml index aa1f57d..7aedd31 100644 --- a/.github/workflows/syntax-tests.yml +++ b/.github/workflows/syntax-tests.yml @@ -21,10 +21,8 @@ jobs: strategy: matrix: include: - - build: 3211 - default_packages: v3211 - - build: 4107 - default_packages: v4107 + - build: 4143 + default_packages: v4143 - build: 4180 default_packages: v4180 - build: latest diff --git a/CoffeeScript Literate.sublime-syntax b/CoffeeScript Literate.sublime-syntax index c058105..0607209 100644 --- a/CoffeeScript Literate.sublime-syntax +++ b/CoffeeScript Literate.sublime-syntax @@ -3,6 +3,9 @@ # http://www.sublimetext.com/docs/syntax.html name: CoffeeScript (Literate) scope: source.litcoffee +version: 2 + +extends: Packages/Markdown/Markdown.sublime-syntax file_extensions: - litcoffee @@ -10,474 +13,45 @@ file_extensions: - coffee.md contexts: - main: - - match: |- - (?x)^ - (?= ([ ]{4}|\t)(?!$)) - push: - - meta_scope: markup.raw.block.markdown - - match: |- - (?x)^ - (?! ([ ]{4}|\t)) - pop: true - - include: block_raw - # We could also use an empty end match and set - # applyEndPatternLast, but then we must be sure that the begin - # pattern will only match stuff matched by the sub-patterns. - - match: |- - (?x)^ - (?= [ ]{,3}>. - | [#]{1,6}\s*+ - | [ ]{,3}(?[-*_])([ ]{,2}\k){2,}[ \t]*+$ - ) - push: - - meta_scope: meta.block-level.markdown - - match: |- - (?x)^ - (?! [ ]{,3}>. - | [#]{1,6}\s*+ - | [ ]{,3}(?[-*_])([ ]{,2}\k){2,}[ \t]*+$ - ) - pop: true - - include: block_quote - - include: heading - - include: separator - - match: '^[ ]{0,3}([*+-])(?=\s)' - captures: - 1: punctuation.definition.list_item.markdown - push: - - meta_scope: markup.list.unnumbered.markdown - - match: ^(?=\S) - captures: - 1: punctuation.definition.list_item.markdown - pop: true - - include: list-paragraph - - match: '^[ ]{0,3}([0-9]+\.)(?=\s)' - captures: - 1: punctuation.definition.list_item.markdown - push: - - meta_scope: markup.list.numbered.markdown - - match: ^(?=\S) - captures: - 1: punctuation.definition.list_item.markdown - pop: true - - include: list-paragraph - # Markdown formatting is disabled inside block-level tags. - - match: '^(?=<(p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math|ins|del)\b)(?!.*?)' - push: - - meta_scope: meta.disable-markdown - - match: (?<=^$\n) - pop: true - - include: scope:text.html.basic - # Same rule but for one line disables. - - match: '^(?=<(p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math|ins|del)\b)' - push: - - meta_scope: meta.disable-markdown - - match: $\n? - pop: true - - include: scope:text.html.basic - - match: |- - (?x: - \s* # Leading whitespace - (\[)(.+?)(\])(:) # Reference name - [ \t]* # Optional whitespace - (?) # The url - [ \t]* # Optional whitespace - (?: - ((\().+?(\))) # Match title in quotes… - | ((").+?(")) # or in parens. - )? # Title is optional - \s* # Optional whitespace - $ - ) - scope: meta.link.reference.def.markdown - captures: - 1: punctuation.definition.constant.markdown - 2: constant.other.reference.link.markdown - 3: punctuation.definition.constant.markdown - 4: punctuation.separator.key-value.markdown - 5: punctuation.definition.link.markdown - 6: markup.underline.link.markdown - 7: punctuation.definition.link.markdown - 8: string.other.link.description.title.markdown - 9: punctuation.definition.string.begin.markdown - 10: punctuation.definition.string.end.markdown - 11: string.other.link.description.title.markdown - 12: punctuation.definition.string.begin.markdown - 13: punctuation.definition.string.end.markdown - - match: '^(?=\S)(?![=-]{3,}(?=$))' - push: - - meta_scope: meta.paragraph.markdown - - match: '^(?:\s*$|(?=[ ]{,3}>.))|(?=[ \t]*\n)(?<=^===|^====|=====|^---|^----|-----)[ \t]*\n|(?=^#)' - pop: true - - include: inline - - include: scope:text.html.basic - - match: '^(={3,})(?=[ \t]*$)' - scope: markup.heading.1.markdown - captures: - 1: punctuation.definition.heading.markdown - - match: '^(-{3,})(?=[ \t]*$)' - scope: markup.heading.2.markdown - captures: - 1: punctuation.definition.heading.markdown - ampersand: - - match: '&(?!([a-zA-Z0-9]+|#[0-9]+|#x[0-9a-fA-F]+);)' - comment: | - Markdown will convert this for us. We match it so that the - HTML grammar will not mark it up as invalid. - scope: meta.other.valid-ampersand.markdown - block_quote: - # We terminate the block quote when seeing an empty line, a - # separator or a line with leading > characters. The latter is - # to “reset” the quote level for quoted lines. - - match: '\G[ ]{,3}(>)(?!$)[ ]?' - captures: - 1: punctuation.definition.blockquote.markdown - push: - - meta_scope: markup.quote.markdown - - match: |- - (?x)^ - (?= \s*$ - | [ ]{,3}(?[-*_])([ ]{,2}\k){2,}[ \t]*+$ - | [ ]{,3}>. - ) - pop: true - - match: |- - (?x)\G - (?= [ ]{,3}>. - ) - push: - - match: ^ - pop: true - - include: block_quote - - match: |- - (?x)\G - (?= ([ ]{4}|\t) - | [#]{1,6}\s*+ - | [ ]{,3}(?[-*_])([ ]{,2}\k){2,}[ \t]*+$ - ) - push: - - include: heading - - include: separator - - match: ^ - pop: true - - match: |- - (?x)\G - (?! $ - | [ ]{,3}>. - | ([ ]{4}|\t) - | [#]{1,6}\s*+ - | [ ]{,3}(?[-*_])([ ]{,2}\k){2,}[ \t]*+$ - ) - push: - - match: $|(?<=\n) - pop: true - - include: inline - block_raw: - - include: scope:source.coffee#script - bold: - - match: |- - (?x) - (\*\*|__)(?=\S) # Open - (?= - ( - <[^>]*+> # HTML tags - | (?`+)([^`]|(?!(?(?!`))`)*+\k - # Raw - | \\[\\`*_{}\[\]()#.!+\->]?+ # Escapes - | \[ - ( - (? # Named group - [^\[\]\\] # Match most chars - | \\. # Escaped chars - | \[ \g*+ \] # Nested brackets - )*+ - \] - ( - ( # Reference Link - [ ]? # Optional space - \[[^\]]*+\] # Ref name - ) - | ( # Inline Link - \( # Opening paren - [ \t]*+ # Optional whtiespace - ? # URL - [ \t]*+ # Optional whtiespace - ( # Optional Title - (?['"]) - (.*?) - \k<title> - )? - \) - ) - ) - ) - | (?!(?<=\S)\1). # Everything besides - # style closer - )++ - (?<=\S)\1 # Close - ) - captures: - 1: punctuation.definition.bold.markdown - push: - - meta_scope: markup.bold.markdown - - match: (?<=\S)(\1) - captures: - 1: punctuation.definition.bold.markdown - pop: true - - match: '(?=<[^>]*?>)' - push: - - include: scope:text.html.basic - - match: (?<=>) - pop: true - - include: escape - - include: ampersand - - include: bracket - - include: raw - - include: italic - - include: image-inline - - include: link-inline - - include: link-inet - - include: link-email - - include: image-ref - - include: link-ref-literal - - include: link-ref - bracket: - # Markdown will convert this for us. We match it so that the - # HTML grammar will not mark it up as invalid. - - match: '<(?![a-z/?\$!])' - scope: meta.other.valid-bracket.markdown - escape: - - match: '\\[-`*_#+.!(){}\[\]\\>]' - scope: constant.character.escape.markdown - heading: - - match: '\G(#{1,6})(?!#)\s*(?=\S)' - captures: - 1: punctuation.definition.heading.markdown - push: - - meta_scope: markup.heading.markdown - - meta_content_scope: entity.name.section.markdown - - match: \s*(#*)$\n? - captures: - 1: punctuation.definition.heading.markdown - pop: true - - include: inline - image-inline: - - match: |- - (?x: - \! # Images start with ! - (\[)((?<square>[^\[\]\\]|\\.|\[\g<square>*+\])*+)(\]) - # Match the link text. - ([ ])? # Space not allowed - (\() # Opening paren for url - (<?)(\S+?)(>?) # The url - [ \t]* # Optional whitespace - (?: - ((\().+?(\))) # Match title in parens… - | ((").+?(")) # or in quotes. - )? # Title is optional - \s* # Optional whitespace - (\)) - ) - scope: meta.image.inline.markdown - captures: - 1: punctuation.definition.string.begin.markdown - 2: string.other.link.description.markdown - 3: punctuation.definition.string.end.markdown - 5: invalid.illegal.whitespace.markdown - 6: punctuation.definition.metadata.markdown - 7: punctuation.definition.link.markdown - 8: markup.underline.link.image.markdown - 9: punctuation.definition.link.markdown - 10: string.other.link.description.title.markdown - 11: punctuation.definition.string.markdown - 12: punctuation.definition.string.markdown - 13: string.other.link.description.title.markdown - 14: punctuation.definition.string.markdown - 15: punctuation.definition.string.markdown - 16: punctuation.definition.metadata.markdown - image-ref: - - match: '\!(\[)((?<square>[^\[\]\\]|\\.|\[\g<square>*+\])*+)(\])[ ]?(\[)(.*?)(\])' - scope: meta.image.reference.markdown - captures: - 1: punctuation.definition.string.begin.markdown - 2: string.other.link.description.markdown - 4: punctuation.definition.string.begin.markdown - 5: punctuation.definition.constant.markdown - 6: constant.other.reference.link.markdown - 7: punctuation.definition.constant.markdown - inline: - - include: escape - - include: ampersand - - include: bracket - - include: raw - - include: bold - - include: italic - - include: line-break - - include: image-inline - - include: link-inline - - include: link-inet - - include: link-email - - include: image-ref - - include: link-ref-literal - - include: link-ref - italic: - - match: |- - (?x) - (\*|_)(?=\S) # Open - (?= - ( - <[^>]*+> # HTML tags - | (?<raw>`+)([^`]|(?!(?<!`)\k<raw>(?!`))`)*+\k<raw> - # Raw - | \\[\\`*_{}\[\]()#.!+\->]?+ # Escapes - | \[ - ( - (?<square> # Named group - [^\[\]\\] # Match most chars - | \\. # Escaped chars - | \[ \g<square>*+ \] # Nested brackets - )*+ - \] - ( - ( # Reference Link - [ ]? # Optional space - \[[^\]]*+\] # Ref name - ) - | ( # Inline Link - \( # Opening paren - [ \t]*+ # Optional whtiespace - <?(.*?)>? # URL - [ \t]*+ # Optional whtiespace - ( # Optional Title - (?<title>['"]) - (.*?) - \k<title> - )? - \) - ) - ) - ) - | \1\1 # Must be bold closer - | (?!(?<=\S)\1). # Everything besides - # style closer - )++ - (?<=\S)\1 # Close - ) - captures: - 1: punctuation.definition.italic.markdown - push: - - meta_scope: markup.italic.markdown - - match: (?<=\S)(\1)((?!\1)|(?=\1\1)) - captures: - 1: punctuation.definition.italic.markdown - pop: true - - match: '(?=<[^>]*?>)' - push: - - include: scope:text.html.basic - - match: (?<=>) - pop: true - - include: escape - - include: ampersand - - include: bracket - - include: raw - - include: bold - - include: image-inline - - include: link-inline - - include: link-inet - - include: link-email - - include: image-ref - - include: link-ref-literal - - include: link-ref - line-break: - - match: ' {2,}$' - scope: meta.dummy.line-break - link-email: - - match: '(<)((?:mailto:)?[-.\w]+@[-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+)(>)' - scope: meta.link.email.lt-gt.markdown - captures: - 1: punctuation.definition.link.markdown - 2: markup.underline.link.markdown - 4: punctuation.definition.link.markdown - link-inet: - - match: (<)((?:https?|ftp)://.*?)(>) - scope: meta.link.inet.markdown - captures: - 1: punctuation.definition.link.markdown - 2: markup.underline.link.markdown - 3: punctuation.definition.link.markdown - link-inline: + + markdown: + - meta_prepend: true + - meta_scope: text.html.markdown.litcoffee + - include: coffee-indented-code-blocks + + list-block-content: + - meta_prepend: true + - include: coffee-indented-code-blocks + + coffee-indented-code-blocks: + - match: '{{indented_code_block}}' + scope: meta.embedded.litcoffee source.coffee.embedded.markdown + embed: scope:source.coffee + embed_scope: meta.embedded.litcoffee source.coffee.embedded.markdown + escape: ^(?=\s*$) + + fenced-syntaxes: + - meta_append: true + - include: fenced-coffee + + fenced-coffee: - match: |- - (?x: - (\[)((?<square>[^\[\]\\]|\\.|\[\g<square>*+\])*+)(\]) - # Match the link text. - ([ ])? # Space not allowed - (\() # Opening paren for url - (<?)(.*?)(>?) # The url - [ \t]* # Optional whitespace - (?: - ((\().+?(\))) # Match title in parens… - | ((").+?(")) # or in quotes. - )? # Title is optional - \s* # Optional whitespace - (\)) - ) - scope: meta.link.inline.markdown - captures: - 1: punctuation.definition.string.begin.markdown - 2: string.other.link.title.markdown - 4: punctuation.definition.string.end.markdown - 5: invalid.illegal.whitespace.markdown - 6: punctuation.definition.metadata.markdown - 7: punctuation.definition.link.markdown - 8: markup.underline.link.markdown - 9: punctuation.definition.link.markdown - 10: string.other.link.description.title.markdown - 11: punctuation.definition.string.begin.markdown - 12: punctuation.definition.string.end.markdown - 13: string.other.link.description.title.markdown - 14: punctuation.definition.string.begin.markdown - 15: punctuation.definition.string.end.markdown - 16: punctuation.definition.metadata.markdown - link-ref: - - match: '(\[)((?<square>[^\[\]\\]|\\.|\[\g<square>*+\])*+)(\])[ ]?(\[)([^\]]*+)(\])' - scope: meta.link.reference.markdown - captures: - 1: punctuation.definition.string.begin.markdown - 2: string.other.link.title.markdown - 4: punctuation.definition.string.end.markdown - 5: punctuation.definition.constant.begin.markdown - 6: constant.other.reference.link.markdown - 7: punctuation.definition.constant.end.markdown - link-ref-literal: - - match: '(\[)((?<square>[^\[\]\\]|\\.|\[\g<square>*+\])*+)(\])[ ]?(\[)(\])' - scope: meta.link.reference.literal.markdown - captures: - 1: punctuation.definition.string.begin.markdown - 2: string.other.link.title.markdown - 4: punctuation.definition.string.end.markdown - 5: punctuation.definition.constant.begin.markdown - 6: punctuation.definition.constant.end.markdown - list-paragraph: - - match: \G\s+(?=\S) - push: - - meta_scope: meta.paragraph.list.markdown - - match: ^\s*$ - pop: true - - include: inline - - match: '^\s*([*+-]|[0-9]+\.)' - comment: Match the list punctuation - captures: - 1: punctuation.definition.list_item.markdown - numeric: - - match: '(?<!\$)\b((0([box])[0-9a-fA-F]+)|([0-9]+(\.[0-9]+)?(e[+\-]?[0-9]+)?))\b' - scope: constant.numeric.coffee - raw: - - match: '(`+)([^`]|(?!(?<!`)\1(?!`))`)*+(\1)' - scope: markup.raw.inline.markdown - captures: - 1: punctuation.definition.raw.markdown - 3: punctuation.definition.raw.markdown - separator: - - match: '\G[ ]{,3}([-*_])([ ]{,2}\1){2,}[ \t]*$\n?' - scope: meta.separator.markdown + (?x) + {{fenced_code_block_start}} + (?i:\s*(coffee(?:script)?|cjsx|cson|iced)) + {{fenced_code_block_trailing_infostring_characters}} + captures: + 0: meta.code-fence.definition.begin.coffee.markdown-gfm + 2: punctuation.definition.raw.code-fence.begin.markdown + 5: constant.other.language-name.markdown + 6: comment.line.infostring.markdown + 7: meta.fold.code-fence.begin.markdown + embed: scope:source.coffee + embed_scope: + markup.raw.code-fence.coffee.markdown-gfm + source.coffee + escape: '{{fenced_code_block_escape}}' + escape_captures: + 0: meta.code-fence.definition.end.coffee.markdown-gfm + 1: punctuation.definition.raw.code-fence.end.markdown + 2: meta.fold.code-fence.end.markdown diff --git a/tests/syntax_test_scope.litcoffee b/tests/syntax_test_scope.litcoffee index 59dcdbc..7c4f1ef 100644 --- a/tests/syntax_test_scope.litcoffee +++ b/tests/syntax_test_scope.litcoffee @@ -1,32 +1,35 @@ | SYNTAX TEST "CoffeeScript Literate.sublime-syntax" # Heading -| <- meta.block-level.markdown markup.heading.markdown punctuation.definition.heading.markdown -|^^^^^^^^^ meta.block-level.markdown markup.heading.markdown +| <- markup.heading.1.markdown punctuation.definition.heading.begin.markdown +|^^^^^^^^^ text.html.markdown.litcoffee markup.heading.1.markdown # Indendet Code Block class App.Router extends Snakeskin.Router - | <- markup.raw.block.markdown meta.class.coffee storage.type.class.coffee - |^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ markup.raw.block.markdown meta.class.coffee - - @index: () => - | ^^^^^^ meta.function.identifier.coffee entity.name.function.coffee - | ^^ meta.function.coffee - | ^^ meta.function.parameters.coffee - | ^^^ meta.function.coffee - | ^^ keyword.declaration.function.coffee + | <- meta.embedded.litcoffee source.coffee.embedded.markdown meta.class.coffee storage.type.class.coffee + |^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ meta.embedded.litcoffee source.coffee.embedded.markdown meta.class.coffee + index: () => @ensureData((data) => @_parseDates(data, ['trending', 'new', 'top']) App.layout.renderExchange('index', data, ['index', 'search']) ) +- In list items + + class App.Router extends Snakeskin.Router + | <- markup.list.unnumbered.markdown meta.embedded.litcoffee source.coffee.embedded.markdown meta.class.coffee storage.type.class.coffee + |^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ markup.list.unnumbered.markdown meta.embedded.litcoffee source.coffee.embedded.markdown meta.class.coffee + index: () => + @ensureData((data) => + @_parseDates(data, ['trending', 'new', 'top']) + App.layout.renderExchange('index', data, ['index', 'search']) + ) + > Not in block quotes > > class App.Router extends Snakeskin.Router -|^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ meta.block-level.markdown markup.quote.markdown - markdup.raw - -# https://github.com/SublimeText/CoffeeScript/issues/203 +| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ markup.quote.markdown markup.raw.block.markdown - source.coffee This is a test showcasing that multiline string interpolation is broken. Copy this post into a .litcoffee file and open it in Sublime to see the breakage. @@ -55,4 +58,4 @@ into a .litcoffee file and open it in Sublime to see the breakage. ...even this markdown is as well. :( | <- meta.paragraph.markdown -|^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ meta.paragraph.markdown +|^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ meta.paragraph.markdown \ No newline at end of file From fd7f94cc80aa7d83ad878025903627db8b782db1 Mon Sep 17 00:00:00 2001 From: deathaxe <deathaxe82@googlemail.com> Date: Mon, 26 Aug 2024 17:50:06 +0200 Subject: [PATCH 02/52] Syntax highlight backtick quoted strings with JSX Resolves #105 --- CoffeeScript.sublime-syntax | 15 ++++++--------- tests/syntax_test_scope.coffee | 17 +++++++++++++++++ 2 files changed, 23 insertions(+), 9 deletions(-) diff --git a/CoffeeScript.sublime-syntax b/CoffeeScript.sublime-syntax index 9eaf4d3..2166aab 100644 --- a/CoffeeScript.sublime-syntax +++ b/CoffeeScript.sublime-syntax @@ -512,15 +512,12 @@ contexts: backtick-quoted-strings: - match: \` - scope: punctuation.definition.string.begin.coffee - push: backtick-quoted-string-body - - backtick-quoted-string-body: - - meta_scope: meta.string.coffee string.quoted.script.coffee - - match: \` - scope: punctuation.definition.string.end.coffee - pop: true - - include: string-escapes + scope: meta.string.coffee string.quoted.script.coffee punctuation.definition.string.begin.coffee + embed: scope:source.jsx + embed_scope: meta.string.coffee meta.embedded.coffee source.jsx.embedded.coffee + escape: \` + escape_captures: + 0: meta.string.coffee string.quoted.script.coffee punctuation.definition.string.end.coffee double-quoted-strings: - match: \" diff --git a/tests/syntax_test_scope.coffee b/tests/syntax_test_scope.coffee index a8a4d3f..f0478ea 100644 --- a/tests/syntax_test_scope.coffee +++ b/tests/syntax_test_scope.coffee @@ -488,6 +488,23 @@ class App.Router extends Snakeskin.Router # ^^^ meta.string.heredoc.coffee string.quoted.single.coffee punctuation.definition.string.end.coffee # ^ - meta.string - string + ` +# ^ meta.string.coffee string.quoted.script.coffee punctuation.definition.string.begin.coffee +# ^ meta.string.coffee meta.embedded.coffee source.jsx.embedded.coffee - string + var i = 0; +# ^^^^^^^^^^^ meta.string.coffee meta.embedded.coffee source.jsx.embedded.coffee +# ^^^ keyword.declaration + return (<h1>Hello {World}</h1>) +# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ meta.string.coffee meta.embedded.coffee source.jsx.embedded.coffee +# ^^^^^^^^^^^^^^^^^^^^^^ meta.group.js meta.jsx.js +# ^^^^ meta.tag +# ^^^^^ meta.tag + ` +# <- meta.string.coffee meta.embedded.coffee source.jsx.embedded.coffee - string +#^ meta.string.coffee meta.embedded.coffee source.jsx.embedded.coffee - string +# ^ meta.string.coffee string.quoted.script.coffee punctuation.definition.string.end.coffee +# ^ - meta.string - string + /// # <- - meta.string #^ - meta.string From ef73b9db797fe81b346e60643da11b11a92741a2 Mon Sep 17 00:00:00 2001 From: deathaxe <deathaxe82@googlemail.com> Date: Mon, 26 Aug 2024 17:52:46 +0200 Subject: [PATCH 03/52] Opt-in to syntax version 2 Opt-in to current syntax engine features. Resolves some overlapping meta scopes and duplicated source.jsx in backtick quoted strings. --- CoffeeScript.sublime-syntax | 1 + tests/syntax_test_scope.coffee | 15 +++++++++++++-- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/CoffeeScript.sublime-syntax b/CoffeeScript.sublime-syntax index 2166aab..71ba7e8 100644 --- a/CoffeeScript.sublime-syntax +++ b/CoffeeScript.sublime-syntax @@ -4,6 +4,7 @@ # CoffeeScript Syntax: version 1 name: CoffeeScript scope: source.coffee +version: 2 file_extensions: - coffee diff --git a/tests/syntax_test_scope.coffee b/tests/syntax_test_scope.coffee index f0478ea..051c42e 100644 --- a/tests/syntax_test_scope.coffee +++ b/tests/syntax_test_scope.coffee @@ -65,24 +65,28 @@ class App.Router extends Snakeskin.Router # ^^^^^ - meta.function - entity.name.function name: -> +# ^^^^^^^^ - meta.function meta.function # ^^^^ meta.function.identifier.coffee entity.name.function.coffee # ^^^^ meta.function.coffee # ^ keyword.operator.assignment.coffee # ^^ keyword.declaration.function.coffee @name: -> +# ^^^^^^^^^ - meta.function meta.function # ^^^^^ meta.function.identifier.coffee entity.name.function.coffee # ^^^^ meta.function.coffee # ^ keyword.operator.assignment.coffee # ^^ keyword.declaration.function.coffee namespace.name: -> +# ^^^^^^^^^^^^^^^^^^ - meta.function meta.function # ^^^^^^^^^^^^^^ meta.function.identifier.coffee entity.name.function.coffee # ^^^^ meta.function.coffee # ^ keyword.operator.assignment.coffee # ^^ keyword.declaration.function.coffee name = => +# ^^^^^^^^^ - meta.function meta.function # ^^^^ meta.function.identifier.coffee entity.name.function.coffee # ^ meta.function.identifier.coffee - entity # ^^^^ meta.function.coffee @@ -90,6 +94,7 @@ class App.Router extends Snakeskin.Router # ^^ keyword.declaration.function.coffee namespace.name = => +# ^^^^^^^^^^^^^^^^^^^ - meta.function meta.function # ^^^^^^^^^^^^^^ meta.function.identifier.coffee entity.name.function.coffee # ^ meta.function.identifier.coffee - entity # ^^^^ meta.function.coffee @@ -97,18 +102,21 @@ class App.Router extends Snakeskin.Router # ^^ keyword.declaration.function.coffee name: => +# ^^^^^^^^ - meta.function meta.function # ^^^^ meta.function.identifier.coffee entity.name.function.coffee # ^^^^ meta.function.coffee # ^ keyword.operator.assignment.coffee # ^^ keyword.declaration.function.coffee namespace.name: => +# ^^^^^^^^^^^^^^^^^^ - meta.function meta.function # ^^^^^^^^^^^^^^ meta.function.identifier.coffee entity.name.function.coffee # ^^^^ meta.function.coffee # ^ keyword.operator.assignment.coffee # ^^ keyword.declaration.function.coffee name = => +# ^^^^^^^^^ - meta.function meta.function # ^^^^ meta.function.identifier.coffee entity.name.function.coffee # ^ meta.function.identifier.coffee - entity # ^^^^ meta.function.coffee @@ -116,6 +124,7 @@ class App.Router extends Snakeskin.Router # ^^ keyword.declaration.function.coffee namespace.name = => +# ^^^^^^^^^^^^^^^^^^^ - meta.function meta.function # ^^^^^^^^^^^^^^ meta.function.identifier.coffee entity.name.function.coffee # ^ meta.function.identifier.coffee - entity # ^^^^ meta.function.coffee @@ -123,6 +132,7 @@ class App.Router extends Snakeskin.Router # ^^ keyword.declaration.function.coffee name: () -> +# ^^^^^^^^^^^ - meta.function meta.function # ^^^^ meta.function.identifier.coffee entity.name.function.coffee # ^^ meta.function.coffee # ^^ meta.function.parameters.coffee @@ -133,6 +143,7 @@ class App.Router extends Snakeskin.Router # ^^ keyword.declaration.function.coffee name: (foo, bar = undefined, baz="buuz", ...) -> +# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - meta.function meta.function # ^^^^ meta.function.identifier.coffee entity.name.function.coffee # ^^ meta.function.coffee # ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ meta.function.parameters.coffee @@ -492,10 +503,10 @@ class App.Router extends Snakeskin.Router # ^ meta.string.coffee string.quoted.script.coffee punctuation.definition.string.begin.coffee # ^ meta.string.coffee meta.embedded.coffee source.jsx.embedded.coffee - string var i = 0; -# ^^^^^^^^^^^ meta.string.coffee meta.embedded.coffee source.jsx.embedded.coffee +# ^^^^^^^^^^^ meta.string.coffee meta.embedded.coffee source.jsx.embedded.coffee - source.jsx source.jsx # ^^^ keyword.declaration return (<h1>Hello {World}</h1>) -# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ meta.string.coffee meta.embedded.coffee source.jsx.embedded.coffee +# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ meta.string.coffee meta.embedded.coffee source.jsx.embedded.coffee - source.jsx source.jsx # ^^^^^^^^^^^^^^^^^^^^^^ meta.group.js meta.jsx.js # ^^^^ meta.tag # ^^^^^ meta.tag From cfa43bad5881c5795c1b3b02f02ed33c5a0df93f Mon Sep 17 00:00:00 2001 From: deathaxe <deathaxe82@googlemail.com> Date: Mon, 26 Aug 2024 18:13:54 +0200 Subject: [PATCH 04/52] Replace pop: true with pop: 1 --- CoffeeScript.sublime-syntax | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/CoffeeScript.sublime-syntax b/CoffeeScript.sublime-syntax index 71ba7e8..21a2282 100644 --- a/CoffeeScript.sublime-syntax +++ b/CoffeeScript.sublime-syntax @@ -64,7 +64,7 @@ contexts: - meta_scope: comment.block.coffee - match: \###(?!#) scope: punctuation.definition.comment.coffee - pop: true + pop: 1 - match: (@)\w* scope: variable.annotation.coffee captures: @@ -73,7 +73,7 @@ contexts: line-comment-body: - meta_scope: comment.line.number-sign.coffee - match: $\n? - pop: true + pop: 1 shebang: - meta_include_prototype: false @@ -81,7 +81,7 @@ contexts: scope: punctuation.definition.comment.coffee set: shebang-body - match: ^|(?=\S) # Note: Ensure to highlight shebang if Python is embedded. - pop: true + pop: 1 shebang-body: - meta_include_prototype: false @@ -90,7 +90,7 @@ contexts: - match: coffee(?:\d(?:\.\d+)?)?\b scope: constant.language.shebang.coffee - match: $\n? - pop: true + pop: 1 ###[ CLASS DECLARATIONS ]##################################################### @@ -111,7 +111,7 @@ contexts: push: - meta_scope: meta.function.symbols.coffee - match: $ - pop: true + pop: 1 - include: script # anonymous functions - match: (?=\([^()]*?\)\s*[=-]>) @@ -156,14 +156,14 @@ contexts: parameter-value: - match: (?=[,)]) - pop: true + pop: 1 - include: expressions function-body: - meta_content_scope: meta.function.coffee - match: '[=-]>' scope: meta.function.coffee keyword.declaration.function.coffee - pop: true + pop: 1 - include: else-pop ###[ FUNCTION CALLS ]######################################################### @@ -272,7 +272,7 @@ contexts: - meta_scope: meta.function-call.arguments.coffee - match: \) scope: punctuation.section.group.end.coffee - pop: true + pop: 1 - include: script ###[ KEYWORDS ]############################################################### @@ -360,7 +360,7 @@ contexts: 2: punctuation.accessor.dot.coffee - match: '{{identifier}}' scope: support.class.coffee - pop: true + pop: 1 - include: else-pop ###[ OBJECTS ]################################################################ @@ -446,7 +446,7 @@ contexts: captures: 1: punctuation.definition.string.end.coffee 2: constant.language.flags.coffee - pop: true + pop: 1 - include: comments - include: string-escapes - include: string-interpolations @@ -495,7 +495,7 @@ contexts: - meta_scope: meta.string.heredoc.coffee string.quoted.double.coffee - match: \"{3} scope: punctuation.definition.string.end.coffee - pop: true + pop: 1 - match: \\. scope: constant.character.escape.coffee - include: string-interpolations @@ -509,7 +509,7 @@ contexts: - meta_scope: meta.string.heredoc.coffee string.quoted.single.coffee - match: \'{3} scope: punctuation.definition.string.end.coffee - pop: true + pop: 1 backtick-quoted-strings: - match: \` @@ -529,7 +529,7 @@ contexts: - meta_scope: meta.string.coffee string.quoted.double.coffee - match: \" scope: punctuation.definition.string.end.coffee - pop: true + pop: 1 - include: string-escapes - include: string-interpolations @@ -542,7 +542,7 @@ contexts: - meta_scope: meta.string.coffee string.quoted.single.coffee - match: \' scope: punctuation.definition.string.end.coffee - pop: true + pop: 1 - include: string-escapes string-escapes: @@ -562,7 +562,7 @@ contexts: - meta_scope: meta.embedded.coffee source.coffee.embedded.source - match: \} scope: punctuation.section.embedded.coffee - pop: true + pop: 1 - include: script string-embedded-body: @@ -570,7 +570,7 @@ contexts: - meta_scope: meta.embedded.coffee source.coffee.embedded.source - match: '%>' scope: punctuation.section.embedded.coffee - pop: true + pop: 1 - include: script ###[ VARIABLES ]############################################################### @@ -585,11 +585,11 @@ contexts: else-pop: - match: (?=\S) - pop: true + pop: 1 immediately-pop: - match: '' - pop: true + pop: 1 ############################################################################### From 52392b31537dbda7a559bd71ea0792362fa2c291 Mon Sep 17 00:00:00 2001 From: deathaxe <deathaxe82@googlemail.com> Date: Mon, 26 Aug 2024 18:13:05 +0200 Subject: [PATCH 05/52] Add HTML (CoffeeScript) syntax definition Resolves #122 --- HTML (CoffeeScript).sublime-syntax | 89 ++++++++++++++++++++++++++++++ tests/syntax_test_scope.html | 51 +++++++++++++++++ 2 files changed, 140 insertions(+) create mode 100644 HTML (CoffeeScript).sublime-syntax create mode 100644 tests/syntax_test_scope.html diff --git a/HTML (CoffeeScript).sublime-syntax b/HTML (CoffeeScript).sublime-syntax new file mode 100644 index 0000000..92cf857 --- /dev/null +++ b/HTML (CoffeeScript).sublime-syntax @@ -0,0 +1,89 @@ +%YAML 1.2 +--- +# http://www.sublimetext.com/docs/syntax.html +name: HTML (CoffeeScript) +scope: text.html.coffee +version: 2 + +extends: Packages/HTML/HTML.sublime-syntax + +file_extensions: + - coffee.html + +contexts: + + script-common: + - meta_prepend: true + - match: (?i:lang){{attribute_name_break}} + scope: meta.attribute-with-value.html entity.other.attribute-name.html + set: script-lang-attribute-assignment + + script-lang-attribute-assignment: + - meta_content_scope: meta.tag.script.begin.html meta.attribute-with-value.html + - match: = + scope: punctuation.separator.key-value.html + set: script-lang-attribute-value + - match: (?=\S) + set: script-javascript + + script-lang-attribute-value: + - meta_include_prototype: false + - meta_scope: meta.tag.script.begin.html meta.attribute-with-value.html + - match: (?=(?i:coffee{{unquoted_attribute_break}}|'coffee'|"coffee")) + set: + - script-coffee + - tag-generic-attribute-meta + - tag-generic-attribute-value + - match: (?=\S) + set: + - script-javascript + - tag-generic-attribute-meta + - tag-generic-attribute-value + + script-type-decider: + - meta_prepend: true + - match: (?={{coffee_mime_type}}{{unquoted_attribute_break}}|'{{coffee_mime_type}}'|"{{coffee_mime_type}}") + set: + - script-coffee + - tag-generic-attribute-meta + - tag-generic-attribute-value + + script-coffee: + - meta_include_prototype: false + - meta_scope: meta.tag.script.begin.html + - match: '>' + scope: punctuation.definition.tag.end.html + set: script-coffee-content + - include: script-common + + script-coffee-content: + - meta_include_prototype: false + - match: \s*((<!\[)(CDATA)(\[)) + captures: + 1: meta.tag.sgml.cdata.html + 2: punctuation.definition.tag.begin.html + 3: keyword.declaration.cdata.html + 4: punctuation.definition.tag.begin.html + pop: 1 # make sure to match only once + embed: scope:source.coffee + embed_scope: meta.tag.sgml.cdata.html source.coffee.embedded.html + escape: \]\]> + escape_captures: + 0: meta.tag.sgml.cdata.html punctuation.definition.tag.end.html + - match: '{{script_content_begin}}' + captures: + 1: comment.block.html punctuation.definition.comment.begin.html + pop: 1 # make sure to match only once + embed: scope:source.coffee + embed_scope: source.coffee.embedded.html + escape: '{{script_content_end}}' + escape_captures: + 1: source.coffee.embedded.html + 2: comment.block.html punctuation.definition.comment.end.html + 3: source.coffee.embedded.html + 4: comment.block.html punctuation.definition.comment.end.html + +variables: + + coffee_mime_type: |- + (?xi: (?: (?: application |text ) / coffee(?:script)? ) {{mime_type_parameters}}? ) diff --git a/tests/syntax_test_scope.html b/tests/syntax_test_scope.html new file mode 100644 index 0000000..ee354ec --- /dev/null +++ b/tests/syntax_test_scope.html @@ -0,0 +1,51 @@ +# SYNTAX TEST "HTML (CoffeeScript).sublime-syntax" + +<script type="text/coffeescript"> +# <- meta.tag.script.begin.html punctuation.definition.tag.begin.html +#^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ meta.tag.script.begin.html +#^^^^^^ entity.name.tag.script.html +# ^^^^ entity.other.attribute-name.html +# ^ punctuation.separator.key-value.html +# ^^^^^^^^^^^^^^^^^^^ meta.string.html string.quoted.double.html +# ^ punctuation.definition.tag.end.html + class extends parentClass + # <- source.coffee.embedded.html meta.class.coffee storage.type.class.coffee + #^^^^^^^^^^^^^^^^^^^^^^^^ source.coffee.embedded.html meta.class.coffee + #^^^^ storage.type.class.coffee + # ^^^^^^^ keyword.control.inheritance.coffee + # ^^^^^^^^^^^ entity.other.inherited-class.coffee + constructor: -> + return + # <- source.coffee.embedded.html + # ^^^^^^ source.coffee.embedded.html keyword.control.flow.coffee +</script> +# <- meta.tag.script.end.html punctuation.definition.tag.begin.html +#^^^^^^^^ meta.tag.script.end.html +#^ punctuation.definition.tag.begin.html +# ^^^^^^ entity.name.tag.script.html +# ^ punctuation.definition.tag.end.html + +<script lang="coffee"> +# <- meta.tag.script.begin.html punctuation.definition.tag.begin.html +#^^^^^^^^^^^^^^^^^^^^^ meta.tag.script.begin.html +#^^^^^^ entity.name.tag.script.html +# ^^^^ entity.other.attribute-name.html +# ^ punctuation.separator.key-value.html +# ^^^^^^^^ meta.string.html string.quoted.double.html +# ^ punctuation.definition.tag.end.html + class extends parentClass + # <- source.coffee.embedded.html meta.class.coffee storage.type.class.coffee + #^^^^^^^^^^^^^^^^^^^^^^^^ source.coffee.embedded.html meta.class.coffee + #^^^^ storage.type.class.coffee + # ^^^^^^^ keyword.control.inheritance.coffee + # ^^^^^^^^^^^ entity.other.inherited-class.coffee + constructor: -> + return + # <- source.coffee.embedded.html + # ^^^^^^ source.coffee.embedded.html keyword.control.flow.coffee +</script> +# <- meta.tag.script.end.html punctuation.definition.tag.begin.html +#^^^^^^^^ meta.tag.script.end.html +#^ punctuation.definition.tag.begin.html +# ^^^^^^ entity.name.tag.script.html +# ^ punctuation.definition.tag.end.html From 45560453450e22c035e2d20511368602da303cc6 Mon Sep 17 00:00:00 2001 From: deathaxe <deathaxe82@googlemail.com> Date: Mon, 26 Aug 2024 19:06:50 +0200 Subject: [PATCH 06/52] Initial support for JSX tags Resolves #148 Fixes #218 --- CoffeeScript.sublime-syntax | 180 +++++++++++++++++++++++++++++++++ Comments JSX.tmPreferences | 26 +++++ tests/syntax_test_scope.coffee | 46 +++++++++ 3 files changed, 252 insertions(+) create mode 100644 Comments JSX.tmPreferences diff --git a/CoffeeScript.sublime-syntax b/CoffeeScript.sublime-syntax index 21a2282..7eb2cc4 100644 --- a/CoffeeScript.sublime-syntax +++ b/CoffeeScript.sublime-syntax @@ -32,6 +32,7 @@ contexts: - include: classes - include: keywords - include: functions + - include: jsx-tags - include: expressions expressions: @@ -573,6 +574,171 @@ contexts: pop: 1 - include: script +###[ JSX TAGS ]################################################################ + + jsx-tags: + - match: (<)(?:({{component_names}})|({{tag_names}}))(?=\s|/?>) + captures: + 1: punctuation.definition.tag.begin.coffee + 2: entity.name.tag.component.coffee + 3: entity.name.tag.coffee + push: + - jsx-meta + - jsx-tag-open-body + + jsx-meta: + - meta_include_prototype: false + - meta_scope: meta.jsx.coffee + - include: immediately-pop + + jsx-body: + - match: (</)(?:({{component_names}})|({{tag_names}}))(?=\s|/?>) + captures: + 1: punctuation.definition.tag.begin.coffee + 2: entity.name.tag.component.coffee + 3: entity.name.tag.coffee + set: jsx-tag-close-body + - match: (<)(?:({{component_names}})|({{tag_names}}))(?=\s|/?>) + captures: + 1: punctuation.definition.tag.begin.coffee + 2: entity.name.tag.component.coffee + 3: entity.name.tag.coffee + push: jsx-tag-open-body + - include: jsx-text-interpolations + + jsx-tag-close-body: + - meta_include_prototype: false + - meta_scope: meta.tag.coffee + - match: /?> + scope: punctuation.definition.tag.end.coffee + pop: 1 + + jsx-tag-open-body: + - meta_include_prototype: false + - meta_scope: meta.tag.coffee + - match: /> + scope: punctuation.definition.tag.end.coffee + pop: 1 + - match: \> + scope: punctuation.definition.tag.end.coffee + set: jsx-body + - match: '{{attribute_name_start}}' + push: + - jsx-tag-attribute-meta + - jsx-tag-attribute-assignment + - jsx-tag-attribute-name + + jsx-tag-attribute-name: + - meta_scope: entity.other.attribute-name.coffee + - include: jsx-string-interpolations + - match: '{{attribute_name_break}}' + pop: 1 + - match: '["''`<]' + scope: invalid.illegal.attribute-name.coffee + + jsx-tag-attribute-meta: + - meta_include_prototype: false + - meta_scope: meta.attribute-with-value.coffee + - include: immediately-pop + + jsx-tag-attribute-assignment: + - meta_include_prototype: false + - match: = + scope: punctuation.separator.key-value.coffee + set: jsx-tag-attribute-value + - include: else-pop + + jsx-tag-attribute-value: + - meta_include_prototype: false + - match: \" + scope: punctuation.definition.string.begin.coffee + set: jsx-tag-attribute-value-double-quoted-content + - match: \' + scope: punctuation.definition.string.begin.coffee + set: jsx-tag-attribute-value-single-quoted-content + - match: '{{unquoted_attribute_start}}' + set: jsx-tag-attribute-value-unquoted-content + - include: else-pop + + jsx-tag-attribute-value-double-quoted-content: + # See the top of this file for why prototype is excluded + - meta_include_prototype: false + - meta_scope: meta.string.coffee string.quoted.double.coffee + - match: \" + scope: punctuation.definition.string.end.coffee + pop: 1 + - include: jsx-string-interpolations + + jsx-tag-attribute-value-single-quoted-content: + # See the top of this file for why prototype is excluded + - meta_include_prototype: false + - meta_scope: meta.string.coffee string.quoted.single.coffee + - match: \' + scope: punctuation.definition.string.end.coffee + pop: 1 + - include: jsx-string-interpolations + + jsx-tag-attribute-value-unquoted-content: + # See the top of this file for why prototype is excluded + - meta_include_prototype: false + - meta_content_scope: meta.string.coffee string.unquoted.coffee + - include: jsx-string-interpolations + - match: '{{unquoted_attribute_break}}' + pop: 1 + - match: '["''`<]' + scope: invalid.illegal.attribute-value.coffee + + jsx-string-interpolations: + - match: \{# + scope: punctuation.definition.comment.begin.coffee + push: jsx-string-comment-body + - match: \{ + scope: punctuation.section.interpolation.begin.coffee + push: jsx-string-interpolation-body + + jsx-string-comment-body: + # required to support "toggle_comment" + - clear_scopes: 1 + - meta_scope: meta.interpolation.coffee comment.block.coffee + - include: jsx-text-comment-body + + jsx-string-interpolation-body: + - clear_scopes: 1 + - meta_scope: meta.interpolation.coffee + - meta_content_scope: source.coffee.embedded.jsx + - include: jsx-text-interpolation-body + + jsx-text-interpolations: + - match: \{# + scope: punctuation.definition.comment.begin.coffee + push: jsx-text-comment-body + - match: \{ + scope: punctuation.section.interpolation.begin.coffee + push: jsx-text-interpolation-body + + jsx-text-comment-body: + # required to support "toggle_comment" + - meta_scope: meta.interpolation.coffee comment.block.coffee + - match: \} + scope: punctuation.definition.comment.end.coffee + pop: 1 + + jsx-text-interpolation-body: + - meta_scope: meta.interpolation.coffee + - meta_content_scope: source.coffee.embedded.jsx + - match: \} + scope: punctuation.section.interpolation.end.coffee + pop: 1 + - match: \# + scope: punctuation.definition.comment.coffee + push: jsx-line-comment-body + - include: script + + jsx-line-comment-body: + - meta_scope: comment.line.number-sign.coffee + - match: $\n?|(?=}) + pop: 1 + ###[ VARIABLES ]############################################################### instance-variables: @@ -596,4 +762,18 @@ contexts: variables: + ascii_space: '\t\n\f ' + identifier: '[a-zA-Z\$_]\w*' + + component_names: '[A-Z][[:alnum:]_.-]*' + tag_names: '[[:alpha:]][[:alnum:]_.-]*' + + # https://html.spec.whatwg.org/multipage/syntax.coffee#attributes-2 + attribute_name_break_char: '[{{ascii_space}}=/>]' + attribute_name_break: (?={{attribute_name_break_char}}) + attribute_name_start: (?=[^{{attribute_name_break_char}}]) + + # https://html.spec.whatwg.org/multipage/syntax.coffee#syntax-attribute-value + unquoted_attribute_break: (?=[{{ascii_space}}]|/?>) + unquoted_attribute_start: (?=[^{{ascii_space}}=>]) diff --git a/Comments JSX.tmPreferences b/Comments JSX.tmPreferences new file mode 100644 index 0000000..6448f16 --- /dev/null +++ b/Comments JSX.tmPreferences @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>scope</key> + <string>source.coffee meta.jsx, source.coffee meta.jsx meta.interpolation comment.line</string> + <key>settings</key> + <dict> + <key>shellVariables</key> + <array> + <dict> + <key>name</key> + <string>TM_COMMENT_START</string> + <key>value</key> + <string>{# </string> + </dict> + <dict> + <key>name</key> + <string>TM_COMMENT_END</string> + <key>value</key> + <string> }</string> + </dict> + </array> + </dict> +</dict> +</plist> diff --git a/tests/syntax_test_scope.coffee b/tests/syntax_test_scope.coffee index 051c42e..ba729f1 100644 --- a/tests/syntax_test_scope.coffee +++ b/tests/syntax_test_scope.coffee @@ -561,3 +561,49 @@ class App.Router extends Snakeskin.Router @variable # ^^^^^^^^^ variable.other.readwrite.instance.coffee # ^ punctuation.definition.variable.coffee + +###[ JSX ]##################################################################### + + <Component attrib="va{@lue}"> +# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ meta.jsx.coffee meta.tag.coffee +# ^ punctuation.definition.tag.begin.coffee +# ^^^^^^^^^ entity.name.tag.component.coffee +# ^^^^^^^^^^^^^^^^^ meta.attribute-with-value.coffee +# ^^^^^^ entity.other.attribute-name.coffee +# ^ punctuation.separator.key-value.coffee +# ^^^ meta.string.coffee string.quoted.double.coffee +# ^^^^^^ meta.string.coffee meta.interpolation.coffee +# ^ punctuation.section.interpolation.begin.coffee +# ^^^^ source.coffee.embedded.jsx variable.other.readwrite.instance.coffee +# ^ punctuation.section.interpolation.end.coffee +# ^ meta.string.coffee string.quoted.double.coffee punctuation.definition.string.end.coffee +# ^ punctuation.definition.tag.end.coffee + <h1>Text {# comment}!</h1> +# ^^^^ meta.jsx.coffee meta.tag.coffee +# ^ punctuation.definition.tag.begin.coffee +# ^^ entity.name.tag.coffee +# ^ punctuation.definition.tag.end.coffee +# ^^^^^ meta.jsx.coffee - meta.interpolation +# ^^^^^^^^^^^ meta.jsx.coffee meta.interpolation.coffee comment.block.coffee +# ^^ punctuation.definition.comment.begin.coffee +# ^ punctuation.definition.comment.end.coffee +# ^ meta.jsx.coffee - meta.interpolation +# ^^^^^ meta.jsx.coffee meta.tag.coffee +# ^^ punctuation.definition.tag.begin.coffee +# ^^ entity.name.tag.coffee +# ^ punctuation.definition.tag.end.coffee + </Component> +# ^^^^^^^^^^^^ meta.jsx.coffee meta.tag.coffee +# ^^ punctuation.definition.tag.begin.coffee +# ^^^^^^^^^ entity.name.tag.component.coffee +# ^ punctuation.definition.tag.end.coffee +# ^ - meta.jsx - meta.tag + + <EmailInput + {# THIS IS A GOOD COMMENT } +# ^^^^^^^^^^^^^^^^^^^^^^^^^^^ meta.jsx.coffee meta.tag.coffee meta.attribute-with-value.coffee meta.interpolation.coffee comment.block.coffee + {...@props} +# ^^^^^^^^^^^ meta.jsx.coffee meta.tag.coffee meta.attribute-with-value.coffee meta.interpolation.coffee + /> +# ^^ meta.jsx.coffee meta.tag.coffee punctuation.definition.tag.end.coffee +# ^ - meta.jsx - meta.tag From 62a9d3c18ad7ecfbd66e3aa456d734d5986aa183 Mon Sep 17 00:00:00 2001 From: deathaxe <deathaxe82@googlemail.com> Date: Mon, 26 Aug 2024 20:50:23 +0200 Subject: [PATCH 07/52] Add triple backtick quoted strings --- CoffeeScript.sublime-syntax | 13 ++++++++++++- tests/syntax_test_scope.coffee | 21 +++++++++++++++++++++ 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/CoffeeScript.sublime-syntax b/CoffeeScript.sublime-syntax index 7eb2cc4..3320bba 100644 --- a/CoffeeScript.sublime-syntax +++ b/CoffeeScript.sublime-syntax @@ -513,11 +513,18 @@ contexts: pop: 1 backtick-quoted-strings: + - match: \`{3} + scope: meta.string.heredoc.coffee string.quoted.script.coffee punctuation.definition.string.begin.coffee + embed: scope:source.jsx + embed_scope: meta.string.heredoc.coffee meta.embedded.coffee source.jsx.embedded.coffee + escape: '{{no_escape_behind}}`{3}' + escape_captures: + 0: meta.string.heredoc.coffee string.quoted.script.coffee punctuation.definition.string.end.coffee - match: \` scope: meta.string.coffee string.quoted.script.coffee punctuation.definition.string.begin.coffee embed: scope:source.jsx embed_scope: meta.string.coffee meta.embedded.coffee source.jsx.embedded.coffee - escape: \` + escape: '{{no_escape_behind}}`' escape_captures: 0: meta.string.coffee string.quoted.script.coffee punctuation.definition.string.end.coffee @@ -764,6 +771,10 @@ variables: ascii_space: '\t\n\f ' + # A lookbehind used in embed..escape patterns, to check for unescaped characters + # in embed...escape statements. + no_escape_behind: (?<![^\\]\\)(?<![\\]{3}) + identifier: '[a-zA-Z\$_]\w*' component_names: '[A-Z][[:alnum:]_.-]*' diff --git a/tests/syntax_test_scope.coffee b/tests/syntax_test_scope.coffee index ba729f1..4436649 100644 --- a/tests/syntax_test_scope.coffee +++ b/tests/syntax_test_scope.coffee @@ -497,6 +497,27 @@ class App.Router extends Snakeskin.Router # <- meta.string.heredoc.coffee string.quoted.single.coffee #^ meta.string.heredoc.coffee string.quoted.single.coffee # ^^^ meta.string.heredoc.coffee string.quoted.single.coffee punctuation.definition.string.end.coffee +# ^ - meta.string - string + + ``` +# ^^^ meta.string.heredoc.coffee string.quoted.script.coffee punctuation.definition.string.begin.coffee +# ^ meta.string.heredoc.coffee meta.embedded.coffee source.jsx.embedded.coffee - string + + var i = `back ${tick} string`; +# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ meta.string.heredoc.coffee meta.embedded.coffee source.jsx.embedded.coffee - source.jsx source.jsx +# ^^^ keyword.declaration +# ^^^^^^ meta.string string.quoted.other.js +# ^^^^^^^ meta.string meta.interpolation.js +# ^^^^^^^ meta.string string.quoted.other.js + return (<h1>Hello {World}</h1>) +# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ meta.string.heredoc.coffee meta.embedded.coffee source.jsx.embedded.coffee - source.jsx source.jsx +# ^^^^^^^^^^^^^^^^^^^^^^ meta.group.js meta.jsx.js +# ^^^^ meta.tag +# ^^^^^ meta.tag + ``` +# <- meta.string.heredoc.coffee meta.embedded.coffee source.jsx.embedded.coffee - string +#^ meta.string.heredoc.coffee meta.embedded.coffee source.jsx.embedded.coffee - string +# ^^^ meta.string.heredoc.coffee string.quoted.script.coffee punctuation.definition.string.end.coffee # ^ - meta.string - string ` From 460009d2955a062db015494a45917acb91f98bae Mon Sep 17 00:00:00 2001 From: deathaxe <deathaxe82@googlemail.com> Date: Mon, 26 Aug 2024 20:56:51 +0200 Subject: [PATCH 08/52] Add `await` keyword --- CoffeeScript.sublime-syntax | 2 +- tests/syntax_test_scope.coffee | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CoffeeScript.sublime-syntax b/CoffeeScript.sublime-syntax index 3320bba..0d80125 100644 --- a/CoffeeScript.sublime-syntax +++ b/CoffeeScript.sublime-syntax @@ -314,7 +314,7 @@ contexts: - match: while(?!\s*:)\b scope: keyword.control.loop.while.coffee # flow - - match: (?:break|continue|return|throw|yield(?:\s+from)?)(?!\s*:)\b + - match: (?:await|break|continue|return|throw|yield(?:\s+from)?)(?!\s*:)\b scope: keyword.control.flow.coffee # other - match: (?:debugger\b|\\) diff --git a/tests/syntax_test_scope.coffee b/tests/syntax_test_scope.coffee index 4436649..5d700a8 100644 --- a/tests/syntax_test_scope.coffee +++ b/tests/syntax_test_scope.coffee @@ -236,6 +236,11 @@ class App.Router extends Snakeskin.Router # ^^^^^^^^^^ keyword.control.flow.coffee # ^^^^ variable.other.readwrite.instance.coffee + await return @foo; +# ^^^^^ keyword.control.flow.coffee +# ^^^^^^ keyword.control.flow.coffee +# ^^^^ variable.other.readwrite.instance.coffee + ###[ OPERATORS ]############################################################### as From 5e96f227d497ec96c970ef98ef71d66a55afc884 Mon Sep 17 00:00:00 2001 From: deathaxe <deathaxe82@googlemail.com> Date: Tue, 27 Aug 2024 18:50:58 +0200 Subject: [PATCH 09/52] Restrict and disable key bindings by default Resolves #147 Key bindings were globally enabled even in languages they don't make sense. This commit therefore... 1. adds "selector" contexts to restrict them to coffeescript 2. disables them by default as all commands are available via Command Palette. Users have to opt-in via "Preferences: CoffeeScript Key Bindings". Actually most of them rather seem to be "Build System Variants". --- Default (Linux).sublime-keymap | 72 +++++++++++++++++++++++++++---- Default (OSX).sublime-keymap | 74 +++++++++++++++++++++++++++----- Default (Windows).sublime-keymap | 72 +++++++++++++++++++++++++++---- 3 files changed, 190 insertions(+), 28 deletions(-) diff --git a/Default (Linux).sublime-keymap b/Default (Linux).sublime-keymap index 11d3b1b..7b5539f 100644 --- a/Default (Linux).sublime-keymap +++ b/Default (Linux).sublime-keymap @@ -1,11 +1,65 @@ [ - {"keys": ["alt+shift+s"], "command": "check_syntax"}, - {"keys": ["alt+shift+r"], "command": "run_script"}, - {"keys": ["alt+shift+t"], "command": "run_cake_task"}, - {"keys": ["alt+shift+c"], "command": "compile"}, - {"keys": ["alt+shift+d"], "command": "compile_and_display", "args": {"opt": "-p"}}, - {"keys": ["alt+shift+l"], "command": "compile_and_display", "args": {"opt": "-t"}}, - {"keys": ["alt+shift+n"], "command": "compile_and_display", "args": {"opt": "-n"}}, - {"keys": ["alt+shift+w"], "command": "toggle_watch"}, - {"keys": ["alt+shift+z"], "command": "toggle_output_panel"} + // { + // "keys": ["alt+shift+s"], + // "command": "check_syntax", + // "context": [ + // { "key": "selector", "operand": "source.coffee, source.litcoffee" } + // ] + // }, + // { + // "keys": ["alt+shift+r"], + // "command": "run_script", + // "context": [ + // { "key": "selector", "operand": "source.coffee, source.litcoffee" } + // ] + // }, + // { + // "keys": ["alt+shift+t"], + // "command": "run_cake_task", + // "context": [ + // { "key": "selector", "operand": "source.coffee, source.litcoffee" } + // ] + // }, + // { + // "keys": ["alt+shift+c"], + // "command": "compile", + // "context": [ + // { "key": "selector", "operand": "source.coffee, source.litcoffee" } + // ] + // }, + // { + // "keys": ["alt+shift+d"], + // "command": "compile_and_display", "args": {"opt": "-p"}, + // "context": [ + // { "key": "selector", "operand": "source.coffee, source.litcoffee" } + // ] + // }, + // { + // "keys": ["alt+shift+l"], + // "command": "compile_and_display", "args": {"opt": "-t"}, + // "context": [ + // { "key": "selector", "operand": "source.coffee, source.litcoffee" } + // ] + // }, + // { + // "keys": ["alt+shift+n"], + // "command": "compile_and_display", "args": {"opt": "-n"}, + // "context": [ + // { "key": "selector", "operand": "source.coffee, source.litcoffee" } + // ] + // }, + // { + // "keys": ["alt+shift+w"], + // "command": "toggle_watch", + // "context": [ + // { "key": "selector", "operand": "source.coffee, source.litcoffee" } + // ] + // }, + // { + // "keys": ["alt+shift+z"], + // "command": "toggle_output_panel", + // "context": [ + // { "key": "selector", "operand": "source.coffee, source.litcoffee" } + // ] + // } ] diff --git a/Default (OSX).sublime-keymap b/Default (OSX).sublime-keymap index 3c125a3..af2329c 100644 --- a/Default (OSX).sublime-keymap +++ b/Default (OSX).sublime-keymap @@ -1,11 +1,65 @@ [ - {"keys": ["alt+shift+s"], "command": "check_syntax"}, - {"keys": ["alt+shift+r"], "command": "run_script"}, - {"keys": ["alt+shift+t"], "command": "run_cake_task"}, - {"keys": ["alt+shift+c"], "command": "compile"}, - {"keys": ["alt+shift+d"], "command": "compile_and_display", "args": {"opt": "-p"}}, - {"keys": ["alt+shift+x"], "command": "compile_and_display", "args": {"opt": "-t"}}, - {"keys": ["alt+shift+n"], "command": "compile_and_display", "args": {"opt": "-n"}}, - {"keys": ["alt+shift+w"], "command": "toggle_watch"}, - {"keys": ["alt+shift+z"], "command": "toggle_output_panel"} -] + // { + // "keys": ["alt+shift+s"], + // "command": "check_syntax", + // "context": [ + // { "key": "selector", "operand": "source.coffee, source.litcoffee" } + // ] + // }, + // { + // "keys": ["alt+shift+r"], + // "command": "run_script", + // "context": [ + // { "key": "selector", "operand": "source.coffee, source.litcoffee" } + // ] + // }, + // { + // "keys": ["alt+shift+t"], + // "command": "run_cake_task", + // "context": [ + // { "key": "selector", "operand": "source.coffee, source.litcoffee" } + // ] + // }, + // { + // "keys": ["alt+shift+c"], + // "command": "compile", + // "context": [ + // { "key": "selector", "operand": "source.coffee, source.litcoffee" } + // ] + // }, + // { + // "keys": ["alt+shift+d"], + // "command": "compile_and_display", "args": { "opt": "-p" }, + // "context": [ + // { "key": "selector", "operand": "source.coffee, source.litcoffee" } + // ] + // }, + // { + // "keys": ["alt+shift+x"], + // "command": "compile_and_display", "args": { "opt": "-t" }, + // "context": [ + // { "key": "selector", "operand": "source.coffee, source.litcoffee" } + // ] + // }, + // { + // "keys": ["alt+shift+n"], + // "command": "compile_and_display", "args": { "opt": "-n" }, + // "context": [ + // { "key": "selector", "operand": "source.coffee, source.litcoffee" } + // ] + // }, + // { + // "keys": ["alt+shift+w"], + // "command": "toggle_watch", + // "context": [ + // { "key": "selector", "operand": "source.coffee, source.litcoffee" } + // ] + // }, + // { + // "keys": ["alt+shift+z"], + // "command": "toggle_output_panel", + // "context": [ + // { "key": "selector", "operand": "source.coffee, source.litcoffee" } + // ] + // } +] \ No newline at end of file diff --git a/Default (Windows).sublime-keymap b/Default (Windows).sublime-keymap index 11d3b1b..7b5539f 100644 --- a/Default (Windows).sublime-keymap +++ b/Default (Windows).sublime-keymap @@ -1,11 +1,65 @@ [ - {"keys": ["alt+shift+s"], "command": "check_syntax"}, - {"keys": ["alt+shift+r"], "command": "run_script"}, - {"keys": ["alt+shift+t"], "command": "run_cake_task"}, - {"keys": ["alt+shift+c"], "command": "compile"}, - {"keys": ["alt+shift+d"], "command": "compile_and_display", "args": {"opt": "-p"}}, - {"keys": ["alt+shift+l"], "command": "compile_and_display", "args": {"opt": "-t"}}, - {"keys": ["alt+shift+n"], "command": "compile_and_display", "args": {"opt": "-n"}}, - {"keys": ["alt+shift+w"], "command": "toggle_watch"}, - {"keys": ["alt+shift+z"], "command": "toggle_output_panel"} + // { + // "keys": ["alt+shift+s"], + // "command": "check_syntax", + // "context": [ + // { "key": "selector", "operand": "source.coffee, source.litcoffee" } + // ] + // }, + // { + // "keys": ["alt+shift+r"], + // "command": "run_script", + // "context": [ + // { "key": "selector", "operand": "source.coffee, source.litcoffee" } + // ] + // }, + // { + // "keys": ["alt+shift+t"], + // "command": "run_cake_task", + // "context": [ + // { "key": "selector", "operand": "source.coffee, source.litcoffee" } + // ] + // }, + // { + // "keys": ["alt+shift+c"], + // "command": "compile", + // "context": [ + // { "key": "selector", "operand": "source.coffee, source.litcoffee" } + // ] + // }, + // { + // "keys": ["alt+shift+d"], + // "command": "compile_and_display", "args": {"opt": "-p"}, + // "context": [ + // { "key": "selector", "operand": "source.coffee, source.litcoffee" } + // ] + // }, + // { + // "keys": ["alt+shift+l"], + // "command": "compile_and_display", "args": {"opt": "-t"}, + // "context": [ + // { "key": "selector", "operand": "source.coffee, source.litcoffee" } + // ] + // }, + // { + // "keys": ["alt+shift+n"], + // "command": "compile_and_display", "args": {"opt": "-n"}, + // "context": [ + // { "key": "selector", "operand": "source.coffee, source.litcoffee" } + // ] + // }, + // { + // "keys": ["alt+shift+w"], + // "command": "toggle_watch", + // "context": [ + // { "key": "selector", "operand": "source.coffee, source.litcoffee" } + // ] + // }, + // { + // "keys": ["alt+shift+z"], + // "command": "toggle_output_panel", + // "context": [ + // { "key": "selector", "operand": "source.coffee, source.litcoffee" } + // ] + // } ] From 59f5968e3aee212408077e50c74faac1ec099989 Mon Sep 17 00:00:00 2001 From: deathaxe <deathaxe82@googlemail.com> Date: Tue, 27 Aug 2024 20:36:27 +0200 Subject: [PATCH 10/52] Fix multi-line function parameter lists Fixes #160 --- CoffeeScript.sublime-syntax | 76 ++++++++++++++++++++++++++-------- tests/syntax_test_scope.coffee | 73 +++++++++++++++++--------------- 2 files changed, 99 insertions(+), 50 deletions(-) diff --git a/CoffeeScript.sublime-syntax b/CoffeeScript.sublime-syntax index 0d80125..70c3c72 100644 --- a/CoffeeScript.sublime-syntax +++ b/CoffeeScript.sublime-syntax @@ -115,36 +115,77 @@ contexts: pop: 1 - include: script # anonymous functions - - match: (?=\([^()]*?\)\s*[=-]>) - push: function-parameter-list + - match: (?=\() + branch_point: anonymous + branch: + - anonymous-parameter-list + - group # named functions - - match: |- - (?x) - \s* - ( [a-zA-Z\$_@] [\w\$:.]* ) \s* - (?=[:=](?: \s*.\( $ | (?: \s*\(.*\) )? \s* [=-]> ) ) - scope: meta.function.identifier.coffee - captures: - 1: entity.name.function.coffee - push: function-parameter-list + - match: (?=(?:(?!and=|or=)|@){{identifier}}\s*[:=][^>]) + branch_point: function + branch: + - function-name + - immediately-pop - match: '[=-]>' scope: keyword.declaration.function.coffee - function-parameter-list: + anonymous-parameter-list: + - match: \( + scope: punctuation.section.parameters.begin.coffee + set: + - anonymous-body + - function-parameter-list-body + - include: comments + - include: else-pop + + anonymous-body: - meta_content_scope: meta.function.coffee + - match: '[=-]>' + scope: meta.function.coffee keyword.declaration.function.coffee + pop: 1 + - match: (?=\S) + fail: anonymous + + group: + - match: \( + scope: punctuation.section.group.begin.coffee + pop: 1 + + function-name: + - meta_include_prototype: false + - meta_scope: meta.function.identifier.coffee + - match: (@?){{identifier}} + scope: entity.name.function.coffee + captures: + 1: punctuation.definition.variable.coffee + set: + - function-body + - function-parameter-list + - function-assignment + - match: (?=\S) + fail: function + + function-assignment: + - meta_include_prototype: false - match: '[:=](?!>)' scope: keyword.operator.assignment.coffee + pop: 1 + - match: (?=\S) + fail: function + + function-parameter-list: - match: \( scope: punctuation.section.parameters.begin.coffee set: function-parameter-list-body - - match: (?=\S) - set: function-body + - include: comments + - include: else-pop function-parameter-list-body: + - clear_scopes: 1 - meta_scope: meta.function.parameters.coffee - match: \) scope: punctuation.section.parameters.end.coffee - set: function-body + pop: 1 - match: '[:=]' scope: keyword.operator.assignment.coffee push: parameter-value @@ -165,7 +206,8 @@ contexts: - match: '[=-]>' scope: meta.function.coffee keyword.declaration.function.coffee pop: 1 - - include: else-pop + - match: (?=\S) + fail: function ###[ FUNCTION CALLS ]######################################################### @@ -775,7 +817,7 @@ variables: # in embed...escape statements. no_escape_behind: (?<![^\\]\\)(?<![\\]{3}) - identifier: '[a-zA-Z\$_]\w*' + identifier: '[[:alpha:]_$]\w*' component_names: '[A-Z][[:alnum:]_.-]*' tag_names: '[[:alpha:]][[:alnum:]_.-]*' diff --git a/tests/syntax_test_scope.coffee b/tests/syntax_test_scope.coffee index 5d700a8..0be4619 100644 --- a/tests/syntax_test_scope.coffee +++ b/tests/syntax_test_scope.coffee @@ -78,29 +78,14 @@ class App.Router extends Snakeskin.Router # ^ keyword.operator.assignment.coffee # ^^ keyword.declaration.function.coffee - namespace.name: -> -# ^^^^^^^^^^^^^^^^^^ - meta.function meta.function -# ^^^^^^^^^^^^^^ meta.function.identifier.coffee entity.name.function.coffee -# ^^^^ meta.function.coffee -# ^ keyword.operator.assignment.coffee -# ^^ keyword.declaration.function.coffee - name = => # ^^^^^^^^^ - meta.function meta.function # ^^^^ meta.function.identifier.coffee entity.name.function.coffee -# ^ meta.function.identifier.coffee - entity +# ^ meta.function.coffee - entity # ^^^^ meta.function.coffee # ^ keyword.operator.assignment.coffee # ^^ keyword.declaration.function.coffee - namespace.name = => -# ^^^^^^^^^^^^^^^^^^^ - meta.function meta.function -# ^^^^^^^^^^^^^^ meta.function.identifier.coffee entity.name.function.coffee -# ^ meta.function.identifier.coffee - entity -# ^^^^ meta.function.coffee -# ^ keyword.operator.assignment.coffee -# ^^ keyword.declaration.function.coffee - name: => # ^^^^^^^^ - meta.function meta.function # ^^^^ meta.function.identifier.coffee entity.name.function.coffee @@ -108,29 +93,14 @@ class App.Router extends Snakeskin.Router # ^ keyword.operator.assignment.coffee # ^^ keyword.declaration.function.coffee - namespace.name: => -# ^^^^^^^^^^^^^^^^^^ - meta.function meta.function -# ^^^^^^^^^^^^^^ meta.function.identifier.coffee entity.name.function.coffee -# ^^^^ meta.function.coffee -# ^ keyword.operator.assignment.coffee -# ^^ keyword.declaration.function.coffee - name = => # ^^^^^^^^^ - meta.function meta.function # ^^^^ meta.function.identifier.coffee entity.name.function.coffee -# ^ meta.function.identifier.coffee - entity +# ^ meta.function.coffee - entity # ^^^^ meta.function.coffee # ^ keyword.operator.assignment.coffee # ^^ keyword.declaration.function.coffee - namespace.name = => -# ^^^^^^^^^^^^^^^^^^^ - meta.function meta.function -# ^^^^^^^^^^^^^^ meta.function.identifier.coffee entity.name.function.coffee -# ^ meta.function.identifier.coffee - entity -# ^^^^ meta.function.coffee -# ^ keyword.operator.assignment.coffee -# ^^ keyword.declaration.function.coffee - name: () -> # ^^^^^^^^^^^ - meta.function meta.function # ^^^^ meta.function.identifier.coffee entity.name.function.coffee @@ -164,6 +134,30 @@ class App.Router extends Snakeskin.Router # ^ punctuation.section.parameters.end.coffee # ^^ keyword.declaration.function.coffee + name: +# ^^^^ meta.function.identifier.coffee entity.name.function.coffee +# ^ meta.function.coffee keyword.operator.assignment.coffee + (foo, bar = undefined, +#^^^ meta.function.coffee +# ^^^^^^^^^^^^^^^^^^^^^^^ meta.function.parameters.coffee +# ^ punctuation.section.parameters.begin.coffee +# ^^^ variable.parameter.coffee +# ^ punctuation.separator.sequence.coffee +# ^^^ variable.parameter.coffee +# ^ keyword.operator.assignment.coffee +# ^^^^^^^^^ constant.language.coffee +# ^ punctuation.separator.sequence.coffee + baz="buuz", ...) -> +# ^^^^^^^^^^^^^^^^^^ meta.function.parameters.coffee +# ^^^ meta.function.coffee +# ^^^ variable.parameter.coffee +# ^ keyword.operator.assignment.coffee +# ^^^^^^ meta.string.coffee string.quoted.double.coffee +# ^ punctuation.separator.sequence.coffee +# ^^^ keyword.operator.variadic.coffee +# ^ punctuation.section.parameters.end.coffee +# ^^ keyword.declaration.function.coffee + name: ( # ^^^^ meta.function.identifier.coffee entity.name.function.coffee # ^^ meta.function.coffee @@ -203,13 +197,26 @@ class App.Router extends Snakeskin.Router # ^^ keyword.declaration.function.coffee (foo) -> -# ^^^^^ meta.function.parameters.coffee +# ^^^^^ meta.function.parameters.coffee - meta.function meta.function # ^^^ meta.function.coffee # ^ punctuation.section.parameters.begin.coffee # ^^^ variable.parameter.coffee # ^ punctuation.section.parameters.end.coffee # ^^ keyword.declaration.function.coffee + (foo, + bar="baz") -> +# ^^^^^^^^^^^^ meta.function.parameters.coffee - meta.function meta.function +# ^^^ meta.function.coffee - meta.function meta.function +# ^^^ variable.parameter.coffee +# ^ keyword.operator.assignment.coffee +# ^^^^^ meta.string.coffee string.quoted.double.coffee +# ^ punctuation.section.parameters.end.coffee +# ^^ keyword.declaration.function.coffee + + -> +# ^^ keyword.declaration.function.coffee + ###[ KEYWORDS ]################################################################ if .if _if $if From 9d7ab17c077cd33fce650b2b8a8d6ef8926363b0 Mon Sep 17 00:00:00 2001 From: deathaxe <deathaxe82@googlemail.com> Date: Tue, 27 Aug 2024 20:42:01 +0200 Subject: [PATCH 11/52] Fix at-function calls --- CoffeeScript.sublime-syntax | 4 +++- tests/syntax_test_scope.coffee | 27 +++++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/CoffeeScript.sublime-syntax b/CoffeeScript.sublime-syntax index 70c3c72..c62829b 100644 --- a/CoffeeScript.sublime-syntax +++ b/CoffeeScript.sublime-syntax @@ -302,8 +302,10 @@ contexts: | Uint(?: 8 | 16 | 32 | 64)Array | XMLHttpRequest | Symbol )\b scope: support.class.coffee # user function calls - - match: '{{identifier}}(?=\()' + - match: (@?){{identifier}}(?=\() scope: meta.function-call.identifier.coffee variable.function.coffee + captures: + 1: punctuation.definition.variable.coffee push: function-call-argument-list function-call-argument-list: diff --git a/tests/syntax_test_scope.coffee b/tests/syntax_test_scope.coffee index 0be4619..40b2fca 100644 --- a/tests/syntax_test_scope.coffee +++ b/tests/syntax_test_scope.coffee @@ -366,6 +366,33 @@ class App.Router extends Snakeskin.Router # ^ punctuation.section.group.begin.coffee # ^ punctuation.section.group.end.coffee +###[ USER FUNCTIONS ]########################################################## + + func() +# ^^^^ meta.function-call.identifier.coffee variable.function.coffee +# ^^ meta.function-call.arguments.coffee + + $func() +# ^^^^^ meta.function-call.identifier.coffee variable.function.coffee +# ^^ meta.function-call.arguments.coffee + + $('') +# ^ meta.function-call.identifier.coffee variable.function.coffee +# ^^^^ meta.function-call.arguments.coffee + + @name() +# ^^^^^ meta.function-call.identifier.coffee variable.function.coffee +# ^^ meta.function-call.arguments.coffee + + @$('#notification') +# ^^ meta.function-call.identifier.coffee variable.function.coffee +# ^^^^^^^^^^^^^^^^^ meta.function-call.arguments.coffee +# ^ punctuation.definition.variable.coffee +# ^ variable.function.coffee +# ^ punctuation.section.group.begin.coffee +# ^^^^^^^^^^^^^^^ meta.string.coffee string.quoted.single.coffee +# ^ punctuation.section.group.end.coffee + ###[ OBJECT MEMBERS ]########################################################## this.key From df4c43c57c0cf1bc7b716c1bcebbfe564a818e7d Mon Sep 17 00:00:00 2001 From: deathaxe <deathaxe82@googlemail.com> Date: Tue, 27 Aug 2024 20:51:13 +0200 Subject: [PATCH 12/52] Add CS2 operators --- CoffeeScript.sublime-syntax | 2 +- tests/syntax_test_scope.coffee | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/CoffeeScript.sublime-syntax b/CoffeeScript.sublime-syntax index c62829b..975d17a 100644 --- a/CoffeeScript.sublime-syntax +++ b/CoffeeScript.sublime-syntax @@ -368,7 +368,7 @@ contexts: # symbolic - match: (?:and|or|<<|>>>?|[-+*/%&|^])= scope: keyword.operator.assignment.augmented.coffee - - match: \+\+?|\-\-?|[*/%] + - match: \+\+?|\-\-?|\*\*?|//?|%%? scope: keyword.operator.arithmetic.coffee - match: '[=!]==?|[<>]=?' scope: keyword.operator.comparison.coffee diff --git a/tests/syntax_test_scope.coffee b/tests/syntax_test_scope.coffee index 40b2fca..eb25845 100644 --- a/tests/syntax_test_scope.coffee +++ b/tests/syntax_test_scope.coffee @@ -333,12 +333,15 @@ class App.Router extends Snakeskin.Router # ^ keyword.operator.bitwise.coffee # ^ keyword.operator.bitwise.coffee - + - * / % + + - * / % ** // %% # ^ keyword.operator.arithmetic.coffee # ^ keyword.operator.arithmetic.coffee # ^ keyword.operator.arithmetic.coffee # ^ keyword.operator.arithmetic.coffee # ^ keyword.operator.arithmetic.coffee +# ^^ keyword.operator.arithmetic.coffee +# ^^ keyword.operator.arithmetic.coffee +# ^^ keyword.operator.arithmetic.coffee = : # ^ keyword.operator.assignment.coffee From 229a5dd215b480c39f9555f002e575be48c9fe4f Mon Sep 17 00:00:00 2001 From: deathaxe <deathaxe82@googlemail.com> Date: Tue, 27 Aug 2024 20:59:56 +0200 Subject: [PATCH 13/52] Cleanup variables --- CoffeeScript.sublime-syntax | 74 ++++++++++++++-------------------- tests/syntax_test_scope.coffee | 10 ++--- 2 files changed, 35 insertions(+), 49 deletions(-) diff --git a/CoffeeScript.sublime-syntax b/CoffeeScript.sublime-syntax index 975d17a..d0de7cf 100644 --- a/CoffeeScript.sublime-syntax +++ b/CoffeeScript.sublime-syntax @@ -47,9 +47,8 @@ contexts: - include: triple-single-quoted-strings - include: double-quoted-strings - include: single-quoted-strings - - include: instance-variables - include: numbers - - include: objects + - include: variables ###[ COMMENTS ]############################################################### @@ -408,45 +407,6 @@ contexts: pop: 1 - include: else-pop -###[ OBJECTS ]################################################################ - - objects: - - match: (?:this|extends)(?!\s*[:=])\b - scope: variable.language.coffee - - match: ({{identifier}})?(\.) - captures: - 1: variable.other.object.coffee - 2: punctuation.accessor.dot.coffee - push: member - - match: '{{identifier}}' - scope: variable.other.readwrite.coffee - - member: - - meta_scope: meta.path.coffee - # member objects - - match: ({{identifier}})(\.) - captures: - 1: variable.other.object.coffee - 2: punctuation.accessor.dot.coffee - # method - - match: '{{identifier}}(?=\()' - scope: meta.function-call.identifier.coffee variable.function.coffee - set: function-call-argument-list - # member variable - - match: '{{identifier}}' - scope: variable.other.member.coffee - pop: 1 - - include: immediately-pop - - variables: - # member objects - - match: ({{identifier}})(\.) - captures: - 1: variable.other.object.coffee - 2: punctuation.accessor.dot.coffee - - match: '{{identifier}}' - scope: variable.other.member.coffee - ###[ OPERATORS ]############################################################## punctuations: @@ -790,13 +750,39 @@ contexts: - match: $\n?|(?=}) pop: 1 -###[ VARIABLES ]############################################################### +###[ VARIABLES ]############################################################## - instance-variables: + variables: + - match: (?:this|extends)(?!\s*[:=])\b + scope: variable.language.coffee + - match: ({{identifier}})?(\.) + captures: + 1: variable.other.object.coffee + 2: punctuation.accessor.dot.coffee + push: member - match: (@)(?:{{identifier}})? - scope: variable.other.readwrite.instance.coffee + scope: variable.other.member.coffee captures: 1: punctuation.definition.variable.coffee + - match: '{{identifier}}' + scope: variable.other.readwrite.coffee + + member: + - meta_scope: meta.path.coffee + # member objects + - match: ({{identifier}})(\.) + captures: + 1: variable.other.object.coffee + 2: punctuation.accessor.dot.coffee + # method + - match: '{{identifier}}(?=\()' + scope: meta.function-call.identifier.coffee variable.function.coffee + set: function-call-argument-list + # member variable + - match: '{{identifier}}' + scope: variable.other.member.coffee + pop: 1 + - include: immediately-pop ###[ PROTOTYPES ]############################################################## diff --git a/tests/syntax_test_scope.coffee b/tests/syntax_test_scope.coffee index eb25845..ee41cca 100644 --- a/tests/syntax_test_scope.coffee +++ b/tests/syntax_test_scope.coffee @@ -237,16 +237,16 @@ class App.Router extends Snakeskin.Router yield @foo # ^^^^^ keyword.control.flow.coffee -# ^^^^ variable.other.readwrite.instance.coffee +# ^^^^ variable.other.member.coffee yield from @foo # ^^^^^^^^^^ keyword.control.flow.coffee -# ^^^^ variable.other.readwrite.instance.coffee +# ^^^^ variable.other.member.coffee await return @foo; # ^^^^^ keyword.control.flow.coffee # ^^^^^^ keyword.control.flow.coffee -# ^^^^ variable.other.readwrite.instance.coffee +# ^^^^ variable.other.member.coffee ###[ OPERATORS ]############################################################### @@ -622,7 +622,7 @@ class App.Router extends Snakeskin.Router # ^ meta.number.integer.decimal.coffee constant.numeric.value.coffee @variable -# ^^^^^^^^^ variable.other.readwrite.instance.coffee +# ^^^^^^^^^ variable.other.member.coffee # ^ punctuation.definition.variable.coffee ###[ JSX ]##################################################################### @@ -637,7 +637,7 @@ class App.Router extends Snakeskin.Router # ^^^ meta.string.coffee string.quoted.double.coffee # ^^^^^^ meta.string.coffee meta.interpolation.coffee # ^ punctuation.section.interpolation.begin.coffee -# ^^^^ source.coffee.embedded.jsx variable.other.readwrite.instance.coffee +# ^^^^ source.coffee.embedded.jsx variable.other.member.coffee # ^ punctuation.section.interpolation.end.coffee # ^ meta.string.coffee string.quoted.double.coffee punctuation.definition.string.end.coffee # ^ punctuation.definition.tag.end.coffee From 5d6bce505c8999bf587f70a96f3115f2b4e5f724 Mon Sep 17 00:00:00 2001 From: deathaxe <deathaxe82@googlemail.com> Date: Thu, 29 Aug 2024 19:30:42 +0200 Subject: [PATCH 14/52] Restore fully qualified function definitions --- CoffeeScript.sublime-syntax | 37 ++++++++++++++++++++++++---- tests/syntax_test_scope.coffee | 44 +++++++++++++++++++++++++++++++++- 2 files changed, 76 insertions(+), 5 deletions(-) diff --git a/CoffeeScript.sublime-syntax b/CoffeeScript.sublime-syntax index d0de7cf..565d9b4 100644 --- a/CoffeeScript.sublime-syntax +++ b/CoffeeScript.sublime-syntax @@ -169,8 +169,7 @@ contexts: - match: '[:=](?!>)' scope: keyword.operator.assignment.coffee pop: 1 - - match: (?=\S) - fail: function + - include: else-pop function-parameter-list: - match: \( @@ -778,9 +777,39 @@ contexts: - match: '{{identifier}}(?=\()' scope: meta.function-call.identifier.coffee variable.function.coffee set: function-call-argument-list - # member variable + # member function definition or variable + - match: (?=(?:(?!and=|or=)|@){{identifier}}) + branch_point: member + branch: + - member-function + - member-variable + pop: 1 + + member-function: + - meta_include_prototype: false + - meta_scope: meta.function.identifier.coffee meta.path.coffee + - match: (@?){{identifier}} + scope: entity.name.function.coffee + captures: + 1: punctuation.definition.variable.coffee + set: + - member-function-body + - function-parameter-list + - function-assignment + - match: '' + fail: member + + member-function-body: + - meta_content_scope: meta.function.coffee + - match: '[=-]>' + scope: meta.function.coffee keyword.declaration.function.coffee + pop: 1 + - match: (?=\S) + fail: member + + member-variable: - match: '{{identifier}}' - scope: variable.other.member.coffee + scope: meta.path.coffee variable.other.member.coffee pop: 1 - include: immediately-pop diff --git a/tests/syntax_test_scope.coffee b/tests/syntax_test_scope.coffee index ee41cca..9bd9371 100644 --- a/tests/syntax_test_scope.coffee +++ b/tests/syntax_test_scope.coffee @@ -78,7 +78,17 @@ class App.Router extends Snakeskin.Router # ^ keyword.operator.assignment.coffee # ^^ keyword.declaration.function.coffee - name = => + namespace.name: -> +# ^^^^^^^^^ meta.path.coffee variable.other.object.coffee +# ^ meta.path.coffee punctuation.accessor.dot.coffee +# ^^^^^^^^ - meta.function meta.function +# ^^^^ meta.function.identifier.coffee meta.path.coffee entity.name.function.coffee +# ^ meta.function.coffee keyword.operator.assignment.coffee +# ^ meta.function.coffee - keyword +# ^^ meta.function.coffee keyword.declaration.function.coffee +# ^ - meta.function + + name = -> # ^^^^^^^^^ - meta.function meta.function # ^^^^ meta.function.identifier.coffee entity.name.function.coffee # ^ meta.function.coffee - entity @@ -86,6 +96,17 @@ class App.Router extends Snakeskin.Router # ^ keyword.operator.assignment.coffee # ^^ keyword.declaration.function.coffee + namespace.name = -> +# ^^^^^^^^^ meta.path.coffee variable.other.object.coffee +# ^ meta.path.coffee punctuation.accessor.dot.coffee +# ^^^^^^^^^ - meta.function meta.function +# ^^^^ meta.function.identifier.coffee meta.path.coffee entity.name.function.coffee +# ^ meta.function.coffee - keyword +# ^ meta.function.coffee keyword.operator.assignment.coffee +# ^ meta.function.coffee - keyword +# ^^ meta.function.coffee keyword.declaration.function.coffee +# ^ - meta.function + name: => # ^^^^^^^^ - meta.function meta.function # ^^^^ meta.function.identifier.coffee entity.name.function.coffee @@ -93,6 +114,16 @@ class App.Router extends Snakeskin.Router # ^ keyword.operator.assignment.coffee # ^^ keyword.declaration.function.coffee + namespace.name: => +# ^^^^^^^^^ meta.path.coffee variable.other.object.coffee +# ^ meta.path.coffee punctuation.accessor.dot.coffee +# ^^^^^^^^ - meta.function meta.function +# ^^^^ meta.function.identifier.coffee meta.path.coffee entity.name.function.coffee +# ^ meta.function.coffee keyword.operator.assignment.coffee +# ^ meta.function.coffee - keyword +# ^^ meta.function.coffee keyword.declaration.function.coffee +# ^ - meta.function + name = => # ^^^^^^^^^ - meta.function meta.function # ^^^^ meta.function.identifier.coffee entity.name.function.coffee @@ -101,6 +132,17 @@ class App.Router extends Snakeskin.Router # ^ keyword.operator.assignment.coffee # ^^ keyword.declaration.function.coffee + namespace.name = => +# ^^^^^^^^^ meta.path.coffee variable.other.object.coffee +# ^ meta.path.coffee punctuation.accessor.dot.coffee +# ^^^^^^^^^ - meta.function meta.function +# ^^^^ meta.function.identifier.coffee meta.path.coffee entity.name.function.coffee +# ^ meta.function.coffee - keyword +# ^ meta.function.coffee keyword.operator.assignment.coffee +# ^ meta.function.coffee - keyword +# ^^ meta.function.coffee keyword.declaration.function.coffee +# ^ - meta.function + name: () -> # ^^^^^^^^^^^ - meta.function meta.function # ^^^^ meta.function.identifier.coffee entity.name.function.coffee From e60e410f14d6e07477b5faab21567a140b8ce47e Mon Sep 17 00:00:00 2001 From: deathaxe <deathaxe82@googlemail.com> Date: Thu, 29 Aug 2024 21:37:44 +0200 Subject: [PATCH 15/52] Refine class definitions --- CoffeeScript.sublime-syntax | 43 +++++++++++++++++++++++++++++----- tests/syntax_test_scope.coffee | 28 ++++++++++++++-------- 2 files changed, 55 insertions(+), 16 deletions(-) diff --git a/CoffeeScript.sublime-syntax b/CoffeeScript.sublime-syntax index 565d9b4..1b36e5d 100644 --- a/CoffeeScript.sublime-syntax +++ b/CoffeeScript.sublime-syntax @@ -95,13 +95,44 @@ contexts: ###[ CLASS DECLARATIONS ]##################################################### classes: - - match: (class\b)\s+((?!extends)@?[a-zA-Z\$_][\w\.]*)?(?:\s*(extends)\s+(@?[a-zA-Z\$\._][\w\.]*))? - scope: meta.class.coffee + - match: class\b + scope: meta.class.coffee keyword.declaration.class.coffee + push: + - class-extends + - class-name + + class-name: + - match: (?=extends\b) + pop: 1 + - match: ({{identifier}})(\.) + captures: + 1: support.class.coffee + 2: punctuation.accessor.dot.coffee + - match: '{{identifier}}' + scope: entity.name.class.coffee + pop: 1 + - include: comments + - include: else-pop + + class-extends: + - meta_content_scope: meta.class.identifier.coffee + - match: extends\b + scope: storage.modifier.extends.coffee + set: class-extends-name + - include: comments + - include: else-pop + + class-extends-name: + - meta_scope: meta.class.extends.coffee + - match: ({{identifier}})(\.) captures: - 1: storage.type.class.coffee - 2: entity.name.type.class.coffee - 3: keyword.control.inheritance.coffee - 4: entity.other.inherited-class.coffee + 1: support.class.coffee + 2: punctuation.accessor.dot.coffee + - match: '{{identifier}}' + scope: entity.other.inherited-class.coffee + pop: 1 + - include: comments + - include: else-pop ###[ FUNCTION DECLARATIONS ]################################################## diff --git a/tests/syntax_test_scope.coffee b/tests/syntax_test_scope.coffee index 9bd9371..f3bfe62 100644 --- a/tests/syntax_test_scope.coffee +++ b/tests/syntax_test_scope.coffee @@ -43,21 +43,29 @@ export parentClass ###[ CLASSES ]################################################################ class extends parentClass -# <- meta.class.coffee storage.type.class.coffee -#^^^^^^^^^^^^^^^^^^^^^^^^ meta.class.coffee -#^^^^ storage.type.class.coffee -# ^^^^^^^ keyword.control.inheritance.coffee +# <- meta.class.coffee keyword.declaration.class.coffee +#^^^^ meta.class.coffee +# ^ meta.class.identifier.coffee +# ^^^^^^^^^^^^^^^^^^^ meta.class.extends.coffee +#^^^^ keyword.declaration.class.coffee +# ^^^^^^^ storage.modifier.extends.coffee # ^^^^^^^^^^^ entity.other.inherited-class.coffee constructor: -> return class App.Router extends Snakeskin.Router -# <- meta.class.coffee storage.type.class.coffee -#^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ meta.class.coffee -#^^^^ storage.type.class.coffee -# ^^^^^^^^^^ entity.name.type.class.coffee -# ^^^^^^^ keyword.control.inheritance.coffee -# ^^^^^^^^^^^^^^^^ entity.other.inherited-class.coffee +# <- meta.class.coffee keyword.declaration.class.coffee +#^^^^ meta.class.coffee +# ^^^^^^^^^^^^ meta.class.identifier.coffee +# ^^^^^^^^^^^^^^^^^^^^^^^^ meta.class.extends.coffee +#^^^^ keyword.declaration.class.coffee +# ^^^ support.class.coffee +# ^ punctuation.accessor.dot.coffee +# ^^^^^^ entity.name.class.coffee +# ^^^^^^^ storage.modifier.extends.coffee +# ^^^^^^^^^ support.class.coffee +# ^ punctuation.accessor.dot.coffee +# ^^^^^^ entity.other.inherited-class.coffee ###[ FUNCTIONS ]############################################################### From 2a67e4f066990debc1d24d8df6f2b42ad0194b48 Mon Sep 17 00:00:00 2001 From: deathaxe <deathaxe82@googlemail.com> Date: Sat, 31 Aug 2024 21:45:18 +0200 Subject: [PATCH 16/52] Fix syntax tests --- tests/syntax_test_scope.html | 16 ++++++++-------- tests/syntax_test_scope.litcoffee | 8 ++++---- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/tests/syntax_test_scope.html b/tests/syntax_test_scope.html index ee354ec..a7f67e0 100644 --- a/tests/syntax_test_scope.html +++ b/tests/syntax_test_scope.html @@ -9,10 +9,10 @@ # ^^^^^^^^^^^^^^^^^^^ meta.string.html string.quoted.double.html # ^ punctuation.definition.tag.end.html class extends parentClass - # <- source.coffee.embedded.html meta.class.coffee storage.type.class.coffee - #^^^^^^^^^^^^^^^^^^^^^^^^ source.coffee.embedded.html meta.class.coffee - #^^^^ storage.type.class.coffee - # ^^^^^^^ keyword.control.inheritance.coffee + # <- source.coffee.embedded.html meta.class.coffee keyword.declaration.class.coffee + #^^^^^^^^^^^^^^^^^^^^^^^^ source.coffee.embedded.html meta.class + #^^^^ keyword.declaration.class.coffee + # ^^^^^^^ storage.modifier.extends.coffee # ^^^^^^^^^^^ entity.other.inherited-class.coffee constructor: -> return @@ -34,10 +34,10 @@ # ^^^^^^^^ meta.string.html string.quoted.double.html # ^ punctuation.definition.tag.end.html class extends parentClass - # <- source.coffee.embedded.html meta.class.coffee storage.type.class.coffee - #^^^^^^^^^^^^^^^^^^^^^^^^ source.coffee.embedded.html meta.class.coffee - #^^^^ storage.type.class.coffee - # ^^^^^^^ keyword.control.inheritance.coffee + # <- source.coffee.embedded.html meta.class.coffee keyword.declaration.class.coffee + #^^^^^^^^^^^^^^^^^^^^^^^^ source.coffee.embedded.html meta.class + #^^^^ keyword.declaration.class.coffee + # ^^^^^^^ storage.modifier.extends.coffee # ^^^^^^^^^^^ entity.other.inherited-class.coffee constructor: -> return diff --git a/tests/syntax_test_scope.litcoffee b/tests/syntax_test_scope.litcoffee index 7c4f1ef..f45843d 100644 --- a/tests/syntax_test_scope.litcoffee +++ b/tests/syntax_test_scope.litcoffee @@ -7,8 +7,8 @@ # Indendet Code Block class App.Router extends Snakeskin.Router - | <- meta.embedded.litcoffee source.coffee.embedded.markdown meta.class.coffee storage.type.class.coffee - |^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ meta.embedded.litcoffee source.coffee.embedded.markdown meta.class.coffee + | <- meta.embedded.litcoffee source.coffee.embedded.markdown meta.class.coffee keyword.declaration.class.coffee + |^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ meta.embedded.litcoffee source.coffee.embedded.markdown meta.class index: () => @ensureData((data) => @_parseDates(data, ['trending', 'new', 'top']) @@ -18,8 +18,8 @@ - In list items class App.Router extends Snakeskin.Router - | <- markup.list.unnumbered.markdown meta.embedded.litcoffee source.coffee.embedded.markdown meta.class.coffee storage.type.class.coffee - |^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ markup.list.unnumbered.markdown meta.embedded.litcoffee source.coffee.embedded.markdown meta.class.coffee + | <- markup.list.unnumbered.markdown meta.embedded.litcoffee source.coffee.embedded.markdown meta.class.coffee keyword.declaration.class.coffee + |^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ markup.list.unnumbered.markdown meta.embedded.litcoffee source.coffee.embedded.markdown meta.class index: () => @ensureData((data) => @_parseDates(data, ['trending', 'new', 'top']) From d35463466f77096905a7386a896620e96cf88314 Mon Sep 17 00:00:00 2001 From: deathaxe <deathaxe82@googlemail.com> Date: Sat, 7 Sep 2024 10:37:33 +0200 Subject: [PATCH 17/52] README: Remove maintainer request --- README.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/README.md b/README.md index 59cb54e..c2f5e95 100644 --- a/README.md +++ b/README.md @@ -3,9 +3,6 @@ **This package is for Sublime Text 3+**. An old version for Sublime Text 2 is accessible via the [`st2` branch](https://github.com/SublimeText/BetterCoffeeScript/tree/st2). -**We are looking for a maintainer!** -See https://github.com/SublimeText/BetterCoffeeScript/issues/244 for details. - ## Description CoffeeScript plug-in was originally created by @Xavura. As I, @aponxi, began writing a lot of code in CoffeeScript, I felt the need for side-by-side view for compiled CoffeeScript. Since Xavura's repository have been inactive I decided to branch out my own version. The biggest change in my branch is the Watch Mode which updates the compiled JavaScript view whenever you modify the CoffeeScript thus enabling you to view your progress side-by-side. From 008a045bd567d8aa25831ad99b10bbbcbc77f7bd Mon Sep 17 00:00:00 2001 From: deathaxe <deathaxe82@googlemail.com> Date: Tue, 10 Sep 2024 20:08:28 +0200 Subject: [PATCH 18/52] Fix number patterns Fixes #253 Syncs number patterns with ST's core JavaScript syntax. --- CoffeeScript.sublime-syntax | 25 ++++++++++++++++++++----- tests/syntax_test_scope.coffee | 7 ++++++- 2 files changed, 26 insertions(+), 6 deletions(-) diff --git a/CoffeeScript.sublime-syntax b/CoffeeScript.sublime-syntax index 1b36e5d..aec8017 100644 --- a/CoffeeScript.sublime-syntax +++ b/CoffeeScript.sublime-syntax @@ -499,26 +499,34 @@ contexts: scope: constant.language.null.coffee numbers: - - match: \b(0b)([01]+)\b + - match: (0b)({{bin_digit}}+)\b scope: meta.number.integer.binary.coffee captures: 1: constant.numeric.base.coffee 2: constant.numeric.value.coffee - - match: \b(0o)([0-7]+)\b + - match: (0o)({{oct_digit}}+)\b scope: meta.number.integer.octal.coffee captures: 1: constant.numeric.base.coffee 2: constant.numeric.value.coffee - - match: \b(0x)(\h+)\b + - match: (0x)({{hex_digit}}+)\b scope: meta.number.integer.hexadecimal.coffee captures: 1: constant.numeric.base.coffee 2: constant.numeric.value.coffee - - match: \b\d+(?:(\.)\d*(?:e[-+]?\d+)?|(?:e[-+]?\d+))\b + # floats + - match: |- + (?x: + # 1., 1.1, 1.1e1, 1.1e-1, 1.e1, 1.e-1 | 1e1, 1e-1 + {{dec_integer}} (?: (\.) {{dec_digit}}* (?:{{dec_exponent}})? | {{dec_exponent}} ) + # .1, .1e1, .1e-1 + | (\.) {{dec_digit}}+ (?:{{dec_exponent}})? + )\b scope: meta.number.float.decimal.coffee constant.numeric.value.coffee captures: 1: punctuation.separator.decimal.coffee - - match: \b\d+\b + 2: punctuation.separator.decimal.coffee + - match: '{{dec_integer}}\b' scope: meta.number.integer.decimal.coffee constant.numeric.value.coffee triple-double-quoted-strings: @@ -865,6 +873,13 @@ variables: # in embed...escape statements. no_escape_behind: (?<![^\\]\\)(?<![\\]{3}) + bin_digit: '[01_]' + oct_digit: '[0-7_]' + dec_digit: '[0-9_]' + hex_digit: '[\h_]' + dec_integer: (?:0|[1-9]{{dec_digit}}*) + dec_exponent: '[Ee](?:[-+]|(?![-+])){{dec_digit}}*' + identifier: '[[:alpha:]_$]\w*' component_names: '[A-Z][[:alnum:]_.-]*' diff --git a/tests/syntax_test_scope.coffee b/tests/syntax_test_scope.coffee index f3bfe62..ac53bf3 100644 --- a/tests/syntax_test_scope.coffee +++ b/tests/syntax_test_scope.coffee @@ -547,12 +547,17 @@ class App.Router extends Snakeskin.Router 10.e5 # ^^^^^ meta.number.float.decimal.coffee constant.numeric.value.coffee +# ^ punctuation.separator.decimal.coffee - 10.23 + 10.23 .23 # ^^^^^ meta.number.float.decimal.coffee constant.numeric.value.coffee +# ^ punctuation.separator.decimal.coffee +# ^^^ meta.number.float.decimal.coffee constant.numeric.value.coffee +# ^ punctuation.separator.decimal.coffee 10.23e-5 # ^^^^^^^^ meta.number.float.decimal.coffee constant.numeric.value.coffee +# ^ punctuation.separator.decimal.coffee 52 # ^^ meta.number.integer.decimal.coffee constant.numeric.value.coffee From 70efe3419bb4c1fa06db45b37fbda248a81f5b11 Mon Sep 17 00:00:00 2001 From: deathaxe <deathaxe82@googlemail.com> Date: Wed, 11 Sep 2024 20:57:06 +0200 Subject: [PATCH 19/52] Fix l-value variable scope in assignments Fixes #254 --- CoffeeScript.sublime-syntax | 54 ++++++++++++++++++++++------------ tests/syntax_test_scope.coffee | 24 +++++++++++++++ 2 files changed, 60 insertions(+), 18 deletions(-) diff --git a/CoffeeScript.sublime-syntax b/CoffeeScript.sublime-syntax index aec8017..589c054 100644 --- a/CoffeeScript.sublime-syntax +++ b/CoffeeScript.sublime-syntax @@ -30,8 +30,8 @@ contexts: script: - include: classes - - include: keywords - include: functions + - include: keywords - include: jsx-tags - include: expressions @@ -155,7 +155,7 @@ contexts: branch_point: function branch: - function-name - - immediately-pop + - variable - match: '[=-]>' scope: keyword.declaration.function.coffee @@ -353,41 +353,41 @@ contexts: keywords: # export/import - - match: export(?!\s*:)\b + - match: export\b scope: keyword.control.export.coffee - - match: (?:import|from)(?!\s*:)\b + - match: (?:import|from)\b scope: keyword.control.import.coffee # excpetion - - match: (?:catch|finally|try)(?!\s*:)\b + - match: (?:catch|finally|try)\b scope: keyword.control.exception.coffee # conditional - - match: if(?!\s*:)\b + - match: if\b scope: keyword.control.conditional.if.coffee - - match: else(?!\s*:)\b + - match: else\b scope: keyword.control.conditional.else.coffee - - match: switch(?!\s*:)\b + - match: switch\b scope: keyword.control.conditional.switch.coffee - - match: then(?!\s*:)\b + - match: then\b scope: keyword.control.conditional.then.coffee - - match: unless(?!\s*:)\b + - match: unless\b scope: keyword.control.conditional.unless.coffee - - match: when(?!\s*:)\b + - match: when\b scope: keyword.control.conditional.when.coffee # loop - - match: by(?!\s*:)\b + - match: by\b scope: keyword.control.loop.by.coffee - - match: do(?!\s*:)\b + - match: do\b scope: keyword.control.loop.do.coffee - - match: for(?:\s+own)?(?!\s*:)\b + - match: for(?:\s+own)?\b scope: keyword.control.loop.for.coffee - - match: loop(?!\s*:)\b + - match: loop\b scope: keyword.control.loop.loopcoffee - - match: until(?!\s*:)\b + - match: until\b scope: keyword.control.loop.until.coffee - - match: while(?!\s*:)\b + - match: while\b scope: keyword.control.loop.while.coffee # flow - - match: (?:await|break|continue|return|throw|yield(?:\s+from)?)(?!\s*:)\b + - match: (?:await|break|continue|return|throw|yield(?:\s+from)?)\b scope: keyword.control.flow.coffee # other - match: (?:debugger\b|\\) @@ -805,6 +805,24 @@ contexts: - match: '{{identifier}}' scope: variable.other.readwrite.coffee + variable: + - match: (?:this|extends)(?!\s*[:=])\b + scope: variable.language.coffee + pop: 1 + - match: ({{identifier}})?(\.) + captures: + 1: variable.other.object.coffee + 2: punctuation.accessor.dot.coffee + set: member + - match: (@)(?:{{identifier}})? + scope: variable.other.member.coffee + captures: + 1: punctuation.definition.variable.coffee + pop: 1 + - match: '{{identifier}}' + scope: variable.other.readwrite.coffee + pop: 1 + member: - meta_scope: meta.path.coffee # member objects diff --git a/tests/syntax_test_scope.coffee b/tests/syntax_test_scope.coffee index ac53bf3..89eda1f 100644 --- a/tests/syntax_test_scope.coffee +++ b/tests/syntax_test_scope.coffee @@ -267,6 +267,30 @@ class App.Router extends Snakeskin.Router -> # ^^ keyword.declaration.function.coffee + geometry = new Class(); +# ^^^^^^^^ variable.other.readwrite.coffee +# ^ keyword.operator.assignment.coffee +# ^^^ keyword.operator.object.new.coffee +# ^^^^^ support.class.coffee + + try = new Class(); +# ^^^ variable.other.readwrite.coffee +# ^ keyword.operator.assignment.coffee +# ^^^ keyword.operator.object.new.coffee +# ^^^^^ support.class.coffee + + geometry: new Class(); +# ^^^^^^^^ variable.other.readwrite.coffee +# ^ keyword.operator.assignment.coffee +# ^^^ keyword.operator.object.new.coffee +# ^^^^^ support.class.coffee + + try: new Class(); +# ^^^ variable.other.readwrite.coffee +# ^ keyword.operator.assignment.coffee +# ^^^ keyword.operator.object.new.coffee +# ^^^^^ support.class.coffee + ###[ KEYWORDS ]################################################################ if .if _if $if From 008336138c9e125821820647c3602e1bf291d8f8 Mon Sep 17 00:00:00 2001 From: deathaxe <deathaxe82@googlemail.com> Date: Wed, 23 Oct 2024 19:35:33 +0200 Subject: [PATCH 20/52] Fix highlighting of class instantiation arguments reported at https://forum.sublimetext.com/t/coffeescript-syntax-highlighting-failing-in-very-basic-way/73975 --- CoffeeScript.sublime-syntax | 15 ++++++++++++++- tests/syntax_test_scope.coffee | 11 ++++++++++- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/CoffeeScript.sublime-syntax b/CoffeeScript.sublime-syntax index 589c054..094d41c 100644 --- a/CoffeeScript.sublime-syntax +++ b/CoffeeScript.sublime-syntax @@ -434,9 +434,22 @@ contexts: 2: punctuation.accessor.dot.coffee - match: '{{identifier}}' scope: support.class.coffee - pop: 1 + set: maybe-class-arguments + - include: else-pop + + maybe-class-arguments: + - match: \( + scope: punctuation.section.group.begin.coffee + set: class-arguments-body - include: else-pop + class-arguments-body: + - meta_scope: meta.group.coffee + - match: \) + scope: punctuation.section.group.end.coffee + pop: 1 + - include: script + ###[ OPERATORS ]############################################################## punctuations: diff --git a/tests/syntax_test_scope.coffee b/tests/syntax_test_scope.coffee index 89eda1f..a70c2dd 100644 --- a/tests/syntax_test_scope.coffee +++ b/tests/syntax_test_scope.coffee @@ -284,12 +284,21 @@ class App.Router extends Snakeskin.Router # ^ keyword.operator.assignment.coffee # ^^^ keyword.operator.object.new.coffee # ^^^^^ support.class.coffee +# ^ punctuation.section.group.begin.coffee +# ^ punctuation.section.group.end.coffee - try: new Class(); + try: new Class("Unknown sort: #{info.type}"); # ^^^ variable.other.readwrite.coffee # ^ keyword.operator.assignment.coffee # ^^^ keyword.operator.object.new.coffee # ^^^^^ support.class.coffee +# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ meta.group.coffee +# ^ punctuation.section.group.begin.coffee +# ^^^^^^^^^^^^^^^ meta.string.coffee string.quoted.double.coffee - meta.interpolation +# ^^^^^^^^^^^^ meta.string.coffee meta.embedded.coffee source.coffee.embedded.source +# ^ meta.string.coffee string.quoted.double.coffee - meta.interpolation +# ^ punctuation.section.group.end.coffee +# ^ punctuation.terminator.statement.coffee ###[ KEYWORDS ]################################################################ From 54fa5458c242eff210a87318358056f78fb83b7c Mon Sep 17 00:00:00 2001 From: deathaxe <deathaxe82@googlemail.com> Date: Wed, 23 Oct 2024 20:07:40 +0200 Subject: [PATCH 21/52] Fix groups vs. anonymous functions This commit assumes parentheses as expression group until it being followed by an arrow. --- CoffeeScript.sublime-syntax | 18 ++++++++++++++---- tests/syntax_test_scope.coffee | 10 ++++++++++ 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/CoffeeScript.sublime-syntax b/CoffeeScript.sublime-syntax index 094d41c..7107b67 100644 --- a/CoffeeScript.sublime-syntax +++ b/CoffeeScript.sublime-syntax @@ -148,8 +148,8 @@ contexts: - match: (?=\() branch_point: anonymous branch: - - anonymous-parameter-list - group + - anonymous-parameter-list # named functions - match: (?=(?:(?!and=|or=)|@){{identifier}}\s*[:=][^>]) branch_point: function @@ -173,13 +173,23 @@ contexts: - match: '[=-]>' scope: meta.function.coffee keyword.declaration.function.coffee pop: 1 - - match: (?=\S) - fail: anonymous group: - match: \( scope: punctuation.section.group.begin.coffee - pop: 1 + set: group-body + + group-body: + - meta_scope: meta.group.coffee + - match: \) + scope: punctuation.section.group.end.coffee + set: group-check + - include: expressions + + group-check: + - match: (?=[=-]>) + fail: anonymous + - include: else-pop function-name: - meta_include_prototype: false diff --git a/tests/syntax_test_scope.coffee b/tests/syntax_test_scope.coffee index a70c2dd..131bc80 100644 --- a/tests/syntax_test_scope.coffee +++ b/tests/syntax_test_scope.coffee @@ -267,6 +267,16 @@ class App.Router extends Snakeskin.Router -> # ^^ keyword.declaration.function.coffee + (a : "group") +# ^^^^^^^^^^^^^ meta.group.coffee +# ^ punctuation.section.group.begin.coffee +# ^ variable.other.readwrite.coffee +# ^ keyword.operator.assignment.coffee +# ^^^^^^^ meta.string.coffee string.quoted.double.coffee +# ^ punctuation.definition.string.begin.coffee +# ^ punctuation.definition.string.end.coffee +# ^ punctuation.section.group.end.coffee + geometry = new Class(); # ^^^^^^^^ variable.other.readwrite.coffee # ^ keyword.operator.assignment.coffee From 290504bb9d7c746aa2ecca71511d8278e21bc72a Mon Sep 17 00:00:00 2001 From: deathaxe <deathaxe82@googlemail.com> Date: Thu, 31 Oct 2024 13:59:21 +0100 Subject: [PATCH 22/52] Add lang/type completion support This commit... 1. adds `language` to list of supported tag attributes used to detect script language. 2. adds dedicated meta scopes for `lang` and `type` attributes 3. adds completions for those values. Follows https://github.com/sublimehq/Packages/pull/4061 --- HTML (CoffeeScript).sublime-syntax | 27 ++++++++++++++++++-------- Script Lang Values.sublime-completions | 10 ++++++++++ Script Type Values.sublime-completions | 25 ++++++++++++++++++++++++ tests/syntax_test_scope.html | 27 ++++++++++++++++++++++++++ 4 files changed, 81 insertions(+), 8 deletions(-) create mode 100644 Script Lang Values.sublime-completions create mode 100644 Script Type Values.sublime-completions diff --git a/HTML (CoffeeScript).sublime-syntax b/HTML (CoffeeScript).sublime-syntax index 92cf857..2db1d0d 100644 --- a/HTML (CoffeeScript).sublime-syntax +++ b/HTML (CoffeeScript).sublime-syntax @@ -14,12 +14,12 @@ contexts: script-common: - meta_prepend: true - - match: (?i:lang){{attribute_name_break}} - scope: meta.attribute-with-value.html entity.other.attribute-name.html + - match: (?i:lang(?:uage)?){{attribute_name_break}} + scope: meta.attribute-with-value.lang.html entity.other.attribute-name.html set: script-lang-attribute-assignment script-lang-attribute-assignment: - - meta_content_scope: meta.tag.script.begin.html meta.attribute-with-value.html + - meta_content_scope: meta.tag.script.begin.html meta.attribute-with-value.lang.html - match: = scope: punctuation.separator.key-value.html set: script-lang-attribute-value @@ -28,26 +28,37 @@ contexts: script-lang-attribute-value: - meta_include_prototype: false - - meta_scope: meta.tag.script.begin.html meta.attribute-with-value.html + - meta_scope: meta.tag.script.begin.html meta.attribute-with-value.lang.html - match: (?=(?i:coffee{{unquoted_attribute_break}}|'coffee'|"coffee")) set: - script-coffee - - tag-generic-attribute-meta + - tag-lang-attribute-meta - tag-generic-attribute-value - match: (?=\S) set: - script-javascript - - tag-generic-attribute-meta + - tag-lang-attribute-meta - tag-generic-attribute-value + tag-lang-attribute-meta: + - meta_include_prototype: false + - meta_scope: meta.attribute-with-value.lang.html + - include: immediately-pop + script-type-decider: - meta_prepend: true - match: (?={{coffee_mime_type}}{{unquoted_attribute_break}}|'{{coffee_mime_type}}'|"{{coffee_mime_type}}") set: - script-coffee - - tag-generic-attribute-meta + - tag-type-attribute-meta - tag-generic-attribute-value + tag-type-attribute-meta: + # required until ST4184 + - meta_include_prototype: false + - meta_scope: meta.attribute-with-value.type.html + - include: immediately-pop + script-coffee: - meta_include_prototype: false - meta_scope: meta.tag.script.begin.html @@ -86,4 +97,4 @@ contexts: variables: coffee_mime_type: |- - (?xi: (?: (?: application |text ) / coffee(?:script)? ) {{mime_type_parameters}}? ) + (?xi: (?: (?: application | text ) / coffee(?:script)? ) {{mime_type_parameters}}? ) diff --git a/Script Lang Values.sublime-completions b/Script Lang Values.sublime-completions new file mode 100644 index 0000000..90e5f70 --- /dev/null +++ b/Script Lang Values.sublime-completions @@ -0,0 +1,10 @@ +{ + "scope": "text.html meta.tag.script meta.attribute-with-value.lang", + "completions": [ + { + "trigger": "coffee", + "kind": ["type", "t", "Language Type"], + "details": "CoffeeScript" + } + ] +} \ No newline at end of file diff --git a/Script Type Values.sublime-completions b/Script Type Values.sublime-completions new file mode 100644 index 0000000..c32fa95 --- /dev/null +++ b/Script Type Values.sublime-completions @@ -0,0 +1,25 @@ +{ + "scope": "text.html meta.tag.script meta.attribute-with-value.type", + "completions": [ + { + "trigger": "application/coffee", + "kind": ["type", "t", "Mime Type"], + "details": "CoffeeScript" + }, + { + "trigger": "application/coffeescript", + "kind": ["type", "t", "Mime Type"], + "details": "CoffeeScript" + }, + { + "trigger": "text/coffee", + "kind": ["type", "t", "Mime Type"], + "details": "CoffeeScript" + }, + { + "trigger": "text/coffeescript", + "kind": ["type", "t", "Mime Type"], + "details": "CoffeeScript" + } + ] +} \ No newline at end of file diff --git a/tests/syntax_test_scope.html b/tests/syntax_test_scope.html index a7f67e0..fec4d33 100644 --- a/tests/syntax_test_scope.html +++ b/tests/syntax_test_scope.html @@ -28,6 +28,7 @@ <script lang="coffee"> # <- meta.tag.script.begin.html punctuation.definition.tag.begin.html #^^^^^^^^^^^^^^^^^^^^^ meta.tag.script.begin.html +# ^^^^^^^^^^^^^ meta.attribute-with-value.lang.html #^^^^^^ entity.name.tag.script.html # ^^^^ entity.other.attribute-name.html # ^ punctuation.separator.key-value.html @@ -49,3 +50,29 @@ #^ punctuation.definition.tag.begin.html # ^^^^^^ entity.name.tag.script.html # ^ punctuation.definition.tag.end.html + +<script language="coffee"> +# <- meta.tag.script.begin.html punctuation.definition.tag.begin.html +#^^^^^^^^^^^^^^^^^^^^^^^^^ meta.tag.script.begin.html +# ^^^^^^^^^^^^^^^^^ meta.attribute-with-value.lang.html +#^^^^^^ entity.name.tag.script.html +# ^^^^^^^^ entity.other.attribute-name.html +# ^ punctuation.separator.key-value.html +# ^^^^^^^^ meta.string.html string.quoted.double.html +# ^ punctuation.definition.tag.end.html + class extends parentClass + # <- source.coffee.embedded.html meta.class.coffee keyword.declaration.class.coffee + #^^^^^^^^^^^^^^^^^^^^^^^^ source.coffee.embedded.html meta.class + #^^^^ keyword.declaration.class.coffee + # ^^^^^^^ storage.modifier.extends.coffee + # ^^^^^^^^^^^ entity.other.inherited-class.coffee + constructor: -> + return + # <- source.coffee.embedded.html + # ^^^^^^ source.coffee.embedded.html keyword.control.flow.coffee +</script> +# <- meta.tag.script.end.html punctuation.definition.tag.begin.html +#^^^^^^^^ meta.tag.script.end.html +#^ punctuation.definition.tag.begin.html +# ^^^^^^ entity.name.tag.script.html +# ^ punctuation.definition.tag.end.html From 8925ec514a7cbb73ac4db1ce0198d135d929b8b9 Mon Sep 17 00:00:00 2001 From: deathaxe <deathaxe82@googlemail.com> Date: Fri, 22 Nov 2024 13:55:01 +0100 Subject: [PATCH 23/52] Restrict type/lang attribute completions This commit restricts completions to `text.html.coffee` so every html dialect can specify its own set of supported languages without creating duplicate completions. --- HTML (CoffeeScript).sublime-syntax | 2 +- Script Lang Values.sublime-completions | 2 +- Script Type Values.sublime-completions | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/HTML (CoffeeScript).sublime-syntax b/HTML (CoffeeScript).sublime-syntax index 2db1d0d..c78da88 100644 --- a/HTML (CoffeeScript).sublime-syntax +++ b/HTML (CoffeeScript).sublime-syntax @@ -29,7 +29,7 @@ contexts: script-lang-attribute-value: - meta_include_prototype: false - meta_scope: meta.tag.script.begin.html meta.attribute-with-value.lang.html - - match: (?=(?i:coffee{{unquoted_attribute_break}}|'coffee'|"coffee")) + - match: (?=(?i:coffee(?:script)?{{unquoted_attribute_break}}|'coffee(?:script)?'|"coffee(?:script)?")) set: - script-coffee - tag-lang-attribute-meta diff --git a/Script Lang Values.sublime-completions b/Script Lang Values.sublime-completions index 90e5f70..74d885b 100644 --- a/Script Lang Values.sublime-completions +++ b/Script Lang Values.sublime-completions @@ -1,5 +1,5 @@ { - "scope": "text.html meta.tag.script meta.attribute-with-value.lang", + "scope": "text.html.coffee meta.tag.script meta.attribute-with-value.lang", "completions": [ { "trigger": "coffee", diff --git a/Script Type Values.sublime-completions b/Script Type Values.sublime-completions index c32fa95..c5668f6 100644 --- a/Script Type Values.sublime-completions +++ b/Script Type Values.sublime-completions @@ -1,5 +1,5 @@ { - "scope": "text.html meta.tag.script meta.attribute-with-value.type", + "scope": "text.html.coffee meta.tag.script meta.attribute-with-value.type", "completions": [ { "trigger": "application/coffee", From 1ebd12a53fe3da297c195da4c12eae727d8b54a4 Mon Sep 17 00:00:00 2001 From: deathaxe <deathaxe82@googlemail.com> Date: Wed, 20 Nov 2024 10:05:24 +0100 Subject: [PATCH 24/52] CI: Copy workflow from Astro --- .github/workflows/syntax-tests.yml | 46 ++++++++++++++++++++++++++---- 1 file changed, 40 insertions(+), 6 deletions(-) diff --git a/.github/workflows/syntax-tests.yml b/.github/workflows/syntax-tests.yml index 7aedd31..100334c 100644 --- a/.github/workflows/syntax-tests.yml +++ b/.github/workflows/syntax-tests.yml @@ -3,11 +3,17 @@ name: CI Syntax Tests on: push: branches: - - '**' + - 'master' tags-ignore: - '**' paths: - - '.github/workflows/syntax-tests.yml' + - '.github/workflows/ci-syntax-tests.yml' + - '**.sublime-syntax' + - '**/syntax_test_*' + - '**.tmPreferences' + pull_request: + paths: + - '.github/workflows/ci-syntax-tests.yml' - '**.sublime-syntax' - '**/syntax_test_*' - '**.tmPreferences' @@ -19,17 +25,45 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 15 # default is 6 hours! strategy: + fail-fast: false matrix: include: + # Stable ST4 builds - build: 4143 default_packages: v4143 + - build: 4152 + default_packages: v4152 + - build: 4169 + default_packages: v4169 - build: 4180 default_packages: v4180 + # Latest dev build - build: latest default_packages: master steps: - - uses: actions/checkout@v4 - - uses: SublimeText/syntax-test-action@v2 + - name: Checkout Default Packages + uses: actions/checkout@v4 with: - build: ${{ matrix.build }} - default_packages: ${{ matrix.default_packages }} + repository: sublimehq/Packages + ref: ${{ matrix.default_packages }} + path: st_syntax_tests/Data/Packages + + - name: Delete default package tests + run: | + find st_syntax_tests/Data/Packages/*/ -type f -name 'syntax_test*' -exec rm -v '{}' \; + + - name: Checkout CoffeeScript + uses: actions/checkout@v4 + with: + path: st_syntax_tests/Data/Packages/CoffeeScript + + - name: Run Syntax Tests for Sublime Text ${{ matrix.build }} + run: | + if [[ "${{ matrix.build }}" == "latest" ]]; then + wget -O st_syntax_tests.tar.xz https://download.sublimetext.com/latest/dev/linux/x64/syntax_tests + else + wget -O st_syntax_tests.tar.xz https://download.sublimetext.com/st_syntax_tests_build_${{ matrix.build }}_x64.tar.xz + fi + tar xf st_syntax_tests.tar.xz + cd st_syntax_tests + ./syntax_tests From c3828394aec05241be25e0374e060d2e41b994c0 Mon Sep 17 00:00:00 2001 From: Mihail Latyshov <kutu182@gmail.com> Date: Fri, 11 Apr 2025 12:28:58 +0400 Subject: [PATCH 25/52] Don't highlight `class` if its a property of an object (#256) --- CoffeeScript.sublime-syntax | 2 +- tests/syntax_test_scope.coffee | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/CoffeeScript.sublime-syntax b/CoffeeScript.sublime-syntax index 7107b67..85604ab 100644 --- a/CoffeeScript.sublime-syntax +++ b/CoffeeScript.sublime-syntax @@ -29,8 +29,8 @@ contexts: push: [script, shebang] script: - - include: classes - include: functions + - include: classes - include: keywords - include: jsx-tags - include: expressions diff --git a/tests/syntax_test_scope.coffee b/tests/syntax_test_scope.coffee index 131bc80..cab7361 100644 --- a/tests/syntax_test_scope.coffee +++ b/tests/syntax_test_scope.coffee @@ -531,6 +531,16 @@ class App.Router extends Snakeskin.Router # ^^^^^ meta.string.coffee string.quoted.double.coffee # ^ punctuation.section.group.end.coffee + class: 1 +# ^^^^^ variable.other.readwrite.coffee +# ^ keyword.operator.assignment.coffee + + obj.class: 1 +# ^^^^^^^^^ meta.path.coffee +# ^^^ variable.other.object.coffee +# ^ punctuation.accessor.dot.coffee +# ^^^^^ variable.other.member.coffee + ###[ LITERALS ]################################################################ Infinity NaN undefined .Infinity .NaN .undefined From 25621d498f22fd29d860b6d2496d809b053f01b9 Mon Sep 17 00:00:00 2001 From: deathaxe <deathaxe82@gmail.com> Date: Sun, 27 Apr 2025 20:49:38 +0200 Subject: [PATCH 26/52] Tweak iteration keyword scope --- CoffeeScript.sublime-syntax | 2 +- tests/syntax_test_scope.coffee | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CoffeeScript.sublime-syntax b/CoffeeScript.sublime-syntax index 85604ab..5ce4403 100644 --- a/CoffeeScript.sublime-syntax +++ b/CoffeeScript.sublime-syntax @@ -423,7 +423,7 @@ contexts: - match: (?:as)\b scope: keyword.operator.word.coffee keyword.operator.assignment.as.coffee - match: (?:in|of)\b - scope: keyword.operator.word.coffee keyword.operator.iterator.coffee + scope: keyword.operator.word.coffee keyword.operator.iteration.coffee - match: (?:and|or|is|isnt|not)\b scope: keyword.operator.word.coffee keyword.operator.logical.coffee - match: (?:new)\b diff --git a/tests/syntax_test_scope.coffee b/tests/syntax_test_scope.coffee index cab7361..fe0eb38 100644 --- a/tests/syntax_test_scope.coffee +++ b/tests/syntax_test_scope.coffee @@ -347,8 +347,8 @@ class App.Router extends Snakeskin.Router # ^^ keyword.operator.assignment.as.coffee in of -# ^^ keyword.operator.iterator.coffee -# ^^ keyword.operator.iterator.coffee +# ^^ keyword.operator.iteration.coffee +# ^^ keyword.operator.iteration.coffee and or is isnt not # ^^^ keyword.operator.logical.coffee From e6415408073f63b9dd859fdf34ed48cf1eb088ba Mon Sep 17 00:00:00 2001 From: Mihail Latyshov <kutu182@gmail.com> Date: Sun, 3 Aug 2025 14:21:27 +0400 Subject: [PATCH 27/52] Fix highlighting of `constructor` function if `class` is nameless (#258) --- CoffeeScript.sublime-syntax | 2 ++ tests/syntax_test_scope.coffee | 5 +++++ 2 files changed, 7 insertions(+) diff --git a/CoffeeScript.sublime-syntax b/CoffeeScript.sublime-syntax index 5ce4403..8779aee 100644 --- a/CoffeeScript.sublime-syntax +++ b/CoffeeScript.sublime-syntax @@ -102,6 +102,8 @@ contexts: - class-name class-name: + - match: $ + pop: 1 - match: (?=extends\b) pop: 1 - match: ({{identifier}})(\.) diff --git a/tests/syntax_test_scope.coffee b/tests/syntax_test_scope.coffee index fe0eb38..e0d1b82 100644 --- a/tests/syntax_test_scope.coffee +++ b/tests/syntax_test_scope.coffee @@ -67,6 +67,11 @@ class App.Router extends Snakeskin.Router # ^ punctuation.accessor.dot.coffee # ^^^^^^ entity.other.inherited-class.coffee +class +# <- meta.class.coffee keyword.declaration.class.coffee + constructor: -> +# ^ meta.function.identifier.coffee entity.name.function.coffee + ###[ FUNCTIONS ]############################################################### name: From 107485e82b1056e30815b53d45df1190d56075e5 Mon Sep 17 00:00:00 2001 From: Mihail Latyshov <kutu182@gmail.com> Date: Sun, 3 Aug 2025 14:22:22 +0400 Subject: [PATCH 28/52] Fix indentation after `class` as a property of an object (#257) --- Indentation Rules.tmPreferences | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Indentation Rules.tmPreferences b/Indentation Rules.tmPreferences index 5922b59..aaea42e 100644 --- a/Indentation Rules.tmPreferences +++ b/Indentation Rules.tmPreferences @@ -11,7 +11,7 @@ <key>increaseIndentPattern</key> <string>(?x) ^\s* - ( class\b + ( .*\bclass\b(?!\s*:) # class keyword without colon after it (otherwise we are in object with property name "class") | [a-zA-Z\$_](\w|\$|:|\.)*\s*(?=\:(\s*\(.*\))?\s*((=|-)>\s*$)) # function that is not one line | [a-zA-Z\$_](\w|\$|\.)*\s*(:|=)\s*((if|while)(?!.*?then)|for|$) # assignment using multiline if/while/for | (if|while|unless)\b(?!.*?then)|(for|switch|when|loop)\b From 3520ffc601354a30908a7edfd600fd63701c65e2 Mon Sep 17 00:00:00 2001 From: deathaxe <deathaxe82@gmail.com> Date: Fri, 2 Jan 2026 18:20:32 +0100 Subject: [PATCH 29/52] Fix operand scope after typeof --- CoffeeScript.sublime-syntax | 4 +++- tests/syntax_test_scope.coffee | 6 +++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/CoffeeScript.sublime-syntax b/CoffeeScript.sublime-syntax index 8779aee..843a62d 100644 --- a/CoffeeScript.sublime-syntax +++ b/CoffeeScript.sublime-syntax @@ -435,9 +435,11 @@ contexts: scope: keyword.operator.word.coffee keyword.operator.object.delete.coffee - match: (?:super)\b scope: keyword.operator.word.coffee keyword.operator.object.super.coffee - - match: (?:instanceof|typeof)\b + - match: (?:instanceof)\b scope: keyword.operator.word.coffee keyword.operator.comparison.type.coffee push: maybe-class + - match: (?:typeof)\b + scope: keyword.operator.word.coffee keyword.operator.object.typeof.coffee maybe-class: - match: ({{identifier}})?(\.) diff --git a/tests/syntax_test_scope.coffee b/tests/syntax_test_scope.coffee index e0d1b82..d632023 100644 --- a/tests/syntax_test_scope.coffee +++ b/tests/syntax_test_scope.coffee @@ -382,10 +382,10 @@ class # ^^^^^^ keyword.operator.object.delete.coffee # ^^^ variable.other.readwrite.coffee - obj typeof Bar + obj = typeof obj # ^^^ variable.other.readwrite.coffee -# ^^^^^^ keyword.operator.comparison.type.coffee -# ^^^ support.class.coffee +# ^^^^^^ keyword.operator.object.typeof.coffee +# ^^^ variable.other.readwrite.coffee obj instanceof Bar # ^^^ variable.other.readwrite.coffee From a5d7d9658513e54e2b315638929a52329b175fc5 Mon Sep 17 00:00:00 2001 From: deathaxe <deathaxe82@gmail.com> Date: Fri, 2 Jan 2026 18:21:13 +0100 Subject: [PATCH 30/52] Fix rational operator scopes --- CoffeeScript.sublime-syntax | 6 +++--- tests/syntax_test_scope.coffee | 17 +++++++++-------- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/CoffeeScript.sublime-syntax b/CoffeeScript.sublime-syntax index 843a62d..c395aef 100644 --- a/CoffeeScript.sublime-syntax +++ b/CoffeeScript.sublime-syntax @@ -424,9 +424,9 @@ contexts: # alphanumeric - match: (?:as)\b scope: keyword.operator.word.coffee keyword.operator.assignment.as.coffee - - match: (?:in|of)\b - scope: keyword.operator.word.coffee keyword.operator.iteration.coffee - - match: (?:and|or|is|isnt|not)\b + - match: (?:in|is(?:nt| not)?|of)\b + scope: keyword.operator.word.coffee keyword.operator.comparison.coffee + - match: (?:and|or|not)\b scope: keyword.operator.word.coffee keyword.operator.logical.coffee - match: (?:new)\b scope: keyword.operator.word.coffee keyword.operator.object.new.coffee diff --git a/tests/syntax_test_scope.coffee b/tests/syntax_test_scope.coffee index d632023..4350d41 100644 --- a/tests/syntax_test_scope.coffee +++ b/tests/syntax_test_scope.coffee @@ -351,16 +351,17 @@ class as # ^^ keyword.operator.assignment.as.coffee - in of -# ^^ keyword.operator.iteration.coffee -# ^^ keyword.operator.iteration.coffee - - and or is isnt not + in is isnt is not of +# ^^ keyword.operator.comparison.coffee +# ^^ keyword.operator.comparison.coffee +# ^^^^ keyword.operator.comparison.coffee +# ^^^^^^ keyword.operator.comparison.coffee +# ^^ keyword.operator.comparison.coffee + + and or not # ^^^ keyword.operator.logical.coffee # ^^ keyword.operator.logical.coffee -# ^^ keyword.operator.logical.coffee -# ^^^^ keyword.operator.logical.coffee -# ^^^ keyword.operator.logical.coffee +# ^^^ keyword.operator.logical.coffee new # ^^^ keyword.operator.object.new.coffee From 6bbbc16ed4782426f72a4692d75fed8e5b6b5ea2 Mon Sep 17 00:00:00 2001 From: deathaxe <deathaxe82@gmail.com> Date: Fri, 2 Jan 2026 18:22:13 +0100 Subject: [PATCH 31/52] Fix loop keyword scope --- CoffeeScript.sublime-syntax | 2 +- tests/syntax_test_scope.coffee | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CoffeeScript.sublime-syntax b/CoffeeScript.sublime-syntax index c395aef..381a897 100644 --- a/CoffeeScript.sublime-syntax +++ b/CoffeeScript.sublime-syntax @@ -393,7 +393,7 @@ contexts: - match: for(?:\s+own)?\b scope: keyword.control.loop.for.coffee - match: loop\b - scope: keyword.control.loop.loopcoffee + scope: keyword.control.loop.loop.coffee - match: until\b scope: keyword.control.loop.until.coffee - match: while\b diff --git a/tests/syntax_test_scope.coffee b/tests/syntax_test_scope.coffee index 4350d41..e0d83c3 100644 --- a/tests/syntax_test_scope.coffee +++ b/tests/syntax_test_scope.coffee @@ -325,6 +325,10 @@ class # ^^^ keyword.control.loop.for.coffee # ^^^^^^^^^^^^^^^^ - keyword + loop .loop _loop $loop +# ^^^^ keyword.control.loop.loop.coffee +# ^^^^^^^^^^^^^^^^^ - keyword + break .break _break $break # ^^^^^ keyword.control.flow.coffee # ^^^^^^^^^^^^^^^^^^^^^ - keyword From 364d5aadc462adc829668312a1fe32f52876c8e0 Mon Sep 17 00:00:00 2001 From: deathaxe <deathaxe82@gmail.com> Date: Fri, 2 Jan 2026 18:22:24 +0100 Subject: [PATCH 32/52] Fix in keyword scope in loops --- CoffeeScript.sublime-syntax | 13 +++++++++++-- tests/syntax_test_scope.coffee | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 2 deletions(-) diff --git a/CoffeeScript.sublime-syntax b/CoffeeScript.sublime-syntax index 381a897..735b479 100644 --- a/CoffeeScript.sublime-syntax +++ b/CoffeeScript.sublime-syntax @@ -31,12 +31,12 @@ contexts: script: - include: functions - include: classes - - include: keywords - include: jsx-tags - include: expressions expressions: - include: comments + - include: keywords - include: constants - include: function-calls - include: patterns @@ -392,6 +392,7 @@ contexts: scope: keyword.control.loop.do.coffee - match: for(?:\s+own)?\b scope: keyword.control.loop.for.coffee + push: for-args - match: loop\b scope: keyword.control.loop.loop.coffee - match: until\b @@ -405,6 +406,15 @@ contexts: - match: (?:debugger\b|\\) scope: keyword.other.coffee + for-args: + - match: in\b + scope: keyword.control.loop.in.coffee + pop: 1 + - match: ',' + scope: punctuation.separator.sequence.coffee + - include: variables + - include: else-pop + operators: # symbolic - match: (?:and|or|<<|>>>?|[-+*/%&|^])= @@ -909,7 +919,6 @@ contexts: ############################################################################### - variables: ascii_space: '\t\n\f ' diff --git a/tests/syntax_test_scope.coffee b/tests/syntax_test_scope.coffee index e0d83c3..26dde8b 100644 --- a/tests/syntax_test_scope.coffee +++ b/tests/syntax_test_scope.coffee @@ -315,6 +315,39 @@ class # ^ punctuation.section.group.end.coffee # ^ punctuation.terminator.statement.coffee +###[ LOOP STATEMENTS ]######################################################### + + for a, b in @links +# ^^^ keyword.control.loop.for.coffee +# ^ variable.other.readwrite.coffee +# ^ punctuation.separator.sequence.coffee +# ^ variable.other.readwrite.coffee +# ^^ keyword.control.loop.in.coffee +# ^^^^^^ variable.other.member.coffee +# ^ punctuation.definition.variable.coffee + + @links = ($(a) for a in @$links unless a in filtered) +# ^^^^^^ variable.other.member.coffee +# ^ punctuation.definition.variable.coffee +# ^ keyword.operator.assignment.coffee +# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ meta.group.coffee +# ^ punctuation.section.group.begin.coffee +# ^ meta.function-call.identifier.coffee variable.function.coffee +# ^^^ meta.function-call.arguments.coffee +# ^ punctuation.section.group.begin.coffee +# ^ variable.other.readwrite.coffee +# ^ punctuation.section.group.end.coffee +# ^^^ keyword.control.loop.for.coffee +# ^ variable.other.readwrite.coffee +# ^^ keyword.control.loop.in.coffee +# ^^^^^^^ variable.other.member.coffee +# ^ punctuation.definition.variable.coffee +# ^^^^^^ keyword.control.conditional.unless.coffee +# ^ variable.other.readwrite.coffee +# ^^ keyword.operator.word.coffee keyword.operator.comparison.coffee +# ^^^^^^^^ variable.other.readwrite.coffee +# ^ punctuation.section.group.end.coffee + ###[ KEYWORDS ]################################################################ if .if _if $if From 8ede670fc2bea8eebf0101ced0157132effb81d8 Mon Sep 17 00:00:00 2001 From: deathaxe <deathaxe82@gmail.com> Date: Fri, 2 Jan 2026 18:22:50 +0100 Subject: [PATCH 33/52] Reorganize regexp patterns into literals --- CoffeeScript.sublime-syntax | 50 ++++++++++++++++++------------------- 1 file changed, 24 insertions(+), 26 deletions(-) diff --git a/CoffeeScript.sublime-syntax b/CoffeeScript.sublime-syntax index 735b479..b104def 100644 --- a/CoffeeScript.sublime-syntax +++ b/CoffeeScript.sublime-syntax @@ -497,32 +497,6 @@ contexts: - match: \, scope: punctuation.separator.sequence.coffee -###[ REGULAR EXPRESSIONS ]##################################################### - - patterns: - - match: /{3} - scope: punctuation.definition.string.begin.coffee - push: heredoc-pattern-body - - match: (/)((?![\s=/*+{}?])(?:\\.|.)*?)(/)([igmy]{0,4}(?![a-zA-Z0-9])) - scope: meta.string.regexp.coffee - captures: - 1: punctuation.definition.string.begin.coffee - 2: string.regexp.coffee - 3: punctuation.definition.string.end.coffee - 4: constant.language.flags.coffee - - heredoc-pattern-body: - - meta_scope: meta.string.heredoc.coffee - - meta_content_scope: string.regexp.coffee - - match: (/{3})([imgy]{0,4}) - captures: - 1: punctuation.definition.string.end.coffee - 2: constant.language.flags.coffee - pop: 1 - - include: comments - - include: string-escapes - - include: string-interpolations - ###[ LITERALS ]################################################################ constants: @@ -566,6 +540,30 @@ contexts: - match: '{{dec_integer}}\b' scope: meta.number.integer.decimal.coffee constant.numeric.value.coffee + patterns: + - match: /{3} + scope: punctuation.definition.string.begin.coffee + push: heredoc-pattern-body + - match: (/)((?![\s=/*+{}?])(?:\\.|.)*?)(/)([igmy]{0,4}(?![a-zA-Z0-9])) + scope: meta.string.regexp.coffee + captures: + 1: punctuation.definition.string.begin.coffee + 2: string.regexp.coffee + 3: punctuation.definition.string.end.coffee + 4: constant.language.flags.coffee + + heredoc-pattern-body: + - meta_scope: meta.string.heredoc.coffee + - meta_content_scope: string.regexp.coffee + - match: (/{3})([imgy]{0,4}) + captures: + 1: punctuation.definition.string.end.coffee + 2: constant.language.flags.coffee + pop: 1 + - include: comments + - include: string-escapes + - include: string-interpolations + triple-double-quoted-strings: - match: \"{3} scope: punctuation.definition.string.begin.coffee From 31875f7c13b21b2692482ec8f12df74112433dbb Mon Sep 17 00:00:00 2001 From: deathaxe <deathaxe82@gmail.com> Date: Fri, 2 Jan 2026 18:23:00 +0100 Subject: [PATCH 34/52] Fix super/this variable scopes --- CoffeeScript.sublime-syntax | 66 ++++++++++++++++++++-------------- tests/syntax_test_scope.coffee | 53 ++++++++++++++++++--------- 2 files changed, 75 insertions(+), 44 deletions(-) diff --git a/CoffeeScript.sublime-syntax b/CoffeeScript.sublime-syntax index b104def..e99563e 100644 --- a/CoffeeScript.sublime-syntax +++ b/CoffeeScript.sublime-syntax @@ -153,7 +153,7 @@ contexts: - group - anonymous-parameter-list # named functions - - match: (?=(?:(?!and=|or=)|@){{identifier}}\s*[:=][^>]) + - match: (?=(?:(?!and=|or=)){{identifier}}\s*[:=][^>]) branch_point: function branch: - function-name @@ -196,10 +196,8 @@ contexts: function-name: - meta_include_prototype: false - meta_scope: meta.function.identifier.coffee - - match: (@?){{identifier}} + - match: '{{identifier}}' scope: entity.name.function.coffee - captures: - 1: punctuation.definition.variable.coffee set: - function-body - function-parameter-list @@ -343,10 +341,8 @@ contexts: | Uint(?: 8 | 16 | 32 | 64)Array | XMLHttpRequest | Symbol )\b scope: support.class.coffee # user function calls - - match: (@?){{identifier}}(?=\() + - match: '{{identifier}}(?=\()' scope: meta.function-call.identifier.coffee variable.function.coffee - captures: - 1: punctuation.definition.variable.coffee push: function-call-argument-list function-call-argument-list: @@ -443,8 +439,6 @@ contexts: push: maybe-class - match: (?:delete)\b scope: keyword.operator.word.coffee keyword.operator.object.delete.coffee - - match: (?:super)\b - scope: keyword.operator.word.coffee keyword.operator.object.super.coffee - match: (?:instanceof)\b scope: keyword.operator.word.coffee keyword.operator.comparison.type.coffee push: maybe-class @@ -826,34 +820,54 @@ contexts: ###[ VARIABLES ]############################################################## variables: - - match: (?:this|extends)(?!\s*[:=])\b - scope: variable.language.coffee + - match: (super)(\.) + captures: + 1: variable.language.super.coffee + 2: punctuation.accessor.dot.coffee + push: member + - match: super(?!\s*[:=])\b + scope: variable.language.super.coffee + - match: (this)(\.) + captures: + 1: variable.language.this.coffee + 2: punctuation.accessor.dot.coffee + push: member + - match: this(?!\s*[:=])\b + scope: variable.language.this.coffee + - match: \@ + scope: variable.language.this.coffee + push: member - match: ({{identifier}})?(\.) captures: 1: variable.other.object.coffee 2: punctuation.accessor.dot.coffee push: member - - match: (@)(?:{{identifier}})? - scope: variable.other.member.coffee - captures: - 1: punctuation.definition.variable.coffee - match: '{{identifier}}' scope: variable.other.readwrite.coffee variable: - - match: (?:this|extends)(?!\s*[:=])\b - scope: variable.language.coffee - pop: 1 + - match: (super)(\.) + captures: + 1: variable.language.super.coffee + 2: punctuation.accessor.dot.coffee + set: member + - match: super(?!\s*[:=])\b + scope: variable.language.super.coffee + - match: (this)(\.) + captures: + 1: variable.language.this.coffee + 2: punctuation.accessor.dot.coffee + set: member + - match: this(?!\s*[:=])\b + scope: variable.language.this.coffee + - match: \@ + scope: variable.language.this.coffee + set: member - match: ({{identifier}})?(\.) captures: 1: variable.other.object.coffee 2: punctuation.accessor.dot.coffee set: member - - match: (@)(?:{{identifier}})? - scope: variable.other.member.coffee - captures: - 1: punctuation.definition.variable.coffee - pop: 1 - match: '{{identifier}}' scope: variable.other.readwrite.coffee pop: 1 @@ -870,7 +884,7 @@ contexts: scope: meta.function-call.identifier.coffee variable.function.coffee set: function-call-argument-list # member function definition or variable - - match: (?=(?:(?!and=|or=)|@){{identifier}}) + - match: (?=(?:(?!and=|or=)){{identifier}}) branch_point: member branch: - member-function @@ -880,10 +894,8 @@ contexts: member-function: - meta_include_prototype: false - meta_scope: meta.function.identifier.coffee meta.path.coffee - - match: (@?){{identifier}} + - match: '{{identifier}}' scope: entity.name.function.coffee - captures: - 1: punctuation.definition.variable.coffee set: - member-function-body - function-parameter-list diff --git a/tests/syntax_test_scope.coffee b/tests/syntax_test_scope.coffee index 26dde8b..8bddbfe 100644 --- a/tests/syntax_test_scope.coffee +++ b/tests/syntax_test_scope.coffee @@ -86,7 +86,8 @@ class @name: -> # ^^^^^^^^^ - meta.function meta.function -# ^^^^^ meta.function.identifier.coffee entity.name.function.coffee +# ^ variable.language.this.coffee +# ^^^^ meta.function.identifier.coffee entity.name.function.coffee # ^^^^ meta.function.coffee # ^ keyword.operator.assignment.coffee # ^^ keyword.declaration.function.coffee @@ -323,12 +324,12 @@ class # ^ punctuation.separator.sequence.coffee # ^ variable.other.readwrite.coffee # ^^ keyword.control.loop.in.coffee -# ^^^^^^ variable.other.member.coffee -# ^ punctuation.definition.variable.coffee +# ^ variable.language.this.coffee +# ^^^^^ variable.other.member.coffee @links = ($(a) for a in @$links unless a in filtered) -# ^^^^^^ variable.other.member.coffee -# ^ punctuation.definition.variable.coffee +# ^ variable.language.this.coffee +# ^^^^^ variable.other.member.coffee # ^ keyword.operator.assignment.coffee # ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ meta.group.coffee # ^ punctuation.section.group.begin.coffee @@ -340,8 +341,8 @@ class # ^^^ keyword.control.loop.for.coffee # ^ variable.other.readwrite.coffee # ^^ keyword.control.loop.in.coffee -# ^^^^^^^ variable.other.member.coffee -# ^ punctuation.definition.variable.coffee +# ^ variable.language.this.coffee +# ^^^^^^ variable.other.member.coffee # ^^^^^^ keyword.control.conditional.unless.coffee # ^ variable.other.readwrite.coffee # ^^ keyword.operator.word.coffee keyword.operator.comparison.coffee @@ -372,16 +373,19 @@ class yield @foo # ^^^^^ keyword.control.flow.coffee -# ^^^^ variable.other.member.coffee +# ^ variable.language.this.coffee +# ^^^ variable.other.member.coffee yield from @foo # ^^^^^^^^^^ keyword.control.flow.coffee -# ^^^^ variable.other.member.coffee +# ^ variable.language.this.coffee +# ^^^ variable.other.member.coffee await return @foo; # ^^^^^ keyword.control.flow.coffee # ^^^^^^ keyword.control.flow.coffee -# ^^^^ variable.other.member.coffee +# ^ variable.language.this.coffee +# ^^^ variable.other.member.coffee ###[ OPERATORS ]############################################################### @@ -520,13 +524,14 @@ class # ^^^^ meta.function-call.arguments.coffee @name() -# ^^^^^ meta.function-call.identifier.coffee variable.function.coffee +# ^ variable.language.this.coffee +# ^^^^ meta.function-call.identifier.coffee variable.function.coffee # ^^ meta.function-call.arguments.coffee @$('#notification') -# ^^ meta.function-call.identifier.coffee variable.function.coffee +# ^ variable.language.this.coffee +# ^ meta.function-call.identifier.coffee variable.function.coffee # ^^^^^^^^^^^^^^^^^ meta.function-call.arguments.coffee -# ^ punctuation.definition.variable.coffee # ^ variable.function.coffee # ^ punctuation.section.group.begin.coffee # ^^^^^^^^^^^^^^^ meta.string.coffee string.quoted.single.coffee @@ -534,11 +539,23 @@ class ###[ OBJECT MEMBERS ]########################################################## + super.key +# ^^^^^^^^^ meta.path.coffee +# ^^^^^ variable.language.super.coffee +# ^ meta.path.coffee punctuation.accessor.dot.coffee +# ^^^ meta.path.coffee variable.other.member.coffee + this.key -# ^^^^ variable.language.coffee +# ^^^^^^^^ meta.path.coffee +# ^^^^ variable.language.this.coffee # ^ meta.path.coffee punctuation.accessor.dot.coffee # ^^^ meta.path.coffee variable.other.member.coffee + @key +# ^^^^ meta.path.coffee +# ^ variable.language.this.coffee +# ^^^ variable.other.member.coffee + obj.Object # ^^^^^^^^^^ meta.path.coffee # ^^^ variable.other.object.coffee @@ -773,8 +790,8 @@ class # ^ meta.number.integer.decimal.coffee constant.numeric.value.coffee @variable -# ^^^^^^^^^ variable.other.member.coffee -# ^ punctuation.definition.variable.coffee +# ^ variable.language.this.coffee +# ^^^^^^^^ variable.other.member.coffee ###[ JSX ]##################################################################### @@ -788,7 +805,9 @@ class # ^^^ meta.string.coffee string.quoted.double.coffee # ^^^^^^ meta.string.coffee meta.interpolation.coffee # ^ punctuation.section.interpolation.begin.coffee -# ^^^^ source.coffee.embedded.jsx variable.other.member.coffee +# ^^^^ source.coffee.embedded.jsx +# ^ variable.language.this.coffee +# ^^^ variable.other.member.coffee # ^ punctuation.section.interpolation.end.coffee # ^ meta.string.coffee string.quoted.double.coffee punctuation.definition.string.end.coffee # ^ punctuation.definition.tag.end.coffee From 6e891d4284a4137266ca2dc8777fed8d5534615d Mon Sep 17 00:00:00 2001 From: deathaxe <deathaxe82@gmail.com> Date: Fri, 2 Jan 2026 18:23:21 +0100 Subject: [PATCH 35/52] Remove redundant groups --- CoffeeScript.sublime-syntax | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/CoffeeScript.sublime-syntax b/CoffeeScript.sublime-syntax index e99563e..8e7196e 100644 --- a/CoffeeScript.sublime-syntax +++ b/CoffeeScript.sublime-syntax @@ -428,21 +428,21 @@ contexts: - match: \.{2,3} scope: keyword.operator.variadic.coffee # alphanumeric - - match: (?:as)\b + - match: as\b scope: keyword.operator.word.coffee keyword.operator.assignment.as.coffee - match: (?:in|is(?:nt| not)?|of)\b scope: keyword.operator.word.coffee keyword.operator.comparison.coffee - match: (?:and|or|not)\b scope: keyword.operator.word.coffee keyword.operator.logical.coffee - - match: (?:new)\b + - match: new\b scope: keyword.operator.word.coffee keyword.operator.object.new.coffee push: maybe-class - - match: (?:delete)\b + - match: delete\b scope: keyword.operator.word.coffee keyword.operator.object.delete.coffee - - match: (?:instanceof)\b + - match: instanceof\b scope: keyword.operator.word.coffee keyword.operator.comparison.type.coffee push: maybe-class - - match: (?:typeof)\b + - match: typeof\b scope: keyword.operator.word.coffee keyword.operator.object.typeof.coffee maybe-class: From 66eda187b4fc9c53690db60268286b8426377bdc Mon Sep 17 00:00:00 2001 From: deathaxe <deathaxe82@gmail.com> Date: Fri, 2 Jan 2026 18:23:30 +0100 Subject: [PATCH 36/52] Fix class declaration termination Restrict class declarations to single line --- CoffeeScript.sublime-syntax | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/CoffeeScript.sublime-syntax b/CoffeeScript.sublime-syntax index 8e7196e..c69dce1 100644 --- a/CoffeeScript.sublime-syntax +++ b/CoffeeScript.sublime-syntax @@ -102,8 +102,6 @@ contexts: - class-name class-name: - - match: $ - pop: 1 - match: (?=extends\b) pop: 1 - match: ({{identifier}})(\.) @@ -115,6 +113,7 @@ contexts: pop: 1 - include: comments - include: else-pop + - include: eol-pop class-extends: - meta_content_scope: meta.class.identifier.coffee @@ -123,6 +122,7 @@ contexts: set: class-extends-name - include: comments - include: else-pop + - include: eol-pop class-extends-name: - meta_scope: meta.class.extends.coffee @@ -135,6 +135,7 @@ contexts: pop: 1 - include: comments - include: else-pop + - include: eol-pop ###[ FUNCTION DECLARATIONS ]################################################## @@ -923,7 +924,12 @@ contexts: - match: (?=\S) pop: 1 + eol-pop: + - match: $ + pop: 1 + immediately-pop: + - meta_include_prototype: false - match: '' pop: 1 From 7a7a6abc132faabe80721dae863e295a5c1a8e2f Mon Sep 17 00:00:00 2001 From: deathaxe <deathaxe82@gmail.com> Date: Fri, 2 Jan 2026 18:26:40 +0100 Subject: [PATCH 37/52] Fix spec references --- CoffeeScript.sublime-syntax | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CoffeeScript.sublime-syntax b/CoffeeScript.sublime-syntax index c69dce1..6849014 100644 --- a/CoffeeScript.sublime-syntax +++ b/CoffeeScript.sublime-syntax @@ -1,7 +1,7 @@ %YAML 1.2 --- -# http://www.sublimetext.com/docs/syntax.html -# CoffeeScript Syntax: version 1 +# https://coffeescript.org/ +# https://www.sublimetext.com/docs/syntax.html name: CoffeeScript scope: source.coffee version: 2 From b34974ae91e8e912b90a663c2dbeeeb11690e154 Mon Sep 17 00:00:00 2001 From: deathaxe <deathaxe82@gmail.com> Date: Fri, 2 Jan 2026 18:43:57 +0100 Subject: [PATCH 38/52] Fix console scope --- CoffeeScript.sublime-syntax | 2 +- tests/syntax_test_scope.coffee | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CoffeeScript.sublime-syntax b/CoffeeScript.sublime-syntax index 6849014..c5dc913 100644 --- a/CoffeeScript.sublime-syntax +++ b/CoffeeScript.sublime-syntax @@ -266,7 +266,7 @@ contexts: )? scope: meta.path.coffee captures: - 1: variable.language.console.coffee + 1: support.class.console.coffee 2: punctuation.accessor.dot.coffee 3: support.function.console.coffee - match: |- diff --git a/tests/syntax_test_scope.coffee b/tests/syntax_test_scope.coffee index 8bddbfe..918f992 100644 --- a/tests/syntax_test_scope.coffee +++ b/tests/syntax_test_scope.coffee @@ -493,7 +493,7 @@ class ###[ BUILTIN FUNCTIONS ]####################################################### console.log() -# ^^^^^^^ variable.language.console.coffee +# ^^^^^^^ support.class.console.coffee # ^ punctuation.accessor.dot.coffee # ^^^ support.function.console.coffee # ^ punctuation.section.group.begin.coffee From 31eeeeeb19592f638a941d490196171af85a42c0 Mon Sep 17 00:00:00 2001 From: deathaxe <deathaxe82@gmail.com> Date: Fri, 2 Jan 2026 18:46:24 +0100 Subject: [PATCH 39/52] More specific constant scopes --- CoffeeScript.sublime-syntax | 8 ++++++-- tests/syntax_test_scope.coffee | 4 ++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/CoffeeScript.sublime-syntax b/CoffeeScript.sublime-syntax index c5dc913..b59dbb5 100644 --- a/CoffeeScript.sublime-syntax +++ b/CoffeeScript.sublime-syntax @@ -495,12 +495,16 @@ contexts: ###[ LITERALS ]################################################################ constants: - - match: (?:Infinity|NaN|undefined)\b - scope: constant.language.coffee - match: (?:true|on|yes)(?!\s*[:=])\b scope: constant.language.boolean.true.coffee - match: (?:false|off|no)(?!\s*[:=])\b scope: constant.language.boolean.false.coffee + - match: Infinity(?!\s*[:=])\b + scope: constant.language.infinity.coffee + - match: NaN(?!\s*[:=])\b + scope: constant.language.nan.coffee + - match: undefined(?!\s*[:=])\b + scope: constant.language.coffee - match: null(?!\s*[:=])\b scope: constant.language.null.coffee diff --git a/tests/syntax_test_scope.coffee b/tests/syntax_test_scope.coffee index 918f992..6ab0e89 100644 --- a/tests/syntax_test_scope.coffee +++ b/tests/syntax_test_scope.coffee @@ -604,8 +604,8 @@ class ###[ LITERALS ]################################################################ Infinity NaN undefined .Infinity .NaN .undefined -# ^^^^^^^^ constant.language.coffee -# ^^^ constant.language.coffee +# ^^^^^^^^ constant.language.infinity.coffee +# ^^^ constant.language.nan.coffee # ^^^^^^^^^ constant.language.coffee # ^ punctuation.accessor.dot.coffee # ^^^^^^^^ variable.other.member.coffee From e6dbaf4fe13f78ddc6e3a5c3bb165efae90568a8 Mon Sep 17 00:00:00 2001 From: deathaxe <deathaxe82@gmail.com> Date: Fri, 2 Jan 2026 19:12:56 +0100 Subject: [PATCH 40/52] Update README --- .gitattributes | 15 ++- README.md | 355 +++++++++++++++++++++++-------------------------- logo.svg | 3 + preview.coffee | 30 +++++ preview.png | Bin 0 -> 70785 bytes 5 files changed, 207 insertions(+), 196 deletions(-) create mode 100644 logo.svg create mode 100644 preview.coffee create mode 100644 preview.png diff --git a/.gitattributes b/.gitattributes index eec6973..7f91ba8 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,4 +1,11 @@ -/.github/ export-ignore -/.gitattributes export-ignore -/.gitignore export-ignore -/tests export-ignore \ No newline at end of file +# git +.github/ export-ignore +.git export-ignore +.gitattributes export-ignore +.gitignore export-ignore +# development +tests/ export-ignore +*.cmd export-ignore +# other +logo.* export-ignore +preview.* export-ignore diff --git a/README.md b/README.md index c2f5e95..621ab71 100644 --- a/README.md +++ b/README.md @@ -1,93 +1,91 @@ -## Overview +# CoffeeScript <img src="logo.svg" title="CoffeeScript" width="32" height="32"> -**This package is for Sublime Text 3+**. -An old version for Sublime Text 2 is accessible via the [`st2` branch](https://github.com/SublimeText/BetterCoffeeScript/tree/st2). - -## Description +[CoffeeScript](https://coffeescript.org/) syntax definitions build system and snippets for [Sublime Text](https://www.sublimetext.com). -CoffeeScript plug-in was originally created by @Xavura. As I, @aponxi, began writing a lot of code in CoffeeScript, I felt the need for side-by-side view for compiled CoffeeScript. Since Xavura's repository have been inactive I decided to branch out my own version. The biggest change in my branch is the Watch Mode which updates the compiled JavaScript view whenever you modify the CoffeeScript thus enabling you to view your progress side-by-side. +![preview](preview.png) -## Contributing - -- Please use the [issues page](https://github.com/SublimeText/BetterCoffeeScript/issues) to make requests or report bugs. -- Please make _pull requests_ to the `master` branch only. ST2 is not supported anymore. +**This package is for Sublime Text 3+**. +An old version for Sublime Text 2 is accessible via the [`st2` branch](https://github.com/SublimeText/BetterCoffeeScript/tree/st2). # Installation -## via Package Control - -> This is the recommended installation method. +### Package Control -If you have [Package Control](https://packagecontrol.io/), you know what to do. If not, well: it's a package manager for Sublime Text 3. Installation guide can be [found here](https://packagecontrol.io/installation). After installing the package manager: +The easiest way to install is using [Package Control](https://packagecontrol.io). It's listed as `CoffeeScript`. -* Open the Command Pallete (`ctrl+shift+P` or `cmd+shift+P`). -* Type "Install Package" and hit return. -* Type "CoffeeScript" and hit return. +1. Open `Command Palette` using <kbd>ctrl+shift+P</kbd> or menu item `Tools → Command Palette...` +2. Choose `Package Control: Install Package` +3. Find `CoffeeScript` and hit <kbd>Enter</kbd> -## via Source Control +## Source Control -> If you plan to contribute, then you should install via this method. Otherwise it is recommended that you install the package via Package Control, see above. +> [!TIP] +> +> Only recommended, if contributions to this package are planned. Sublime stores packages in the following locations: - Nix: ~/.config/sublime-text-3/packages - Mac: ~/Library/Application\ Support/Sublime\ Text\ 3/Packages - Win: %APPDATA%\Sublime Text 3\Packages - -When using Sublime Text 4 or higher, -the directory without the "3" (and the preceding separator character) -will be preferred. +```sh +Nix: ~/.config/sublime-text/packages +Mac: ~/Library/Application\ Support/Sublime\ Text/Packages +Win: %APPDATA%\Sublime Text\Packages +``` ### As a repository within the packages directory Open a Terminal/Console and run the following commands, replacing `PACKAGE_PATH` with the path corresponding to your OS above. - cd PACKAGE_PATH - git clone https://github.com/SublimeText/BetterCoffeeScript.git "CoffeeScript" +```sh +cd PACKAGE_PATH +git clone https://github.com/SublimeText/BetterCoffeeScript.git "CoffeeScript" +``` ### As a repository outside of the packages directory If you use Github for Mac/Windows which store repositories in a specific location, or if you just don't want a repository in your packages directory, then instead you can use a link. -If you don't yet have the repository, then grab it via your GUI program or via the command line: - - cd WHEREVER_YOU_WANT - git clone https://github.com/SublimeText/BetterCoffeeScript.git - -Once that is done, we will create the link: - -#### Windows: - - cd PACKAGE_PATH - mklink /D "CoffeeScript" ABSOLUTE_PATH_TO_REPOSITORY - -#### Nix/Mac: - - cd PACKAGE_PATH - ln -s ABSOLUTE_PATH_TO_REPOSITORY "CoffeeScript" - +1. Clone the repository via GUI program or command line: + + ```sh + cd WHEREVER_YOU_WANT + git clone https://github.com/SublimeText/BetterCoffeeScript.git + ``` + +2. Once that is done, create the link: + + **Windows:** + + ```sh + cd PACKAGE_PATH + mklink /D "CoffeeScript" ABSOLUTE_PATH_TO_REPOSITORY + ``` + + **Nix/Mac:** + + ```sh + cd PACKAGE_PATH + ln -s ABSOLUTE_PATH_TO_REPOSITORY "CoffeeScript" + ``` # Commands/Shortcuts You can access the commands either using the command palette (`ctrl+shift+P` or `cmd+shift+P`) or via shortcuts. - alt+shift+t - Run a Cake task - alt+shift+r - Run some CoffeeScript (prints output to a panel on the bottom) - alt+shift+s - Run a syntax check - alt+shift+c - Compile a file - alt+shift+d - Display compiled JavaScript - alt+shift+l - Display lexer tokens - alt+shift+n - Display parser nodes - alt+shift+w - Toggle watch mode - alt+shift+p - Toggle output panel + alt+shift+t - Run a Cake task + alt+shift+r - Run some CoffeeScript (prints output to a panel on the bottom) + alt+shift+s - Run a syntax check + alt+shift+c - Compile a file + alt+shift+d - Display compiled JavaScript + alt+shift+l - Display lexer tokens + alt+shift+n - Display parser nodes + alt+shift+w - Toggle watch mode + alt+shift+p - Toggle output panel Context menu has `Compile Output` that compiles the current CoffeeScript and outputs the javascript code that is run, in a panel. **Note:** Some of the commands use the Status Bar for output, so you'll probably want to enable it (View » Show Status Bar). - - # Snippets - Use `TAB` to run a snippet after typing the trigger. @@ -98,32 +96,32 @@ Context menu has `Compile Output` that compiles the current CoffeeScript and out **Comprehension** - Array: forin - Object: forof - Range: fori (inclusive) - Range: forx (exclusive) + Array: forin + Object: forof + Range: fori (inclusive) + Range: forx (exclusive) **Statements** - If: if - Else: el - If Else: ifel - Else If: elif - Switch: swi - Ternary: ter - Try Catch: try - Unless: unl + If: if + Else: el + If Else: ifel + Else If: elif + Switch: swi + Ternary: ter + Try Catch: try + Unless: unl **Classes** - Class - cla - Class extends SuperClass - clx + Class - cla + Class extends SuperClass - clx **Other** - Function: - - Function: = (bound) - Interpolation: # + Function: - + Function: = (bound) + Interpolation: # # Building @@ -137,124 +135,113 @@ Let's say before distributing your project that you would like to combine all of That's what this is for! You would create a `Cakefile` and inside it you would write a task: - task 'sbuild', 'Prepare project for distribution.', -> - # ... + task 'sbuild', 'Prepare project for distribution.', -> + # ... # Settings Go to `Preferences > Package Settings > CoffeeScript > Settings - User` to change settings. -```Javascript +```jsonc { - /* - The directories you would like to include in $PATH environment variable. - Use this if your node installation is at a separate location and getting errors such as `cannot find node executable` - - example: - "envPATH": "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" - - */ - "envPATH": "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", - /* - The directory containing your coffee binary. Usually - /usr/local/bin. - */ - "binDir": "/usr/local/bin" - - /* - Compile without the top-level function wrapper (coffee -b). - */ - -, "noWrapper": true - - /* - Enable or disable refresh the compiled Output on Save. - Only available for watch mode. - */ -, "watchOnSave": true - /* - Enable refreshing compiled JS when CoffeeScript is modified. - - Put false to disable - Put a number of seconds to delay the refresh - */ -, "watchOnModified": 0.5 - /* - Enable Compiling on save. It will compile into the same folder. - */ -, "compileOnSave": true - /* - ## Enable outputting the results of the compiled coffeescript in a panel - */ -, "showOutputOnSave": false - /* - ## Enable compiling to a specific directory. - #### Description - - if it is a string like 'some/directory' then `-o some/directory` will be added to `coffee` compiler. - if it is false or not string then it will compile your `script.coffee` to the directory it is in. - - #### Example: - Directory is relative to the file you are editing if specified such as - compileDir": "out" - Directory is absolute if specified such as - compileDir": "/home/logan/Desktop/out" - - */ -, "compileDir": false - /* - ## Enable compiling to a specific relative directories. - - #### Example: - Set absolute path for compile dir: - "compileDir": "/home/user/projects/js" - And specified folders - "relativeDir": "/home/user/projects/coffee" - "compilePaths": - { - "/home/user/projects/coffee": "/home/user/projects/first/js", - "/home/user/projects/second/coffee": "../js", - } - - So - "/home/user/projects/coffee/app.coffee" will compile to "/home/user/projects/first/js/app.js" - "/home/user/projects/coffee/models/prod.coffee" will compile to "/home/user/projects/first/js/models/prod.js" - "/home/user/projects/coffee/second/coffee/app2.coffee" will compile to "/home/user/projects/second/js/app2.js" - "/home/user/projects/main.coffee" will compile to "/home/user/projects/js/main.js" - - */ -, "compilePaths": false - - - + /* + The directories you would like to include in $PATH environment variable. + Use this if your node installation is at a separate location and getting errors such as `cannot find node executable` + + example: + "envPATH": "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" + + */ + "envPATH": "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", + /* + The directory containing your coffee binary. Usually + /usr/local/bin. + */ + "binDir": "/usr/local/bin", + + /* + Compile without the top-level function wrapper (coffee -b). + */ + + "noWrapper": true, + + /* + Enable or disable refresh the compiled Output on Save. + Only available for watch mode. + */ + "watchOnSave": true, + /* + Enable refreshing compiled JS when CoffeeScript is modified. + + Put false to disable + Put a number of seconds to delay the refresh + */ + "watchOnModified": 0.5, + /* + Enable Compiling on save. It will compile into the same folder. + */ + "compileOnSave": true, + /* + ## Enable outputting the results of the compiled coffeescript in a panel + */ + "showOutputOnSave": false, + /* + ## Enable compiling to a specific directory. + #### Description + + if it is a string like 'some/directory' then `-o some/directory` will be added to `coffee` compiler. + if it is false or not string then it will compile your `script.coffee` to the directory it is in. + + #### Example: + Directory is relative to the file you are editing if specified such as + compileDir": "out" + Directory is absolute if specified such as + compileDir": "/home/logan/Desktop/out" + + */ + "compileDir": false, + /* + ## Enable compiling to a specific relative directories. + + #### Example: + Set absolute path for compile dir: + "compileDir": "/home/user/projects/js" + And specified folders + "relativeDir": "/home/user/projects/coffee" + "compilePaths": + { + "/home/user/projects/coffee": "/home/user/projects/first/js", + "/home/user/projects/second/coffee": "../js", + } + + So + "/home/user/projects/coffee/app.coffee" will compile to "/home/user/projects/first/js/app.js" + "/home/user/projects/coffee/models/prod.coffee" will compile to "/home/user/projects/first/js/models/prod.js" + "/home/user/projects/coffee/second/coffee/app2.coffee" will compile to "/home/user/projects/second/js/app2.js" + "/home/user/projects/main.coffee" will compile to "/home/user/projects/js/main.js" + + */ + "compilePaths": false, } - - ``` ## Project settings Go to `Project > Edit Project` to change project settings. -```Javascript +```json { - "folders": - [ - ... - ], - "settings": - { - "CoffeeScript": - { - "noWrapper": true, - "compileOnSave": true, - "compileDir": "out" - } - } + "folders": [ + "." + ], + "settings": { + "CoffeeScript": { + "noWrapper": true, + "compileOnSave": true, + "compileDir": "out" + } + } } - - - ``` # FAQ @@ -294,19 +281,3 @@ This path will go into the `binDir` setting. - I'm getting the error message `'coffee' is not recognized as an internal or external command,` when saving. The coffee-script binary probably is not installed. Either install coffee-script or set `checkSyntaxOnSave` and `compileOnSave` to `false` in `Preferences > Package Settings > CoffeeScript > Settings - User`. - - - -# Latest Changelog -### v0.7.0 01/June/2013 - -- merged st3 with master branch -- now the sublime text 2 support is in st2 branch -- fixed the @ highlight in language definitions -- fixed an error you would get when it was looking for project settings when it wasn't a project we were editing - -# Special Thanks - - -Thanks to [everyone who has contributed to this project](https://github.com/SublimeText/BetterCoffeeScript/graphs/contributors). -You guys rock! diff --git a/logo.svg b/logo.svg new file mode 100644 index 0000000..872f380 --- /dev/null +++ b/logo.svg @@ -0,0 +1,3 @@ +<svg xmlns="http://www.w3.org/2000/svg" fill-rule="evenodd" fill="#ea7130" viewBox="0 0 16 16"> + <path d="M6.554,2.492A0.21,0.21,0,0,1,6.4,2.71a1.636,1.636,0,0,0-.817-0.122C5.264,2.614,5,2.723,5.035,2.955s0.357,0.367.906,0.313c1.34-.122,1.328-1.089,3.294-1.279,1.532-.15,2.387.354,2.5,0.966,0.089,0.476-.281.939-1.392,1.034-0.983.1-1.557-.191-1.609-0.476-0.026-.15.051-0.367,0.523-0.422a0.759,0.759,0,0,0,.957.408c0.46-.041.843-0.218,0.792-0.49s-0.536-.449-1.3-0.381C8.15,2.778,7.767,3.69,6.439,3.812A1.552,1.552,0,0,1,4.626,3c-0.038-.2-0.038-0.68.957-0.776a0.918,0.918,0,0,1,.97.272h0ZM1.575,8.181a2.947,2.947,0,0,0-.587,1.933,2.292,2.292,0,0,0,.83,1.633,1.913,1.913,0,0,0,1.685.422,5.5,5.5,0,0,0,.753-0.259,2.048,2.048,0,0,1-1.379-.544,2.232,2.232,0,0,1-.868-1.511,2.133,2.133,0,0,1,.306-1.606,1.965,1.965,0,0,1,1.379-.8,2.3,2.3,0,0,1,1.609.5,3.608,3.608,0,0,0-.434-0.463,1.957,1.957,0,0,0-1.736-.367A2.6,2.6,0,0,0,1.575,8.181h0ZM8.418,5.037a19.246,19.246,0,0,1-4.175-.422c-1.136-.3-1.736-0.626-1.736-1.048a0.615,0.615,0,0,1,.306-0.5c-0.715.3-1.1,0.544-1.1,0.925,0.038,0.422.677,0.844,1.979,1.17a18.6,18.6,0,0,0,4.685.5,18.384,18.384,0,0,0,4.685-.5c1.3-.327,1.928-0.762,1.928-1.17a1.051,1.051,0,0,0-.791-0.8,0.466,0.466,0,0,1,.2.367c0,0.422-.587.762-1.775,1.048a18.534,18.534,0,0,1-4.213.436h0Zm4.7,1.17a19.947,19.947,0,0,1-4.685.5,20.81,20.81,0,0,1-4.724-.5,3.228,3.228,0,0,1-1.889-.966,12.952,12.952,0,0,0,1.3,4.083c0.472,0.762.945,1.429,1.417,2.15a5.509,5.509,0,0,1,.434,1.266,2.285,2.285,0,0,0,1.3.925,5.019,5.019,0,0,0,2.094.327H8.443a5.6,5.6,0,0,0,2.157-.327,2.444,2.444,0,0,0,1.264-.925H11.9a5.364,5.364,0,0,1,.4-1.266c0.472-.721.945-1.388,1.417-2.15a13.756,13.756,0,0,0,1.3-4.083,3.13,3.13,0,0,1-1.9.966h0Z"/> +</svg> diff --git a/preview.coffee b/preview.coffee new file mode 100644 index 0000000..e769a17 --- /dev/null +++ b/preview.coffee @@ -0,0 +1,30 @@ +# Assignment: +number = 42 +opposite = true + +# Conditions: +number = -42 if opposite + +# Functions: +square = (x) -> x * x + +# Arrays: +list = [1, 2, 3, 4, 5] + +# Objects: +math = + root: Math.sqrt + square: square + cube: (x) -> x * square x + +# Splats: +race = (winner, runners...) -> + print winner, runners + +# Array comprehensions: +cubes = (math.cube num for num in list) + +# Existence: +alert "I knew it!" if elvis? + + diff --git a/preview.png b/preview.png new file mode 100644 index 0000000000000000000000000000000000000000..6daec9351ad5fc9035d7759bb471c98e543e1d18 GIT binary patch literal 70785 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⋙$iwKPHH9CS$S$<+<V-I^ws&Vk3=QvtQ_^<Dc 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 literal 0 HcmV?d00001 From 70a490c921fc199a0bf3b434bc6db350e3b0eef5 Mon Sep 17 00:00:00 2001 From: deathaxe <deathaxe82@gmail.com> Date: Fri, 2 Jan 2026 20:31:03 +0100 Subject: [PATCH 41/52] Fix group vs function at eof --- CoffeeScript.sublime-syntax | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/CoffeeScript.sublime-syntax b/CoffeeScript.sublime-syntax index b59dbb5..5aa77f4 100644 --- a/CoffeeScript.sublime-syntax +++ b/CoffeeScript.sublime-syntax @@ -203,7 +203,8 @@ contexts: - function-body - function-parameter-list - function-assignment - - match: (?=\S) + # any non-whitespace char or end of file + - match: \S|\Z fail: function function-assignment: @@ -246,7 +247,8 @@ contexts: - match: '[=-]>' scope: meta.function.coffee keyword.declaration.function.coffee pop: 1 - - match: (?=\S) + # any non-whitespace char or end of file + - match: \S|\Z fail: function ###[ FUNCTION CALLS ]######################################################### From 8880e9ac3b39a012f4e7720e0c3c4a87319b38ca Mon Sep 17 00:00:00 2001 From: deathaxe <deathaxe82@gmail.com> Date: Fri, 2 Jan 2026 20:49:58 +0100 Subject: [PATCH 42/52] CI: update syntax tests --- .github/workflows/syntax-tests.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/syntax-tests.yml b/.github/workflows/syntax-tests.yml index 100334c..192fa91 100644 --- a/.github/workflows/syntax-tests.yml +++ b/.github/workflows/syntax-tests.yml @@ -37,12 +37,14 @@ jobs: default_packages: v4169 - build: 4180 default_packages: v4180 + - build: 4200 + default_packages: v4200 # Latest dev build - build: latest default_packages: master steps: - name: Checkout Default Packages - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: repository: sublimehq/Packages ref: ${{ matrix.default_packages }} @@ -53,7 +55,7 @@ jobs: find st_syntax_tests/Data/Packages/*/ -type f -name 'syntax_test*' -exec rm -v '{}' \; - name: Checkout CoffeeScript - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: path: st_syntax_tests/Data/Packages/CoffeeScript From 4d12657db8703c12ddc6c01e6d47b9db2bb73ecb Mon Sep 17 00:00:00 2001 From: deathaxe <deathaxe82@gmail.com> Date: Fri, 2 Jan 2026 20:50:14 +0100 Subject: [PATCH 43/52] Add release script --- README.md | 22 +++++++++++++++++++--- make.cmd | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 3 deletions(-) create mode 100644 make.cmd diff --git a/README.md b/README.md index 621ab71..d623433 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,23 @@ The easiest way to install is using [Package Control](https://packagecontrol.io) 2. Choose `Package Control: Install Package` 3. Find `CoffeeScript` and hit <kbd>Enter</kbd> -## Source Control +### Manual Install + +1. Download appropriate [CoffeeScript.sublime-package](https://github.com/SublimeText/CoffeeScript/releases) for your Sublime Text build. +2. Copy it into _Installed Packages_ directory + +> [!NOTE] +> +> To find _Installed Packages_... +> +> 1. call _Menu > Preferences > Browse Packages.._ +> 2. Navigate to parent folder + +> [!WARNING] +> +> Manually installed packages are not automatically updated by Package Control. + +### Source Control > [!TIP] > @@ -31,7 +47,7 @@ Mac: ~/Library/Application\ Support/Sublime\ Text/Packages Win: %APPDATA%\Sublime Text\Packages ``` -### As a repository within the packages directory +#### As a repository within the packages directory Open a Terminal/Console and run the following commands, replacing `PACKAGE_PATH` with the path corresponding to your OS above. @@ -40,7 +56,7 @@ cd PACKAGE_PATH git clone https://github.com/SublimeText/BetterCoffeeScript.git "CoffeeScript" ``` -### As a repository outside of the packages directory +#### As a repository outside of the packages directory If you use Github for Mac/Windows which store repositories in a specific location, or if you just don't want a repository in your packages directory, then instead you can use a link. diff --git a/make.cmd b/make.cmd new file mode 100644 index 0000000..a54f14f --- /dev/null +++ b/make.cmd @@ -0,0 +1,32 @@ +@echo off +setlocal +chcp 65001 >nul +pushd %~dp0 + +if /i "%1" == "release" goto RELEASE +goto :usage + +:RELEASE + if "%2"== "" goto :usage + + for %%d in ("%~dp0.") do set package=%%~nxd + + echo Createing assets for "%package%"... + + set build=4143 + set archive=%package%.sublime-package + call git archive --format zip -o "%archive%" master + + :: create the release + gh release create --target master -t "v%2" "%build%-%2" *.sublime-package + del /f /q *.sublime-package + git fetch + goto :eof + +:USAGE + echo USAGE: + echo. + echo make ^[release^] + echo. + echo release ^<semver^> -- create and publish a release (e.g. 1.2.3) + goto :eof From 21983e1d6f89ffb24e65b872f3584398ba8c4beb Mon Sep 17 00:00:00 2001 From: deathaxe <deathaxe82@gmail.com> Date: Fri, 2 Jan 2026 20:57:17 +0100 Subject: [PATCH 44/52] Adjust litcoffee code fence scopes --- CoffeeScript Literate.sublime-syntax | 5 +++-- tests/syntax_test_scope.litcoffee | 13 ++++++++++++- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/CoffeeScript Literate.sublime-syntax b/CoffeeScript Literate.sublime-syntax index 0607209..45d61d1 100644 --- a/CoffeeScript Literate.sublime-syntax +++ b/CoffeeScript Literate.sublime-syntax @@ -41,17 +41,18 @@ contexts: (?i:\s*(coffee(?:script)?|cjsx|cson|iced)) {{fenced_code_block_trailing_infostring_characters}} captures: - 0: meta.code-fence.definition.begin.coffee.markdown-gfm + 0: meta.code-fence.definition.begin.markdown-gfm 2: punctuation.definition.raw.code-fence.begin.markdown 5: constant.other.language-name.markdown 6: comment.line.infostring.markdown 7: meta.fold.code-fence.begin.markdown embed: scope:source.coffee embed_scope: + meta.code-fence.body.markdown.markdown-gfm markup.raw.code-fence.coffee.markdown-gfm source.coffee escape: '{{fenced_code_block_escape}}' escape_captures: - 0: meta.code-fence.definition.end.coffee.markdown-gfm + 0: meta.code-fence.definition.end.markdown-gfm 1: punctuation.definition.raw.code-fence.end.markdown 2: meta.fold.code-fence.end.markdown diff --git a/tests/syntax_test_scope.litcoffee b/tests/syntax_test_scope.litcoffee index f45843d..fcfe635 100644 --- a/tests/syntax_test_scope.litcoffee +++ b/tests/syntax_test_scope.litcoffee @@ -58,4 +58,15 @@ into a .litcoffee file and open it in Sublime to see the breakage. ...even this markdown is as well. :( | <- meta.paragraph.markdown -|^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ meta.paragraph.markdown \ No newline at end of file +|^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ meta.paragraph.markdown + +```coffee +| <- text.html.markdown.litcoffee meta.code-fence.definition.begin.markdown-gfm punctuation.definition.raw.code-fence.begin.markdown +|^^^^^^^^ text.html.markdown.litcoffee meta.code-fence.definition.begin.markdown-gfm +|^^ punctuation.definition.raw.code-fence.begin.markdown +| ^^^^^^ constant.other.language-name.markdown + +| <- text.html.markdown.litcoffee meta.code-fence.body.markdown.markdown-gfm markup.raw.code-fence.coffee.markdown-gfm source.coffee +``` +| <- text.html.markdown.litcoffee meta.code-fence.definition.end.markdown-gfm punctuation.definition.raw.code-fence.end.markdown +|^^ text.html.markdown.litcoffee meta.code-fence.definition.end.markdown-gfm punctuation.definition.raw.code-fence.end.markdown From 6f8eb9d6d4e4b2086392b4615c56cf76f731724f Mon Sep 17 00:00:00 2001 From: deathaxe <deathaxe82@gmail.com> Date: Sat, 3 Jan 2026 18:11:52 +0100 Subject: [PATCH 45/52] More specific control keyword scopes --- CoffeeScript.sublime-syntax | 30 ++++++++--- tests/syntax_test_scope.coffee | 97 ++++++++++++++++++++++++---------- tests/syntax_test_scope.html | 6 +-- 3 files changed, 93 insertions(+), 40 deletions(-) diff --git a/CoffeeScript.sublime-syntax b/CoffeeScript.sublime-syntax index 5aa77f4..81285f7 100644 --- a/CoffeeScript.sublime-syntax +++ b/CoffeeScript.sublime-syntax @@ -368,9 +368,6 @@ contexts: scope: keyword.control.export.coffee - match: (?:import|from)\b scope: keyword.control.import.coffee - # excpetion - - match: (?:catch|finally|try)\b - scope: keyword.control.exception.coffee # conditional - match: if\b scope: keyword.control.conditional.if.coffee @@ -384,11 +381,16 @@ contexts: scope: keyword.control.conditional.unless.coffee - match: when\b scope: keyword.control.conditional.when.coffee + # exceptions + - match: catch\b + scope: keyword.control.exception.catch.coffee + - match: finally\b + scope: keyword.control.exception.finally.coffee + - match: try\b + scope: keyword.control.exception.try.coffee # loop - match: by\b scope: keyword.control.loop.by.coffee - - match: do\b - scope: keyword.control.loop.do.coffee - match: for(?:\s+own)?\b scope: keyword.control.loop.for.coffee push: for-args @@ -399,11 +401,23 @@ contexts: - match: while\b scope: keyword.control.loop.while.coffee # flow - - match: (?:await|break|continue|return|throw|yield(?:\s+from)?)\b - scope: keyword.control.flow.coffee + - match: await\b + scope: keyword.control.flow.await.coffee + - match: break\b + scope: keyword.control.flow.break.coffee + - match: continue\b + scope: keyword.control.flow.continue.coffee + - match: do\b + scope: keyword.control.flow.do.coffee + - match: return\b + scope: keyword.control.flow.return.coffee + - match: throw\b + scope: keyword.control.flow.throw.coffee + - match: yield(?:\s+from)?\b + scope: keyword.control.flow.yield.coffee # other - match: (?:debugger\b|\\) - scope: keyword.other.coffee + scope: keyword.control.coffee for-args: - match: in\b diff --git a/tests/syntax_test_scope.coffee b/tests/syntax_test_scope.coffee index 6ab0e89..99c77f9 100644 --- a/tests/syntax_test_scope.coffee +++ b/tests/syntax_test_scope.coffee @@ -351,41 +351,80 @@ class ###[ KEYWORDS ]################################################################ - if .if _if $if -# ^^ keyword.control.conditional.if.coffee + await .await _await $await +# ^^^^^ keyword.control.flow.await.coffee +# ^^^^^^^^^^^^^^^^^^^^^ - keyword + break .break _break $break +# ^^^^^ keyword.control.flow.break.coffee +# ^^^^^^^^^^^^^^^^^^^^^ - keyword + by .by _by $by +# ^^ keyword.control.loop.by.coffee # ^^^^^^^^^^^^ - keyword - + catch .catch _catch $catch +# ^^^^^ keyword.control.exception.catch.coffee +# ^^^^^^^^^^^^^^^^^^^^^ - keyword + continue .continue _continue $continue +# ^^^^^^^^ keyword.control.flow.continue.coffee +# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - keyword + debugger .debugger _debugger $debugger +# ^^^^^^^^ keyword.control.coffee +# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - keyword + do .do _do $do +# ^^ keyword.control.flow.do.coffee +# ^^^^^^^^^^^^ - keyword + else .else _else $else +# ^^^^ keyword.control.conditional.else.coffee +# ^^^^^^^^^^^^^^^^^^ - keyword + finally .finally _finally $finally +# ^^^^^^^ keyword.control.exception.finally.coffee +# ^^^^^^^^^^^^^^^^^^^^^^^^^^^ - keyword for .for _for $for # ^^^ keyword.control.loop.for.coffee -# ^^^^^^^^^^^^^^^^ - keyword - +# ^^^^^^^^^^^^^^^ - keyword + from .from _from $from +# ^^^^ keyword.control.import.coffee +# ^^^^^^^^^^^^^^^^^^ - keyword + if .if _if $if +# ^^ keyword.control.conditional.if.coffee +# ^^^^^^^^^^^^ - keyword + import .import _import $import +# ^^^^^^ keyword.control.import.coffee +# ^^^^^^^^^^^^^^^^^^^^^^^^ - keyword loop .loop _loop $loop # ^^^^ keyword.control.loop.loop.coffee -# ^^^^^^^^^^^^^^^^^ - keyword - - break .break _break $break -# ^^^^^ keyword.control.flow.coffee +# ^^^^^^^^^^^^^^^^^^ - keyword + return .return _return $return +# ^^^^^^ keyword.control.flow.return.coffee +# ^^^^^^^^^^^^^^^^^^^^^^^^ - keyword + switch .switch _switch $switch +# ^^^^^^ keyword.control.conditional.switch.coffee +# ^^^^^^^^^^^^^^^^^^^^^^^^ - keyword + then .then _then $then +# ^^^^ keyword.control.conditional.then.coffee +# ^^^^^^^^^^^^^^^^^^ - keyword + throw .throw _throw $throw +# ^^^^^ keyword.control.flow.throw.coffee # ^^^^^^^^^^^^^^^^^^^^^ - keyword - - continue .continue _continue $continue -# ^^^^^^^^ keyword.control.flow.coffee -# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - keyword - - yield @foo -# ^^^^^ keyword.control.flow.coffee -# ^ variable.language.this.coffee -# ^^^ variable.other.member.coffee - - yield from @foo -# ^^^^^^^^^^ keyword.control.flow.coffee -# ^ variable.language.this.coffee -# ^^^ variable.other.member.coffee - - await return @foo; -# ^^^^^ keyword.control.flow.coffee -# ^^^^^^ keyword.control.flow.coffee -# ^ variable.language.this.coffee -# ^^^ variable.other.member.coffee + try .try _try $try +# ^^^ keyword.control.exception.try.coffee +# ^^^^^^^^^^^^^^^ - keyword + unless .unless _unless $unless +# ^^^^^^ keyword.control.conditional.unless.coffee +# ^^^^^^^^^^^^^^^^^^^^^^^^ - keyword + until .until _until $until +# ^^^^^ keyword.control.loop.until.coffee +# ^^^^^^^^^^^^^^^^^^^^^ - keyword + when .when _when $when +# ^^^^ keyword.control.conditional.when.coffee +# ^^^^^^^^^^^^^^^^^^ - keyword + while .while _while $while +# ^^^^^ keyword.control.loop.while.coffee +# ^^^^^^^^^^^^^^^^^^^^^ - keyword + yield .yield _yield $yield +# ^^^^^ keyword.control.flow.yield.coffee +# ^^^^^^^^^^^^^^^^^^^^^ - keyword + yield from +# ^^^^^^^^^^ keyword.control.flow.yield.coffee ###[ OPERATORS ]############################################################### diff --git a/tests/syntax_test_scope.html b/tests/syntax_test_scope.html index fec4d33..a6230a8 100644 --- a/tests/syntax_test_scope.html +++ b/tests/syntax_test_scope.html @@ -17,7 +17,7 @@ constructor: -> return # <- source.coffee.embedded.html - # ^^^^^^ source.coffee.embedded.html keyword.control.flow.coffee + # ^^^^^^ source.coffee.embedded.html keyword.control.flow.return.coffee </script> # <- meta.tag.script.end.html punctuation.definition.tag.begin.html #^^^^^^^^ meta.tag.script.end.html @@ -43,7 +43,7 @@ constructor: -> return # <- source.coffee.embedded.html - # ^^^^^^ source.coffee.embedded.html keyword.control.flow.coffee + # ^^^^^^ source.coffee.embedded.html keyword.control.flow.return.coffee </script> # <- meta.tag.script.end.html punctuation.definition.tag.begin.html #^^^^^^^^ meta.tag.script.end.html @@ -69,7 +69,7 @@ constructor: -> return # <- source.coffee.embedded.html - # ^^^^^^ source.coffee.embedded.html keyword.control.flow.coffee + # ^^^^^^ source.coffee.embedded.html keyword.control.flow.return.coffee </script> # <- meta.tag.script.end.html punctuation.definition.tag.begin.html #^^^^^^^^ meta.tag.script.end.html From 0cca1271fd978b64de682c9cfe73764bd832bf24 Mon Sep 17 00:00:00 2001 From: deathaxe <deathaxe82@gmail.com> Date: Sat, 3 Jan 2026 18:17:37 +0100 Subject: [PATCH 46/52] Extend word boundaries Excludes `$` from word boundaries. --- CoffeeScript.sublime-settings | 2 + CoffeeScript.sublime-syntax | 125 +++++++++++++++++---------------- tests/syntax_test_scope.coffee | 84 +++++++++++----------- 3 files changed, 108 insertions(+), 103 deletions(-) diff --git a/CoffeeScript.sublime-settings b/CoffeeScript.sublime-settings index bd6df7e..1f33dab 100644 --- a/CoffeeScript.sublime-settings +++ b/CoffeeScript.sublime-settings @@ -1,4 +1,6 @@ { + "word_separators": "./\\()\"'-:,.;<>~!@#%^&*|+=[]{}`~?", + /* The directories you would like to include in $PATH environment variable. Use this if your node installation is at a seperate location and getting errors such as `cannot find node executable` diff --git a/CoffeeScript.sublime-syntax b/CoffeeScript.sublime-syntax index 81285f7..d77e38a 100644 --- a/CoffeeScript.sublime-syntax +++ b/CoffeeScript.sublime-syntax @@ -87,7 +87,7 @@ contexts: - meta_include_prototype: false - meta_scope: comment.line.shebang.coffee # Note: Keep sync with first_line_match! - - match: coffee(?:\d(?:\.\d+)?)?\b + - match: coffee(?:\d(?:\.\d+)?)?{{break}} scope: constant.language.shebang.coffee - match: $\n? pop: 1 @@ -95,14 +95,14 @@ contexts: ###[ CLASS DECLARATIONS ]##################################################### classes: - - match: class\b + - match: class{{break}} scope: meta.class.coffee keyword.declaration.class.coffee push: - class-extends - class-name class-name: - - match: (?=extends\b) + - match: (?=extends{{break}}) pop: 1 - match: ({{identifier}})(\.) captures: @@ -117,7 +117,7 @@ contexts: class-extends: - meta_content_scope: meta.class.identifier.coffee - - match: extends\b + - match: extends{{break}} scope: storage.modifier.extends.coffee set: class-extends-name - include: comments @@ -257,14 +257,14 @@ contexts: - match: |- (?x) ( decodeURI(?: Component)? | encodeURI(?: Component)? | eval - | parse(?: Float | Int ) | require )\b + | parse(?: Float | Int ) | require ){{break}} scope: support.function.coffee - match: |- (?x) (console) (?: (\.) - ( assert | debug | dir | error | info | log | time | timeEnd | warn )\b + ( assert | debug | dir | error | info | log | time | timeEnd | warn ){{break}} )? scope: meta.path.coffee captures: @@ -277,7 +277,7 @@ contexts: ( apply | call | concat | every | filter | forEach | from | hasOwnProperty | indexOf | isPrototypeOf | join | lastIndexOf | map | of | pop | propertyIsEnumerable | push | reduce(?:Right)? | reverse | shift | slice - | some | sort | splice | to(?:Locale)?String | unshift | valueOf )\b + | some | sort | splice | to(?:Locale)?String | unshift | valueOf ){{break}} scope: meta.path.coffee captures: 1: punctuation.accessor.dot.coffee @@ -287,7 +287,7 @@ contexts: (Array) (?: (\.) - ( isArray )\b + ( isArray ){{break}} )? scope: meta.path.coffee captures: @@ -301,7 +301,7 @@ contexts: (\.) ( create | definePropert(?: ies | y ) | freeze | getOwnProperty(?: Descriptors? | Names ) | getProperty(Descriptor | Names) | getPrototypeOf | is(?: Extensible | Frozen | Sealed)? - | isnt | keys | preventExtensions | seal )\b + | isnt | keys | preventExtensions | seal ){{break}} )? scope: meta.path.coffee captures: @@ -316,7 +316,7 @@ contexts: ( abs | acos | acosh | asin | asinh | atan | atan2 | atanh | ceil | cos | cosh | exp | expm1 | floor | hypot | log | log10 | log1p | log2 | max | min | pow | random | round | sign | sin | sinh | sqrt | tan | tanh - | trunc )\b + | trunc ){{break}} )? scope: meta.path.coffee captures: @@ -328,7 +328,7 @@ contexts: (Number) (?: (\.) - ( is(?: Finite | Integer | NaN ) | toInteger )\b + ( is(?: Finite | Integer | NaN ) | toInteger ){{break}} )? scope: meta.path.coffee captures: @@ -338,10 +338,10 @@ contexts: # other classes - match: |- (?x) - \b( ArrayBuffer | Blob | Boolean | Date | document | event + ( ArrayBuffer | Blob | Boolean | Date | document | event | Float(?: 32 | 64)Array | Function | Int(?: 8 | 16 | 32 | 64)Array | Map | Proxy | RegExp | Set | String | WeakMap | window - | Uint(?: 8 | 16 | 32 | 64)Array | XMLHttpRequest | Symbol )\b + | Uint(?: 8 | 16 | 32 | 64)Array | XMLHttpRequest | Symbol ){{break}} scope: support.class.coffee # user function calls - match: '{{identifier}}(?=\()' @@ -364,63 +364,63 @@ contexts: keywords: # export/import - - match: export\b + - match: export{{break}} scope: keyword.control.export.coffee - - match: (?:import|from)\b + - match: (?:import|from){{break}} scope: keyword.control.import.coffee # conditional - - match: if\b + - match: if{{break}} scope: keyword.control.conditional.if.coffee - - match: else\b + - match: else{{break}} scope: keyword.control.conditional.else.coffee - - match: switch\b + - match: switch{{break}} scope: keyword.control.conditional.switch.coffee - - match: then\b + - match: then{{break}} scope: keyword.control.conditional.then.coffee - - match: unless\b + - match: unless{{break}} scope: keyword.control.conditional.unless.coffee - - match: when\b + - match: when{{break}} scope: keyword.control.conditional.when.coffee # exceptions - - match: catch\b + - match: catch{{break}} scope: keyword.control.exception.catch.coffee - - match: finally\b + - match: finally{{break}} scope: keyword.control.exception.finally.coffee - - match: try\b + - match: try{{break}} scope: keyword.control.exception.try.coffee # loop - - match: by\b + - match: by{{break}} scope: keyword.control.loop.by.coffee - - match: for(?:\s+own)?\b + - match: for(?:\s+own)?{{break}} scope: keyword.control.loop.for.coffee push: for-args - - match: loop\b + - match: loop{{break}} scope: keyword.control.loop.loop.coffee - - match: until\b + - match: until{{break}} scope: keyword.control.loop.until.coffee - - match: while\b + - match: while{{break}} scope: keyword.control.loop.while.coffee # flow - - match: await\b + - match: await{{break}} scope: keyword.control.flow.await.coffee - - match: break\b + - match: break{{break}} scope: keyword.control.flow.break.coffee - - match: continue\b + - match: continue{{break}} scope: keyword.control.flow.continue.coffee - - match: do\b + - match: do{{break}} scope: keyword.control.flow.do.coffee - - match: return\b + - match: return{{break}} scope: keyword.control.flow.return.coffee - - match: throw\b + - match: throw{{break}} scope: keyword.control.flow.throw.coffee - - match: yield(?:\s+from)?\b + - match: yield(?:\s+from)?{{break}} scope: keyword.control.flow.yield.coffee # other - - match: (?:debugger\b|\\) + - match: (?:debugger{{break}}|\\) scope: keyword.control.coffee for-args: - - match: in\b + - match: in{{break}} scope: keyword.control.loop.in.coffee pop: 1 - match: ',' @@ -445,21 +445,21 @@ contexts: - match: \.{2,3} scope: keyword.operator.variadic.coffee # alphanumeric - - match: as\b + - match: as{{break}} scope: keyword.operator.word.coffee keyword.operator.assignment.as.coffee - - match: (?:in|is(?:nt| not)?|of)\b + - match: (?:in|is(?:nt| not)?|of){{break}} scope: keyword.operator.word.coffee keyword.operator.comparison.coffee - - match: (?:and|or|not)\b + - match: (?:and|or|not){{break}} scope: keyword.operator.word.coffee keyword.operator.logical.coffee - - match: new\b + - match: new{{break}} scope: keyword.operator.word.coffee keyword.operator.object.new.coffee push: maybe-class - - match: delete\b + - match: delete{{break}} scope: keyword.operator.word.coffee keyword.operator.object.delete.coffee - - match: instanceof\b + - match: instanceof{{break}} scope: keyword.operator.word.coffee keyword.operator.comparison.type.coffee push: maybe-class - - match: typeof\b + - match: typeof{{break}} scope: keyword.operator.word.coffee keyword.operator.object.typeof.coffee maybe-class: @@ -511,31 +511,31 @@ contexts: ###[ LITERALS ]################################################################ constants: - - match: (?:true|on|yes)(?!\s*[:=])\b + - match: (?:true|on|yes)(?!\s*[:=]){{break}} scope: constant.language.boolean.true.coffee - - match: (?:false|off|no)(?!\s*[:=])\b + - match: (?:false|off|no)(?!\s*[:=]){{break}} scope: constant.language.boolean.false.coffee - - match: Infinity(?!\s*[:=])\b + - match: Infinity(?!\s*[:=]){{break}} scope: constant.language.infinity.coffee - - match: NaN(?!\s*[:=])\b + - match: NaN(?!\s*[:=]){{break}} scope: constant.language.nan.coffee - - match: undefined(?!\s*[:=])\b + - match: undefined(?!\s*[:=]){{break}} scope: constant.language.coffee - - match: null(?!\s*[:=])\b + - match: null(?!\s*[:=]){{break}} scope: constant.language.null.coffee numbers: - - match: (0b)({{bin_digit}}+)\b + - match: (0b)({{bin_digit}}+){{break}} scope: meta.number.integer.binary.coffee captures: 1: constant.numeric.base.coffee 2: constant.numeric.value.coffee - - match: (0o)({{oct_digit}}+)\b + - match: (0o)({{oct_digit}}+){{break}} scope: meta.number.integer.octal.coffee captures: 1: constant.numeric.base.coffee 2: constant.numeric.value.coffee - - match: (0x)({{hex_digit}}+)\b + - match: (0x)({{hex_digit}}+){{break}} scope: meta.number.integer.hexadecimal.coffee captures: 1: constant.numeric.base.coffee @@ -547,12 +547,12 @@ contexts: {{dec_integer}} (?: (\.) {{dec_digit}}* (?:{{dec_exponent}})? | {{dec_exponent}} ) # .1, .1e1, .1e-1 | (\.) {{dec_digit}}+ (?:{{dec_exponent}})? - )\b + ){{break}} scope: meta.number.float.decimal.coffee constant.numeric.value.coffee captures: 1: punctuation.separator.decimal.coffee 2: punctuation.separator.decimal.coffee - - match: '{{dec_integer}}\b' + - match: '{{dec_integer}}{{break}}' scope: meta.number.integer.decimal.coffee constant.numeric.value.coffee patterns: @@ -846,14 +846,14 @@ contexts: 1: variable.language.super.coffee 2: punctuation.accessor.dot.coffee push: member - - match: super(?!\s*[:=])\b + - match: super(?!\s*[:=]){{break}} scope: variable.language.super.coffee - match: (this)(\.) captures: 1: variable.language.this.coffee 2: punctuation.accessor.dot.coffee push: member - - match: this(?!\s*[:=])\b + - match: this(?!\s*[:=]){{break}} scope: variable.language.this.coffee - match: \@ scope: variable.language.this.coffee @@ -872,14 +872,14 @@ contexts: 1: variable.language.super.coffee 2: punctuation.accessor.dot.coffee set: member - - match: super(?!\s*[:=])\b + - match: super(?!\s*[:=]){{break}} scope: variable.language.super.coffee - match: (this)(\.) captures: 1: variable.language.this.coffee 2: punctuation.accessor.dot.coffee set: member - - match: this(?!\s*[:=])\b + - match: this(?!\s*[:=]){{break}} scope: variable.language.this.coffee - match: \@ scope: variable.language.this.coffee @@ -958,6 +958,7 @@ contexts: variables: ascii_space: '\t\n\f ' + break: (?!{{ident_char}}) # A lookbehind used in embed..escape patterns, to check for unescaped characters # in embed...escape statements. @@ -970,7 +971,9 @@ variables: dec_integer: (?:0|[1-9]{{dec_digit}}*) dec_exponent: '[Ee](?:[-+]|(?![-+])){{dec_digit}}*' - identifier: '[[:alpha:]_$]\w*' + identifier: '{{ident_start}}{{ident_char}}*' + ident_start: '[[:alpha:]$_]' + ident_char: '[[:alnum:]$_]' component_names: '[A-Z][[:alnum:]_.-]*' tag_names: '[[:alpha:]][[:alnum:]_.-]*' diff --git a/tests/syntax_test_scope.coffee b/tests/syntax_test_scope.coffee index 99c77f9..dbee03c 100644 --- a/tests/syntax_test_scope.coffee +++ b/tests/syntax_test_scope.coffee @@ -351,78 +351,78 @@ class ###[ KEYWORDS ]################################################################ - await .await _await $await + await .await _await await_ $await await$ # ^^^^^ keyword.control.flow.await.coffee -# ^^^^^^^^^^^^^^^^^^^^^ - keyword - break .break _break $break +# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - keyword + break .break _break break_ $break break$ # ^^^^^ keyword.control.flow.break.coffee -# ^^^^^^^^^^^^^^^^^^^^^ - keyword +# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - keyword by .by _by $by # ^^ keyword.control.loop.by.coffee # ^^^^^^^^^^^^ - keyword - catch .catch _catch $catch + catch .catch _catch catch_ $catch catch$ # ^^^^^ keyword.control.exception.catch.coffee -# ^^^^^^^^^^^^^^^^^^^^^ - keyword - continue .continue _continue $continue +# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - keyword + continue .continue _continue continue_ $continue continue$ # ^^^^^^^^ keyword.control.flow.continue.coffee -# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - keyword - debugger .debugger _debugger $debugger +# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - keyword + debugger .debugger _debugger debugger_ $debugger debugger$ # ^^^^^^^^ keyword.control.coffee -# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - keyword +# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - keyword do .do _do $do # ^^ keyword.control.flow.do.coffee # ^^^^^^^^^^^^ - keyword - else .else _else $else + else .else _else else_ $else else$ # ^^^^ keyword.control.conditional.else.coffee -# ^^^^^^^^^^^^^^^^^^ - keyword - finally .finally _finally $finally +# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - keyword + finally .finally _finally finally_ $finally finally$ # ^^^^^^^ keyword.control.exception.finally.coffee -# ^^^^^^^^^^^^^^^^^^^^^^^^^^^ - keyword - for .for _for $for +# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - keyword + for .for _for for_ $for for$ # ^^^ keyword.control.loop.for.coffee -# ^^^^^^^^^^^^^^^ - keyword - from .from _from $from +# ^^^^^^^^^^^^^^^^^^^^^^^^^ - keyword + from .from _from from_ $from from$ # ^^^^ keyword.control.import.coffee -# ^^^^^^^^^^^^^^^^^^ - keyword +# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - keyword if .if _if $if # ^^ keyword.control.conditional.if.coffee # ^^^^^^^^^^^^ - keyword - import .import _import $import + import .import _import import_ $import import$ # ^^^^^^ keyword.control.import.coffee -# ^^^^^^^^^^^^^^^^^^^^^^^^ - keyword - loop .loop _loop $loop +# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - keyword + loop .loop _loop loop_ $loop loop$ # ^^^^ keyword.control.loop.loop.coffee -# ^^^^^^^^^^^^^^^^^^ - keyword - return .return _return $return +# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - keyword + return .return _return return_ $return return$ # ^^^^^^ keyword.control.flow.return.coffee -# ^^^^^^^^^^^^^^^^^^^^^^^^ - keyword - switch .switch _switch $switch +# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - keyword + switch .switch _switch switch_ $switch switch$ # ^^^^^^ keyword.control.conditional.switch.coffee -# ^^^^^^^^^^^^^^^^^^^^^^^^ - keyword - then .then _then $then +# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - keyword + then .then _then then_ $then then$ # ^^^^ keyword.control.conditional.then.coffee -# ^^^^^^^^^^^^^^^^^^ - keyword - throw .throw _throw $throw +# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - keyword + throw .throw _throw throw_ $throw throw$ # ^^^^^ keyword.control.flow.throw.coffee -# ^^^^^^^^^^^^^^^^^^^^^ - keyword - try .try _try $try +# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - keyword + try .try _try try_ $try try$ # ^^^ keyword.control.exception.try.coffee -# ^^^^^^^^^^^^^^^ - keyword - unless .unless _unless $unless +# ^^^^^^^^^^^^^^^^^^^^^^^^^ - keyword + unless .unless _unless unless_ $unless unless$ # ^^^^^^ keyword.control.conditional.unless.coffee -# ^^^^^^^^^^^^^^^^^^^^^^^^ - keyword - until .until _until $until +# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - keyword + until .until _until until_ $until until$ # ^^^^^ keyword.control.loop.until.coffee -# ^^^^^^^^^^^^^^^^^^^^^ - keyword - when .when _when $when +# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - keyword + when .when _when when_ $when when$ # ^^^^ keyword.control.conditional.when.coffee -# ^^^^^^^^^^^^^^^^^^ - keyword - while .while _while $while +# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - keyword + while .while _while while_ $while while$ # ^^^^^ keyword.control.loop.while.coffee -# ^^^^^^^^^^^^^^^^^^^^^ - keyword - yield .yield _yield $yield +# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - keyword + yield .yield _yield yield_ $yield yield$ # ^^^^^ keyword.control.flow.yield.coffee -# ^^^^^^^^^^^^^^^^^^^^^ - keyword +# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - keyword yield from # ^^^^^^^^^^ keyword.control.flow.yield.coffee From 783a19b957436d682df507690e4d840b7279c746 Mon Sep 17 00:00:00 2001 From: deathaxe <deathaxe82@gmail.com> Date: Sat, 3 Jan 2026 18:21:54 +0100 Subject: [PATCH 47/52] Fix shebang patterns Use same patterns for both, first_line_match and highlighting. Note: This commit does not use variables for backward compatibility reasons. --- CoffeeScript.sublime-syntax | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CoffeeScript.sublime-syntax b/CoffeeScript.sublime-syntax index d77e38a..6d86095 100644 --- a/CoffeeScript.sublime-syntax +++ b/CoffeeScript.sublime-syntax @@ -15,8 +15,8 @@ file_extensions: first_line_match: |- (?xi: - ^ \#! .* \b(coffee(-script)?)\b # shebang - | ^ \s* \# .*? -\*- .*? \bcoffee(-script)?\b .*? -\*- # editorconfig + ^ \#! .* \b(coffee(-script)?(?:\d(?:\.\d+)?)?)\b # shebang + | ^ \s* \# .*? -\*- .*? \bcoffee(-script)?(?:\d(?:\.\d+)?)?\b .*? -\*- # editorconfig ) ############################################################################## @@ -87,7 +87,7 @@ contexts: - meta_include_prototype: false - meta_scope: comment.line.shebang.coffee # Note: Keep sync with first_line_match! - - match: coffee(?:\d(?:\.\d+)?)?{{break}} + - match: coffee(-script)?(?:\d(?:\.\d+)?)?{{break}} scope: constant.language.shebang.coffee - match: $\n? pop: 1 From 95a1c63f7e131e09801c15c5b6519a34bebb88e9 Mon Sep 17 00:00:00 2001 From: deathaxe <deathaxe82@gmail.com> Date: Sat, 3 Jan 2026 18:30:02 +0100 Subject: [PATCH 48/52] CI: disable tests against latest dev builds Test runner Not available at the time of committing this. Rename workflow file according to specified paths in its content. --- .github/workflows/{syntax-tests.yml => ci-syntax-tests.yml} | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename .github/workflows/{syntax-tests.yml => ci-syntax-tests.yml} (96%) diff --git a/.github/workflows/syntax-tests.yml b/.github/workflows/ci-syntax-tests.yml similarity index 96% rename from .github/workflows/syntax-tests.yml rename to .github/workflows/ci-syntax-tests.yml index 192fa91..3e88d64 100644 --- a/.github/workflows/syntax-tests.yml +++ b/.github/workflows/ci-syntax-tests.yml @@ -40,8 +40,8 @@ jobs: - build: 4200 default_packages: v4200 # Latest dev build - - build: latest - default_packages: master + # - build: latest + # default_packages: master steps: - name: Checkout Default Packages uses: actions/checkout@v6 From 569ca397770271a431f250f0faa79ecf26271866 Mon Sep 17 00:00:00 2001 From: deathaxe <16542113+deathaxe@users.noreply.github.com> Date: Tue, 6 Jan 2026 17:23:46 +0000 Subject: [PATCH 49/52] Fix missing bailout from incomplete member terms (#262) Fixes #261 --- CoffeeScript.sublime-syntax | 1 + tests/syntax_test_scope.coffee | 58 ++++++++++++++++++++++++++++------ 2 files changed, 49 insertions(+), 10 deletions(-) diff --git a/CoffeeScript.sublime-syntax b/CoffeeScript.sublime-syntax index 6d86095..485692d 100644 --- a/CoffeeScript.sublime-syntax +++ b/CoffeeScript.sublime-syntax @@ -911,6 +911,7 @@ contexts: - member-function - member-variable pop: 1 + - include: immediately-pop member-function: - meta_include_prototype: false diff --git a/tests/syntax_test_scope.coffee b/tests/syntax_test_scope.coffee index dbee03c..255486b 100644 --- a/tests/syntax_test_scope.coffee +++ b/tests/syntax_test_scope.coffee @@ -562,10 +562,19 @@ class # ^ meta.function-call.identifier.coffee variable.function.coffee # ^^^^ meta.function-call.arguments.coffee - @name() + @name(@, obj.obj.) # ^ variable.language.this.coffee # ^^^^ meta.function-call.identifier.coffee variable.function.coffee -# ^^ meta.function-call.arguments.coffee +# ^^^^^^^^^^^^^ meta.function-call.arguments.coffee +# ^ punctuation.section.group.begin.coffee +# ^ variable.language.this.coffee +# ^ punctuation.separator.sequence.coffee +# ^^^^^^^^ meta.path.coffee +# ^^^ variable.other.object.coffee +# ^ punctuation.accessor.dot.coffee +# ^^^ variable.other.object.coffee +# ^ punctuation.accessor.dot.coffee +# ^ punctuation.section.group.end.coffee @$('#notification') # ^ variable.language.this.coffee @@ -581,19 +590,48 @@ class super.key # ^^^^^^^^^ meta.path.coffee # ^^^^^ variable.language.super.coffee -# ^ meta.path.coffee punctuation.accessor.dot.coffee -# ^^^ meta.path.coffee variable.other.member.coffee +# ^ punctuation.accessor.dot.coffee +# ^^^ variable.other.member.coffee +# ^ - meta + + this.member +# ^^^^^^^^^^^ meta.path.coffee +# ^^^^ variable.language.this.coffee +# ^ punctuation.accessor.dot.coffee +# ^^^^^^ variable.other.member.coffee +# ^ - meta - this.key -# ^^^^^^^^ meta.path.coffee + this.obj.member +# ^^^^^^^^^^^^^^^ meta.path.coffee # ^^^^ variable.language.this.coffee -# ^ meta.path.coffee punctuation.accessor.dot.coffee -# ^^^ meta.path.coffee variable.other.member.coffee +# ^ punctuation.accessor.dot.coffee +# ^^^ variable.other.object.coffee +# ^ punctuation.accessor.dot.coffee +# ^^^^^^ variable.other.member.coffee +# ^ - meta + + @member +# ^^^^^^^ meta.path.coffee +# ^ variable.language.this.coffee +# ^^^^^^ variable.other.member.coffee +# ^ - meta - @key + @obj.member # ^^^^ meta.path.coffee # ^ variable.language.this.coffee -# ^^^ variable.other.member.coffee +# ^^^ variable.other.object.coffee +# ^ punctuation.accessor.dot.coffee +# ^^^^^^ variable.other.member.coffee +# ^ - meta + + @obj.obj. +# ^^^^^^^^^ meta.path.coffee +# ^ variable.language.this.coffee +# ^^^ variable.other.object.coffee +# ^ punctuation.accessor.dot.coffee +# ^^^ variable.other.object.coffee +# ^ punctuation.accessor.dot.coffee +# ^ - meta obj.Object # ^^^^^^^^^^ meta.path.coffee From 1931434288c557dc6a32ee1cc54aab9a85a61b55 Mon Sep 17 00:00:00 2001 From: deathaxe <deathaxe82@gmail.com> Date: Sun, 1 Feb 2026 14:46:26 +0100 Subject: [PATCH 50/52] Ensure compatibility with future ST releases caused by https://github.com/sublimehq/Packages/pull/4430 --- CoffeeScript Literate.sublime-syntax | 74 ++++++++++++++++++++++------ tests/syntax_test_scope.litcoffee | 2 +- 2 files changed, 60 insertions(+), 16 deletions(-) diff --git a/CoffeeScript Literate.sublime-syntax b/CoffeeScript Literate.sublime-syntax index 45d61d1..a237c94 100644 --- a/CoffeeScript Literate.sublime-syntax +++ b/CoffeeScript Literate.sublime-syntax @@ -30,28 +30,72 @@ contexts: embed_scope: meta.embedded.litcoffee source.coffee.embedded.markdown escape: ^(?=\s*$) - fenced-syntaxes: - - meta_append: true - - include: fenced-coffee +###[ FENCED CODE BLOCKS (NEW STYLE) ]########################################### - fenced-coffee: + fenced-code-block-body: + # requires: https://github.com/sublimehq/Packages/pull/4430 + - meta_prepend: true - match: |- - (?x) - {{fenced_code_block_start}} - (?i:\s*(coffee(?:script)?|cjsx|cson|iced)) - {{fenced_code_block_trailing_infostring_characters}} + (?xi) + [ \t]* + (coffee(?:script)?|cjsx|cson|iced) + (?=[ \t\n;:]) captures: - 0: meta.code-fence.definition.begin.markdown-gfm - 2: punctuation.definition.raw.code-fence.begin.markdown - 5: constant.other.language-name.markdown - 6: comment.line.infostring.markdown - 7: meta.fold.code-fence.begin.markdown + 1: constant.other.language-name.markdown + push: + - fenced-code-block-coffee-content + - fenced-code-block-coffee-infostring + + fenced-code-block-coffee-infostring: + - meta_include_prototype: false + - meta_scope: meta.code-fence.definition.begin.markdown-gfm + - meta_content_scope: comment.line.infostring.markdown + - match: \s*$\n? + scope: meta.fold.code-fence.begin.markdown + pop: 1 + - match: \{ + scope: punctuation.definition.attributes.begin.markdown + push: fenced-code-block-coffee-infostring-attributes + + fenced-code-block-coffee-infostring-attributes: + - meta_scope: meta.attributes.markdown + - include: tag-attributes + + fenced-code-block-coffee-content: + - meta_include_prototype: false + - match: ^ embed: scope:source.coffee embed_scope: - meta.code-fence.body.markdown.markdown-gfm + meta.code-fence.body.markdown-gfm markup.raw.code-fence.coffee.markdown-gfm source.coffee - escape: '{{fenced_code_block_escape}}' + escape: (?!) + +###[ FENCED CODE BLOCKS (OLD STYLE) ]########################################### + + fenced-syntaxes: + # required until ST4202 + - meta_append: true + - match: |- + (?x) + [ \t]* # leading whitespace + ( (`)``+(?![^`]*`) | (~)~~+ ) # punctuation + ((?i:coffee(?:script)?|cjsx|cson|iced)) # language identifier + (?: (?: \s+ | (?=[:;]) ) (\S.*?) )? # remaining infostring + (\s*$\n?) # fold marker + captures: + 0: meta.code-fence.definition.begin.markdown-gfm + 1: punctuation.definition.raw.code-fence.begin.markdown + 4: constant.other.language-name.markdown + 5: comment.line.infostring.markdown + 6: meta.fold.code-fence.begin.markdown + embed: scope:source.coffee + embed_scope: markup.raw.code-fence.markdown-gfm source.coffee + escape: |- + (?x) + ^[ \t]* # leading whitespace + ( \1 (?:(?:\2)*|(?:\3)*) ) # punctuation + (\s*$\n?) # fold marker escape_captures: 0: meta.code-fence.definition.end.markdown-gfm 1: punctuation.definition.raw.code-fence.end.markdown diff --git a/tests/syntax_test_scope.litcoffee b/tests/syntax_test_scope.litcoffee index fcfe635..8369361 100644 --- a/tests/syntax_test_scope.litcoffee +++ b/tests/syntax_test_scope.litcoffee @@ -66,7 +66,7 @@ into a .litcoffee file and open it in Sublime to see the breakage. |^^ punctuation.definition.raw.code-fence.begin.markdown | ^^^^^^ constant.other.language-name.markdown -| <- text.html.markdown.litcoffee meta.code-fence.body.markdown.markdown-gfm markup.raw.code-fence.coffee.markdown-gfm source.coffee +| <- text.html.markdown.litcoffee markup.raw.code-fence source.coffee ``` | <- text.html.markdown.litcoffee meta.code-fence.definition.end.markdown-gfm punctuation.definition.raw.code-fence.end.markdown |^^ text.html.markdown.litcoffee meta.code-fence.definition.end.markdown-gfm punctuation.definition.raw.code-fence.end.markdown From 3c9dfe084871645cf0022d3b238dce3a1efe1ec3 Mon Sep 17 00:00:00 2001 From: deathaxe <16542113+deathaxe@users.noreply.github.com> Date: Fri, 6 Mar 2026 16:29:35 +0000 Subject: [PATCH 51/52] Fix of keyword in for loops (#264) --- CoffeeScript.sublime-syntax | 3 +++ tests/syntax_test_scope.coffee | 31 +++++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/CoffeeScript.sublime-syntax b/CoffeeScript.sublime-syntax index 485692d..26d50f6 100644 --- a/CoffeeScript.sublime-syntax +++ b/CoffeeScript.sublime-syntax @@ -423,6 +423,9 @@ contexts: - match: in{{break}} scope: keyword.control.loop.in.coffee pop: 1 + - match: of{{break}} + scope: keyword.control.loop.of.coffee + pop: 1 - match: ',' scope: punctuation.separator.sequence.coffee - include: variables diff --git a/tests/syntax_test_scope.coffee b/tests/syntax_test_scope.coffee index 255486b..afd7f55 100644 --- a/tests/syntax_test_scope.coffee +++ b/tests/syntax_test_scope.coffee @@ -347,6 +347,37 @@ class # ^ variable.other.readwrite.coffee # ^^ keyword.operator.word.coffee keyword.operator.comparison.coffee # ^^^^^^^^ variable.other.readwrite.coffee +# ^ punctuation.section.group.end.coffee + + for a, b of @links +# ^^^ keyword.control.loop.for.coffee +# ^ variable.other.readwrite.coffee +# ^ punctuation.separator.sequence.coffee +# ^ variable.other.readwrite.coffee +# ^^ keyword.control.loop.of.coffee +# ^ variable.language.this.coffee +# ^^^^^ variable.other.member.coffee + + @links = ($(a) for a of @$links unless a of filtered) +# ^ variable.language.this.coffee +# ^^^^^ variable.other.member.coffee +# ^ keyword.operator.assignment.coffee +# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ meta.group.coffee +# ^ punctuation.section.group.begin.coffee +# ^ meta.function-call.identifier.coffee variable.function.coffee +# ^^^ meta.function-call.arguments.coffee +# ^ punctuation.section.group.begin.coffee +# ^ variable.other.readwrite.coffee +# ^ punctuation.section.group.end.coffee +# ^^^ keyword.control.loop.for.coffee +# ^ variable.other.readwrite.coffee +# ^^ keyword.control.loop.of.coffee +# ^ variable.language.this.coffee +# ^^^^^^ variable.other.member.coffee +# ^^^^^^ keyword.control.conditional.unless.coffee +# ^ variable.other.readwrite.coffee +# ^^ keyword.operator.word.coffee keyword.operator.comparison.coffee +# ^^^^^^^^ variable.other.readwrite.coffee # ^ punctuation.section.group.end.coffee ###[ KEYWORDS ]################################################################ From fc4ead03de3adfdeb04042a1d4b2990adf54840c Mon Sep 17 00:00:00 2001 From: deathaxe <deathaxe82@gmail.com> Date: Fri, 6 Mar 2026 17:49:52 +0100 Subject: [PATCH 52/52] Modify member/function branch termination This commit attempts to avoid possible deadlocks when matching functions vs. variable assignments. Just a shot into the blue. --- CoffeeScript.sublime-syntax | 25 ++++++++++--------------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/CoffeeScript.sublime-syntax b/CoffeeScript.sublime-syntax index 26d50f6..af41781 100644 --- a/CoffeeScript.sublime-syntax +++ b/CoffeeScript.sublime-syntax @@ -196,16 +196,13 @@ contexts: function-name: - meta_include_prototype: false - - meta_scope: meta.function.identifier.coffee - match: '{{identifier}}' - scope: entity.name.function.coffee - set: + scope: meta.function.identifier.coffee entity.name.function.coffee + push: - function-body - function-parameter-list - function-assignment - # any non-whitespace char or end of file - - match: \S|\Z - fail: function + - include: immediately-pop function-assignment: - meta_include_prototype: false @@ -246,9 +243,9 @@ contexts: - meta_content_scope: meta.function.coffee - match: '[=-]>' scope: meta.function.coffee keyword.declaration.function.coffee - pop: 1 + pop: 2 # any non-whitespace char or end of file - - match: \S|\Z + - match: (?=\S|\Z) fail: function ###[ FUNCTION CALLS ]######################################################### @@ -918,22 +915,20 @@ contexts: member-function: - meta_include_prototype: false - - meta_scope: meta.function.identifier.coffee meta.path.coffee - match: '{{identifier}}' - scope: entity.name.function.coffee - set: + scope: meta.function.identifier.coffee meta.path.coffee entity.name.function.coffee + push: - member-function-body - function-parameter-list - function-assignment - - match: '' - fail: member + - include: immediately-pop member-function-body: - meta_content_scope: meta.function.coffee - match: '[=-]>' scope: meta.function.coffee keyword.declaration.function.coffee - pop: 1 - - match: (?=\S) + pop: 2 + - match: (?=\S|\Z) fail: member member-variable: