]> code.delx.au - gnu-emacs/blobdiff - lisp/progmodes/perl-mode.el
Add bug-gnu-emacs to some specialized bug report addresses
[gnu-emacs] / lisp / progmodes / perl-mode.el
index aab5f8f65a49abf219767134a451ba1bbbe39103..bd58a7300ec9ba3ce5cfa6f6b2664b09f241d5fe 100644 (file)
@@ -1,6 +1,6 @@
-;;; perl-mode.el --- Perl code editing commands for GNU Emacs
+;;; perl-mode.el --- Perl code editing commands for GNU Emacs  -*- coding: utf-8 -*-
 
-;; Copyright (C) 1990, 1994, 2001-201 Free Software Foundation, Inc.
+;; Copyright (C) 1990, 1994, 2001-2013 Free Software Foundation, Inc.
 
 ;; Author: William F. Mann
 ;; Maintainer: FSF
 ;;; Commentary:
 
 ;; To enter perl-mode automatically, add (autoload 'perl-mode "perl-mode")
-;; to your .emacs file and change the first line of your perl script to:
+;; to your init file and change the first line of your perl script to:
 ;; #!/usr/bin/perl --   # -*-Perl-*-
 ;; With arguments to perl:
 ;; #!/usr/bin/perl -P-  # -*-Perl-*-
 ;; To handle files included with do 'filename.pl';, add something like
 ;; (setq auto-mode-alist (append (list (cons "\\.pl\\'" 'perl-mode))
 ;;                               auto-mode-alist))
-;; to your .emacs file; otherwise the .pl suffix defaults to prolog-mode.
+;; to your init file; otherwise the .pl suffix defaults to prolog-mode.
 
 ;; This code is based on the 18.53 version c-mode.el, with extensive
 ;; rewriting.  Most of the features of c-mode survived intact.
 
 ;;; Code:
 
-(eval-when-compile (require 'cl))
-
-(defvar font-lock-comment-face)
-(defvar font-lock-doc-face)
-(defvar font-lock-string-face)
-
 (defgroup perl nil
   "Major mode for editing Perl code."
   :link '(custom-group-link :tag "Font Lock Faces group" font-lock-faces)
 
 (defvar perl-mode-map
   (let ((map (make-sparse-keymap)))
-    (define-key map "{" 'perl-electric-terminator)
-    (define-key map "}" 'perl-electric-terminator)
-    (define-key map ";" 'perl-electric-terminator)
-    (define-key map ":" 'perl-electric-terminator)
     (define-key map "\e\C-a" 'perl-beginning-of-function)
     (define-key map "\e\C-e" 'perl-end-of-function)
     (define-key map "\e\C-h" 'perl-mark-function)
     (define-key map "\e\C-q" 'perl-indent-exp)
     (define-key map "\177" 'backward-delete-char-untabify)
-    (define-key map "\t" 'perl-indent-command)
     map)
   "Keymap used in Perl mode.")
 
-(autoload 'c-macro-expand "cmacexp"
-  "Display the result of expanding all C macros occurring in the region.
-The expansion is entirely correct because it uses the C preprocessor."
-  t)
-
 (defvar perl-mode-syntax-table
   (let ((st (make-syntax-table (standard-syntax-table))))
     (modify-syntax-entry ?\n ">" st)
@@ -164,16 +148,54 @@ The expansion is entirely correct because it uses the C preprocessor."
 
 (defvar perl-imenu-generic-expression
   '(;; Functions
-    (nil "^sub\\s-+\\([-A-Za-z0-9+_:]+\\)" 1)
+    (nil "^[ \t]*sub\\s-+\\([-A-Za-z0-9+_:]+\\)" 1)
     ;;Variables
     ("Variables" "^\\(?:my\\|our\\)\\s-+\\([$@%][-A-Za-z0-9+_:]+\\)\\s-*=" 1)
-    ("Packages" "^package\\s-+\\([-A-Za-z0-9+_:]+\\);" 1)
+    ("Packages" "^[ \t]*package\\s-+\\([-A-Za-z0-9+_:]+\\);" 1)
     ("Doc sections" "^=head[0-9][ \t]+\\(.*\\)" 1))
   "Imenu generic expression for Perl mode.  See `imenu-generic-expression'.")
 
 ;; Regexps updated with help from Tom Tromey <tromey@cambric.colorado.edu> and
 ;; Jim Campbell <jec@murzim.ca.boeing.com>.
 
+(defcustom perl-prettify-symbols t
+  "If non-nil, some symbols will be displayed using Unicode chars."
+  :type 'boolean)
+
+(defconst perl--prettify-symbols-alist
+  '(;;("andalso" . ?∧) ("orelse"  . ?∨) ("as" . ?≡)("not" . ?¬)
+    ;;("div" . ?÷) ("*"   . ?×) ("o"   . ?○)
+    ("->"  . ?→)
+    ("=>"  . ?⇒)
+    ;;("<-"  . ?←) ("<>"  . ?≠) (">="  . ?≥) ("<="  . ?≤) ("..." . ?⋯)
+    ("::" . ?∷)
+    ))
+
+(defun perl--font-lock-compose-symbol ()
+  "Compose a sequence of ascii chars into a symbol.
+Regexp match data 0 points to the chars."
+  ;; Check that the chars should really be composed into a symbol.
+  (let* ((start (match-beginning 0))
+        (end (match-end 0))
+        (syntaxes (if (eq (char-syntax (char-after start)) ?w)
+                      '(?w) '(?. ?\\))))
+    (if (or (memq (char-syntax (or (char-before start) ?\ )) syntaxes)
+           (memq (char-syntax (or (char-after end) ?\ )) syntaxes)
+            (nth 8 (syntax-ppss)))
+       ;; No composition for you.  Let's actually remove any composition
+       ;; we may have added earlier and which is now incorrect.
+       (remove-text-properties start end '(composition))
+      ;; That's a symbol alright, so add the composition.
+      (compose-region start end (cdr (assoc (match-string 0)
+                                            perl--prettify-symbols-alist)))))
+  ;; Return nil because we're not adding any face property.
+  nil)
+
+(defun perl--font-lock-symbols-keywords ()
+  (when perl-prettify-symbols
+    `((,(regexp-opt (mapcar 'car perl--prettify-symbols-alist) t)
+       (0 (perl--font-lock-compose-symbol))))))
+
 (defconst perl-font-lock-keywords-1
   '(;; What is this for?
     ;;("\\(--- .* ---\\|=== .* ===\\)" . font-lock-string-face)
@@ -196,32 +218,32 @@ The expansion is entirely correct because it uses the C preprocessor."
   "Subdued level highlighting for Perl mode.")
 
 (defconst perl-font-lock-keywords-2
-  (append perl-font-lock-keywords-1
-   (list
-    ;;
-    ;; Fontify keywords, except those fontified otherwise.
-    (concat "\\<"
-           (regexp-opt '("if" "until" "while" "elsif" "else" "unless"
-                         "do" "dump" "for" "foreach" "exit" "die"
-                         "BEGIN" "END" "return" "exec" "eval") t)
-           "\\>")
-    ;;
-    ;; Fontify local and my keywords as types.
-    '("\\<\\(local\\|my\\)\\>" . font-lock-type-face)
-    ;;
-    ;; Fontify function, variable and file name references.
-    '("&\\(\\sw+\\(::\\sw+\\)*\\)" 1 font-lock-function-name-face)
-    ;; Additionally underline non-scalar variables.  Maybe this is a bad idea.
-    ;;'("[$@%*][#{]?\\(\\sw+\\)" 1 font-lock-variable-name-face)
-    '("[$*]{?\\(\\sw+\\(::\\sw+\\)*\\)" 1 font-lock-variable-name-face)
-    '("\\([@%]\\|\\$#\\)\\(\\sw+\\(::\\sw+\\)*\\)"
+  (append
+   perl-font-lock-keywords-1
+   `( ;; Fontify keywords, except those fontified otherwise.
+     ,(concat "\\<"
+              (regexp-opt '("if" "until" "while" "elsif" "else" "unless"
+                            "do" "dump" "for" "foreach" "exit" "die"
+                            "BEGIN" "END" "return" "exec" "eval") t)
+              "\\>")
+     ;;
+     ;; Fontify local and my keywords as types.
+     ("\\<\\(local\\|my\\)\\>" . font-lock-type-face)
+     ;;
+     ;; Fontify function, variable and file name references.
+     ("&\\(\\sw+\\(::\\sw+\\)*\\)" 1 font-lock-function-name-face)
+     ;; Additionally underline non-scalar variables.  Maybe this is a bad idea.
+     ;;'("[$@%*][#{]?\\(\\sw+\\)" 1 font-lock-variable-name-face)
+     ("[$*]{?\\(\\sw+\\(::\\sw+\\)*\\)" 1 font-lock-variable-name-face)
+     ("\\([@%]\\|\\$#\\)\\(\\sw+\\(::\\sw+\\)*\\)"
       (2 (cons font-lock-variable-name-face '(underline))))
-    '("<\\(\\sw+\\)>" 1 font-lock-constant-face)
-    ;;
-    ;; Fontify keywords with/and labels as we do in `c++-font-lock-keywords'.
-    '("\\<\\(continue\\|goto\\|last\\|next\\|redo\\)\\>[ \t]*\\(\\sw+\\)?"
+     ("<\\(\\sw+\\)>" 1 font-lock-constant-face)
+     ;;
+     ;; Fontify keywords with/and labels as we do in `c++-font-lock-keywords'.
+     ("\\<\\(continue\\|goto\\|last\\|next\\|redo\\)\\>[ \t]*\\(\\sw+\\)?"
       (1 font-lock-keyword-face) (2 font-lock-constant-face nil t))
-    '("^[ \t]*\\(\\sw+\\)[ \t]*:[^:]" 1 font-lock-constant-face)))
+     ("^[ \t]*\\(\\sw+\\)[ \t]*:[^:]" 1 font-lock-constant-face)
+     ,@(perl--font-lock-symbols-keywords)))
   "Gaudy level highlighting for Perl mode.")
 
 (defvar perl-font-lock-keywords perl-font-lock-keywords-1
@@ -511,6 +533,14 @@ If nil, continued arguments are aligned with the first argument."
   :type '(choice integer (const nil))
   :group 'perl)
 
+(defcustom perl-indent-parens-as-block nil
+  "Non-nil means that non-block ()-, {}- and []-groups are indented as blocks.
+The closing bracket is aligned with the line of the opening bracket,
+not the contents of the brackets."
+  :version "24.3"
+  :type 'boolean
+  :group 'perl)
+
 (defcustom perl-tab-always-indent tab-always-indent
   "Non-nil means TAB in Perl mode always indents the current line.
 Otherwise it inserts a tab character if you type it past the first
@@ -541,11 +571,20 @@ create a new comment."
 
 (defun perl-outline-level ()
   (cond
-   ((looking-at "package\\s-") 0)
-   ((looking-at "sub\\s-") 1)
+   ((looking-at "[ \t]*\\(package\\)\\s-")
+    (- (match-beginning 1) (match-beginning 0)))
+   ((looking-at "[ \t]*s\\(ub\\)\\s-")
+    (- (match-beginning 1) (match-beginning 0)))
    ((looking-at "=head[0-9]") (- (char-before (match-end 0)) ?0))
    ((looking-at "=cut") 1)
    (t 3)))
+
+(defun perl-current-defun-name ()
+  "The `add-log-current-defun' function in Perl mode."
+  (save-excursion
+    (if (re-search-backward "^sub[ \t]+\\([^({ \t\n]+\\)" nil t)
+       (match-string-no-properties 1))))
+
 \f
 (defvar perl-mode-hook nil
   "Normal hook to run when entering Perl mode.")
@@ -599,15 +638,15 @@ Various indentation styles:       K&R  BSD  BLK  GNU  LW
 
 Turning on Perl mode runs the normal hook `perl-mode-hook'."
   :abbrev-table perl-mode-abbrev-table
-  (set (make-local-variable 'paragraph-start) (concat "$\\|" page-delimiter))
-  (set (make-local-variable 'paragraph-separate) paragraph-start)
-  (set (make-local-variable 'paragraph-ignore-fill-prefix) t)
-  (set (make-local-variable 'indent-line-function) #'perl-indent-line)
-  (set (make-local-variable 'comment-start) "# ")
-  (set (make-local-variable 'comment-end) "")
-  (set (make-local-variable 'comment-start-skip) "\\(^\\|\\s-\\);?#+ *")
-  (set (make-local-variable 'comment-indent-function) #'perl-comment-indent)
-  (set (make-local-variable 'parse-sexp-ignore-comments) t)
+  (setq-local paragraph-start (concat "$\\|" page-delimiter))
+  (setq-local paragraph-separate paragraph-start)
+  (setq-local paragraph-ignore-fill-prefix t)
+  (setq-local indent-line-function #'perl-indent-line)
+  (setq-local comment-start "# ")
+  (setq-local comment-end "")
+  (setq-local comment-start-skip "\\(^\\|\\s-\\);?#+ *")
+  (setq-local comment-indent-function #'perl-comment-indent)
+  (setq-local parse-sexp-ignore-comments t)
   ;; Tell font-lock.el how to handle Perl.
   (setq font-lock-defaults '((perl-font-lock-keywords
                              perl-font-lock-keywords-1
@@ -615,17 +654,21 @@ Turning on Perl mode runs the normal hook `perl-mode-hook'."
                             nil nil ((?\_ . "w")) nil
                              (font-lock-syntactic-face-function
                               . perl-font-lock-syntactic-face-function)))
-  (set (make-local-variable 'syntax-propertize-function)
-       #'perl-syntax-propertize-function)
+  (setq-local syntax-propertize-function #'perl-syntax-propertize-function)
   (add-hook 'syntax-propertize-extend-region-functions
             #'syntax-propertize-multiline 'append 'local)
+  ;; Electricity.
+  ;; FIXME: setup electric-layout-rules.
+  (setq-local electric-indent-chars
+             (append '(?\{ ?\} ?\; ?\:) electric-indent-chars))
+  (add-hook 'electric-indent-functions #'perl-electric-noindent-p nil t)
   ;; Tell imenu how to handle Perl.
-  (set (make-local-variable 'imenu-generic-expression)
-       perl-imenu-generic-expression)
+  (setq-local imenu-generic-expression perl-imenu-generic-expression)
   (setq imenu-case-fold-search nil)
   ;; Setup outline-minor-mode.
-  (set (make-local-variable 'outline-regexp) perl-outline-regexp)
-  (set (make-local-variable 'outline-level) 'perl-outline-level))
+  (setq-local outline-regexp perl-outline-regexp)
+  (setq-local outline-level 'perl-outline-level)
+  (setq-local add-log-current-defun-function #'perl-current-defun-name))
 \f
 ;; This is used by indent-for-comment
 ;; to decide how much to indent a comment in Perl code
@@ -635,7 +678,11 @@ Turning on Perl mode runs the normal hook `perl-mode-hook'."
       0                                        ;Existing comment at bol stays there.
     comment-column))
 
-(defalias 'electric-perl-terminator 'perl-electric-terminator)
+(define-obsolete-function-alias 'electric-perl-terminator
+  'perl-electric-terminator "22.1")
+(defun perl-electric-noindent-p (char)
+  (unless (eolp) 'no-indent))
+
 (defun perl-electric-terminator (arg)
   "Insert character and maybe adjust indentation.
 If at end-of-line, and not in a comment or a quote, correct the indentation."
@@ -659,6 +706,7 @@ If at end-of-line, and not in a comment or a quote, correct the indentation."
           (perl-indent-line)
           (delete-char -1))))
   (self-insert-command (prefix-numeric-value arg)))
+(make-obsolete 'perl-electric-terminator 'electric-indent-mode "24.4")
 
 ;; not used anymore, but may be useful someday:
 ;;(defun perl-inside-parens-p ()
@@ -742,6 +790,7 @@ following list:
                        (t
                         (message "Use backslash to quote # characters.")
                         (ding t)))))))))
+(make-obsolete 'perl-indent-command 'indent-according-to-mode "24.4")
 
 (defun perl-indent-line (&optional nochange parse-start)
   "Indent current line as Perl code.
@@ -853,7 +902,8 @@ Optional argument PARSE-START should be the position of `beginning-of-defun'."
       (cond ((nth 3 state) 'noindent)  ; In a quoted string?
            ((null containing-sexp)     ; Line is at top level.
             (skip-chars-forward " \t\f")
-            (if (= (following-char) ?{)
+            (if (memq (following-char)
+                      (if perl-indent-parens-as-block '(?\{ ?\( ?\[) '(?\{)))
                 0  ; move to beginning of line if it starts a function body
               ;; indent a little if this is a continuation line
               (perl-backward-to-noncomment)
@@ -897,7 +947,9 @@ Optional argument PARSE-START should be the position of `beginning-of-defun'."
                          0 perl-continued-statement-offset)
                      (current-column)
                      (if (save-excursion (goto-char indent-point)
-                                         (looking-at "[ \t]*{"))
+                                         (looking-at
+                                          (if perl-indent-parens-as-block
+                                              "[ \t]*[{(\[]" "[ \t]*{")))
                          perl-continued-brace-offset 0)))
               ;; This line starts a new statement.
               ;; Position at last unclosed open.