diff --git a/README.md b/README.md
index ec5a11e..baa0947 100644
--- a/README.md
+++ b/README.md
@@ -74,8 +74,12 @@ If you want to customize Self-closing tags style:
### 4. JSX Support
If current major-mode is in `emmet-jsx-major-modes`, then JSX features will be supported:
+
- Expand `.class` to `className="..."` instead of `class="..."`
- Expand value of properties as variables: `div[value={v}]` -> `
`
+- Expand variables inside text: `div{{v}}` -> `{v}
`
+**Note**: '}' can be escaped using backslash, i.e. `div{{v\}}}` -> `{v}}
`.
+Please make sure your curly braces(not counting escaped ones) are always balanced.
To enable the JSX support, add your major-mode to `emmet-jsx-major-modes`:
diff --git a/emmet-mode.el b/emmet-mode.el
index 7af5741..b79dc94 100644
--- a/emmet-mode.el
+++ b/emmet-mode.el
@@ -3101,6 +3101,31 @@ tbl))
it))
(emmet-parse "\\([^\\|]+\\)" 1 "filter name" `(,(elt it 1)))))
+(defun emmet-extract-inner-text (input)
+ "Extract inner-text in the form of {inner_text}...
+Return `(,inner-text ,input-without-inner-text) if succeeds, otherwise return
+`(error ,error-message)"
+ (cl-labels (
+ (string-find-paired-right-curly-brace
+ (str)
+ (cl-labels ((char-at-pos (str pos) (string-to-char (substring str pos)))
+ (helper (str pos cnt)
+ (let ((len (length str)))
+ (if (>= pos len) nil
+ (let ((c (char-at-pos str pos)))
+ (cond ((char-equal c ?{) (setq cnt (+ cnt 1)))
+ ((and (char-equal c ?}) (not (char-equal (char-at-pos str (- pos 1)) ?\\))) (setq cnt (- cnt 1))))
+ (if (= cnt 0) pos (helper str (+ pos 1) cnt)))))
+ ))
+ (helper str 0 0))))
+ (let ((err '(error "expected inner text")))
+ (if (or (< (length input) 2) (not (char-equal (string-to-char input) ?{))) err
+ (emmet-aif (string-find-paired-right-curly-brace input)
+ `(,input
+ ,(substring input 1 it)
+ ,(substring input (+ it 1)))
+ err)))))
+
(defun emmet-filter (input filters)
"Construct AST with specified filters."
(emmet-pif (emmet-subexpr input)
@@ -3361,12 +3386,23 @@ tbl))
(defun emmet-text (input)
"A zen coding expression innertext."
- (emmet-parse "{\\(\\(?:\\\\.\\|[^\\\\}]\\)*?\\)}" 2 "inner text"
- (let ((txt (emmet-split-numbering-expressions (elt it 1))))
- (if (listp txt)
- (setq txt (cons (car txt) (cons (replace-regexp-in-string "\\\\\\(.\\)" "\\1" (cadr txt)) (cddr txt))))
- (setq txt (replace-regexp-in-string "\\\\\\(.\\)" "\\1" txt)))
- `((text ,txt) . ,input))))
+ (emmet-pif (emmet-extract-inner-text input)
+ (let ((txt (emmet-split-numbering-expressions (elt it 1))))
+ (if (listp txt)
+ (setq txt (cons (car txt) (cons (replace-regexp-in-string "\\\\\\(.\\)" "\\1" (cadr txt)) (cddr txt))))
+ (setq txt (replace-regexp-in-string "\\\\\\(.\\)" "\\1" txt)))
+ `((text ,txt) . ,(elt it 2)))
+ '(error "expected inner text")))
+
+;; (defun emmet-text (input)
+;; "A zen coding expression innertext."
+;; (emmet-parse "{\\(\\(?:\\\\.\\|[^\\\\}]\\|\\\\}\\)*?\\)}" 2 "inner text"
+;; (let ((txt (emmet-split-numbering-expressions (elt it 1))))
+;; (if (listp txt)
+;; (setq txt (cons (car txt) (cons (replace-regexp-in-string "\\\\\\(.\\)" "\\1" (cadr txt)) (cddr txt))))
+;; (setq txt (replace-regexp-in-string "\\\\\\(.\\)" "\\1" txt)))
+;; `((text ,txt) . ,input))
+;; '(error "expected inner text")))
(defun emmet-properties (input)
"A bracketed emmet property expression."
diff --git a/src/html-abbrev.el b/src/html-abbrev.el
index 2cd2835..b5ee731 100644
--- a/src/html-abbrev.el
+++ b/src/html-abbrev.el
@@ -41,6 +41,31 @@
it))
(emmet-parse "\\([^\\|]+\\)" 1 "filter name" `(,(elt it 1)))))
+(defun emmet-extract-inner-text (input)
+ "Extract inner-text in the form of {inner_text}...
+Return `(,inner-text ,input-without-inner-text) if succeeds, otherwise return
+`(error ,error-message)"
+ (cl-labels (
+ (string-find-paired-right-curly-brace
+ (str)
+ (cl-labels ((char-at-pos (str pos) (string-to-char (substring str pos)))
+ (helper (str pos cnt)
+ (let ((len (length str)))
+ (if (>= pos len) nil
+ (let ((c (char-at-pos str pos)))
+ (cond ((char-equal c ?{) (setq cnt (+ cnt 1)))
+ ((and (char-equal c ?}) (not (char-equal (char-at-pos str (- pos 1)) ?\\))) (setq cnt (- cnt 1))))
+ (if (= cnt 0) pos (helper str (+ pos 1) cnt)))))
+ ))
+ (helper str 0 0))))
+ (let ((err '(error "expected inner text")))
+ (if (or (< (length input) 2) (not (char-equal (string-to-char input) ?{))) err
+ (emmet-aif (string-find-paired-right-curly-brace input)
+ `(,input
+ ,(substring input 1 it)
+ ,(substring input (+ it 1)))
+ err)))))
+
(defun emmet-filter (input filters)
"Construct AST with specified filters."
(emmet-pif (emmet-subexpr input)
@@ -299,12 +324,23 @@
(defun emmet-text (input)
"A zen coding expression innertext."
- (emmet-parse "{\\(\\(?:\\\\.\\|[^\\\\}]\\)*?\\)}" 2 "inner text"
- (let ((txt (emmet-split-numbering-expressions (elt it 1))))
- (if (listp txt)
- (setq txt (cons (car txt) (cons (replace-regexp-in-string "\\\\\\(.\\)" "\\1" (cadr txt)) (cddr txt))))
- (setq txt (replace-regexp-in-string "\\\\\\(.\\)" "\\1" txt)))
- `((text ,txt) . ,input))))
+ (emmet-pif (emmet-extract-inner-text input)
+ (let ((txt (emmet-split-numbering-expressions (elt it 1))))
+ (if (listp txt)
+ (setq txt (cons (car txt) (cons (replace-regexp-in-string "\\\\\\(.\\)" "\\1" (cadr txt)) (cddr txt))))
+ (setq txt (replace-regexp-in-string "\\\\\\(.\\)" "\\1" txt)))
+ `((text ,txt) . ,(elt it 2)))
+ '(error "expected inner text")))
+
+;; (defun emmet-text (input)
+;; "A zen coding expression innertext."
+;; (emmet-parse "{\\(\\(?:\\\\.\\|[^\\\\}]\\|\\\\}\\)*?\\)}" 2 "inner text"
+;; (let ((txt (emmet-split-numbering-expressions (elt it 1))))
+;; (if (listp txt)
+;; (setq txt (cons (car txt) (cons (replace-regexp-in-string "\\\\\\(.\\)" "\\1" (cadr txt)) (cddr txt))))
+;; (setq txt (replace-regexp-in-string "\\\\\\(.\\)" "\\1" txt)))
+;; `((text ,txt) . ,input))
+;; '(error "expected inner text")))
(defun emmet-properties (input)
"A bracketed emmet property expression."