emmet-react-mode is my first attempt at creating a minor mode in Emacs. It is designed as an extension for emmet-mode though it doesn't rely on it. I find myself frequently writing out components for Svelte and React that have several props and I wanted a convenient way to quickly write them in Emacs.
The minor mode is centered around three interactive functions. The first, emmet-react-expand-component
expands a shorthand version of a of a component with props that are separated by periods. If you write the shorthand starting with the component name and then separate the props with periods in the follow form Foo.bar.baz
, it will expand to <Foo bar='' baz='' />
and move the cursor between the first single quotes. If you want to use {}
or ""
, you can set emmet-react-prop-pair
to any pair of characters you want to wrap with.
(defun emmet-react-expand-component () (interactive) (message (string (region-end))) (let ((component) (propList)) (setq propList (emmet-react-get-component)) (setq component (pop propList)) (emmet-react-insert-component propList component)))
emmet-react-wrap-component
is useful for situations where you want to wrap Svelte or JSX in a component. You will be prompted to enter the component in the echo area. If you want to add props to the component, you can separate them with periods and they will be automatically separated. For example Foo.bar.baz
will be expanded to <Foo bar='' baz''></Foo>
and the cursor will be moved between the quotes after baz
.
(defun emmet-react-wrap-component () (interactive) (let ((str (read-string "Enter component: ")) (propList) (body (buffer-substring (region-end) (region-beginning))) (component) (propPos)) (setq propList (split-string str "\\.")) (setq component (pop propList)) (delete-region (region-end) (region-beginning)) (insert (format "<%s" component)) (js-indent-line) (while propList (insert (format " %s=%s" (pop propList) emmet-react-prop-pair)) (if (not propPos) (setq propPos (- (point) 1)))) (insert ">\n") (insert body) (insert (format "</%s>\n" component)) (if emmet-react-auto-indent (web-mode-buffer-indent)) (cond ((eq evil-state 'visual) (evil-insert-state))) (if propPos (goto-char propPos))))
If you want to generate a wrapping style JSX component with props you can use emmet-react-expand-wrap-component
. This function is not used to wrap highlighted text, it simply generates the component but with an opening and closing tag. For example Foo.bar.baz
will expand to <Foo bar='' baz=''></Foo>
.
(defun emmet-react-expand-wrap-component () (interactive) (let ((component) (propList)) (setq propList (emmet-react-get-component)) (setq component (pop propList)) (emmet-react-insert-wrap-component propList component)))
There are a few things I would like to add to this project over time. I would like the functions to be smart enough to recognize when the user has set the prop to a specific value. I think the easiest way to do this will be to continue to split the strings on the periods, but to later also split them by = and wrap what comes after the equals between the selected characters. I think it would also be nice to have a variable to determine where the cursor should be moved after expanding the component.
Messing around with computers and coding since I was 8. Now getting paid to do what I love.