Skip to content

Commit 4556045

Browse files
committed
Initial support for JSX tags
Resolves #148 Fixes #218
1 parent 52392b3 commit 4556045

File tree

3 files changed

+252
-0
lines changed

3 files changed

+252
-0
lines changed

CoffeeScript.sublime-syntax

Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ contexts:
3232
- include: classes
3333
- include: keywords
3434
- include: functions
35+
- include: jsx-tags
3536
- include: expressions
3637

3738
expressions:
@@ -573,6 +574,171 @@ contexts:
573574
pop: 1
574575
- include: script
575576

577+
###[ JSX TAGS ]################################################################
578+
579+
jsx-tags:
580+
- match: (<)(?:({{component_names}})|({{tag_names}}))(?=\s|/?>)
581+
captures:
582+
1: punctuation.definition.tag.begin.coffee
583+
2: entity.name.tag.component.coffee
584+
3: entity.name.tag.coffee
585+
push:
586+
- jsx-meta
587+
- jsx-tag-open-body
588+
589+
jsx-meta:
590+
- meta_include_prototype: false
591+
- meta_scope: meta.jsx.coffee
592+
- include: immediately-pop
593+
594+
jsx-body:
595+
- match: (</)(?:({{component_names}})|({{tag_names}}))(?=\s|/?>)
596+
captures:
597+
1: punctuation.definition.tag.begin.coffee
598+
2: entity.name.tag.component.coffee
599+
3: entity.name.tag.coffee
600+
set: jsx-tag-close-body
601+
- match: (<)(?:({{component_names}})|({{tag_names}}))(?=\s|/?>)
602+
captures:
603+
1: punctuation.definition.tag.begin.coffee
604+
2: entity.name.tag.component.coffee
605+
3: entity.name.tag.coffee
606+
push: jsx-tag-open-body
607+
- include: jsx-text-interpolations
608+
609+
jsx-tag-close-body:
610+
- meta_include_prototype: false
611+
- meta_scope: meta.tag.coffee
612+
- match: /?>
613+
scope: punctuation.definition.tag.end.coffee
614+
pop: 1
615+
616+
jsx-tag-open-body:
617+
- meta_include_prototype: false
618+
- meta_scope: meta.tag.coffee
619+
- match: />
620+
scope: punctuation.definition.tag.end.coffee
621+
pop: 1
622+
- match: \>
623+
scope: punctuation.definition.tag.end.coffee
624+
set: jsx-body
625+
- match: '{{attribute_name_start}}'
626+
push:
627+
- jsx-tag-attribute-meta
628+
- jsx-tag-attribute-assignment
629+
- jsx-tag-attribute-name
630+
631+
jsx-tag-attribute-name:
632+
- meta_scope: entity.other.attribute-name.coffee
633+
- include: jsx-string-interpolations
634+
- match: '{{attribute_name_break}}'
635+
pop: 1
636+
- match: '["''`<]'
637+
scope: invalid.illegal.attribute-name.coffee
638+
639+
jsx-tag-attribute-meta:
640+
- meta_include_prototype: false
641+
- meta_scope: meta.attribute-with-value.coffee
642+
- include: immediately-pop
643+
644+
jsx-tag-attribute-assignment:
645+
- meta_include_prototype: false
646+
- match: =
647+
scope: punctuation.separator.key-value.coffee
648+
set: jsx-tag-attribute-value
649+
- include: else-pop
650+
651+
jsx-tag-attribute-value:
652+
- meta_include_prototype: false
653+
- match: \"
654+
scope: punctuation.definition.string.begin.coffee
655+
set: jsx-tag-attribute-value-double-quoted-content
656+
- match: \'
657+
scope: punctuation.definition.string.begin.coffee
658+
set: jsx-tag-attribute-value-single-quoted-content
659+
- match: '{{unquoted_attribute_start}}'
660+
set: jsx-tag-attribute-value-unquoted-content
661+
- include: else-pop
662+
663+
jsx-tag-attribute-value-double-quoted-content:
664+
# See the top of this file for why prototype is excluded
665+
- meta_include_prototype: false
666+
- meta_scope: meta.string.coffee string.quoted.double.coffee
667+
- match: \"
668+
scope: punctuation.definition.string.end.coffee
669+
pop: 1
670+
- include: jsx-string-interpolations
671+
672+
jsx-tag-attribute-value-single-quoted-content:
673+
# See the top of this file for why prototype is excluded
674+
- meta_include_prototype: false
675+
- meta_scope: meta.string.coffee string.quoted.single.coffee
676+
- match: \'
677+
scope: punctuation.definition.string.end.coffee
678+
pop: 1
679+
- include: jsx-string-interpolations
680+
681+
jsx-tag-attribute-value-unquoted-content:
682+
# See the top of this file for why prototype is excluded
683+
- meta_include_prototype: false
684+
- meta_content_scope: meta.string.coffee string.unquoted.coffee
685+
- include: jsx-string-interpolations
686+
- match: '{{unquoted_attribute_break}}'
687+
pop: 1
688+
- match: '["''`<]'
689+
scope: invalid.illegal.attribute-value.coffee
690+
691+
jsx-string-interpolations:
692+
- match: \{#
693+
scope: punctuation.definition.comment.begin.coffee
694+
push: jsx-string-comment-body
695+
- match: \{
696+
scope: punctuation.section.interpolation.begin.coffee
697+
push: jsx-string-interpolation-body
698+
699+
jsx-string-comment-body:
700+
# required to support "toggle_comment"
701+
- clear_scopes: 1
702+
- meta_scope: meta.interpolation.coffee comment.block.coffee
703+
- include: jsx-text-comment-body
704+
705+
jsx-string-interpolation-body:
706+
- clear_scopes: 1
707+
- meta_scope: meta.interpolation.coffee
708+
- meta_content_scope: source.coffee.embedded.jsx
709+
- include: jsx-text-interpolation-body
710+
711+
jsx-text-interpolations:
712+
- match: \{#
713+
scope: punctuation.definition.comment.begin.coffee
714+
push: jsx-text-comment-body
715+
- match: \{
716+
scope: punctuation.section.interpolation.begin.coffee
717+
push: jsx-text-interpolation-body
718+
719+
jsx-text-comment-body:
720+
# required to support "toggle_comment"
721+
- meta_scope: meta.interpolation.coffee comment.block.coffee
722+
- match: \}
723+
scope: punctuation.definition.comment.end.coffee
724+
pop: 1
725+
726+
jsx-text-interpolation-body:
727+
- meta_scope: meta.interpolation.coffee
728+
- meta_content_scope: source.coffee.embedded.jsx
729+
- match: \}
730+
scope: punctuation.section.interpolation.end.coffee
731+
pop: 1
732+
- match: \#
733+
scope: punctuation.definition.comment.coffee
734+
push: jsx-line-comment-body
735+
- include: script
736+
737+
jsx-line-comment-body:
738+
- meta_scope: comment.line.number-sign.coffee
739+
- match: $\n?|(?=})
740+
pop: 1
741+
576742
###[ VARIABLES ]###############################################################
577743

578744
instance-variables:
@@ -596,4 +762,18 @@ contexts:
596762

597763
variables:
598764

765+
ascii_space: '\t\n\f '
766+
599767
identifier: '[a-zA-Z\$_]\w*'
768+
769+
component_names: '[A-Z][[:alnum:]_.-]*'
770+
tag_names: '[[:alpha:]][[:alnum:]_.-]*'
771+
772+
# https://html.spec.whatwg.org/multipage/syntax.coffee#attributes-2
773+
attribute_name_break_char: '[{{ascii_space}}=/>]'
774+
attribute_name_break: (?={{attribute_name_break_char}})
775+
attribute_name_start: (?=[^{{attribute_name_break_char}}])
776+
777+
# https://html.spec.whatwg.org/multipage/syntax.coffee#syntax-attribute-value
778+
unquoted_attribute_break: (?=[{{ascii_space}}]|/?>)
779+
unquoted_attribute_start: (?=[^{{ascii_space}}=>])

Comments JSX.tmPreferences

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3+
<plist version="1.0">
4+
<dict>
5+
<key>scope</key>
6+
<string>source.coffee meta.jsx, source.coffee meta.jsx meta.interpolation comment.line</string>
7+
<key>settings</key>
8+
<dict>
9+
<key>shellVariables</key>
10+
<array>
11+
<dict>
12+
<key>name</key>
13+
<string>TM_COMMENT_START</string>
14+
<key>value</key>
15+
<string>{# </string>
16+
</dict>
17+
<dict>
18+
<key>name</key>
19+
<string>TM_COMMENT_END</string>
20+
<key>value</key>
21+
<string> }</string>
22+
</dict>
23+
</array>
24+
</dict>
25+
</dict>
26+
</plist>

tests/syntax_test_scope.coffee

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -561,3 +561,49 @@ class App.Router extends Snakeskin.Router
561561
@variable
562562
# ^^^^^^^^^ variable.other.readwrite.instance.coffee
563563
# ^ punctuation.definition.variable.coffee
564+
565+
###[ JSX ]#####################################################################
566+
567+
<Component attrib="va{@lue}">
568+
# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ meta.jsx.coffee meta.tag.coffee
569+
# ^ punctuation.definition.tag.begin.coffee
570+
# ^^^^^^^^^ entity.name.tag.component.coffee
571+
# ^^^^^^^^^^^^^^^^^ meta.attribute-with-value.coffee
572+
# ^^^^^^ entity.other.attribute-name.coffee
573+
# ^ punctuation.separator.key-value.coffee
574+
# ^^^ meta.string.coffee string.quoted.double.coffee
575+
# ^^^^^^ meta.string.coffee meta.interpolation.coffee
576+
# ^ punctuation.section.interpolation.begin.coffee
577+
# ^^^^ source.coffee.embedded.jsx variable.other.readwrite.instance.coffee
578+
# ^ punctuation.section.interpolation.end.coffee
579+
# ^ meta.string.coffee string.quoted.double.coffee punctuation.definition.string.end.coffee
580+
# ^ punctuation.definition.tag.end.coffee
581+
<h1>Text {# comment}!</h1>
582+
# ^^^^ meta.jsx.coffee meta.tag.coffee
583+
# ^ punctuation.definition.tag.begin.coffee
584+
# ^^ entity.name.tag.coffee
585+
# ^ punctuation.definition.tag.end.coffee
586+
# ^^^^^ meta.jsx.coffee - meta.interpolation
587+
# ^^^^^^^^^^^ meta.jsx.coffee meta.interpolation.coffee comment.block.coffee
588+
# ^^ punctuation.definition.comment.begin.coffee
589+
# ^ punctuation.definition.comment.end.coffee
590+
# ^ meta.jsx.coffee - meta.interpolation
591+
# ^^^^^ meta.jsx.coffee meta.tag.coffee
592+
# ^^ punctuation.definition.tag.begin.coffee
593+
# ^^ entity.name.tag.coffee
594+
# ^ punctuation.definition.tag.end.coffee
595+
</Component>
596+
# ^^^^^^^^^^^^ meta.jsx.coffee meta.tag.coffee
597+
# ^^ punctuation.definition.tag.begin.coffee
598+
# ^^^^^^^^^ entity.name.tag.component.coffee
599+
# ^ punctuation.definition.tag.end.coffee
600+
# ^ - meta.jsx - meta.tag
601+
602+
<EmailInput
603+
{# THIS IS A GOOD COMMENT }
604+
# ^^^^^^^^^^^^^^^^^^^^^^^^^^^ meta.jsx.coffee meta.tag.coffee meta.attribute-with-value.coffee meta.interpolation.coffee comment.block.coffee
605+
{...@props}
606+
# ^^^^^^^^^^^ meta.jsx.coffee meta.tag.coffee meta.attribute-with-value.coffee meta.interpolation.coffee
607+
/>
608+
# ^^ meta.jsx.coffee meta.tag.coffee punctuation.definition.tag.end.coffee
609+
# ^ - meta.jsx - meta.tag

0 commit comments

Comments
 (0)