php-mode
php-mode copied to clipboard
Undesired "escape" (backslash) with <M-j> (meta-j) key after "<?php" tag?
In my emacs init.el, I've configured <RET> (return) to invoke <M-j> (alt+j or option+j on Mac).
I have demonstrated this bug in a YouTube video, but I encourage reading on prior to watching.
In php-mode,
Upon hitting <M-j> Emacs performs a function call comment-indent-new-line
, which normally continues comments or indents, and results in an undesired backslash "escape" after the opening <?php
tag.
It looks like this (php file):
<?php|<M-j> \
\
/**
* comment
*/
$variable = 1;
|<M-j> above represents a <M-j> keypress (alt+j or option+j on Mac), which normally invokes the emacs function comment-indent-new-line
Maybe php-mode is processing the <?php
tag as a string that needs escaping? Is this a desired effect?
Is this a desired effect?
TL;DR Yes, but that doesn't mean we shouldn't consider changing it.
Indentation in PHP Mode is built atop CC Mode, which determines what to indent and how based on CC Mode's "syntactic analysis". In PHP Mode we use c-lang-defconst
to make CC Mode's indentation engine aware of various PHP constructs. That is, we assign various PHP constructs to different types of "contexts".
What is happening is that PHP Mode thinks <?php
is a preprocessor macro. You can see this in Emacs by typing <?php
and pressing C-c C-s
. You'll see cpp-macro
is part of the syntactic analysis for that line. The default behavior of CC Mode is to backslash-escape preprocessor macros when you use M-j
, so that's why the slashes appear.
Commit ec4c1669c01d20e30c21096f6710b94b7f62b513 explains why we treat <?php
like a macro definition. Basically it's a work-around to a larger problem. Semantically we could argue that the behavior is correct on the grounds that <?php
is a "processing instruction". But XML doesn't require escaping for processing instructions. And more importantly, on a practical level there is no benefit I can think of for PHP Mode to treat <?php
as a preprocessor macro except for the fact that it corrects the treatment of indentation of everything that comes after <?php
.
So is it desired behavior? Yes, but only because it's the solution to the larger problem described in the commit above. However, that doesn't mean we shouldn't revisit the issue and try to come up with a different solution that does not involve treating <?php
like a C/C++ preprocessor macro.
Thank you for the bug report---I'm considering it a bug since backslash-escaping after <?php
has no practical purpose that I know of.
On an unrelated note, I saw in your video (nice by the way) you twice used M-x eval-expression
. You can simply press M-:
for that, which is bound to eval-expression
by default.
I have been experiencing this for the past year and a half too. Would be nice to see this fixed :+1:
@trashofmasters
Could you maybe post some of your Emacs init.el (php only) configuration. I would like to view it and see how you are currently working around the issue? This is related to the comment you posted on YouTube.
Sadly I wasn't able to fix this problem yet, I practically don't know anything about cc-mode. The snippet of elisp I posted on YouTube was more related to something you mentioned in the video than it does to this issue.
Therefore I try to avoid using M-j as much as possible outside of comment blocks.
@kr1zmo Here's a temporary work around which you can use in your init file. If @ejmr thinks this is a viable solution to #281 and #283 then I'm happy to document, polish, and test the code and raise a PR.
I made a few quick tests and it fixes both issues, although I'm still looking for a solution that doesn't require advising c-append-backslashes-forward
.
(defvar php-open-tag-regexp "<[?]\\(php\\|=\\)?")
(defun php-advice-c-end-of-macro (original-fcn &rest args)
"TODOcument"
(cond
((looking-at php-open-tag-regexp)
(skip-chars-forward "<?php=")
(point))
((looking-back php-open-tag-regexp)
;; Because the regex matches both <? and <?php
;; we must distinguish when we are between <? and php.
(if (looking-at "php")
(+ 3 (point))
(point)))))
(defun php-advice-c-beginning-of-macro (original-fcn &rest args)
"TODOcument"
(cond
((looking-at php-open-tag-regexp)
(not (null (point))))
((looking-back php-open-tag-regexp)
(skip-chars-backward "<?php=")
(not (null (point))))))
(defun php-advice-c-append-backslashes-forward (original-fcn &rest args)
"TODOcument"
(unless (or (looking-at php-open-tag-regexp)
(looking-back php-open-tag-regexp))
(apply original-fcn args)))
(advice-add 'c-end-of-macro :around #'php-advice-c-end-of-macro)
(advice-add 'c-beginning-of-macro :around #'php-advice-c-beginning-of-macro)
(advice-add 'c-append-backslashes-forward :around #'php-advice-c-append-backslashes-forward)
While I write and understand several languages, Lisp (emacs) isn't really one of them-which makes me sad. However, I can follow the code and understand the logic to some degree. You are awesome my friend @trashofmasters!
I hope this somehow gets polished up and pulled into the code at some point. Just finding the right approach and most reasonable solution is the issue from here.
Very glad to help, but be careful because I've noticed the patch above doesn't fully solve #283 just yet, it actually makes it even more frequent, or perhaps I just became really good at triggering it.
If you do use this code, let me know of the Emacs version you're using and if it gives you any headaches with indentation.
This pretending to be a preprocessor macro just feels so wrong. And i guess it might make things worse than better, at least in latest git emacs versions. While searching for an alternative way i looked up the docs at http://php.net/manual/en/language.basic-syntax.phptags.php and there it states "...everything outside of a pair of opening and closing tags is ignored by the PHP parser." This made me think of the opening tag as a closing comment and the closing tag of an opening comment. So i tried to imitate exactly this, and syntax|indenting failed because cc-mode does something with the "?". Finally figured out when marking the whole tag, be it <? or <% or <?php as a whole comment with < beeing the start and ? or p beeing the end makes cc-mode indenting work on git-snapshot and previous versions.
What do you think about this different hack?
~~hm, but it breaks doc comments font locking when there are punctuation or $variables at the end of a doc comment line.~~ No longer is that so.