Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Indentation Fixes #75

Merged
merged 1 commit into from
Aug 18, 2018
Merged

Indentation Fixes #75

merged 1 commit into from
Aug 18, 2018

Conversation

wyuenho
Copy link
Contributor

@wyuenho wyuenho commented May 16, 2018

# Merge after #72

## Diff

Fixes:

Similar to js-jsx-indent-line, but fixes indentation bug with JSXElement after a JSX expression and arrow function. In addition, the > of a multi-line open tag and the /> of a multi-line self-closing tag are aligned to the beginning <. This function also ensures everything inside a JSX context is indented according to js-indent-level using spaces, this is due to the limitation of sgml-indent-line not being able to indent with tabs.

As an added bonus, this also fixes the rjsx-indentation-1 test for Emacs < 26.

@floscr
Copy link

floscr commented Jun 26, 2018

Just wanted to say that this PR works wonderful 👍
Thanks for the work!

@bryzettler
Copy link

bryzettler commented Aug 15, 2018

This beast ready to be merged now that #72 is?
And maybe get a release pushed up to melpa?

@felipeochoa
Copy link
Owner

Yup -- was planning to rebase later today to give it a final look and merge

@wyuenho
Copy link
Contributor Author

wyuenho commented Aug 16, 2018

I've rebased the branch now.

There's some problems with this, will deal with it later tonight

@felipeochoa
Copy link
Owner

Ok, thanks! Let me know when it's ready. I have a bit more time now for open source :)

@wyuenho
Copy link
Contributor Author

wyuenho commented Aug 17, 2018

@felipeochoa I've backed out the docs you've put in this branch, they still exist on your fork in a branch, you can reapply them after this PR is merged. This PR should be good to go.

@felipeochoa
Copy link
Owner

Ok, I think I understand how this is working, and I like it! I have a couple of nits, and a couple of substantive questions. Happy to handle the nits myself if you're busy, and also happy to leave the substantive Qs as bugs/todo, but want to understand nonetheless

@felipeochoa
Copy link
Owner

One other high-level question: does this code handle the case of a top-level tag with its > or /> on a new line? I only ask because it looks like the main cond in rjsx-indent-line exits early for top-level tags.

Thanks again for pulling this together!!

@wyuenho
Copy link
Contributor Author

wyuenho commented Aug 17, 2018

Happy to handle the nits myself if you're busy, and also happy to leave the substantive Qs as bugs/todo, but want to understand nonetheless

Sounds good, but have you reviewed the PR but forgot to submit the review again?

does this code handle the case of a top-level tag with its > or /> on a new line?

No. Top-level JSX is a noop, there's no point dealing with this edge case. The same approach is used for commenting as well.


(defvar-local rjsx--indent-line-columns nil
"A list of indent columns on a per-line basis. The car is the
indent column of the last line, cadr the indent column of the
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nit] Docstring spacing is off here and a couple of other places

limitation of `sgml-indent-line' not being able to indent with
tabs.

Fixes:
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hopefully these will be fixed upstream at some point (!). Maybe better to leave this as a comment than in the docstring?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nah. The only way to fix this upstream satisfactorily is to merge js2-mode and rjsx-mode. Text-based heuristics is a thin PVC pipe under 10 tons of pressure, it doesn't matter how to you plug it, it'll leak.

(let* ((sgml-basic-offset js-indent-level) ;; `sgml-indent-line' can only indent by spaces
(cur-pos (save-excursion (back-to-indentation) (point)))
(cur-char (char-after cur-pos))
(node (js2-node-at-point (- cur-pos rjsx--indent-running-offset))))
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm trying to understand this rjsx--indent-running-offset bit. Is the idea to simply convert between "current" buffer coordinates and "as of last parse" buffer coordinate? If so, a defsubst for this would be great. There's a comment further down where I think we need this but don't have it.

Copy link
Contributor Author

