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."