Search code examples
emacsphp-mode

How to compose region like "<?php foo; bar; ?>"


I want fold php block in php template file.

  <html>
<?php
  $bar = foo();
  echo $bar;
?>
  <h1><?php echo $title; ?></h1>
  </html>

to >>>>

  <html>
  {{PHP}}
  <h1>{{PHP}}</h1>
  </html>

First, I try to use font-lock-add-keywords, it failed. Many thanks to @Gilles for (support? advice? I don't know which word should be here, sorry).

(eval-after-load 'php-mode
  '(progn
     (setq font-lock-multiline t)
     (font-lock-add-keywords
      'php-mode `(("\\(<?php .* ?>\\)("
                   (0 (progn (compose-region (match-beginning 1)
                                             (match-end 1) "の")
                             nil)))))))

Then, I try below, It works.

(defun lot/php-hide ()
  "compose php block in buffer"
  (interactive)
  (beginning-of-buffer)
  (while (re-search-forward "\\(<\\?php\\(.\\|\n\\)*?\\?>\\)" nil t)
    ;; (make-overlay (match-beginning 0) (match-end 0))
    ;; (hide-region-hide)
    ;; 'hide-region-hide' doesn't work, so try compose-region
    (compose-region (match-beginning 0)
                    (match-end 0)
                    ;; "{{PHP}}" ; use word can't work perfect.
                    "の"
                    )
    )
  )

Show me the error if have, Thank you XD


Solution

  • There's a simple mistake in your regexp: ? is a special character, but you want to match it literally, so you need a backslash before it. Note that string literal quoting and regexp quoting are orthogonal, so the regexp backslash needs to be doubled in a string literal.

    Additionally, regexps are greedy, so the .* part may match more than you intended if you have another occurence of ?>( later on the line. If you replace . by [\n>], this will prevent the match from extending beyond the first >. If you use Emacs ≥23, you can use a non-greedy operator instead, to stop the match as early as possible: .*?.

    "\\(<\\?php [^\n>]* \\?>\\)("
    

    This will show things like <?php foo bar?>( as Ƥ(.

    The backslashed parentheses \(\) in a regexp delimit a group; (match-beginning 1) and (match-end 1) return the boundary positions for the first (and only) group.

    The documentation of regexps is in the Emacs manual.

    If you want the match to extend across multiple lines, you need [^>]* or \\(.\\|\n\\)*? in the regexp. Additionally, you must tell the Font Lock library to extend its searches on multiple lines (by default, it limits all searches at the end of the line, for efficiency reasons). Use this:

     (eval-after-load 'php-mode
       '(progn
          (setq font-lock-multiline t)
          (font-lock-add-keywords …)))