@wyuenho wyuenho Aug 17, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's an optimization to prevent having to reparse to get the node of the beginning of the line after indenting a line.

After indenting a line, the "offset" is the difference of the position of the beginning of the line between "before" and "after" indentation. If you are indenting a region, the offsets accumulate in a list of +4 -5 +2..... etc on a line by line basis. Adding them all together and subtracting the current position will get you back the position of the node you need to process, without reparsing the whole file.

rjsx-mode.el Outdated
(cond
;; Top-level tag
((not (rjsx-node-p (js2-node-parent node)))
(cl-letf (((symbol-function #'js--continued-expression-p) 'ignore))
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nit] I get why you're doing this, but cl-letfing a private function is usually worth a quick explanatory comment. E.g.,

;; Prevent `js-indent-line' from treating the leading `<' as a continued expression

rjsx-mode.el Outdated
(rjsx-indent-with-spaces cur-pos (js-indent-line))))
;; Align the /> of a multi-line self-closing tag
((and (not (rjsx-node-closing-tag node))
(save-excursion (goto-char cur-pos) (looking-at "/[[:space:]]*>")))
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nit] Use `looking-at-p' here.

((and (rjsx-node-p (js2-node-parent node))
(char-equal ?> cur-char))
(rjsx-indent-with-spaces cur-pos (js-indent-line)))
;; Get around `js-indent-line' bug with not aligning JSXElement sibling
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nit] Is there a bug number to go with this workaround? One of the ones above?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This just explains the root cause for one of those in the docstring, I don't remember which one tho.

;; Get around `js-indent-line' bug with not aligning JSXElement sibling
;; after a JSX expression.
((rjsx-wrapped-expr-p
(let* ((parent (js2-node-parent node))
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nit] I suspect this could be simplified to avoid looping twice, but for now can you just factor it out into a helper function? It's a bit dense, and I think putting a name + docstring would make it much easier to understand what's going on. (I think it's just looking for the preceding non-blank child?)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suspect this could be simplified to avoid looping twice

O(2 * 1) = O(1 * 2), meh

I think it's just looking for the preceding non-blank child?

That's right. I'd prefer not to make a function that's just used once. I'll put a comment here.

rjsx-mode.el Outdated
;; after a JSX expression.
((rjsx-wrapped-expr-p
(let* ((parent (js2-node-parent node))
(children (cl-loop for kid in (cl-struct-slot-value 'rjsx-node 'kids parent)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nit] isn't there an accessor we could use here?

rjsx-mode.el Outdated
(rjsx-indent-with-spaces cur-pos (js--as-sgml (sgml-indent-line))))
;; Inside JSX expression or looking at the end of it
((rjsx-ancestor node #'rjsx-wrapped-expr-p)
(if (cl-loop for c being the elements of "[])},;" thereis (char-equal c cur-char))
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nit] (memq cur-char '(?\[ ?\] ?\) ?\} ?, ?\;)) is easier to read (for me) and much faster

rjsx-mode.el Outdated
(progn
(setq rjsx--indent-region-p t)
(js2-indent-region start end))
(setq rjsx--indent-running-offset 0
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nit] Are these used anywhere else? If not, why not just let-bind them around the js2-indent-region' call and get rid of the unwind-protect' form?

@felipeochoa
Copy link
Owner

Oops. Submitted now. Makes sense re: top-level. I think I actually understand this one 😄

@wyuenho
Copy link
Contributor Author

wyuenho commented Aug 18, 2018

I've left the docstring alone but cleaned up the rest. Thanks for reviewing!

@felipeochoa felipeochoa merged commit bc62efe into felipeochoa:master Aug 18, 2018
@felipeochoa
Copy link
Owner

@wyuenho Thanks again for all your work on rjsx!!

@wyuenho wyuenho deleted the indent-after-jsx-expr branch August 18, 2018 17:28
felipeochoa added a commit that referenced this pull request Aug 21, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants