;;; php-mode.el --- major mode for editing PHP code ;; Author: Vincent DEBOUT ;; Maintainer: Vincent DEBOUT ;; Keywords: languages php ;; WWW: http://deboutv.free.fr/lisp/php/ ;; Modifier: ice ;; WWW: http://hexe19.net/ ;;; License ;; This program is free software; you can redistribute it and/or ;; modify it under the terms of the GNU General Public License ;; as published by the Free Software Foundation; either version 2 ;; of the License, or (at your option) any later version. ;; This program is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with this program; if not, write to the Free Software ;; Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ;;; History ;; Revision 1.6+ 2008/03/01 16:42:00 ice ;; Customized ;; ;; $Log: php-mode.el,v $ ;; Revision 1.6 2007/03/17 09:57:32 vincent ;; Fix bugs and add APC templates ;; ;; Revision 1.5 2006/09/24 19:15:51 vincent ;; Release the 0.0.3 version ;; ;; Revision 1.4 2006/08/21 16:42:07 vincent ;; Add files and commit a 0.0.3pre release ;; ;; Revision 1.3 2006/07/30 08:31:13 vincent ;; Add templates, license; Fix highlight and missing definition ;; ;; Revision 1.2 2006/07/23 15:47:18 vincent ;; Add Template, Fontification, Indentation ;; ;; Revision 1.1.1.1 2006/07/14 10:16:37 vincent ;; First import ;; ;; Usage ;; ;; ; write below in .emacs ;; (autoload 'php-mode "php-mode" "PHP mode" t) ;; ;; (setq auto-mode-alist ;; (cons '("\\.\\(php\\|php5\\|inc\\)$" . php-mode) auto-mode-alist)) ;; (add-hook 'php-mode-hook '(lambda () ;; (setq php-intelligent-tab nil) ;; (setq intelligent-tab nil) ;; (setq indent-tabs-mode t) ;; (setq c-basic-offset 4) ;; (setq tab-width 4) ;; ) t) (defconst php-version "0.0.4 + 1.2.0" "PHP Mode version number.") (defconst php-time-stamp "2007-07-28" ;"2007-03-17" "PHP Mode time stamp for last update.") ;; From Emacs Php Mode (require 'font-lock) (require 'speedbar) (require 'cc-mode) (require 'custom) (require 'etags) (eval-when-compile (require 'regexp-opt)) (defcustom php-speedbar-config t "*When set to true automatically configures Speedbar to observe PHP files.\ Ignores php-file patterns option; fixed to expression \"\\.\\(inc\\|php[s34]?\\)\"" :type 'boolean :group 'php) (defcustom php-mode-speedbar-open nil "Normally php-mode starts with the speedbar closed.\ Turning this on will open it whenever php-mode is loaded." :type 'boolean :group 'php) (defcustom php-manual-url "http://www.php.net/manual/en/" "*URL at which to find PHP manual.\ You can replace \"en\" with your ISO language code." :type 'string :group 'php) (defcustom php-search-url "http://www.php.net/" "*URL at which to search for documentation on a word" :type 'string :group 'php) (defcustom php-manual-path "" "*Path to the directory which contains the PHP manual" :type 'string :group 'php) (defcustom php-mode-force-pear nil "Normally PEAR coding rules are enforced only when the filename contains \"PEAR\"\ Turning this on will force PEAR rules on all PHP files." :type 'boolean :group 'php) ;; Note whether we're in XEmacs (defconst xemacsp (string-match "Lucid\\|XEmacs" emacs-version) "Non nil if using XEmacs.") ;; ;; Do not force newline at end of file. Such newlines can cause ;; ;; trouble if the PHP file is included in another file before calls ;; ;; to header() or cookie(). ;; ;; PEAR coding standards ;; (make-local-hook 'php-mode-pear-hook) ;; (add-hook 'php-mode-pear-hook ;; (lambda nil (set (make-local-variable 'tab-width) 4)) nil t) ;; (add-hook 'php-mode-pear-hook ;; (lambda nil (set (make-local-variable 'c-basic-offset) 4)) nil t) ;; (add-hook 'php-mode-pear-hook ;; (lambda nil (set (make-local-variable 'c-hanging-comment-ender-p) nil)) nil t) ;; (add-hook 'php-mode-pear-hook ;; (lambda nil (set (make-local-variable 'indent-tabs-mode) nil)) nil t) ;; (add-hook 'php-mode-pear-hook ;; (lambda nil (c-set-offset 'block-open' - )) nil t) ;; (add-hook 'php-mode-pear-hook ;; (lambda nil (c-set-offset 'block-close' 0 )) nil t) ;; (if (or php-mode-force-pear ;; (and (stringp buffer-file-name) ;; (string-match "PEAR\\|pear" ;; (buffer-file-name)) ;; (string-match "\\.php$" (buffer-file-name)))) ;; (run-hooks 'php-mode-pear-hook)) ;; (run-hooks 'php-mode-user-hook)) ;; Handle Speedbar (if php-mode-speedbar-open (speedbar 1)) (if (and php-speedbar-config (symbolp 'speedbar)) (speedbar-add-supported-extension "\\.\\(inc\\|php[s345]?\\)")) ;; Define function documentation function (defun php-search-documentation () "Search PHP documentation for the word at the point." (interactive) (browse-url (concat php-search-url (current-word t)))) ;; Define function for browsing manual (defun php-browse-manual () "Bring up manual for PHP." (interactive) (browse-url php-manual-url)) ;; Syntax check the current buffer ;; using CLI php -l ;; (Buffer has to be saved) (defun php-check-syntax () "Check for Syntax errors using CLI php" (interactive) (let ((msg nil)) (if (not (buffer-modified-p)) (setq msg (shell-command-to-string (format "php -l %s" (buffer-file-name)))) (let ((tmp-name (make-temp-name "/tmp/phpsyntax")) (content (buffer-string))) (with-temp-file tmp-name (insert content)) (setq msg (shell-command-to-string (format "php -l %s" tmp-name))) (delete-file tmp-name))) (if (string= (substring msg 0 2) "No") (message "No Errors found") ; (string-match (format "in '%s'" (message msg)))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Customization ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defgroup php nil "Customizations for PHP mode." :prefix "php-" :group 'languages) (defgroup php-mode nil "Customizations for modes." :group 'php) (defcustom php-electric-mode t "*Non-nil enables electrification (automatic template generation). If nil, template generators can still be invoked through key bindings and menu. Is indicated in the modeline by \"/e\" after the mode name and can be toggled by `\\[php-electric-mode]'." :type 'boolean :group 'php-mode) (defcustom php-stutter-mode t "*Non-nil enables stuttering. Is indicated in the modeline by \"/s\" after the mode name and can be toggled by `\\[php-stutter-mode]'." :type 'boolean :group 'php-mode) (defcustom php-indent-tabs-mode nil "*Non-nil means indentation can insert tabs. Overrides local variable `indent-tabs-mode'." :type 'boolean :group 'php-mode) (defgroup php-menu nil "Customizations for menues." :group 'php) (defcustom php-index-menu t "*Non-nil means add an index menu for a source file when loading. Note that the index menu scans a file when it is opened." :type 'boolean :group 'php-menu) (defcustom php-index-menu-auto-rescan t "*Non-nil means that the Index menu will updated during buffer changes." :type 'boolean :group 'php-menu) (defcustom php-source-file-menu t "*Non-nil means add a menu of all source files in current directory." :type 'boolean :group 'php-menu) (defgroup php-style nil "Customizations for coding styles." :group 'php) (defcustom php-basic-offset 4 "*Amount of basic offset used for indentation. This value is used by + and - symbols in `php-offsets-alist'." :type 'integer :group 'php-style) (defgroup php-phpdocumentor nil "PHP-Documentor customizations." :group 'php) (defcustom php-enable-phpdocumentor-tags t "*Non-nil means add PHP-Documentor tag in the comments." :type 'boolean :group 'php-phpdocumentor) (defcustom php-class-tags '("package") "List of PHP-Documentor tag placed in the class comment." :type '(repeat (string :tag "Tag" "")) :group 'php-phpdocumentor) (defcustom php-function-tags '() "List of PHP-Documentor tag placed in the function comment." :type '(repeat (string :tag "Tag" "")) :group 'php-phpdocumentor) (defgroup php-misc nil "Miscellaneous customizations." :group 'php) (defcustom php-intelligent-tab t "*Non-nil means `TAB' does indentation, word completion and tab insertion. That is, if preceding character is part of a word then complete word, else if not at beginning of line then insert tab, else if last command was a `TAB' or `RET' then dedent one step, else indent current line (i.e. `TAB' is bound to `php-electric-tab'). If nil, TAB always indents current line (i.e. `TAB' is bound to `indent-according-to-mode'). NOTE: Activate the new setting in a PHP buffer by using the menu entry \"Activate Options\"." :type 'boolean :group 'php-misc) (defcustom php-word-completion-case-sensitive nil "*Non-nil means word completion using `TAB' is case sensitive. That is, `TAB' completes words that start with the same letters and case. Otherwise, case is ignored." :type 'boolean :group 'php-misc) (defun php-custom-set (variable value &rest functions) "Set variables as in `custom-set-default' and call FUNCTIONS afterwards." (if (fboundp 'custom-set-default) (custom-set-default variable value) (set-default variable value)) (while functions (when (fboundp (car functions)) (funcall (car functions))) (setq functions (cdr functions)))) (defun php-customize () "Call the customize function with `php' as argument." (interactive) (customize-browse 'php)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Menu tools functions ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defvar php-menu-max-size 20 "*Specifies the maximum size of a menu before splitting it into submenues.") (defun php-menu-split (list title) "Split menu LIST into several submenues, if number of elements > `php-menu-max-size'." (if (> (length list) php-menu-max-size) (let ((remain (nreverse list)) (result '()) (sublist '()) (menuno 1) (i 0)) (while remain (setq sublist (cons (car remain) sublist)) (setq remain (cdr remain)) (setq i (+ i 1)) (if (= i php-menu-max-size) (progn (setq result (cons (cons (format "%s %s" title menuno) (nreverse sublist)) result)) (setq i 0) (setq menuno (+ menuno 1)) (setq sublist '())))) (and sublist (setq result (cons (cons (format "%s %s" title menuno) (nreverse sublist)) result))) (nreverse result)) (nreverse list))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Index menu ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defconst php-imenu-generic-expression '( ("Functions" "^\\s-*\\(function\\)\\s-+\\(\\(\\w\\|\\s_\\)+\\)" 2) ("Class" "^\\s-*\\(class\\)\\s-+\\(\\(\\w\\|\\s_\\)+\\)" 2) ) "Imenu generic expression for PHP Mode. See `imenu-generic-expression'.") (defun php-index-menu-init () "Initialize index menu." (set (make-local-variable 'imenu-case-fold-search) t) (set (make-local-variable 'imenu-generic-expression) php-imenu-generic-expression) (set (make-local-variable 'imenu-auto-rescan) php-index-menu-auto-rescan) (set (make-local-variable 'imenu-sort-function) 'imenu--sort-by-name) (when (and php-index-menu (fboundp 'imenu)) (if (or (not (boundp 'font-lock-maximum-size)) (> font-lock-maximum-size (buffer-size))) (imenu-add-to-menubar "Index") (message "Scanning buffer for index...buffer too big")))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Source file menu ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defvar php-sources-menu nil) ;; Create the source menu (defun php-add-source-files-menu () "Scan directory for all PHP source files and generate menu. The directory of the current source file is scanned." (interactive) (message "Scanning directory for source files ...") (let ((newmap (current-local-map)) (file-list (php-get-source-files)) menu-list found) ;; Create list for menu (setq found nil) (while file-list (setq found t) (setq menu-list (cons (vector (car file-list) (list 'find-file (car file-list)) t) menu-list)) (setq file-list (cdr file-list))) (setq menu-list (php-menu-split menu-list "Sources")) (when found (setq menu-list (cons "--" menu-list))) (setq menu-list (cons ["*Rescan*" php-add-source-files-menu t] menu-list)) (setq menu-list (cons "Sources" menu-list)) ;; Create menu (easy-menu-add menu-list) (easy-menu-define php-sources-menu newmap "PHP source files menu" menu-list)) (message "")) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; PHP menu ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defun php-create-mode-menu () "Create PHP Mode menu." `("PHP" ("Indent" ["Line" indent-according-to-mode :keys "C-c C-i C-l"] ["Region" php-indent-region (mark)] ["Buffer" php-indent-buffer :keys "C-c C-i C-b"]) "--" ["Show Messages" php-show-messages :keys "C-c M-m"] ["PHP Mode Documentation" php-doc-mode :keys "C-c C-h"] ["Version" php-version :keys "C-c C-v"] ["Check Syntax" php-check-syntax :keys "f12"] ["Browse Manual" php-browse-manual :keys "shift-f12"] ["Search Documentation" php-search-documentation :keys "M-f12"] "--" ("Options" ("Mode" ["Electric Mode" (progn (customize-set-variable 'php-electric-mode (not php-electric-mode)) (php-mode-line-update)) :style toggle :selected php-electric-mode :keys "C-c C-m C-e"] ["Stutter Mode" (progn (customize-set-variable 'php-stutter-mode (not php-stutter-mode)) (php-mode-line-update)) :style toggle :selected php-stutter-mode :keys "C-c C-m C-s"] ["Indent Tabs Mode" (progn (customize-set-variable 'php-indent-tabs-mode (not php-indent-tabs-mode)) (setq indent-tabs-mode php-indent-tabs-mode)) :style toggle :selected php-indent-tabs-mode] "--" ["Customize Group..." (customize-group 'php-mode) t]) ("Menu" ["Index Menu" (customize-set-variable 'php-index-menu (not php-index-menu)) :style toggle :selected php-index-menu] ["Auto-Rescan (Index Menu)" (customize-set-variable 'php-index-menu-auto-rescan (not php-index-menu-auto-rescan)) :style toggle :selected php-index-menu-auto-rescan] ["Source Menu" (customize-set-variable 'php-source-file-menu (not php-source-file-menu)) :style toggle :selected php-source-file-menu] "--" ["Customize Group..." (customize-group 'php-menu) t]) ("Style" ["Indent offset..." (customize-option 'php-basic-offset) t] "--" ["Customize Group..." (customize-group 'php-style) t]) ("PHP-Documentor" ["Enable tag" (progn (customize-set-variable 'php-enable-phpdocumentor-tags (not php-enable-phpdocumentor-tags)) (php-activate-customizations)) :style toggle :selected php-enable-phpdocumentor-tags] ["Class tags..." (customize-option 'php-class-tags) t] ["Function tags..." (customize-option 'php-function-tags) t] "--" ["Customize Group..." (customize-group 'php-phpdocumentor) t]) ("Miscellaneous" ["Use Intelligent Tab" (progn (customize-set-variable 'php-intelligent-tab (not php-intelligent-tab)) (php-activate-customizations)) :style toggle :selected php-intelligent-tab] ["Completion is case sensitive" (customize-set-variable 'php-word-completion-case-sensitive (not php-word-completion-case-sensitive)) :style toggle :selected php-word-completion-case-sensitive] "--" ["Customize Group..." (customize-group 'php-misc) t]) "--" ["Save Options" customize-save-customized t] ["Activate Options" php-activate-customizations t] ["Browse Options..." php-customize t]))) (defvar php-mode-menu-list (php-create-mode-menu) "PHP Mode menu.") (defvar php-mode-map nil "Keymap for PHP Mode.") (defun php-update-mode-menu () "Update PHP Mode menu." (interactive) (easy-menu-remove php-mode-menu-list) (setq php-mode-menu-list (php-create-mode-menu)) (easy-menu-add php-mode-menu-list) (easy-menu-define php-mode-menu php-mode-map "Menu keymap for PHP Mode." php-mode-menu-list)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Progress reporting ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defvar php-progress-interval 1 "*Interval used to update progress status during long operations. If a number, percentage complete gets updated after each interval of that many seconds. To inhibit all messages, set this option to nil.") (defvar php-progress-info nil "Array variable for progress information: 0 begin, 1 end, 2 time.") (defun php-update-progress-info (string pos) "Update progress information." (when (and php-progress-info (not noninteractive) (< php-progress-interval (- (nth 1 (current-time)) (aref php-progress-info 2)))) (message (concat string "... (%2d%s)") (/ (* 100 (- pos (aref php-progress-info 0))) (- (aref php-progress-info 1) (aref php-progress-info 0))) "%") (aset php-progress-info 2 (nth 1 (current-time))))) (defmacro php-point (position) "Return the value of point at certain commonly referenced POSITIONs. POSITION can be one of the following symbols: bol -- beginning of line eol -- end of line bod -- beginning of defun boi -- back to indentation eoi -- last whitespace on line ionl -- indentation of next line iopl -- indentation of previous line bonl -- beginning of next line bopl -- beginning of previous line This function does not modify point or mark." (or (and (eq 'quote (car-safe position)) (null (cddr position))) (error "ERROR: Bad buffer position requested: %s" position)) (setq position (nth 1 position)) `(let ((here (point))) ,@(cond ((eq position 'bol) '((beginning-of-line))) ((eq position 'eol) '((end-of-line))) ((eq position 'bod) '((save-match-data (php-beginning-of-defun)))) ((eq position 'boi) '((back-to-indentation))) ((eq position 'eoi) '((end-of-line) (skip-chars-backward " \t"))) ((eq position 'bonl) '((forward-line 1))) ((eq position 'bopl) '((forward-line -1))) ((eq position 'iopl) '((forward-line -1) (back-to-indentation))) ((eq position 'ionl) '((forward-line 1) (back-to-indentation))) (t (error "ERROR: Unknown buffer position requested: %s" position)) ) (prog1 (point) (goto-char here)) ;; workaround for an Emacs18 bug -- blech! Well, at least it ;; doesn't hurt for v19 ,@nil )) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; File/directory manipulation ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defun php-directory-files (directory &optional full match) "Call `directory-files' if DIRECTORY exists, otherwise generate error message." (if (not (file-directory-p directory)) (php-warning-when-idle "No such directory: \"%s\"" directory) (let ((dir (directory-files directory full match))) (setq dir (delete "." dir)) (setq dir (delete ".." dir)) dir))) (defun php-get-source-files (&optional full directory) "Get list of PHP source files in DIRECTORY or current directory." (let ((mode-alist auto-mode-alist) filename-regexp) ;; create regular expressions for matching file names (setq filename-regexp "\\`[^.].*\\(") (while mode-alist (when (eq (cdar mode-alist) 'php-mode) (setq filename-regexp (concat filename-regexp (caar mode-alist) "\\|"))) (setq mode-alist (cdr mode-alist))) (setq filename-regexp (concat (substring filename-regexp 0 (string-match "\\\\|$" filename-regexp)) "\\)")) ;; find files (php-directory-files (or directory default-directory) full filename-regexp))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Messages reporting ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defvar php-warnings nil "Warnings to tell the user during start up.") (defun php-run-when-idle (secs repeat function) "Wait until idle, then run FUNCTION." (if (fboundp 'start-itimer) (start-itimer "php-mode" function secs repeat t) ; (run-with-idle-timer secs repeat function))) ;; explicitely activate timer (necessary when Emacs is already idle) (aset (run-with-idle-timer secs repeat function) 0 nil))) (defun php-warning-when-idle (&rest args) "Wait until idle, then print out warning STRING and beep." (if noninteractive (php-warning (apply 'format args) t) (unless php-warnings (php-run-when-idle .1 nil 'php-print-warnings)) (setq php-warnings (cons (apply 'format args) php-warnings)))) (defun php-warning (string &optional nobeep) "Print out warning STRING and beep." (message (concat "WARNING: " string)) (unless (or nobeep noninteractive) (beep))) (defun php-print-warnings () "Print out messages in variable `php-warnings'." (let ((no-warnings (length php-warnings))) (setq php-warnings (nreverse php-warnings)) (while php-warnings (message (concat "WARNING: " (car php-warnings))) (setq php-warnings (cdr php-warnings))) (beep) (when (> no-warnings 1) (message "WARNING: See warnings in message buffer (type `C-c M-m').")))) (defun php-show-messages () "Get *Messages* buffer to show recent messages." (interactive) (display-buffer " *Message-Log*")) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Variables ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Regular expression used internally to recognize labels in switch ;;; statements. (defvar php-switch-label-regexp (purecopy "case.*:\\|default[ \t]*:")) (defvar php-label-offset -4 ; -2 "*Offset of PHP label lines and case statements relative to usual indentation.") (defvar php-indent-level 4 "*Indentation of PHP statements with respect to containing block.") (defvar php-brace-offset 0 "*Extra indentation for braces, compared with other text in same context.") (defvar php-argdecl-indent 5 "*Indentation level of declarations of PHP function arguments.") (defvar php-continued-statement-offset 0 ; 2 "*Extra indent for lines not starting new statements.") (defvar php-continued-brace-offset 0 "*Extra indent for substatements that start with open-braces.") (defvar php-brace-imaginary-offset 0 "*Imagined indentation of a PHP open brace that actually follows a statement.") ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Functions ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defun php-comment-indent () (if (looking-at "^/\\*") 0 ;Existing comment at bol stays there. (let ((opoint (point))) (save-excursion (beginning-of-line) (cond ((looking-at "[ \t]*}[ \t]*\\($\\|/\\*\\)") ;; A comment following a solitary close-brace ;; should have only one space. (search-forward "}") (1+ (current-column))) ((or (looking-at "^#[ \t]*endif[ \t]*") (looking-at "^#[ \t]*else[ \t]*")) 7) ;2 spaces after #endif ((progn (goto-char opoint) (skip-chars-backward " \t") (and (= comment-column 0) (bolp))) ;; If comment-column is 0, and nothing but space ;; before the comment, align it at 0 rather than 1. 0) (t (max (1+ (current-column)) ;Else indent at comment column comment-column))))))) ; except leave at least one space. (defun php-indent-line-2 () "Indent current line as PHP code. Return the amount the indentation changed by." (let ((indent (php-calculate-indent nil)) beg shift-amt (case-fold-search nil) (pos (- (point-max) (point)))) (beginning-of-line) (setq beg (point)) (cond ((eq indent nil) (setq indent (current-indentation))) ((eq indent t) (setq indent (php-calculate-indent-within-comment))) ;((looking-at "[ \t]*#") ; (setq indent 0)) (t (skip-chars-forward " \t") (if (listp indent) (setq indent (car indent))) (cond ((or (looking-at php-switch-label-regexp) (and (looking-at "[A-Za-z]") (save-excursion (forward-sexp 1) (looking-at ":[^:]")))) (setq indent (max 1 (+ indent php-label-offset)))) ;; Indenting the else statement ((and (looking-at "else\\b") (not (looking-at "else\\s_"))) (setq indent (save-excursion (php-backward-to-start-of-if) (current-indentation)))) ((and (looking-at "}[ \t]*else\\b") (not (looking-at "}[ \t]*else\\s_"))) (setq indent (save-excursion (forward-char) (backward-sexp) (php-backward-to-start-of-if) (current-indentation)))) ((and (looking-at "while\\b") (not (looking-at "while\\s_")) (save-excursion (php-backward-to-start-of-do))) ;; This is a `while' that ends a do-while. (setq indent (save-excursion (php-backward-to-start-of-do) (current-indentation)))) ((= (following-char) ?}) (setq indent (- indent php-indent-level))) ((= (following-char) ?{) (setq indent (+ indent php-brace-offset)))))) (skip-chars-forward " \t") (setq shift-amt (- indent (current-column))) (if (zerop shift-amt) (if (> (- (point-max) pos) (point)) (goto-char (- (point-max) pos))) (delete-region beg (point)) ;;(setq indent (+ php-basic-offset indent)) (indent-to indent) ;; If initial point was within line's indentation, ;; position after the indentation. Else stay at same point in text. (if (> (- (point-max) pos) (point)) (goto-char (- (point-max) pos)))) ;;(indent-to php-basic-offset) shift-amt)) (defun php-indent-line () "Indent the current line as PHP code. Returns the amount of indentation change." (interactive) (let* (indent current-line) (save-excursion (beginning-of-line) (setq current-line (point-marker)) ;(while (and (not (eq (point-marker) (beginning-of-buffer))) ;( (forward-line -1);) (if (eq current-line (point-marker)) (setq indent 0) (setq indent (current-indentation)) (if (and (re-search-forward "^\\s-*\\/\\*" (php-point 'eol) t) (not (re-search-forward "\\*\\/\\s-*$" (php-point 'eol) t))) (setq indent (+ indent 1))) (if (and (re-search-forward "\\*\\/\\s-*$" (php-point 'eol) t) (not (re-search-forward "^\\s-*\\/\\*" (php-point 'eol) t))) (setq indent (- indent 1))) (when (and (re-search-forward "\\({\\|(\\)\\s-*$" (php-point 'eol) t ) (not (re-search-forward "^\\(}\\|)\\)\\s-*" (php-point 'eol) t))) (setq indent (+ indent php-basic-offset))))) (delete-region (php-point 'bol) (php-point 'boi)) (beginning-of-line) (when (re-search-forward "^\\(}\\|)\\)\\s-*" (php-point 'eol) t) (setq indent (- indent php-basic-offset))) (indent-to indent) indent)) (defun php-indent-region (beg end column) "Indent region as PHP code. Adds progress reporting to `indent-region'." (interactive "r\nP") (when php-progress-interval (setq php-progress-info (vector (count-lines (point-min) beg) (count-lines (point-min) end) 0))) (indent-region beg end column) (when php-progress-interval (message "Indenting...done")) (setq php-progress-info nil)) (defun php-indent-buffer () "Indent whole buffer as PHP code. Calls `indent-region' for whole buffer and adds progress reporting." (interactive) (php-indent-region (point-min) (point-max) nil)) (defun php-calculate-indent (&optional parse-start) "Return appropriate indentation for current line as PHP code. In usual case returns an integer: the column to indent to. Returns nil if line starts inside a string, t if in a comment." (save-excursion (beginning-of-line) (let ((indent-point (point)) (case-fold-search nil) state containing-sexp) (if parse-start (goto-char parse-start) (beginning-of-defun)) (while (< (point) indent-point) (setq parse-start (point)) (setq state (parse-partial-sexp (point) indent-point 0)) (setq containing-sexp (car (cdr state)))) (cond ((or (nth 3 state) (nth 4 state)) ;; return nil or t if should not change this line (nth 4 state)) ((null containing-sexp) ;; Line is at top level. May be data or function definition, ;; or may be function argument declaration. ;; Indent like the previous top level line ;; unless that ends in a closeparen without semicolon, ;; in which case this line is the first argument decl. (goto-char indent-point) (skip-chars-forward " \t") (if (= (following-char) ?{) 0 ; Unless it starts a function body (php-backward-to-noncomment (or parse-start (point-min))) ;; Look at previous line that's at column 0 ;; to determine whether we are in top-level decls ;; or function's arg decls. Set basic-indent accordingly. (let (found (basic-indent (save-excursion (re-search-backward "^[^ \^L\t\n#]" nil 'move) (let (comment lim) ;; Recognize the DEFUN macro in Emacs. (if (save-excursion ;; Move down to the (putative) argnames line. (while (and (not (eobp)) (not (looking-at " *[({}#/]"))) (forward-line 1)) ;; Go back to the DEFUN, if it is one. (condition-case nil (backward-sexp 1) (error)) (beginning-of-line) (looking-at "DEFUN\\b")) php-argdecl-indent (if (and (looking-at "\\sw\\|\\s_") ;; This is careful to stop at the first ;; paren if we have ;; int foo Proto ((int, int)); (looking-at "[^\"\n=(]*(") (progn (goto-char (1- (match-end 0))) ;; Skip any number of paren-groups. ;; Consider typedef int (*fcn) (int); (while (= (following-char) ?\() (setq lim (point)) (condition-case nil (forward-sexp 1) (error)) (skip-chars-forward " \t\f")) ;; Have we reached something ;; that shows this isn't a function ;; definition? (and (< (point) indent-point) (not (memq (following-char) '(?\, ?\;))))) ;; Make sure the "function decl" we found ;; is not inside a comment. (progn ;; Move back to the `(' starting arglist (goto-char lim) (beginning-of-line) (while (and (not comment) (search-forward "/*" lim t)) (setq comment (not (search-forward "*/" lim t)))) (not comment))) (progn (setq found nil) (while (and (not found) (re-search-forward ";" indent-point t)) (if (and (not (php-in-comment-p)) (save-excursion (setq start (point-marker)) (setq state (parse-partial-sexp start indent-point)) (if (= 0 (nth 0 state)) t nil))) (setq found t))) (if found 0 php-argdecl-indent)) (progn (if (re-search-forward "else" indent-point t) (progn (setq found nil) (while (and (not found) (re-search-forward ";" indent-point t)) (if (and (not (php-in-comment-p)) (save-excursion (setq start (point-marker)) (setq state (parse-partial-sexp start indent-point)) (if (= 0 (nth 0 state)) t nil))) (setq found t))) (if found 0 php-argdecl-indent)) 0)))))))) basic-indent))) ;; ;; Now add a little if this is a continuation line. ;; (+ basic-indent (if (or (bobp) ;; (memq (preceding-char) '(?\) ?\; ?\})) ;; ;; Line with zero indentation ;; ;; is probably the return-type ;; ;; of a function definition, ;; ;; so following line is function name. ;; (= (current-indentation) 0)) ;; 0 php-continued-statement-offset)) ((/= (char-after containing-sexp) ?{) ;; line is expression, not statement: ;; indent to just after the surrounding open. ;;(goto-char (1+ containing-sexp)) ;;(current-column)) ;; for continuous lines like this: ;; $a = array( ;; 'aaaa' => 'AAAA', ;; 'bbbb' => 'BBBB', ;; ); (beginning-of-line) (skip-chars-forward " \t") (if (= (following-char) ?\)) (progn (goto-char (1+ containing-sexp)) (- (php-calculate-indent-after-brace) php-indent-level)) (progn (goto-char (1+ containing-sexp)) (php-calculate-indent-after-brace)) )) (t ;; Statement level. Is it a continuation or a new statement? ;; Find previous non-comment character. (goto-char indent-point) (php-backward-to-noncomment containing-sexp) ;; Back up over label lines, since they don't ;; affect whether our line is a continuation. (while (or (eq (preceding-char) ?\,) (and (eq (preceding-char) ?:) (or (eq (char-after (- (point) 2)) ?\') (memq (char-syntax (char-after (- (point) 2))) '(?w ?_))))) (if (eq (preceding-char) ?\,) (progn (forward-char -1) (php-backward-to-start-of-continued-exp containing-sexp))) (beginning-of-line) (php-backward-to-noncomment containing-sexp)) ;; Check for a preprocessor statement or its continuation lines. ;; Move back to end of previous non-preprocessor line, ;; or possibly beginning of buffer. (let ((found (point)) stop) (while (not stop) (beginning-of-line) (cond ((bobp) (setq found (point) stop t)) ((save-excursion (forward-char -1) (= (preceding-char) ?\\)) (forward-char -1)) ;; This line is not preceded by a backslash. ;; So either it starts a preprocessor command ;; or any following continuation lines ;; should not be skipped. ((= (following-char) ?#) (forward-char -1) (setq found (point))) (t (setq stop t)))) (goto-char found)) ;; Now we get the answer. (if (and (not (memq (preceding-char) '(0 ?\, ?\; ?\} ?\{))) ;; But don't treat a line with a close-brace ;; as a continuation. It is probably the ;; end of an enum type declaration. (save-excursion (goto-char indent-point) (skip-chars-forward " \t") (not (= (following-char) ?})))) ;; This line is continuation of preceding line's statement; ;; indent php-continued-statement-offset more than the ;; previous line of the statement. (progn (php-backward-to-start-of-continued-exp containing-sexp) (+ php-continued-statement-offset (current-column) (if (save-excursion (goto-char indent-point) (skip-chars-forward " \t") (eq (following-char) ?{)) php-continued-brace-offset 0))) ;; This line starts a new statement. ;; Position following last unclosed open. (goto-char containing-sexp) ;; Is line first statement after an open-brace? (or ;; If no, find that first statement and indent like it. (save-excursion (forward-char 1) (let ((colon-line-end 0)) (while (progn (skip-chars-forward " \t\n") (looking-at "/\\*\\|case[ \t\n'/(].*:\\|[a-zA-Z0-9_$]*:[^:]\\|[a-zA-Z0-9_$]*[^:]:$")) ;; Skip over comments and labels following openbrace. (cond ((= (following-char) ?\#) (forward-line 1)) ((= (following-char) ?\/) (forward-char 2) (search-forward "*/" nil 'move)) ;; case or label: (t (save-excursion (end-of-line) (setq colon-line-end (point))) (search-forward ":")))) ;; The first following code counts ;; if it is before the line we want to indent. (and (< (point) indent-point) (- (if (> colon-line-end (point)) (- (current-indentation) php-label-offset) (current-column)) ;; If prev stmt starts with open-brace, that ;; open brace was offset by php-brace-offset. ;; Compensate to get the column where ;; an ordinary statement would start. (if (= (following-char) ?\{) php-brace-offset 0))))) ;; If no previous statement, ;; indent it relative to line brace is on. (php-calculate-indent-after-brace)))))))) (defun php-calculate-indent-within-comment (&optional after-star) "Return the indentation amount for line inside a block comment. Optional arg AFTER-STAR means, if lines in the comment have a leading star, return the indentation of the text that would follow this star." (let (end star-start) (save-excursion (beginning-of-line) (skip-chars-forward " \t") (setq star-start (= (following-char) ?\*)) (skip-chars-backward " \t\n") (setq end (point)) (beginning-of-line) (skip-chars-forward " \t") (if after-star (and (looking-at "\\*") (re-search-forward "\\*[ \t]*"))) (and (re-search-forward "/\\*[ \t]*" end t) star-start (not after-star) (goto-char (1+ (match-beginning 0)))) (if (and (looking-at "[ \t]*$") (= (preceding-char) ?\*)) (1+ (current-column)) (current-column))))) (defun php-backward-to-noncomment (lim) (let (stop) (while (not stop) (skip-chars-backward " \t\n\f" lim) (if (and (>= (point) (+ 2 lim)) (save-excursion (forward-char -2) (looking-at "\\*/"))) (search-backward "/*" lim 'move) (setq stop (or (<= (point) lim) (save-excursion (while (progn (beginning-of-line) (eq ?\\ (char-after (- (point) 2)))) (forward-char -1) (beginning-of-line)) ;(skip-chars-forward " \t") ;(not (looking-at "\\(#\\|//\\)")) (end-of-line) (setq end-of-line-point (point)) (beginning-of-line) (setq comment-open-found nil) (setq reached-end-of-line nil) ;;(while (not reached-end-of-line) ;; (if (re-search-forward "//" end-of-line-point t) ;; (forward-char -2) ;; (if (re-search-forward "#" end-of-line-point t) ;; (forward-char -1) ;; (setq reached-end-of-line t))) (while (not reached-end-of-line) (if (re-search-forward "//" end-of-line-point t) (forward-char -2) (setq reached-end-of-line t)) (if (not reached-end-of-line) (if (and (not (php-in-string-p)) (= (following-char) ?\/)) (progn (setq reached-end-of-line t) (setq comment-open-found t)) (forward-char 1)) )) (not comment-open-found)))) (or stop (beginning-of-line)))))) (defun php-backward-to-start-of-continued-exp (lim) (if (memq (preceding-char) '(?\) ?\")) (forward-sexp -1)) (beginning-of-line) (if (<= (point) lim) (goto-char (1+ lim))) (skip-chars-forward " \t")) (defun php-backward-to-start-of-if (&optional limit) "Move to the start of the last \"unbalanced\" `if'." (or limit (setq limit (save-excursion (beginning-of-defun) (point)))) (let ((if-level 1) (case-fold-search nil)) (while (and (not (bobp)) (not (zerop if-level))) (backward-sexp 1) (cond ((and (looking-at "else\\b") (not (looking-at "else\\s_"))) (setq if-level (1+ if-level))) ((and (looking-at "if\\b") (not (looking-at "if\\s_"))) (setq if-level (1- if-level))) ((< (point) limit) (setq if-level 0) (goto-char limit)))))) (defun php-backward-to-start-of-do (&optional limit) "If point follows a `do' statement, move to beginning of it and return t. Otherwise return nil and don't move point." (or limit (setq limit (save-excursion (beginning-of-defun) (point)))) (let ((first t) (startpos (point)) (done nil)) (while (not done) (let ((next-start (point))) (condition-case nil ;; Move back one token or one brace or paren group. (backward-sexp 1) ;; If we find an open-brace, we lose. (error (setq done 'fail))) (if done nil ;; If we reached a `do', we win. (if (looking-at "do\\b") (setq done 'succeed) ;; Otherwise, if we skipped a semicolon, we lose. ;; (Exception: we can skip one semicolon before getting ;; to a the last token of the statement, unless that token ;; is a close brace.) (if (save-excursion (forward-sexp 1) (or (and (not first) (= (preceding-char) ?})) (search-forward ";" next-start t (if (and first (/= (preceding-char) ?})) 2 1)))) (setq done 'fail) (setq first nil) ;; If we go too far back in the buffer, we lose. (if (< (point) limit) (setq done 'fail))))))) (if (eq done 'succeed) t (goto-char startpos) nil))) (defun php-calculate-indent-after-brace () "Return the proper PHP indent for the first line after an open-brace. This function is called with point before the brace." ;; For open brace in column zero, don't let statement ;; start there too. If php-indent-level is zero, ;; use php-brace-offset + php-continued-statement-offset instead. ;; For open-braces not the first thing in a line, ;; add in php-brace-imaginary-offset. (+ (if (and (bolp) (zerop php-indent-level)) (+ php-brace-offset php-continued-statement-offset) php-indent-level) ;; Move back over whitespace before the openbrace. ;; If openbrace is not first nonwhite thing on the line, ;; add the c-brace-imaginary-offset. (progn (skip-chars-backward " \t") (if (bolp) 0 php-brace-imaginary-offset)) ;; If the openbrace is preceded by a parenthesized exp, ;; move to the beginning of that; ;; possibly a different line (progn (if (eq (preceding-char) ?\)) (forward-sexp -1)) ;; Get initial indentation of the line we are on. (current-indentation)))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Functions ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defun php-function-name (prefix string &optional postfix) "Generate a Lisp function name. PREFIX, STRING and optional POSTFIX are concatenated by '-' and spaces in STRING are replaced by `-' and substrings are converted to lower case." (let ((name prefix)) (while (string-match "\\(\\w+\\)\\s-*\\(.*\\)" string) (setq name (concat name "-" (downcase (substring string 0 (match-end 1))))) (setq string (substring string (match-beginning 2)))) (when postfix (setq name (concat name "-" postfix))) (intern name))) (defvar php-mode-hook nil "*Hook called by `php-mode'.") (defvar php-constants nil "List of PHP constants.") (defvar php-constants-regexp nil "Regexp for PHP constants.") (defconst php-01-constants '(;; core constants "__LINE__" "__FILE__" "__CLASS__" "__FUNCTION__" "PHP_OS" "PHP_VERSION" "TRUE" "FALSE" "NULL" "E_ERROR" "E_NOTICE" "E_PARSE" "E_WARNING" "E_ALL" "E_USER_ERROR" "E_USER_WARNING" "E_USER_NOTICE" "DEFAULT_INCLUDE_PATH" "PEAR_INSTALL_DIR" "PEAR_EXTENSION_DIR" "PHP_BINDIR" "PHP_LIBDIR" "PHP_DATADIR" "PHP_SYSCONFDIR" "PHP_LOCALSTATEDIR" "PHP_CONFIG_FILE_PATH") "PHP constants.") (defvar php-keywords nil "List of PHP keywords.") (defvar php-keywords-regexp nil "Regexp for PHP keywords.") (defconst php-01-keywords ;; "class", "new" and "extends" get special treatment ;; "case" and "default" get special treatment elsewhere '("and" "as" "break" "continue" "declare" "do" "echo" "else" "elseif" "endfor" "endforeach" "endif" "endswitch" "endwhile" "exit" "extends" "for" "foreach" "global" "if" "include" "include_once" "next" "or" "require" "require_once" "return" "static" "switch" "then" "var" "while" "xor" "private" "throw" "catch" "try" "instanceof" "catch all" "finally") "PHP keywords.") (defconst php-identifier (eval-when-compile '"[a-zA-Z\_\x7f-\xff][a-zA-Z0-9\_\x7f-\xff]*") "Characters in a PHP identifier.") (defvar php-types nil "List of PHP types.") (defvar php-types-regexp nil "Regexp for PHP types.") (defconst php-01-types '("array" "bool" "boolean" "char" "const" "double" "float" "int" "integer" "long" "mixed" "object" "real" "string") "PHP types.") (defvar php-superglobals nil "List of PHP superglobals.") (defvar php-superglobals-regexp nil "Regexp for PHP superglobals.") (defconst php-01-superglobals '("_GET" "_POST" "_COOKIE" "_SESSION" "_ENV" "GLOBALS" "_SERVER" "_FILES" "_REQUEST") "PHP superglobal variables.") (defvar php-functions nil "List of PHP functions.") (defvar php-functions-regexp nil "Regexp for PHP functions.") (defconst php-01-functions '( ;; .NET "dotnet_load" ;; Apache "apache_child_terminate" "apache_get_modules" "apache_get_version" "apache_getenv" "apache_lookup_uri" "apache_note" "apache_request_headers" "apache_reset_timeout" "apache_response_headers" "apache_setenv" "ascii2ebcdic" "ebcdic2ascii" "getallheaders" "virtual" ;; APC "apc_cache_info" "apc_clear_cache" "apc_define_constants" "apc_delete" "apc_fetch" "apc_load_constants" "apc_sma_info" "apc_store" ;; Array "array_change_key_case" "array_chunk" "array_combine" "array_count_values" "array_diff_assoc" "array_diff_key" "array_diff_uassoc" "array_diff_ukey" "array_diff" "array_fill" "array_filter" "array_flip" "array_intersect_assoc" "array_intersect_key" "array_intersect_uassoc" "array_intersect_ukey" "array_intersect" "array_key_exists" "array_keys" "array_map" "array_merge_recursive" "array_merge" "array_multisort" "array_pad" "array_pop" "array_product" "array_push" "array_rand" "array_reduce" "array_reverse" "array_search" "array_shift" "array_slice" "array_splice" "array_sum" "array_udiff_assoc" "array_udiff_uassoc" "array_udiff" "array_uintersect_assoc" "array_uintersect_uassoc" "array_uintersect" "array_unique" "array_unshift" "array_values" "array_walk_recursive" "array_walk" "array" "arsort" "asort" "compact" "count" "current" "each" "end" "extract" "in_array" "key" "krsort" "ksort" "list" "natcasesort" "natsort" "next" "pos" "prev" "range" "reset" "rsort" "shuffle" "sizeof" "sort" "uasort" "uksort" "usort" ;; Date/Time "checkdate" "date_default_timezone_get" "date_default_timezone_set" "date_sunrise" "date_sunset" "date" "getdate" "gettimeofday" "gmdate" "gmmktime" "gmstrftime" "idate" "localtime" "microtime" "mktime" "strftime" "strptime" "strtotime" "time" ;; Directory "chdir" "chroot" "dir" "closedir" "getcwd" "opendir" "readdir" "rewinddir" "scandir" ;; Error and Logging "debug_backtrace" "debug_print_backtrace" "error_log" "error_reporting" "restore_error_handler" "restore_exception_handler" "set_error_handler" "set_exception_handler" "trigger_error" "user_error" ;; File system "basename" "chgrp" "chmod" "chown" "clearstatcache" "copy" "delete" "dirname" "disk_free_space" "disk_total_space" "diskfreespace" "fclose" "feof" "fflush" "fgetc" "fgetcsv" "fgets" "fgetss" "file_exists" "file_get_contents" "file_put_contents" "file" "fileatime" "filectime" "filegroup" "fileinode" "filemtime" "fileowner" "fileperms" "filesize" "filetype" "flock" "fnmatch" "fopen" "fpassthru" "fputcsv" "fputs" "fread" "fscanf" "fseek" "fstat" "ftell" "ftruncate" "fwrite" "glob" "is_dir" "is_executable" "is_file" "is_link" "is_readable" "is_uploaded_file" "is_writable" "is_writeable" "link" "linkinfo" "lstat" "mkdir" "move_uploaded_file" "parse_ini_file" "pathinfo" "pclose" "popen" "readfile" "readlink" "realpath" "rename" "rewind" "rmdir" "set_file_buffer" "stat" "symlink" "tempnam" "tmpfile" "touch" "umask" "unlink" ;; Functions "call_user_func_array" "call_user_func" "create_function" "func_get_arg" "func_get_args" "func_num_args" "function_exists" "get_defined_functions" "register_shutdown_function" "register_tick_function" "unregister_tick_function" ;; Image "gd_info" "getimagesize" "image_type_to_extension" "image_type_to_mime_type" "image2wbmp" "imagealphablending" "imageantialias" "imagearc" "imagechar" "imagecharup" "imagecolorallocate" "imagecolorallocatealpha" "imagecolorat" "imagecolorclosest" "imagecolorclosestalpha" "imagecolorclosesthwb" "imagecolordeallocate" "imagecolorexact" "imagecolorexactalpha" "imagecolormatch" "imagecolorresolve" "imagecolorresolvealpha" "imagecolorset" "imagecolorsforindex" "imagecolorstotal" "imagecolortransparent" "imageconvolution" "imagecopy" "imagecopymerge" "imagecopymergegray" "imagecopyresampled" "imagecopyresized" "imagecreate" "imagecreatefromgd2" "imagecreatefromgd2part" "imagecreatefromgd" "imagecreatefromgif" "imagecreatefromjpeg" "imagecreatefrompng" "imagecreatefromstring" "imagecreatefromwbmp" "imagecreatefromxbm" "imagecreatefromxpm" "imagecreatetruecolor" "imagedashedline" "imagedestroy" "imageellipse" "imagefill" "imagefilledarc" "imagefilledellipse" "imagefilledpolygon" "imagefilledrectangle" "imagefilltoborder" "imagefilter" "imagefontheight" "imagefontwidth" "imageftbbox" "imagefttext" "imagegammacorrect" "imagegd2" "imagegd" "imagegif" "imageinterlace" "imageistruecolor" "imagejpeg" "imagelayereffect" "imageline" "imageloadfont" "imagepalettecopy" "imagepng" "imagepolygon" "imagepsbbox" "imagepsencodefont" "imagepsextendfont" "imagepsfreefont" "imagepsloadfont" "imagepsslantfont" "imagepstext" "imagerectangle" "imagerotate" "imagesavealpha" "imagesetbrush" "imagesetpixel" "imagesetstyle" "imagesetthickness" "imagesettile" "imagestring" "imagestringup" "imagesx" "imagesy" "imagetruecolortopalette" "imagettfbbox" "imagettftext" "imagetypes" "imagewbmp" "imagexbm" "iptcembed" "iptcparse" "jpeg2wbmp" "png2wbmp" ;; Mail "ezmlm_hash" "mail" ;; Mathematical "abs" "acos" "acosh" "asin" "asinh" "atan2" "atan" "atanh" "base_convert" "bindec" "ceil" "cos" "cosh" "decbin" "dechex" "decoct" "deg2rad" "exp" "expm1" "floor" "fmod" "getrandmax" "hexdec" "hypot" "is_finite" "is_infinite" "is_nan" "lcg_value" "log10" "log1p" "log" "max" "min" "mt_getrandmax" "mt_rand" "mt_srand" "octdec" "pi" "pow" "rad2deg" "rand" "round" "sin" "sinh" "sqrt" "srand" "tan" "tanh" ;; Miscellaneous "connection_aborted" "connection_status" "connection_timeout" "constant" "define" "defined" "die" "eval" "exit" "get_browser" "__halt_compiler" "highlight_file" "highlight_string" "ignore_user_abort" "pack" "php_check_syntax" "php_strip_whitespace" "show_source" "sleep" "sys_getloadavg" "time_nanosleep" "time_sleep_until" "uniqid" "unpack" "usleep" ;; MySQL "mysql_affected_rows" "mysql_change_user" "mysql_client_encoding" "mysql_close" "mysql_connect" "mysql_create_db" "mysql_data_seek" "mysql_db_name" "mysql_db_query" "mysql_drop_db" "mysql_errno" "mysql_error" "mysql_escape_string" "mysql_fetch_array" "mysql_fetch_assoc" "mysql_fetch_field" "mysql_fetch_lengths" "mysql_fetch_object" "mysql_fetch_row" "mysql_field_flags" "mysql_field_len" "mysql_field_name" "mysql_field_seek" "mysql_field_table" "mysql_field_type" "mysql_free_result" "mysql_get_client_info" "mysql_get_host_info" "mysql_get_proto_info" "mysql_get_server_info" "mysql_info" "mysql_insert_id" "mysql_list_dbs" "mysql_list_fields" "mysql_list_processes" "mysql_list_tables" "mysql_num_fields" "mysql_num_rows" "mysql_pconnect" "mysql_ping" "mysql_query" "mysql_real_escape_string" "mysql_result" "mysql_select_db" "mysql_stat" "mysql_tablename" "mysql_thread_id" "mysql_unbuffered_query" ;; Regular expressions "ereg_replace" "ereg" "eregi_replace" "eregi" "split" "spliti" "sql_regcase" ;; Session "session_cache_expire" "session_cache_limiter" "session_commit" "session_decode" "session_destroy" "session_encode" "session_get_cookie_params" "session_id" "session_is_registered" "session_module_name" "session_name" "session_regenerate_id" "session_register" "session_save_path" "session_set_cookie_params" "session_set_save_handler" "session_start" "session_unregister" "session_unset" "session_write_close" ;; Strings "addcslashes" "addslashes" "bin2hex" "chop" "chr" "chunk_split" "convert_cyr_string" "convert_uudecode" "convert_uuencode" "count_chars" "crc32" "crypt" "echo" "explode" "fprintf" "get_html_translation_table" "hebrev" "hebrevc" "html_entity_decode" "htmlentities" "htmlspecialchars_decode" "htmlspecialchars" "implode" "join" "levenshtein" "localeconv" "ltrim" "md5_file" "md5" "metaphone" "money_format" "nl_langinfo" "nl2br" "number_format" "ord" "parse_str" "print" "printf" "quoted_printable_decode" "quotemeta" "rtrim" "setlocale" "sha1_file" "sha1" "similar_text" "soundex" "sprintf" "sscanf" "str_ireplace" "str_pad" "str_repeat" "str_replace" "str_rot13" "str_shuffle" "str_split" "str_word_count" "strcasecmp" "strchr" "strcmp" "strcoll" "strcspn" "strip_tags" "stripcslashes" "stripos" "stripslashes" "stristr" "strlen" "strnatcasecmp" "strnatcmp" "strncasecmp" "strncmp" "strpbrk" "strpos" "strrchr" "strrev" "strripos" "strrpos" "strspn" "strstr" "strtok" "strtolower" "strtoupper" "strtr" "substr_compare" "substr_count" "substr_replace" "substr" "trim" "ucfirst" "ucwords" "vfprintf" "vprintf" "vsprintf" "wordwrap" ;; Variable "debug_zval_dump" "doubleval" "empty" "floatval" "get_defined_vars" "get_resource_type" "gettype" "import_request_variables" "intval" "is_array" "is_bool" "is_callable" "is_double" "is_float" "is_int" "is_integer" "is_long" "is_null" "is_numeric" "is_object" "is_real" "is_resource" "is_scalar" "is_string" "isset" "print_r" "serialize" "settype" "strval" "unserialize" "unset" "var_dump" "var_export" ;; XML "utf8_decode" "utf8_encode" "xml_error_string" "xml_get_current_byte_index" "xml_get_current_column_number" "xml_get_current_line_number" "xml_get_error_code" "xml_parse_into_struct" "xml_parse" "xml_parser_create_ns" "xml_parser_create" "xml_parser_free" "xml_parser_get_option" "xml_parser_set_option" "xml_set_character_data_handler" "xml_set_default_handler" "xml_set_element_handler" "xml_set_end_namespace_decl_handler" "xml_set_external_entity_ref_handler" "xml_set_notation_decl_handler" "xml_set_object" "xml_set_processing_instruction_handler" "xml_set_start_namespace_decl_handler" "xml_set_unparsed_entity_decl_handler" ) "PHP functions list") (defconst php-others '("class" "function") "PHP functions list that have a special highlight") (defun php-version () "Echo the current version of PHP Mode in the minibuffer." (interactive) (message "PHP Mode %s (%s)" php-version php-time-stamp) (php-keep-region-active)) ;; active regions (defun php-keep-region-active () "Do whatever is necessary to keep the region active in XEmacs. Ignore byte-compiler warnings you might see." (and (boundp 'zmacs-region-stays) (setq zmacs-region-stays t))) (defmacro php-prepare-search-1 (&rest body) "Enable case insensitive search and switch to syntax table that includes '_', then execute BODY, and finally restore the old environment. Used for consistent searching." `(let ((case-fold-search t) ; case insensitive search (current-syntax-table (syntax-table)) result (restore-prog ; program to restore enviroment '(progn ;; restore syntax table (set-syntax-table current-syntax-table)))) ;; use extended syntax table (set-syntax-table php-mode-ext-syntax-table) ;; execute BODY safely (setq result (condition-case info (progn ,@body) (error (eval restore-prog) ; restore environment on error (error (cadr info))))) ; pass error up ;; restore environment (eval restore-prog) result)) (defmacro php-prepare-search-2 (&rest body) "Enable case insensitive search, switch to syntax table that includes '_', and remove `intangible' overlays, then execute BODY, and finally restore the old environment. Used for consistent searching." `(let ((case-fold-search t) ; case insensitive search (current-syntax-table (syntax-table)) result overlay-all-list overlay-intangible-list overlay (restore-prog ; program to restore enviroment '(progn ;; restore syntax table (set-syntax-table current-syntax-table) ;; restore `intangible' overlays (when (fboundp 'overlay-lists) (while overlay-intangible-list (overlay-put (car overlay-intangible-list) 'intangible t) (setq overlay-intangible-list (cdr overlay-intangible-list))))))) ;; use extended syntax table (set-syntax-table php-mode-ext-syntax-table) ;; remove `intangible' overlays (when (fboundp 'overlay-lists) (setq overlay-all-list (overlay-lists)) (setq overlay-all-list (append (car overlay-all-list) (cdr overlay-all-list))) (while overlay-all-list (setq overlay (car overlay-all-list)) (when (memq 'intangible (overlay-properties overlay)) (setq overlay-intangible-list (cons overlay overlay-intangible-list)) (overlay-put overlay 'intangible nil)) (setq overlay-all-list (cdr overlay-all-list)))) ;; execute BODY safely (setq result (condition-case info (progn ,@body) (error (eval restore-prog) ; restore environment on error (error (cadr info))))) ; pass error up ;; restore environment (eval restore-prog) result)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Words to expand (defun php-words-init () "Initialize reserved words." (setq php-keywords php-01-keywords) (setq php-types php-01-types) (setq php-constants php-01-constants) (setq php-functions php-01-functions) (setq php-superglobals php-01-superglobals) (setq php-keywords-regexp (concat "\\<\\(" (regexp-opt php-keywords) "\\)\\>")) (setq php-types-regexp (concat "\\<\\(" (regexp-opt php-types) "\\)\\>")) (setq php-constants-regexp (concat "\\<\\(" (regexp-opt php-constants) "\\)\\>")) (setq php-functions-regexp (concat "\\<\\(" (regexp-opt php-functions) "\\)\\>")) (setq php-superglobals-regexp (concat "\\<\\(" (regexp-opt php-superglobals) "\\)\\>")) (php-abbrev-list-init)) (defvar php-abbrev-list nil "Predefined abbreviations for PHP.") (defun php-abbrev-list-init () (setq php-abbrev-list (append (list nil) php-keywords (list nil) php-types (list nil) php-constants (list nil) php-functions (list nil) php-superglobals (list nil) php-others))) (defvar php-expand-upper-case nil) (defun php-try-expand-abbrev (old) "Try expanding abbreviations from `php-abbrev-list'." (unless old (he-init-string (he-dabbrev-beg) (point)) (setq he-expand-list (let ((abbrev-list php-abbrev-list) (sel-abbrev-list '())) (while abbrev-list ; (if (stringp (car abbrev-list)) ; (insert (concat " " (car abbrev-list)))) (when (or (not (stringp (car abbrev-list))) (string-match (concat "^" he-search-string) (car abbrev-list))) (setq sel-abbrev-list (cons (car abbrev-list) sel-abbrev-list))) (setq abbrev-list (cdr abbrev-list))) (nreverse sel-abbrev-list)))) (while (and he-expand-list (or (not (stringp (car he-expand-list))) (he-string-member (car he-expand-list) he-tried-table t))) (unless (stringp (car he-expand-list)) (setq php-expand-upper-case (car he-expand-list))) (setq he-expand-list (cdr he-expand-list))) (if (null he-expand-list) (progn (when old (he-reset-string)) nil) (he-substitute-string (if php-expand-upper-case (upcase (car he-expand-list)) (car he-expand-list)) t) (setq he-expand-list (cdr he-expand-list)) t)) ;; initialize reserved words for PHP Mode (php-words-init) ;; function for expanding abbrevs and dabbrevs (defun php-expand-abbrev (arg)) (fset 'php-expand-abbrev (make-hippie-expand-function '(try-expand-dabbrev try-expand-dabbrev-all-buffers php-try-expand-abbrev))) ;; function for expanding parenthesis (defun php-expand-paren (arg)) (fset 'php-expand-paren (make-hippie-expand-function '(try-expand-list try-expand-list-all-buffers))) ;; Syntactic support functions: (defun php-in-comment-p () "Check if point is in a comment." (let (result (here (point)) begin) (save-excursion (beginning-of-line) (setq begin (point)) (goto-char here) ;; (if (or (or (re-search-backward "^\\s-*\#" begin t) ;; (re-search-backward "^\\s-*\/\/" begin t)) ;; (if (or (or (re-search-backward "^.*\#" begin t) ;; (re-search-backward "^.*\/\/" begin t)) (if (or (re-search-backward "^.*\/\/" begin t) (php-in-comment-p2)) (setq result t) (setq result nil))) result)) ;(eq (php-in-literal) 'comment)) (defun php-in-comment-p2 () "Check if point is in a comment '/* */' type" (let (result start-comment (here (point))) (save-excursion (setq result t) (setq start-comment (re-search-backward "\\/\\*" nil t)) (if (and start-comment (re-search-forward "\\*\\/" here t)) (setq result nil)) (if (not start-comment) (setq result nil))) result)) (defun php-in-string-p () "Check if point is in a string." (eq (php-in-literal) 'string)) (defun php-in-quote-p () "Check if point is in a quote ('x')." (or (and (> (point) (point-min)) (< (1+ (point)) (point-max)) (= (char-before (point)) ?\') (= (char-after (1+ (point))) ?\')) (and (> (1- (point)) (point-min)) (< (point) (point-max)) (= (char-before (1- (point))) ?\') (= (char-after (point)) ?\')))) (defun php-in-literal () "Determine if point is in a PHP literal." (save-excursion (let ((here (point)) start state) (beginning-of-line) (setq start (point)) (goto-char here) (setq state (parse-partial-sexp start (point))) (cond ((nth 3 state) 'string) ((nth 4 state) 'comment) ((php-beginning-of-macro) 'pound) (t nil))))) ;; Macro definitions: (defun php-beginning-of-macro (&optional lim) "Go to the beginning of a cpp macro definition (nicked from `cc-engine')." (let ((here (point))) (beginning-of-line) (while (eq (char-before (1- (point))) ?\\) (forward-line -1)) (back-to-indentation) (if (and (<= (point) here) (eq (char-after) ?#)) t (goto-char here) nil))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Electrification ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; correct different behavior of function `unread-command-events' in XEmacs (defun php-character-to-event (arg)) (defalias 'php-character-to-event (if (fboundp 'character-to-event) 'character-to-event 'identity)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Syntax table (defvar php-mode-syntax-table nil "Syntax table used in `php-mode' buffers.") (defvar php-mode-ext-syntax-table nil "Syntax table extended by `_' used in `php-mode' buffers.") (defun php-mode-syntax-table-init () "Initialize `php-mode-syntax-table'." (setq php-mode-syntax-table (make-syntax-table)) ;; define punctuation (modify-syntax-entry ?\# "< b" php-mode-syntax-table) (modify-syntax-entry ?\$ "." php-mode-syntax-table) (modify-syntax-entry ?\% "." php-mode-syntax-table) (modify-syntax-entry ?\& "." php-mode-syntax-table) (modify-syntax-entry ?\' "." php-mode-syntax-table) (modify-syntax-entry ?\+ "." php-mode-syntax-table) (modify-syntax-entry ?\. "." php-mode-syntax-table) (modify-syntax-entry ?\: "." php-mode-syntax-table) (modify-syntax-entry ?\; "." php-mode-syntax-table) (modify-syntax-entry ?\< "." php-mode-syntax-table) (modify-syntax-entry ?\= "." php-mode-syntax-table) (modify-syntax-entry ?\> "." php-mode-syntax-table) ;;(modify-syntax-entry ?\\ "w" php-mode-syntax-table) (modify-syntax-entry ?\| "." php-mode-syntax-table) ;; define string (modify-syntax-entry ?\" "\"" php-mode-syntax-table) (modify-syntax-entry ?\' "\"" php-mode-syntax-table) ;; define underscore (modify-syntax-entry ?\_ "w" php-mode-syntax-table) ;; a single hyphen is punctuation, but a double hyphen starts a comment (if xemacsp (modify-syntax-entry ?\/ ". 1456" php-mode-syntax-table) ;; for XEmacs (modify-syntax-entry ?\/ ". 124b" php-mode-syntax-table)) ;; for Emacs (modify-syntax-entry ?\* ". 23" php-mode-syntax-table) ;; and \n and \^M end a comment (modify-syntax-entry ?\n "> b" php-mode-syntax-table) (modify-syntax-entry ?\^M "> b" php-mode-syntax-table) ;; define parentheses to match (modify-syntax-entry ?\( "()" php-mode-syntax-table) (modify-syntax-entry ?\) ")(" php-mode-syntax-table) (modify-syntax-entry ?\[ "(]" php-mode-syntax-table) (modify-syntax-entry ?\] ")[" php-mode-syntax-table) (modify-syntax-entry ?\{ "(}" php-mode-syntax-table) (modify-syntax-entry ?\} "){" php-mode-syntax-table) ;; extended syntax table including '_' (for simpler search regexps) (setq php-mode-ext-syntax-table (copy-syntax-table php-mode-syntax-table)) (modify-syntax-entry ?_ "w" php-mode-ext-syntax-table)) ;; initialize syntax table for PHP Mode (php-mode-syntax-table-init) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Mode map ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defun php-mode-map-init () "Initialize `php-mode-map'." (setq php-mode-map (make-sparse-keymap)) ;; template key bindings ;;(define-key php-mode-map "\C-c\C-t" php-template-map) ;; mode specific key bindings (define-key php-mode-map "\C-c\C-m\C-e" 'php-electric-mode) (define-key php-mode-map "\C-c\C-m\C-s" 'php-stutter-mode) (define-key php-mode-map "\C-c\C-s\C-u" 'php-add-source-files-menu) (define-key php-mode-map "\M-\C-\\" 'php-indent-region) (define-key php-mode-map "\C-c\C-i\C-b" 'php-indent-buffer) (define-key php-mode-map "\C-c\M-m" 'php-show-messages) (define-key php-mode-map "\C-c\C-h" 'php-doc-mode) (define-key php-mode-map "\C-c\C-v" 'php-version) (define-key php-mode-map "\M-\t" 'insert-tab) ;; electric key bindings ;(define-key php-mode-map " " 'php-electric-space) (when php-intelligent-tab (define-key php-mode-map "\t" 'php-electric-tab)) (define-key php-mode-map "\r" 'php-electric-return) (define-key php-mode-map "*" 'php-electric-star) (define-key php-mode-map "?" 'php-electric-interrogation-point) ;(define-key php-mode-map "(" 'php-electric-open-bracket) ; + ;(define-key php-mode-map ")" 'php-electric-close-bracket) ; + (define-key php-mode-map "}" 'php-electric-close-bracket2) ; + ;(define-key php-mode-map "'" 'php-electric-quote) ;(define-key php-mode-map ";" 'php-electric-semicolon) ; + ;(define-key php-mode-map "," 'php-electric-comma) ;(define-key php-mode-map "." 'php-electric-period) ) ; + ;; initialize mode map for PHP Mode (php-mode-map-init) ;; set up electric character functions to work with ;; `delete-selection-mode' (Emacs) and `pending-delete-mode' (XEmacs) (mapcar (function (lambda (sym) (put sym 'delete-selection t) ; for `delete-selection-mode' (Emacs) (put sym 'pending-delete t))) ; for `pending-delete-mode' (XEmacs) '(php-electric-space php-electric-tab php-electric-return php-electric-star php-electric-interrogation-point ;php-electric-dash php-electric-open-bracket php-electric-close-bracket php-electric-close-bracket2 ;php-electric-quote php-electric-semicolon)) ;php-electric-comma ;php-electric-period ;php-electric-equal)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Indentation commands (defun php-electric-tab (&optional prefix-arg) "If preceding character is part of a word or a paren then hippie-expand, else if right of non whitespace on line then insert tab, else if last command was a tab or return then dedent one step or if a comment toggle between normal indent and inline comment indent, else indent `correctly'." (interactive "*P") (php-prepare-search-2 (cond ;; expand word ((= (char-syntax (preceding-char)) ?w) (let ((case-fold-search (not php-word-completion-case-sensitive)) (case-replace nil) (hippie-expand-only-buffers (or (and (boundp 'hippie-expand-only-buffers) hippie-expand-only-buffers) '(php-mode)))) (php-expand-abbrev prefix-arg))) ;; expand parenthesis ((or (= (preceding-char) ?\() (= (preceding-char) ?\))) (let ((case-fold-search (not php-word-completion-case-sensitive)) (case-replace nil)) (php-expand-paren prefix-arg))) ;; insert tab ((> (current-column) (current-indentation)) (insert-tab)) ;; toggle comment indent ((and (looking-at "//") (or (eq last-command 'php-electric-tab) (eq last-command 'php-electric-return))) (cond ((= (current-indentation) 0) ; no indent (indent-to 1) (indent-according-to-mode)) ((< (current-indentation) comment-column) ; normal indent (indent-to comment-column) (indent-according-to-mode)) (t ; inline comment indent (kill-line -0)))) ;; dedent ((and (>= (current-indentation) php-basic-offset) (or (eq last-command 'php-electric-tab) (eq last-command 'php-electric-return))) (backward-delete-char-untabify php-basic-offset nil)) ;; indent line (t (indent-according-to-mode))) (setq this-command 'php-electric-tab))) (defun php-electric-return () "newline-and-indent or indent-new-comment-line if in comment and preceding character is a space." (interactive) (if (php-in-comment-p2) (php-indent-new-comment-line) (newline-and-indent))) (defun php-electric-space (count) "Expand abbreviations and self-insert space(s), do indent-new-comment-line if in comment and past end-comment-column." (interactive "p") (cond ((php-in-comment-p) (self-insert-command count) (cond ((>= (current-column) (+ 2 end-comment-column)) (backward-char 1) (skip-chars-backward "^ \t\n") (indent-new-comment-line) (skip-chars-forward "^ \t\n") (forward-char 1)) ((>= (current-column) end-comment-column) (indent-new-comment-line)) (t nil))) ((or (and (>= (preceding-char) ?a) (<= (preceding-char) ?z)) (and (>= (preceding-char) ?A) (<= (preceding-char) ?Z))) (php-prepare-search-1 (expand-abbrev)) ;(or (expand-abbrev) (php-fix-case-word -1))) (self-insert-command count)) (t (self-insert-command count)))) (defun php-electric-star (count) "/** start a comment paragraph" (interactive "p") (if (and php-stutter-mode (not (php-in-literal))) (if (= (preceding-char) ?\/) (progn (insert "*") (message "Enter '*' for paragraph, 'SP' for commeting-out code") (let ((next-input (read-char)) here) (if (= next-input ?\*) (progn (insert "* \n") (insert "* ") (php-indent-line-2) (end-of-line) (setq here (point)) (insert "\n*/") (php-indent-line-2) (goto-char here)) (insert " ") (setq here (point)) (insert " */") (goto-char here)))) (self-insert-command count)) (self-insert-command count))) (defun php-electric-interrogation-point (count) "Add '?>' and 'php' to the '\n")) (progn (goto-char beg-pos) (forward-char 1) (insert "?php ") (setq here (point-marker)) (insert " ?>"))) (goto-char here)) (self-insert-command count)) (self-insert-command count)))) (defun php-electric-semicolon (count) "Add a new line after a ';'" (interactive "p") (setq start (point-marker)) (setq for-loop (re-search-backward "[;]*[ \t\n]for\s*(\s*" nil t)) (if for-loop (progn (setq for-point (point-marker)) (goto-char start) (setq for-loop (not (re-search-backward "[;]*[ \t\n]for\s*(\s*.*;.*;.*)" for-point t))))) (goto-char start) (if (and php-stutter-mode (not (php-in-comment-p)) (not (php-in-literal)) (not for-loop)) (progn (insert ";") (newline-and-indent)) (self-insert-command count))) (defun php-electric-open-bracket (count) "'(' --> '(', '((' --> '[', '[(' --> '{'" (interactive "p") (if (and php-stutter-mode (= count 1) (not (php-in-literal))) (if (= (preceding-char) ?\() (progn (delete-char -1) (insert-char ?\[ 1)) (if (= (preceding-char) ?\[) (progn (delete-char -1) (insert-char ?\{ 1)) (insert-char ?\( 1))) (self-insert-command count))) (defun php-electric-close-bracket (count) "')' --> ')', '))' --> ']', '])' --> '}'" (interactive "p") (if (and php-stutter-mode (= count 1) (not (php-in-literal))) (progn (if (= (preceding-char) ?\)) (progn (delete-char -1) (insert-char ?\] 1)) (if (= (preceding-char) ?\]) (progn (delete-char -1) (insert-char ?} 1) (save-excursion (php-indent-line-2))) (insert-char ?\) 1))) (blink-matching-open)) (self-insert-command count))) (defun php-electric-close-bracket2 (count) "Automatic indent" (interactive "p") (if (and php-stutter-mode (not (php-in-literal))) (progn (insert "}") (save-excursion (php-indent-line-2))) (self-insert-command count))) (defun php-indent-new-comment-line () "Add a new line, indent it and comment it" (interactive) (let ((here (point)) found) (setq found (re-search-backward "^\\s-*\\*" (php-point 'bol) t)) (goto-char here) (newline-and-indent) (if found (insert "* ")))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Enabling/disabling (defun php-mode-line-update () "Update the modeline string for PHP major mode." (setq mode-name (concat "PHP" (and (or php-electric-mode php-stutter-mode) "/") (and php-electric-mode "e") (and php-stutter-mode "s"))) (force-mode-line-update t)) (defun php-electric-mode (arg) "Toggle PHP electric mode. Turn on if ARG positive, turn off if ARG negative, toggle if ARG zero or nil." (interactive "P") (setq php-electric-mode (cond ((or (not arg) (zerop arg)) (not php-electric-mode)) ((> arg 0) t) (t nil))) (php-mode-line-update)) (defun php-stutter-mode (arg) "Toggle PHP stuttering mode. Turn on if ARG positive, turn off if ARG negative, toggle if ARG zero or nil." (interactive "P") (setq php-stutter-mode (cond ((or (not arg) (zerop arg)) (not php-stutter-mode)) ((> arg 0) t) (t nil))) (php-mode-line-update)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Fontification ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Set up font locking (defconst php-font-lock-keywords-1 (list ;; Fontify constants (cons (concat "\\<\\(" php-constants-regexp "\\)\\>") 'font-lock-constant-face) ;; Fontify keywords (cons (concat "\\<\\(" php-keywords-regexp "\\)\\>") 'font-lock-keyword-face) (cons (concat "\\<\\(" php-functions-regexp "\\)\\>") 'font-lock-keyword-face) ;; Fontify keywords and targets, and case default tags. (list "\\<\\(break\\|case\\|continue\\)\\>[ \t]*\\(-?\\(?:\\sw\\|\\s_\\)+\\)?" '(1 font-lock-keyword-face) '(2 font-lock-constant-face t t)) ;; This must come after the one for keywords and targets. '(":" ("^[ \t]*\\(\\(?:\\sw\\|\\s_\\)+\\)[ \t]*:[ \t]*$" (beginning-of-line) (end-of-line) (1 font-lock-constant-face))) ;; treat 'print' as keyword only when not used like a function name '("\\" . font-lock-keyword-face) ;; Fontify PHP tag '("<\\?\\(php\\)?" . font-lock-constant-face) '("\\?>" . font-lock-constant-face) ;; Fontify ASP-style tag '("<\\%\\(=\\)?" . font-lock-constant-face) '("\\%>" . font-lock-constant-face) '("\/\/\\([A-z ;:.,?!-éࡦ]*\\)$" . font-lock-comment-face) '("\/\/.*" . font-lock-comment-face) ) "Subdued level highlighting for PHP mode.") (defconst php-font-lock-keywords-2 (append php-font-lock-keywords-1 (list ;; class declaration '("\\<\\(class\\|interface\\)[ \t]*\\(\\(?:\\sw\\|\\s_\\)+\\)?" (1 font-lock-keyword-face) (2 font-lock-type-face nil t)) ;; handle several words specially, to include following word, ;; thereby excluding it from unknown-symbol checks later ;; FIX to handle implementing multiple ;; currently breaks on "class Foo implements Bar, Baz" '("\\<\\(new\\|extends\\|implements\\)\\s-+\\$?\\(\\(?:\\sw\\|\\s_\\)+\\)" (1 font-lock-keyword-face) (2 font-lock-type-face)) ;; function declaration '("\\<\\(function\\)\\s-+&?\\(\\(?:\\sw\\|\\s_\\)+\\)\\s-*(" (1 font-lock-keyword-face) (2 font-lock-function-name-face nil t)) ;; class hierarchy '("[^A-z0-9_]\\(self\\|parent\\)\\W" (1 font-lock-constant-face nil nil)) ;; method and variable features '("\\<\\(private\\|protected\\|public\\)\\s-+\\$?\\(?:\\sw\\|\\s_\\)+" (1 font-lock-keyword-face)) ;; method features '("^[ \t]*\\(abstract\\|static\\|final\\)\\s-+\\$?\\(?:\\sw\\|\\s_\\)+" (1 font-lock-keyword-face)) ;; variable features '("^[ \t]*\\(static\\|const\\)\\s-+\\$?\\(?:\\sw\\|\\s_\\)+" (1 font-lock-keyword-face)) )) "Medium level highlighting for PHP mode.") (defconst php-font-lock-keywords-3 (append php-font-lock-keywords-2 (list ;; or for HTML '("]*>" . font-lock-constant-face) ;; HTML entities '("&\\w+;" . font-lock-variable-name-face) ;; warn about '$' immediately after -> ;;;;'("\\$\\(?:\\sw\\|\\s_\\)+->\\s-*\\(\\$\\)\\(\\(?:\\sw\\|\\s_\\)+\\)" ;;;; (1 font-lock-warning-face) (2 default)) '("\\$\\(?:\\sw\\|\\s_\\)+->\\s-*\\(\\$\\)\\(\\(?:\\sw\\|\\s_\\)+\\)" (if (not (php-in-comment-p)))(progn (1 font-lock-warning-face) (2 default))) ;; warn about $word.word -- it could be a valid concatenation, ;; but without any spaces we'll assume $word->word was meant. '("\\$\\(?:\\sw\\|\\s_\\)+\\(\\.\\)\\sw" 1 font-lock-warning-face) ;; Warn about ==> instead of => '("==+>" . font-lock-warning-face) ;; exclude casts from bare-word treatment (may contain spaces) `(,(concat "(\\s-*\\(" php-types-regexp "\\)\\s-*)") 1 font-lock-type-face) ;; PHP5: function declarations may contain classes as parameters type `(,(concat "[(,]\\s-*\\(\\(?:\\sw\\|\\s_\\)+\\)\\s-+\\$\\(?:\\sw\\|\\s_\\)+\\>") 1 font-lock-type-face) ;; Fontify variables and function calls '("\\$\\(this\\|that\\)\\W" (1 font-lock-constant-face nil nil)) `(,(concat "\\$\\(" php-superglobals-regexp "\\)\\W") (1 font-lock-constant-face nil nil)) ; $_GET & co '("\\$\\(\\(?:\\sw\\|\\s_\\)+\\)" (1 font-lock-variable-name-face)) ; $variable '("->\\(\\(?:\\sw\\|\\s_\\)+\\)" (1 font-lock-variable-name-face t t)) ; ->variable '("->\\(\\(?:\\sw\\|\\s_\\)+\\)\\s-*(" . (1 default t t)) ; ->function_call '("\\(?:\\sw\\|\\s_\\)+::\\(?:\\sw\\|\\s_\\)+\\s-*(" . default) ; class::method call '("\\<\\(?:\\sw\\|\\s_\\)+\\s-*[[(]" . default) ; word( or word[ '("\\<[0-9]+" . default) ; number (also matches word) ;; Warn on any words not already fontified '("\\<\\(?:\\sw\\|\\s_\\)+\\>" . font-lock-warning-face) )) "Gauchy level highlighting for PHP mode.") (defconst php-font-lock-syntactic-keywords (if xemacsp nil ;; Mark shell-style comments. font-lock handles this in a ;; separate pass from normal syntactic scanning (somehow), so we ;; get a chance to mark these in addition to C and C++ style ;; comments. This only works in GNU Emacs, not XEmacs 21 which ;; seems to ignore this same code if we try to use it. (list ;; Mark _all_ # chars as being comment-start. That will be ;; ignored when inside a quoted string. ;'("\\(\#\\)" (1 (11 . nil))) '("\\(//\\)" (1 (11 . nil))) ;; Mark all newlines ending a line with # as being comment-end. ;; This causes a problem, premature end-of-comment, when '#' ;; appears inside a multiline C-style comment. Oh well. ;'("#.*\\([\n]\\)" (1 (12 . nil))) '("//.*\\([\n]\\)" (1 (12 . nil))) ))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; PHP-mode ;;;###autoload (defun php-mode () "PHP Mode ******** PHP Mode is a GNU XEmacs major mode for editing files containing PHP code. 1 Introduction ************** Inspired from PHP-Mode (http://php-mode.sourceforge.net/) and from VHDL-Mode (http://www.iis.ee.ethz.ch/~zimmi/emacs/vhdl-mode.html), this new PHP Mode combines the advantages of the two modes to simplify the writing of PHP code: highlight, indentation, completion, templates, navigation into the file (functions and class), navigation into source files... Features (new features in bold) : * Completion * Customizable * Highlight * Indentation * Menu * Stuttering This manual describes PHP Mode version 0.0.4. 2 Installation ************** 2.1 Requirements ================ PHP Mode is a XEmacs major mode that needs the following software/packages: * XEmacs (http://www.xemacs.org/). * `font-lock' mode generaly installed with XEmacs. * `assoc' mode generaly installed with XEmacs. * `easymenu' mode generaly installed with XEmacs. * `hippie-exp' mode generaly installed with XEmacs. Before continuing, you must be sure to have all this packages installed. 2.2 Download ============ Two internet address to download PHP Mode : * Principal: PHP-Mode 0.0.4 (http://deboutv.free.fr/lisp/php/download/php-0.0.4.tar.gz) (http://deboutv.free.fr/lisp/php/) * Secondary: PHP-Mode 0.0.4 (http://www.morinie.fr/lisp/php/download/php-0.0.4.tar.gz) (http://www.morinie.fr/lisp/php/) * Old releases: PHP-Mode (http://deboutv.free.fr/lisp/php/download.php) (http://deboutv.free.fr/lisp/php/) 2.3 Installation ================ 2.3.1 Installation ------------------ To install PHP Mode you need to choose an installation directory (for example `/usr/local/share/lisp' or `c:\lisp'). The administrator must have the write rights on this directory. With your favorite unzip software, unzip the archive in the installation directory. Example: cd /usr/local/share/lisp tar zxvf php-0.0.4.tar.gz Now you have a `php' directory in the installation directory. This directory contains 2 files `php-mode.el' and `php-mode.elc' and another directory `docs' containing the documentation. You need to configure XEmacs. open you initialization file `init.el' (open the file or start XEmacs then choose the Options menu and Edit Init File). Add the following lines (the installation directory in this example is `/usr/local/share/lisp') : (setq load-path (append (list \"/usr/local/share/lisp/\") load-path)) (autoload 'php-mode \"php-mode\" \"PHP mode\" t) 2.3.2 Update ------------ The update is easy. You need to unzip the archive in the installation directory to remove the old release. Example: cd /usr/local/share/lisp rm -rf php tar zxvf php-0.0.4.tar.gz 2.4 Invoke PHP-Mode =================== You have two possibilities to invoke the PHP Mode. - Manually: At each file opening you need to launch PHP Mode with the following command: `M-x php-mode' - Automatically: Add the following linesin your initialization file `init.el' : (setq auto-mode-alist (append '((\"\\.php$\" . php-mode) (\"\\.php5$\" . php-mode)) auto-mode-alist)) 3 Customization *************** This chapter describes the differents parameters and functions that you can change to customize PHP Mode. To do that, open a PHP file, click on the PHP menu and choose Options then Browse Options.... 3.1 Parameters ============== 3.1.1 Mode ---------- PHP Mode has 3 modes allowing to simplify the writing of PHP code. You can enable/disable each mode individually. `php-electric-mode' Type: boolean Default value: `t' Description: If `t'; enable automatic generation of template. If `nil'; template generators can still be invoked through key bindings and menu. Is indicated in the modeline by \"/e\" after the mode name and can be toggled by `php-electric-mode'. `php-stutter-mode' Type: boolean Default value: `t' Description: If `t'; enable the stuttering. Is indicated in the modeline by \"/s\" after the mode name and can be toggled by `php-stutter-mode'. `php-indent-tabs-mode' Type: boolean Default value: `nil' Description: If `t'; indentation is made with tabs. If `nil'; indentation is made with spaces. 3.1.2 Menu ---------- PHP Mode has also 2 menus tha you can enable/disable. The menus Index and Sources are specific to each PHP files opened. `php-index-menu' Type: boolean Default value: `t' Description: If `t'; the Index menu is enabled. It shows the list of class and functions of the opened file. The Index menu scans the file when it is opened. `php-index-menu-auto-rescan' Type: boolean Default value: `t' Description: If `t'; the Index menu is updated when a function or a class is added or removed. `php-source-file-menu' Type: boolean Default value: `t' Description: If `t'; the Sources menu is enabled. This menu contains the list of PHP file located in the current directory. The Sources menu scans the directory when a file is opened. 3.1.4 Style ----------- `php-basic-offset' Type: integer Default value: `4' Description: Amount of basic offset used for indentation. 3.1.5 PHPDocumentor ------------------- `php-enable-phpdocumentor-tags' Type: bool Default value: `t' Description: If `t'; PHP-Documentor tags are added into the class and functions comments. `php-class-tags' Type: list (string) Default value: `'(\"package\")' Description: List of PHP-Documentor tags to add into the class comments. `php-function-tags' Type: list (string) Default value: `'()' Description: List of PHP-Documentor tags to add into the functions comments. 3.1.6 Miscellaneous ------------------- `php-intelligent-tab' Type: bool Default value: `t' Description: If `t'; TAB does indentation; completion and insert tabulations. If `nil'; TAB does only indentation. `php-word-completion-case-sensitive' Type: bool Default value: `nil' Description: If `t'; completion is case sensitive. 3.2 Functions ============= 3.2.1 Mode ---------- `php-electric-mode' Menu: PHP -> Options -> Mode -> Electric Mode Keybinding: `C-c C-m C-e' Description: This functions is used to enable/disable the electric mode. `php-stutter-mode' Menu: PHP -> Options -> Mode -> Stutter Mode Keybinding: `C-c C-m C-s' Description: This function is used to enable/disable the stutter mode. 4 Menus ******* There are 3 menus: PHP, Index and Sources. All theses menus can be accessed from the menubar or from the right click. This chapter describes each menus. 4.1 PHP ======= This is the main menu of PHP Mode. It allows an easy access to the main features of the PHP Mode: Templates (see *Note Templates::), Indentation (see *Note Indentation::) and Options (see *Note Customization::). This menu contains also 3 functions that are discussed in the next part. 4.1.1 Functions --------------- `php-show-messages' Menu: PHP -> Show Messages Keybinding: `C-c M-m' Description: This function opens the *Messages* buffer to display previous error messages. `php-doc-mode' Menu: PHP -> PHP Mode Documentation Keybinding: `C-c C-h' Description: This function opens the *Help* buffer and prints in it the PHP Mode documentation. `php-version' Menu: PHP -> Version Keybinding: `C-c C-v' Description: This function displays in the minibuffer the current PHP Mode version with the timestamp. 4.2 Index ========= The Index menu allows you to navigate into the current buffer to find functions and classes. This menu is built during the buffer openning and you need to refresh it if you add or remove functions or classes. There is one Index menu per buffer. 4.2.1 Customization ------------------- `php-index-menu' Type: boolean Default value: `t' Description: If `t'; the Index menu is enabled. It shows the list of class and functions of the opened file. The Index menu scans the file when it is opened. `php-index-menu-auto-rescan' Type: boolean Default value: `t' Description: If `t'; the Index menu is updated when a function or a class is added or removed. 4.3 Sources =========== The Sources menu shows the PHP files in the current directory. If you add or delete a file in the current directory, you need to refresh the menu. 4.3.1 Customization ------------------- `php-source-file-menu' Type: boolean Default value: `t' Description: If `t'; the Sources menu is enabled. This menu contains the list of PHP file located in the current directory. The Sources menu scans the directory when a file is opened. 4.3.2 Functions --------------- `php-add-source-files-menu' Menu: Sources -> *Rescan* Keybinding: `C-c C-s C-u' Description: This function is used to refresh the Sources menu. 5 Completion ************ Completion allows you to write PHP code by reducing errors in function names or variable names. It also suggests PHP function names. To use completion, write the first letters and use to complete the word until you find the perfect completion. For example, in a blank PHP buffer, type \"mysql_\" and complete the function name with : all mysql_* functions are proposed. Completion can occurs with the supported PHP functions, the PHP code written in the current buffer and the PHP code written in all other files that are opened in the current XEmacs window. Completion is customizable. 5.1 Customization ================= `php-intelligent-tab' Type: bool Default value: `t' Description: If `t'; TAB does indentation; completion and insert tabulations. If `nil'; TAB does only indentation. `php-word-completion-case-sensitive' Type: bool Default value: `nil' Description: If `t'; completion is case sensitive. 6 Indentation ************* Indentation can be done with spaces or TAB characters. A new line is automtically indented. Tou can manually indent or remove indentation by placing the cursor at the beginning of the first word and by using the key. 6.1 Customization ================= `php-indent-tabs-mode' Type: boolean Default value: `nil' Description: If `t'; indentation is made with tabs. If `nil'; indentation is made with spaces. `php-basic-offset' Type: integer Default value: `4' Description: Amount of basic offset used for indentation. 6.2 Functions ============= `indent-according-to-mode' Menu: PHP -> Indent -> Line Keybinding: `C-c C-i C-l' Description: This function is used to indent the current line (the line where the cursor is). `php-indent-region' Menu: PHP -> Indent -> Region Keybinding: `M-C-, This function is used to indent the selected region in the current buffer.' Description: `php-indent-buffer' Menu: PHP -> Indent -> Buffer Keybinding: `C-c C-i C-b' Description: This function is used to indent the buffer. 7 Stuttering ************ The stutter mode is a mode that affects a function to a key. For example, when you use the `ENTER' key, the associated function will create a new line and indent it. 7.1 Customization ================= `php-stutter-mode' Type: boolean Default value: `t' Description: If `t'; enable the stuttering. Is indicated in the modeline by \"/s\" after the mode name and can be toggled by `php-stutter-mode'. 7.2 Functions ============= `SPACE' If in comment, indent the comment and add new line if necessary. In other case, add a space. `ENTER' Insert a new line and indent it. `}' Insert a new line and indent it. `;' Insert a new line and indent it. `*' If the previous character is a `/', a prompt will ask you for inserting a `/* */' comment type (with the `SPACE' key) or `/** * */' coment type (with the `*' key). `(' If the previous character is a `(', the `((' will be replaced by `['. If the previous character is a `[', the `[(' will be replaced by `{'. In other case, insert a `('. `)' If the previous character is a `)', the `))' will be replaced by `]'. If the previous character is a `]', the `])' will be replaced by `}'. In other case, insert a `)'. 8 Templates *********** In the PHP Mode, the PHP functions (like if, while, for, fopen, fclose) are predefined in functions called \"Templates\". Each template can be invoked by the function name or by using the key after the PHP function name in the buffer (Note, using `M-' disable the template). A template can be aborted by using the `C-g' or by lefting empty the tempate prompt (in the minibuffer). 8.1 Customization ================= `php-electric-mode' Type: boolean Default value: `t' Description: If `t'; enable automatic generation of template. If `nil'; template generators can still be invoked through key bindings and menu. Is indicated in the modeline by \"/e\" after the mode name and can be toggled by `php-electric-mode'. For a complete description of the template customizable variables, see *Note Cu01-Pa01-Template:: 9 Bugs, Help ************ * To report bugs: Bugtracker (http://bugtracker.morinie.fr/lisp/) * To obtain help you can post on the dedicated forum: Forum (http://forum.morinie.fr/lisp/) 10 Key bindings *************** \\{php-mode-map}" (interactive) (kill-all-local-variables) (setq major-mode 'php-mode) (setq mode-name "PHP") ;; set maps and tables (use-local-map php-mode-map) (set-syntax-table php-mode-syntax-table) ;; set local variables (set (make-local-variable 'parse-sexp-ignore-comments) t) (set (make-local-variable 'indent-line-function) 'php-indent-line-2) (set (make-local-variable 'comment-start) "/* ") (set (make-local-variable 'comment-end) " */") (set (make-local-variable 'comment-start-skip) "/\\*+ *") (set (make-local-variable 'comment-column) 32) (set (make-local-variable 'end-comment-column) 200) ; + default=70 (set (make-local-variable 'comment-indent-function) 'php-comment-indent) (set (make-local-variable 'comment-multi-line) t) (set (make-local-variable 'indent-tabs-mode) nil) (set (make-local-variable 'hippie-expand-verbose) nil) ;; (set (make-local-variable 'font-lock-support-mode) 'lazy-lock-mode) ;; (set (make-local-variable 'lazy-lock-defer-contextually) nil) ;; (set (make-local-variable 'lazy-lock-defer-on-the-fly) t) ;; (set (make-local-variable 'lazy-lock-defer-on-scrolling) t) ;; add index menu (php-index-menu-init) ;; add source file menu (if php-source-file-menu (php-add-source-files-menu)) ;; add PHP menu (easy-menu-add php-mode-menu-list) (easy-menu-define php-mode-menu php-mode-map "Menu keymap for PHP Mode." php-mode-menu-list) (make-local-variable 'font-lock-defaults) (setq font-lock-defaults '((php-font-lock-keywords-1 php-font-lock-keywords-2 ;; Comment-out the next line if the font-coloring is too ;; extreme/ugly for you. php-font-lock-keywords-3) nil ; KEYWORDS-ONLY t ; CASE-FOLD nil ; SYNTAX-ALIST nil ; SYNTAX-BEGIN (font-lock-syntactic-keywords . php-font-lock-syntactic-keywords))) ;; miscellaneous (message "PHP Mode %s.%s" php-version (if noninteractive "" " See menu for documentation and release notes.")) (php-mode-line-update) ;; run hooks (run-hooks 'php-mode-hook)) (defun php-activate-customizations () "Activate all customizations on local variables." (interactive) (php-mode-map-init) (use-local-map php-mode-map) (set-syntax-table php-mode-syntax-table) (php-update-mode-menu) (run-hooks 'menu-bar-update-hook) (php-mode-line-update)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Template functions utilities ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defun php-doc-mode () "Display PHP Mode documentation in *Help* buffer." (interactive) ;;(unless php-xemacs ;; (help-setup-xref (list #'php-doc-mode) (interactive-p))) (with-output-to-temp-buffer (if (fboundp 'help-buffer) (help-buffer) "*Help*") (princ mode-name) (princ " mode:\n") (princ (documentation 'php-mode)) (with-current-buffer standard-output (help-mode)) (print-help-return-message))) (provide 'php-mode)