summaryrefslogtreecommitdiff
path: root/lisp/progmodes/cperl-mode.el
diff options
context:
space:
mode:
Diffstat (limited to 'lisp/progmodes/cperl-mode.el')
-rw-r--r--lisp/progmodes/cperl-mode.el1265
1 files changed, 794 insertions, 471 deletions
diff --git a/lisp/progmodes/cperl-mode.el b/lisp/progmodes/cperl-mode.el
index 3370df64919..fe9612a09a9 100644
--- a/lisp/progmodes/cperl-mode.el
+++ b/lisp/progmodes/cperl-mode.el
@@ -308,12 +308,11 @@ Can be overwritten by `cperl-hairy' if nil."
Can be overwritten by `cperl-hairy' if nil.
Uses `abbrev-mode' to do the expansion. If you want to use your
-own abbrevs in cperl-mode, but do not want keywords to be
+own abbrevs in `cperl-mode', but do not want keywords to be
electric, you must redefine `cperl-mode-abbrev-table': do
\\[edit-abbrevs], search for `cperl-mode-abbrev-table', and, in
that paragraph, delete the words that appear at the ends of lines and
-that begin with \"cperl-electric\".
-"
+that begin with \"cperl-electric\"."
:type '(choice (const null) boolean)
:group 'cperl-affected-by-hairy)
@@ -362,14 +361,14 @@ Affects: `cperl-font-lock', `cperl-electric-lbrace-space',
;; :group 'cperl)
(defcustom cperl-info-on-command-no-prompt nil
- "Not-nil (and non-null) means not to prompt on C-h f.
+ "Not-nil (and non-null) means not to prompt on \\[cperl-info-on-command].
The opposite behavior is always available if prefixed with C-c.
Can be overwritten by `cperl-hairy' if nil."
:type '(choice (const null) boolean)
:group 'cperl-affected-by-hairy)
(defcustom cperl-clobber-lisp-bindings nil
- "Not-nil (and non-null) means not overwrite C-h f.
+ "Not-nil (and non-null) means not overwrite \\[describe-function].
The function is available on \\[cperl-info-on-command], \\[cperl-get-help].
Can be overwritten by `cperl-hairy' if nil."
:type '(choice (const null) boolean)
@@ -508,9 +507,9 @@ Currently used with `cperl-check-syntax' only."
:group 'cperl-help-system)
(defcustom cperl-indent-region-fix-constructs 1
- "Amount of space to insert between `}' and `else' or `elsif'
-in `cperl-indent-region'. Set to nil to leave as is. Values other
-than 1 and nil will probably not work."
+ "Amount of space to insert between `}' and `else' or `elsif'.
+Used by `cperl-indent-region'. Set to nil to leave as is.
+Values other than 1 and nil will probably not work."
:type '(choice (const nil) (const 1))
:group 'cperl-indentation-details)
@@ -767,8 +766,7 @@ line-breaks/spacing between elements of the construct.
10) Uses a linear-time algorithm for indentation of regions.
-11) Syntax-highlight, indentation, sexp-recognition inside regular expressions.
-")
+11) Syntax-highlight, indentation, sexp-recognition inside regular expressions.")
(defvar cperl-speed 'please-ignore-this-line
"This is an incomplete compendium of what is available in other parts
@@ -865,9 +863,7 @@ In regular expressions (including character classes):
backslashes of escape sequences
`font-lock-variable-name-face' Interpolated constructs, embedded code,
POSIX classes (inside charclasses)
- `font-lock-comment-face' Embedded comments
-
-")
+ `font-lock-comment-face' Embedded comments")
@@ -1023,15 +1019,9 @@ Unless KEEP, removes the old indentation."
(define-key map [(control ?c) (control ?h) ?v]
;;(concat (char-to-string help-char) "v") ; does not work
'cperl-get-help))
- (substitute-key-definition
- 'indent-sexp 'cperl-indent-exp
- map global-map)
- (substitute-key-definition
- 'indent-region 'cperl-indent-region
- map global-map)
- (substitute-key-definition
- 'indent-for-comment 'cperl-indent-for-comment
- map global-map)
+ (define-key map [remap indent-sexp] #'cperl-indent-exp)
+ (define-key map [remap indent-region] #'cperl-indent-region)
+ (define-key map [remap indent-for-comment] #'cperl-indent-for-comment)
map)
"Keymap used in CPerl mode.")
@@ -1203,153 +1193,198 @@ The expansion is entirely correct because it uses the C preprocessor."
;; minimalistic Perl grammar, to be used instead of individual (and
;; not always consistent) literal regular expressions.
-(defconst cperl--basic-identifier-regexp
- (rx (sequence (or alpha "_") (* (or word "_"))))
- "A regular expression for the name of a \"basic\" Perl variable.
+;; This is necessary to compile this file under Emacs 26.1
+;; (there's no rx-define which would help)
+(eval-and-compile
+
+ (defconst cperl--basic-identifier-rx
+ '(sequence (or alpha "_") (* (or word "_")))
+ "A regular expression for the name of a \"basic\" Perl variable.
Neither namespace separators nor sigils are included. As is,
this regular expression applies to labels,subroutine calls where
the ampersand sigil is not required, and names of subroutine
attributes.")
-(defconst cperl--label-regexp
- (rx-to-string
- `(sequence
- symbol-start
- (regexp ,cperl--basic-identifier-regexp)
- (0+ space)
- ":"))
- "A regular expression for a Perl label.
+ (defconst cperl--label-rx
+ `(sequence symbol-start
+ ,cperl--basic-identifier-rx
+ (0+ space)
+ ":")
+ "A regular expression for a Perl label.
By convention, labels are uppercase alphabetics, but this isn't
enforced.")
-(defconst cperl--normal-identifier-regexp
- (rx-to-string
- `(or
- (sequence
- (1+ (sequence
- (opt (regexp ,cperl--basic-identifier-regexp))
- "::"))
- (opt (regexp ,cperl--basic-identifier-regexp)))
- (regexp ,cperl--basic-identifier-regexp)))
- "A regular expression for a Perl variable name with optional namespace.
+ (defconst cperl--false-label-rx
+ '(sequence (or (in "sym") "tr") (0+ space) ":")
+ "A regular expression which is similar to a label, but might as
+well be a quote-like operator with a colon as delimiter.")
+
+ (defconst cperl--normal-identifier-rx
+ `(or (sequence (1+ (sequence
+ (opt ,cperl--basic-identifier-rx)
+ "::"))
+ (opt ,cperl--basic-identifier-rx))
+ ,cperl--basic-identifier-rx)
+ "A regular expression for a Perl variable name with optional namespace.
Examples are `foo`, `Some::Module::VERSION`, and `::` (yes, that
is a legal variable name).")
-(defconst cperl--special-identifier-regexp
- (rx-to-string
- `(or
- (1+ digit) ; $0, $1, $2, ...
- (sequence "^" (any "A-Z" "]^_?\\")) ; $^V
- (sequence "{" (0+ space) ; ${^MATCH}
- "^" (any "A-Z" "]^_?\\")
- (0+ (any "A-Z" "_" digit))
- (0+ space) "}")
- (in "!\"$%&'()+,-./:;<=>?@\\]^_`|~"))) ; $., $|, $", ... but not $^ or ${
- "The list of Perl \"punctuation\" variables, as listed in perlvar.")
-
-(defconst cperl--ws-regexp
- (rx-to-string
- '(or space "\n"))
- "Regular expression for a single whitespace in Perl.")
-
-(defconst cperl--eol-comment-regexp
- (rx-to-string
- '(sequence "#" (0+ (not (in "\n"))) "\n"))
- "Regular expression for a single end-of-line comment in Perl")
-
-(defconst cperl--ws-or-comment-regexp
- (rx-to-string
- `(1+
- (or
- (regexp ,cperl--ws-regexp)
- (regexp ,cperl--eol-comment-regexp))))
- "Regular expression for a sequence of whitespace and comments in Perl.")
-
-(defconst cperl--ows-regexp
- (rx-to-string
- `(opt (regexp ,cperl--ws-or-comment-regexp)))
- "Regular expression for optional whitespaces or comments in Perl")
-
-(defconst cperl--version-regexp
- (rx-to-string
- `(or
- (sequence (opt "v")
- (>= 2 (sequence (1+ digit) "."))
- (1+ digit)
- (opt (sequence "_" (1+ word))))
- (sequence (1+ digit)
- (opt (sequence "." (1+ digit)))
- (opt (sequence "_" (1+ word))))))
- "A sequence for recommended version number schemes in Perl.")
-
-(defconst cperl--package-regexp
- (rx-to-string
- `(sequence
- "package" ; FIXME: the "class" and "role" keywords need to be
- ; recognized soon...ish.
- (regexp ,cperl--ws-or-comment-regexp)
- (group (regexp ,cperl--normal-identifier-regexp))
- (opt
- (sequence
- (regexp ,cperl--ws-or-comment-regexp)
- (group (regexp ,cperl--version-regexp))))))
- "A regular expression for package NAME VERSION in Perl.
-Contains two groups for the package name and version.")
-
-(defconst cperl--package-for-imenu-regexp
- (rx-to-string
- `(sequence
- (regexp ,cperl--package-regexp)
- (regexp ,cperl--ows-regexp)
- (group (or ";" "{"))))
- "A regular expression to collect package names for `imenu`.
+ (defconst cperl--special-identifier-rx
+ '(or
+ (1+ digit) ; $0, $1, $2, ...
+ (sequence "^" (any "A-Z" "]^_?\\")) ; $^V
+ (sequence "{" (0+ space) ; ${^MATCH}
+ "^" (any "A-Z" "]^_?\\")
+ (0+ (any "A-Z" "_" digit))
+ (0+ space) "}")
+ (in "!\"$%&'()+,-./:;<=>?@\\]^_`|~")) ; $., $|, $", ... but not $^ or ${
+ "The list of Perl \"punctuation\" variables, as listed in perlvar.")
+
+ (defconst cperl--ws-rx
+ '(sequence (or space "\n"))
+ "Regular expression for a single whitespace in Perl.")
+
+ (defconst cperl--eol-comment-rx
+ '(sequence "#" (0+ (not (in "\n"))) "\n")
+ "Regular expression for a single end-of-line comment in Perl")
+
+ (defconst cperl--ws-or-comment-rx
+ `(or ,cperl--ws-rx
+ ,cperl--eol-comment-rx)
+ "A regular expression for either whitespace or comment")
+
+ (defconst cperl--ws*-rx
+ `(0+ ,cperl--ws-or-comment-rx)
+ "Regular expression for optional whitespaces or comments in Perl")
+
+ (defconst cperl--ws+-rx
+ `(1+ ,cperl--ws-or-comment-rx)
+ "Regular expression for a sequence of whitespace and comments in Perl.")
+
+ ;; This is left as a string regexp. There are many version schemes in
+ ;; the wild, so people might want to fiddle with this variable.
+ (defconst cperl--version-regexp
+ (rx-to-string
+ `(or
+ (sequence (optional "v")
+ (>= 2 (sequence (1+ digit) "."))
+ (1+ digit)
+ (optional (sequence "_" (1+ word))))
+ (sequence (1+ digit)
+ (optional (sequence "." (1+ digit)))
+ (optional (sequence "_" (1+ word))))))
+ "A sequence for recommended version number schemes in Perl.")
+
+ (defconst cperl--package-rx
+ `(sequence (group "package")
+ ,cperl--ws+-rx
+ (group ,cperl--normal-identifier-rx)
+ (optional (sequence ,cperl--ws+-rx
+ (group (regexp ,cperl--version-regexp)))))
+ "A regular expression for package NAME VERSION in Perl.
+Contains three groups for the keyword \"package\", for the
+package name and for the version.")
+
+ (defconst cperl--package-for-imenu-rx
+ `(sequence symbol-start
+ (group-n 1 "package")
+ ,cperl--ws*-rx
+ (group-n 2 ,cperl--normal-identifier-rx)
+ (optional (sequence ,cperl--ws+-rx
+ (regexp ,cperl--version-regexp)))
+ ,cperl--ws*-rx
+ (group-n 3 (or ";" "{")))
+ "A regular expression to collect package names for `imenu'.
Catches \"package NAME;\", \"package NAME VERSION;\", \"package
NAME BLOCK\" and \"package NAME VERSION BLOCK.\" Contains three
-groups: Two from `cperl--package-regexp` for the package name and
-version, and a third to detect \"package BLOCK\" syntax.")
-
-(defconst cperl--sub-name-regexp
- (rx-to-string
- `(sequence
- (optional (sequence (group (or "my" "state" "our"))
- (regexp ,cperl--ws-or-comment-regexp)))
- "sub" ; FIXME: the "method" and maybe "fun" keywords need to be
- ; recognized soon...ish.
- (regexp ,cperl--ws-or-comment-regexp)
- (group (regexp ,cperl--normal-identifier-regexp))))
- "A regular expression to detect a subroutine start.
-Contains two groups: One for to distinguish lexical from
-\"normal\" subroutines and one for the subroutine name.")
-
-(defconst cperl--pod-heading-regexp
- (rx-to-string
- `(sequence
- line-start "=head"
- (group (in "1-4"))
- (1+ (in " \t"))
- (group (1+ (not (in "\n"))))
- line-end)) ; that line-end seems to be redundant?
+groups: One for the keyword \"package\", one for the package
+name, and one for the discovery of a following BLOCK.")
+
+ (defconst cperl--sub-name-for-imenu-rx
+ `(sequence symbol-start
+ (optional (sequence (group-n 3 (or "my" "state" "our"))
+ ,cperl--ws+-rx))
+ (group-n 1 "sub")
+ ,cperl--ws+-rx
+ (group-n 2 ,cperl--normal-identifier-rx))
+ "A regular expression to detect a subroutine start.
+Contains three groups: One one to distinguish lexical from
+\"normal\" subroutines, for the keyword \"sub\", and one for the
+subroutine name.")
+
+(defconst cperl--block-declaration-rx
+ `(sequence
+ (or "package" "sub") ; "class" and "method" coming soon
+ (1+ ,cperl--ws-or-comment-rx)
+ ,cperl--normal-identifier-rx)
+ "A regular expression to find a declaration for a named block.
+Used for indentation. These declarations introduce a block which
+does not need a semicolon to terminate the statement.")
+
+(defconst cperl--pod-heading-rx
+ `(sequence line-start
+ (group-n 1 "=head")
+ (group-n 3 (in "1-4"))
+ (1+ (in " \t"))
+ (group-n 2 (1+ (not (in "\n")))))
"A regular expression to detect a POD heading.
Contains two groups: One for the heading level, and one for the
heading text.")
-(defconst cperl--imenu-entries-regexp
- (rx-to-string
- `(or
- (regexp ,cperl--package-for-imenu-regexp) ; 1..3
- (regexp ,cperl--sub-name-regexp) ; 4..5
- (regexp ,cperl--pod-heading-regexp))) ; 6..7
- "A regular expression to collect stuff that goes into the `imenu` index.
+(defconst cperl--imenu-entries-rx
+ `(or ,cperl--package-for-imenu-rx
+ ,cperl--sub-name-for-imenu-rx
+ ,cperl--pod-heading-rx)
+ "A regular expression to collect stuff that goes into the `imenu' index.
Covers packages, subroutines, and POD headings.")
+;; end of eval-and-compiled stuff
+)
+
+
+(defun cperl-block-declaration-p ()
+ "Test whether the following ?\\{ opens a declaration block.
+Returns the column where the declarating keyword is found, or nil
+if this isn't a declaration block. Declaration blocks are named
+subroutines, packages and the like. They start with a keyword
+and a name, to be followed by various descriptive items which are
+just skipped over for our purpose. Declaration blocks end a
+statement, so there's no semicolon."
+ ;; A scan error means that none of the declarators has been found
+ (condition-case nil
+ (let ((is-block-declaration nil)
+ (continue-searching t))
+ (while (and continue-searching (not (bobp)))
+ (forward-sexp -1)
+ (cond
+ ((looking-at (rx (eval cperl--block-declaration-rx)))
+ (setq is-block-declaration (current-column)
+ continue-searching nil))
+ ;; Another brace means this is no block declaration
+ ((looking-at "{")
+ (setq continue-searching nil))
+ (t
+ (cperl-backward-to-noncomment (point-min))
+ ;; A semicolon or an opening brace prevent this block from
+ ;; being a block declaration
+ (when (or (eq (preceding-char) ?\;)
+ (eq (preceding-char) ?{))
+ (setq continue-searching nil)))))
+ is-block-declaration)
+ (error nil)))
+
;; These two must be unwound, otherwise take exponential time
-(defconst cperl-maybe-white-and-comment-rex "[ \t\n]*\\(#[^\n]*\n[ \t\n]*\\)*"
+(defconst cperl-maybe-white-and-comment-rex
+ (rx (group (eval cperl--ws*-rx)))
+ ;; was: "[ \t\n]*\\(#[^\n]*\n[ \t\n]*\\)*"
"Regular expression to match optional whitespace with interspersed comments.
Should contain exactly one group.")
;; This one is tricky to unwind; still very inefficient...
-(defconst cperl-white-and-comment-rex "\\([ \t\n]\\|#[^\n]*\n\\)+"
+(defconst cperl-white-and-comment-rex
+ (rx (group (eval cperl--ws+-rx)))
+ ;; was: "\\([ \t\n]\\|#[^\n]*\n\\)+"
"Regular expression to match whitespace with interspersed comments.
Should contain exactly one group.")
@@ -1366,7 +1401,7 @@ the last)."
(concat ; Assume n groups before this...
"\\(" ; n+1=name-group
cperl-white-and-comment-rex ; n+2=pre-name
- "\\(::[a-zA-Z_0-9:']+\\|[a-zA-Z_'][a-zA-Z_0-9:']*\\)" ; n+3=name
+ (rx-to-string `(group ,cperl--normal-identifier-rx))
"\\)" ; END n+1=name-group
(if named "" "?")
"\\(" ; n+4=proto-group
@@ -1405,28 +1440,9 @@ the last)."
when (eq char (aref keyword (1- (length keyword))))
return t))
-;; Details of groups in this are used in `cperl-imenu--create-perl-index'
-;; and `cperl-outline-level'.
-;; Was: 2=sub|package; now 2=package-group, 5=package-name 8=sub-name (+3)
-(defvar cperl-imenu--function-name-regexp-perl
- (concat
- "^\\(" ; 1 = all
- "\\([ \t]*package" ; 2 = package-group
- "\\(" ; 3 = package-name-group
- cperl-white-and-comment-rex ; 4 = pre-package-name
- "\\([a-zA-Z_0-9:']+\\)\\)?\\)" ; 5 = package-name
- "\\|"
- "[ \t]*"
- cperl-sub-regexp
- (cperl-after-sub-regexp 'named nil) ; 8=name 11=proto 14=attr-start
- cperl-maybe-white-and-comment-rex ; 15=pre-block
- "\\|"
- "=head\\([1-4]\\)[ \t]+" ; 16=level
- "\\([^\n]+\\)$" ; 17=text
- "\\)"))
-
(defvar cperl-outline-regexp
- (concat cperl-imenu--function-name-regexp-perl "\\|" "\\`"))
+ (rx (sequence line-start (0+ blank) (eval cperl--imenu-entries-rx)))
+ "The regular expression used for `outline-minor-mode'.")
(defvar cperl-mode-syntax-table nil
"Syntax table in use in CPerl mode buffers.")
@@ -1562,7 +1578,7 @@ into
\\{cperl-mode-map}
-Setting the variable `cperl-font-lock' to t switches on font-lock-mode
+Setting the variable `cperl-font-lock' to t switches on `font-lock-mode'
\(even with older Emacsen), `cperl-electric-lbrace-space' to t switches
on electric space between $ and {, `cperl-electric-parens-string' is
the string that contains parentheses that should be electric in CPerl
@@ -1741,7 +1757,9 @@ or as help on variables `cperl-tips', `cperl-problems',
'((cperl-load-font-lock-keywords
cperl-load-font-lock-keywords-1
cperl-load-font-lock-keywords-2)
- nil nil ((?_ . "w"))))
+ nil nil ((?_ . "w")) nil
+ (font-lock-syntactic-face-function
+ . cperl-font-lock-syntactic-face-function)))
;; Reset syntaxification cache.
(setq-local cperl-syntax-state nil)
(when cperl-use-syntax-table-text-property
@@ -2451,7 +2469,8 @@ Will untabify if `cperl-electric-backspace-untabify' is non-nil."
(put 'cperl-electric-backspace 'delete-selection 'supersede)
-(defun cperl-inside-parens-p () ;; NOT USED????
+(defun cperl-inside-parens-p ()
+ (declare (obsolete nil "28.1")) ; not used
(condition-case ()
(save-excursion
(save-restriction
@@ -2514,8 +2533,9 @@ Return the amount the indentation changed by."
(t
(skip-chars-forward " \t")
(if (listp indent) (setq indent (car indent)))
- (cond ((and (looking-at "[A-Za-z_][A-Za-z_0-9]*:[^:]")
- (not (looking-at "[smy]:\\|tr:")))
+ (cond ((and (looking-at (rx (sequence (eval cperl--label-rx)
+ (not (in ":")))))
+ (not (looking-at (rx (eval cperl--false-label-rx)))))
(and (> indent 0)
(setq indent (max cperl-min-label-indent
(+ indent cperl-label-offset)))))
@@ -2547,12 +2567,13 @@ Return the amount the indentation changed by."
'(?w ?_))
(progn
(backward-sexp)
- (looking-at "[a-zA-Z_][a-zA-Z0-9_]*:[^:]"))))
+ (looking-at (rx (sequence (eval cperl--label-rx)
+ (not (in ":"))))))))
(defun cperl-get-state (&optional parse-start start-state)
- "Return list (START STATE DEPTH PRESTART),
+ "Return list (START STATE DEPTH PRESTART).
START is a good place to start parsing, or equal to
-PARSE-START if preset,
+PARSE-START if preset.
STATE is what is returned by `parse-partial-sexp'.
DEPTH is true is we are immediately after end of block
which contains START.
@@ -2707,12 +2728,16 @@ Will not look before LIM."
(and (eq (preceding-char) ?\})
(cperl-after-block-and-statement-beg
(point-min))) ; Was start - too close
+ (and char-after (char-equal char-after ?{)
+ (save-excursion (cperl-block-declaration-p)))
(memq char-after (append ")]}" nil))
(and (eq (preceding-char) ?\:) ; label
(progn
(forward-sexp -1)
(skip-chars-backward " \t")
- (looking-at "[ \t]*[a-zA-Z_][a-zA-Z_0-9]*[ \t]*:")))
+ (looking-at
+ (rx (sequence (0+ blank)
+ (eval cperl--label-rx))))))
(get-text-property (point) 'first-format-line)))
;; Look at previous line that's at column 0
@@ -2750,12 +2775,10 @@ Will not look before LIM."
;; Back up over label lines, since they don't
;; affect whether our line is a continuation.
;; (Had \, too)
- (while;;(or (eq (preceding-char) ?\,)
- (and (eq (preceding-char) ?:)
- (or;;(eq (char-after (- (point) 2)) ?\') ; ????
- (memq (char-syntax (char-after (- (point) 2)))
- '(?w ?_))))
- ;;)
+ (while (and (eq (preceding-char) ?:)
+ (re-search-backward
+ (rx (sequence (eval cperl--label-rx) point))
+ nil t))
;; This is always FALSE?
(if (eq (preceding-char) ?\,)
;; Will go to beginning of line, essentially.
@@ -2767,6 +2790,7 @@ Will not look before LIM."
(if (not (or (eq (1- (point)) containing-sexp)
(and cperl-indent-parens-as-block
(not is-block))
+ (save-excursion (cperl-block-declaration-p))
(memq (preceding-char)
(append (if is-block " ;{" " ,;{") '(nil)))
(and (eq (preceding-char) ?\})
@@ -2795,10 +2819,17 @@ Will not look before LIM."
(forward-char 1)
(let ((colon-line-end 0))
(while
- (progn (skip-chars-forward " \t\n")
- ;; s: foo : bar :x is NOT label
- (and (looking-at "#\\|\\([a-zA-Z0-9_$]+\\):[^:]\\|=[a-zA-Z]")
- (not (looking-at "[sym]:\\|tr:"))))
+ (progn
+ (skip-chars-forward " \t\n")
+ ;; s: foo : bar :x is NOT label
+ (and (looking-at
+ (rx
+ (or "#"
+ (sequence (eval cperl--label-rx)
+ (not (in ":")))
+ (sequence "=" (in "a-zA-Z")))))
+ (not (looking-at
+ (rx (eval cperl--false-label-rx))))))
;; Skip over comments and labels following openbrace.
(cond ((= (following-char) ?\#)
(forward-line 1))
@@ -3057,7 +3088,10 @@ and closing parentheses and brackets."
;; If line starts with label, calculate label indentation
(if (save-excursion
(beginning-of-line)
- (looking-at "[ \t]*[a-zA-Z_][a-zA-Z_0-9]*:[^:]"))
+ (looking-at (rx
+ (sequence (0+ space)
+ (eval cperl--label-rx)
+ (not (in ":"))))))
(if (> (current-indentation) cperl-min-label-indent)
(- (current-indentation) cperl-label-offset)
;; Do not move `parse-data', this should
@@ -3070,8 +3104,9 @@ and closing parentheses and brackets."
(error "Got strange value of indent: %s" i))))))
(defun cperl-calculate-indent-within-comment ()
- "Return the indentation amount for line, assuming that
-the current line is to be regarded as part of a block comment."
+ "Return the indentation amount for line.
+Assume that the current line is to be regarded as part of a block
+comment."
(let (end)
(save-excursion
(beginning-of-line)
@@ -3147,26 +3182,29 @@ Returns true if comment is found. In POD will not move the point."
(while (re-search-forward "^\\s(" e 'to-end)
(put-text-property (1- (point)) (point) 'syntax-table cperl-st-punct))))
-(defun cperl-commentify (bb e string &optional noface)
- (if cperl-use-syntax-table-text-property
- (if (eq noface 'n) ; Only immediate
- nil
- ;; We suppose that e is _after_ the end of construction, as after eol.
- (setq string (if string cperl-st-sfence cperl-st-cfence))
- (if (> bb (- e 2))
+(defun cperl-commentify (begin end string)
+ "Mark text from BEGIN to END as generic string or comment.
+Mark as generic string if STRING, as generic comment otherwise.
+A single character is marked as punctuation and directly
+fontified. Do nothing if BEGIN and END are equal. If
+`cperl-use-syntax-text-property' is nil, just fontify."
+ (if (and cperl-use-syntax-table-text-property
+ (> end begin))
+ (progn
+ (setq string (if string cperl-st-sfence cperl-st-cfence))
+ (if (> begin (- end 2))
;; one-char string/comment?!
- (cperl-modify-syntax-type bb cperl-st-punct)
- (cperl-modify-syntax-type bb string)
- (cperl-modify-syntax-type (1- e) string))
- (if (and (eq string cperl-st-sfence) (> (- e 2) bb))
- (put-text-property (1+ bb) (1- e)
+ (cperl-modify-syntax-type begin cperl-st-punct)
+ (cperl-modify-syntax-type begin string)
+ (cperl-modify-syntax-type (1- end) string))
+ (if (and (eq string cperl-st-sfence) (> (- end 2) begin))
+ (put-text-property (1+ begin) (1- end)
'syntax-table cperl-string-syntax-table))
- (cperl-protect-defun-start bb e))
+ (cperl-protect-defun-start begin end))
;; Fontify
- (or noface
- (not cperl-pod-here-fontify)
- (put-text-property bb e 'face (if string 'font-lock-string-face
- 'font-lock-comment-face)))))
+ (when cperl-pod-here-fontify
+ (put-text-property begin end 'face (if string 'font-lock-string-face
+ 'font-lock-comment-face)))))
(defvar cperl-starters '(( ?\( . ?\) )
( ?\[ . ?\] )
@@ -3333,8 +3371,10 @@ Works before syntax recognition is done."
;; Each non-literal part is marked `syntax-type' ==> `pod'
;; Each literal part is marked `syntax-type' ==> `in-pod'
;; b) HEREs:
+;; The point before start is marked `here-doc-start'
;; Start-to-end is marked `here-doc-group' ==> t
;; The body is marked `syntax-type' ==> `here-doc'
+;; and is also marked as style 2 comment
;; The delimiter is marked `syntax-type' ==> `here-doc-delim'
;; c) FORMATs:
;; First line (to =) marked `first-format-line' ==> t
@@ -3351,8 +3391,36 @@ Works before syntax recognition is done."
;; (value: 0 in //o, 1 if "interpolated variable" is whole-REx, t otherwise).
(defun cperl-unwind-to-safe (before &optional end)
- ;; if BEFORE, go to the previous start-of-line on each step of unwinding
- (let ((pos (point)))
+ "Move point back to a safe place, back up one extra line if BEFORE.
+A place is \"safe\" if it is not within POD, a here-document, a
+format, a quote-like expression, a subroutine attribute list or a
+multiline declaration. These places all have special syntactical
+rules and need to be parsed as a whole. If END, return the
+position of the end of the unsafe construct."
+ (let ((pos (point))
+ (state (syntax-ppss)))
+ ;; Check edge cases for here-documents first
+ (when before ; we need a safe start for parsing
+ (cond
+ ((or (equal (get-text-property (cperl-1- (point)) 'syntax-type)
+ 'here-doc-start)
+ (equal (syntax-after (cperl-1- (point)))
+ (string-to-syntax "> c")))
+ ;; point is either immediately after the start of a here-doc
+ ;; (which may consist of nothing but one newline) or
+ ;; immediately after the now-outdated end marker of the
+ ;; here-doc. In both cases we need to back up to the line
+ ;; where the here-doc delimiters are defined.
+ (forward-char -1)
+ (cperl-backward-to-noncomment (point-min))
+ (beginning-of-line))
+ ((eq 2 (nth 7 state))
+ ;; point is somewhere in a here-document. Back up to the line
+ ;; where the here-doc delimiters are defined.
+ (goto-char (nth 8 state)) ; beginning of this here-doc
+ (cperl-backward-to-noncomment ; skip back over more
+ (point-min)) ; here-documents (if any)
+ (beginning-of-line)))) ; skip back over here-doc starters
(while (and pos (progn
(beginning-of-line)
(get-text-property (setq pos (point)) 'syntax-type)))
@@ -3510,19 +3578,194 @@ Should be called with the point before leading colon of an attribute."
(goto-char endbracket) ; just in case something misbehaves???
t))
+(defvar cperl-here-doc-functions
+ (regexp-opt '("print" "printf" "say" ; print $handle <<EOF
+ "system" "exec" ; system $progname <<EOF
+ "sort") ; sort $subname <<EOF
+ 'symbols) ; avoid false positives
+ "List of keywords after which `$var <<bareword' is a here-document.
+After any other token `$var <<bareword' is treated as the variable `$var'
+left-shifted by the return value of the function `bareword'.")
+
+(defun cperl-is-here-doc-p (start)
+ "Find out whether a \"<<\" construct starting at START is a here-document.
+The point is expected to be after the end of the delimiter.
+Quoted delimiters after \"<<\" are unambiguously starting
+here-documents and are not handled here. This function does not
+move point but does change match data."
+ ;; not a here-doc | here-doc
+ ;; $foo << b; | $f .= <<B;
+ ;; ($f+1) << b; | a($f) . <<B;
+ ;; foo 1, <<B; | $x{a} <<b;
+ ;; Limitations:
+ ;; foo <<bar is statically undecidable. It could be either
+ ;; foo() << bar # left shifting the return value or
+ ;; foo(<<bar) # passing a here-doc to foo().
+ ;; We treat it as here-document and kindly ask programmers to
+ ;; disambiguate by adding parens.
+ (null
+ (or (looking-at "[ \t]*(") ; << function_call()
+ (looking-at ">>") ; <<>> operator
+ (save-excursion ; 1 << func_name, or $foo << 10
+ (condition-case nil
+ (progn
+ (goto-char start)
+ (forward-sexp -1) ;; examine the part before "<<"
+ (save-match-data
+ (cond
+ ((looking-at "[0-9$({]")
+ (forward-sexp 1)
+ (and
+ (looking-at "[ \t]*<<")
+ (condition-case nil
+ ;; print $foo <<EOF
+ (progn
+ (forward-sexp -2)
+ (not
+ (looking-at cperl-here-doc-functions)))
+ (error t)))))))
+ (error nil)))))) ; func(<<EOF)
+
+(defun cperl-process-here-doc (min max end overshoot stop-point
+ end-of-here-doc err-l
+ indented-here-doc-p
+ matched-pos todo-pos
+ delim-begin delim-end)
+ "Process a here-document's delimiters and body.
+The parameters MIN, MAX, END, OVERSHOOT, STOP-POINT, ERR-L are
+used for recursive calls to `cperl-find-pods-here' to handle the
+rest of the line which contains the delimiter. MATCHED-POS and
+TODO-POS are initial values for this function's result.
+END-OF-HERE-DOC is the end of a previous here-doc in the same
+line, or nil if this is the first. DELIM-BEGIN and DELIM-END are
+the positions where the here-document's delimiter has been found.
+This is part of `cperl-find-pods-heres' (below)."
+ (let* ((my-cperl-delimiters-face font-lock-constant-face)
+ (delimiter (buffer-substring-no-properties delim-begin delim-end))
+ (qtag (regexp-quote delimiter))
+ (use-syntax-state (and cperl-syntax-state
+ (>= min (car cperl-syntax-state))))
+ (state-point (if use-syntax-state
+ (car cperl-syntax-state)
+ (point-min)))
+ (state (if use-syntax-state
+ (cdr cperl-syntax-state)))
+ here-doc-start here-doc-end defs-eol
+ warning-message)
+ (when cperl-pod-here-fontify
+ ;; Highlight the starting delimiter
+ (cperl-postpone-fontification delim-begin delim-end
+ 'face my-cperl-delimiters-face)
+ (cperl-put-do-not-fontify delim-begin delim-end t))
+ (forward-line)
+ (setq here-doc-start (point) ; first char of (first) here-doc
+ defs-eol (1- here-doc-start)) ; end of definitions line
+ (if end-of-here-doc
+ ;; skip to the end of the previous here-doc
+ (goto-char end-of-here-doc)
+ ;; otherwise treat the first (or only) here-doc: Check for
+ ;; special cases if the line containing the delimiter(s)
+ ;; ends in a regular comment or a solitary ?#
+ (let* ((eol-state (save-excursion (syntax-ppss defs-eol))))
+ (when (nth 4 eol-state) ; EOL is in a comment
+ (if (= (1- defs-eol) (nth 8 eol-state))
+ ;; line ends with a naked comment starter.
+ ;; We let it start the here-doc.
+ (progn
+ (put-text-property (1- defs-eol) defs-eol
+ 'font-lock-face
+ 'font-lock-comment-face)
+ (put-text-property (1- defs-eol) defs-eol
+ 'syntax-type 'here-doc)
+ (put-text-property (1- defs-eol) defs-eol
+ 'syntax-type 'here-doc)
+ (put-text-property (1- defs-eol) defs-eol
+ 'syntax-table
+ (string-to-syntax "< c"))
+ )
+ ;; line ends with a "regular" comment: make
+ ;; the last character of the comment closing
+ ;; it so that we can use the line feed to
+ ;; start the here-doc
+ (put-text-property (1- defs-eol) defs-eol
+ 'syntax-table
+ (string-to-syntax ">"))))))
+ (setq here-doc-start (point)) ; now points to current here-doc
+ ;; Find the terminating delimiter.
+ ;; We do not search to max, since we may be called from
+ ;; some hook of fontification, and max is random
+ (or (re-search-forward
+ (concat "^" (when indented-here-doc-p "[ \t]*")
+ qtag "$")
+ stop-point 'toend)
+ (progn ; Pretend we matched at the end
+ (goto-char (point-max))
+ (re-search-forward "\\'")
+ (setq warning-message
+ (format "End of here-document `%s' not found." delimiter))
+ (or (car err-l) (setcar err-l here-doc-start))))
+ (when cperl-pod-here-fontify
+ ;; Highlight the ending delimiter
+ (cperl-postpone-fontification
+ (match-beginning 0) (match-end 0)
+ 'face my-cperl-delimiters-face)
+ (cperl-put-do-not-fontify here-doc-start (match-end 0) t))
+ (setq here-doc-end (cperl-1+ (match-end 0))) ; eol after delim
+ (put-text-property here-doc-start (match-beginning 0)
+ 'syntax-type 'here-doc)
+ (put-text-property (match-beginning 0) here-doc-end
+ 'syntax-type 'here-doc-delim)
+ (put-text-property here-doc-start here-doc-end 'here-doc-group t)
+ ;; This makes insertion at the start of HERE-DOC update
+ ;; the whole construct:
+ (put-text-property here-doc-start (cperl-1+ here-doc-start) 'front-sticky '(syntax-type))
+ (cperl-commentify (match-beginning 0) (1- here-doc-end) nil)
+ (put-text-property (1- here-doc-start) here-doc-start
+ 'syntax-type 'here-doc-start)
+ (when (> (match-beginning 0) here-doc-start)
+ ;; here-document has non-zero length
+ (cperl-modify-syntax-type (1- here-doc-start) (string-to-syntax "< c"))
+ (cperl-modify-syntax-type (1- (match-beginning 0))
+ (string-to-syntax "> c")))
+ (cperl-put-do-not-fontify here-doc-start (match-end 0) t)
+ ;; Cache the syntax info...
+ (setq cperl-syntax-state (cons state-point state))
+ ;; ... and process the rest of the line...
+ (setq overshoot
+ (elt ; non-inter ignore-max
+ (cperl-find-pods-heres todo-pos defs-eol
+ t end t here-doc-end)
+ 1))
+ (if (and overshoot (> overshoot (point)))
+ (goto-char overshoot)
+ (setq overshoot here-doc-end))
+ (list (if (> here-doc-end max) matched-pos nil)
+ overshoot
+ warning-message)))
+
;; Debugging this may require (setq max-specpdl-size 2000)...
(defun cperl-find-pods-heres (&optional min max non-inter end ignore-max end-of-here-doc)
- "Scans the buffer for hard-to-parse Perl constructions.
-If `cperl-pod-here-fontify' is not-nil after evaluation, will fontify
-the sections using `cperl-pod-head-face', `cperl-pod-face',
-`cperl-here-face'."
+ "Scan the buffer for hard-to-parse Perl constructions.
+If `cperl-pod-here-fontify' is non-nil after evaluation,
+fontify the sections using `cperl-pod-head-face',
+`cperl-pod-face', `cperl-here-face'. The optional parameters are
+for internal use: scan from MIN to MAX, or the whole buffer if
+these are nil. If NON-INTER, don't write progress messages. If
+IGNORE-MAX, scan to end of buffer. If END, we are after a
+\"__END__\" or \"__DATA__\" token, so ignore unbalanced
+constructs. END-OF-HERE-DOC points to the end of a here-document
+which has already been processed.
+Value is a two-element list of the position where an error
+occurred (if any) and the \"overshoot\", which is used for
+recursive calls in starting lines of here-documents."
(interactive)
(or min (setq min (point-min)
cperl-syntax-state nil
cperl-syntax-done-to min))
(or max (setq max (point-max)))
- (let* ((cperl-pod-here-fontify (eval cperl-pod-here-fontify)) go tmpend
- face head-face here-face b e bb tag qtag b1 e1 argument i c tail tb
+ (font-lock-flush min max)
+ (let* (go tmpend
+ face head-face b e bb tag qtag b1 e1 argument i c tail tb
is-REx is-x-REx REx-subgr-start REx-subgr-end was-subgr i2 hairy-RE
(case-fold-search nil) (inhibit-read-only t) (buffer-undo-list t)
(modified (buffer-modified-p)) overshoot is-o-REx name
@@ -3591,7 +3834,8 @@ the sections using `cperl-pod-head-face', `cperl-pod-face',
"\\<" cperl-sub-regexp "\\>" ; sub with proto/attr
"\\("
cperl-white-and-comment-rex
- "\\(::[a-zA-Z_:'0-9]*\\|[a-zA-Z_'][a-zA-Z_:'0-9]*\\)\\)?" ; name
+ (rx (group (eval cperl--normal-identifier-rx)))
+ "\\)"
"\\("
cperl-maybe-white-and-comment-rex
"\\(([^()]*)\\|:[^:]\\)\\)" ; prototype or attribute start
@@ -3619,20 +3863,20 @@ the sections using `cperl-pod-head-face', `cperl-pod-face',
(and cperl-pod-here-fontify
;; We had evals here, do not know why...
(setq face cperl-pod-face
- head-face cperl-pod-head-face
- here-face cperl-here-face))
- (remove-text-properties min max
- '(syntax-type t in-pod t syntax-table t
- attrib-group t
- REx-interpolated t
- cperl-postpone t
- syntax-subtype t
- rear-nonsticky t
- front-sticky t
- here-doc-group t
- first-format-line t
- REx-part2 t
- indentable t))
+ head-face cperl-pod-head-face))
+ (unless end-of-here-doc
+ (remove-text-properties min max
+ '(syntax-type t in-pod t syntax-table t
+ attrib-group t
+ REx-interpolated t
+ cperl-postpone t
+ syntax-subtype t
+ rear-nonsticky t
+ front-sticky t
+ here-doc-group t
+ first-format-line t
+ REx-part2 t
+ indentable t)))
;; Need to remove face as well...
(goto-char min)
(while (and
@@ -3751,120 +3995,36 @@ the sections using `cperl-pod-head-face', `cperl-pod-face',
;; but multiline quote on the same line as <<HERE confuses us...
;; ;; One extra () before this:
;;"<<"
- ;; "\\(" ; 1 + 1
+ ;; "<<\\(~?\\)" ; HERE-DOC, indented-p = capture 2
;; ;; First variant "BLAH" or just ``.
;; "[ \t]*" ; Yes, whitespace is allowed!
- ;; "\\([\"'`]\\)" ; 2 + 1
- ;; "\\([^\"'`\n]*\\)" ; 3 + 1
- ;; "\\3"
+ ;; "\\([\"'`]\\)" ; 3 + 1
+ ;; "\\([^\"'`\n]*\\)" ; 4 + 1
+ ;; "\\4"
;; "\\|"
;; ;; Second variant: Identifier or \ID or empty
- ;; "\\\\?\\(\\([a-zA-Z_][a-zA-Z_0-9]*\\)?\\)" ; 4 + 1, 5 + 1
+ ;; "\\\\?\\(\\([a-zA-Z_][a-zA-Z_0-9]*\\)?\\)" ; 5 + 1, 6 + 1
;; ;; Do not have <<= or << 30 or <<30 or << $blah.
;; ;; "\\([^= \t0-9$@%&]\\|[ \t]+[^ \t\n0-9$@%&]\\)" ; 6 + 1
- ;; "\\(\\)" ; To preserve count of pars :-( 6 + 1
;; "\\)"
- ((match-beginning 3) ; 2 + 1: found "<<", detect its type
- (setq b (point)
- tb (match-beginning 0)
- c (and ; not HERE-DOC
- (match-beginning 6)
- (save-match-data
- (or (looking-at "[ \t]*(") ; << function_call()
- (looking-at ">>") ; <<>> operator
- (save-excursion ; 1 << func_name, or $foo << 10
- (condition-case nil
- (progn
- (goto-char tb)
- ;;; XXX What to do: foo <<bar ???
- ;;; XXX Need to support print {a} <<B ???
- (forward-sexp -1)
- (save-match-data
- ; $foo << b; $f .= <<B;
- ; ($f+1) << b; a($f) . <<B;
- ; foo 1, <<B; $x{a} <<b;
- (cond
- ((looking-at "[0-9$({]")
- (forward-sexp 1)
- (and
- (looking-at "[ \t]*<<")
- (condition-case nil
- ;; print $foo <<EOF
- (progn
- (forward-sexp -2)
- (not
- (looking-at "\\(printf?\\|say\\|system\\|exec\\|sort\\)\\>")))
- (error t)))))))
- (error nil))) ; func(<<EOF)
- (and (not (match-beginning 7)) ; Empty
- (looking-at
- "[ \t]*[=0-9$@%&(]"))))))
- (if c ; Not here-doc
- nil ; Skip it.
- (setq c (match-end 3)) ; 2 + 1
- (if (match-beginning 6) ;6 + 1
- (setq b1 (match-beginning 6) ; 5 + 1
- e1 (match-end 6)) ; 5 + 1
- (setq b1 (match-beginning 5) ; 4 + 1
- e1 (match-end 5))) ; 4 + 1
- (setq tag (buffer-substring b1 e1)
- qtag (regexp-quote tag))
- (cond (cperl-pod-here-fontify
- ;; Highlight the starting delimiter
- (cperl-postpone-fontification
- b1 e1 'face my-cperl-delimiters-face)
- (cperl-put-do-not-fontify b1 e1 t)))
- (forward-line)
- (setq i (point))
- (if end-of-here-doc
- (goto-char end-of-here-doc))
- (setq b (point))
- ;; We do not search to max, since we may be called from
- ;; some hook of fontification, and max is random
- (or (and (re-search-forward
- (concat "^" (when (equal (match-string 2) "~") "[ \t]*")
- qtag "$")
- stop-point 'toend)
- ;;;(eq (following-char) ?\n) ; XXXX WHY???
- )
- (progn ; Pretend we matched at the end
- (goto-char (point-max))
- (re-search-forward "\\'")
- (setq warning-message
- (format "End of here-document `%s' not found." tag))
- (or (car err-l) (setcar err-l b))))
- (if cperl-pod-here-fontify
- (progn
- ;; Highlight the ending delimiter
- (cperl-postpone-fontification
- (match-beginning 0) (match-end 0)
- 'face my-cperl-delimiters-face)
- (cperl-put-do-not-fontify b (match-end 0) t)
- ;; Highlight the HERE-DOC
- (cperl-postpone-fontification b (match-beginning 0)
- 'face here-face)))
- (setq e1 (cperl-1+ (match-end 0)))
- (put-text-property b (match-beginning 0)
- 'syntax-type 'here-doc)
- (put-text-property (match-beginning 0) e1
- 'syntax-type 'here-doc-delim)
- (put-text-property b e1 'here-doc-group t)
- ;; This makes insertion at the start of HERE-DOC update
- ;; the whole construct:
- (put-text-property b (cperl-1+ b) 'front-sticky '(syntax-type))
- (cperl-commentify b e1 nil)
- (cperl-put-do-not-fontify b (match-end 0) t)
- ;; Cache the syntax info...
- (setq cperl-syntax-state (cons state-point state))
- ;; ... and process the rest of the line...
- (setq overshoot
- (elt ; non-inter ignore-max
- (cperl-find-pods-heres c i t end t e1) 1))
- (if (and overshoot (> overshoot (point)))
- (goto-char overshoot)
- (setq overshoot e1))
- (if (> e1 max)
- (setq tmpend tb))))
+ ((match-beginning 3) ; 2 + 1: found "<<", detect its type
+ (let* ((matched-pos (match-beginning 0))
+ (quoted-delim-p (if (match-beginning 6) nil t))
+ (delim-capture (if quoted-delim-p 5 6)))
+ (when (cperl-is-here-doc-p matched-pos)
+ (let ((here-doc-results
+ (cperl-process-here-doc
+ min max end overshoot stop-point ; for recursion
+ end-of-here-doc err-l ; for recursion
+ (equal (match-string 2) "~") ; indented here-doc?
+ matched-pos ; for recovery (?)
+ (match-end 3) ; todo from here
+ (match-beginning delim-capture) ; starting delimiter
+ (match-end delim-capture)))) ; boundaries
+ (setq tmpend (nth 0 here-doc-results)
+ overshoot (nth 1 here-doc-results))
+ (and (nth 2 here-doc-results)
+ (setq warning-message (nth 2 here-doc-results)))))))
;; format
((match-beginning 8)
;; 1+6=7 extra () before this:
@@ -3950,10 +4110,12 @@ the sections using `cperl-pod-head-face', `cperl-pod-face',
(t t))))
;; <file> or <$file>
(and (eq c ?\<)
- ;; Do not stringify <FH>, <$fh> :
+ ;; Stringify what looks like a glob, but
+ ;; do not stringify file handles <FH>, <$fh> :
(save-match-data
(looking-at
- "\\$?\\([_a-zA-Z:][_a-zA-Z0-9:]*\\)?>"))))
+ (rx (sequence (opt "$")
+ (eval cperl--normal-identifier-rx)))))))
tb (match-beginning 0))
(goto-char (match-beginning b1))
(cperl-backward-to-noncomment (point-min))
@@ -4023,7 +4185,16 @@ the sections using `cperl-pod-head-face', `cperl-pod-face',
(error nil)))
(if (or bb
(looking-at ; $foo -> {s}
- "[$@]\\$*\\([a-zA-Z0-9_:]+\\|[^{]\\)\\([ \t\n]*->\\)?[ \t\n]*{")
+ (rx
+ (sequence
+ (in "$@") (0+ "$")
+ (or
+ (eval cperl--normal-identifier-rx)
+ (not (in "{")))
+ (opt (sequence (eval cperl--ws*-rx))
+ "->")
+ (eval cperl--ws*-rx)
+ "{")))
(and ; $foo[12] -> {s}
(memq (following-char) '(?\{ ?\[))
(progn
@@ -4038,7 +4209,12 @@ the sections using `cperl-pod-head-face', `cperl-pod-face',
(setq bb t))
((and (eq (following-char) ?:)
(eq b1 ?\{) ; Check for $ { s::bar }
- (looking-at "::[a-zA-Z0-9_:]*[ \t\n\f]*}")
+ ;; (looking-at "::[a-zA-Z0-9_:]*[ \t\n\f]*}")
+ (looking-at
+ (rx (sequence "::"
+ (eval cperl--normal-identifier-rx)
+ (eval cperl--ws*-rx)
+ "}")))
(progn
(goto-char (1- go))
(skip-chars-backward " \t\n\f")
@@ -4203,7 +4379,7 @@ the sections using `cperl-pod-head-face', `cperl-pod-face',
"\\(" ;; XXXX 1-char variables, exc. |()\s
"[$@]"
"\\("
- "[_a-zA-Z:][_a-zA-Z0-9:]*"
+ (rx (eval cperl--normal-identifier-rx))
"\\|"
"{[^{}]*}" ; only one-level allowed
"\\|"
@@ -4642,19 +4818,24 @@ the sections using `cperl-pod-head-face', `cperl-pod-face',
(if (< p (point)) (goto-char p))
(setq stop t))))))
-;; Used only in `cperl-calculate-indent'...
+;; Used only in `cperl-sniff-for-indent'...
(defun cperl-block-p ()
- "Point is before ?\\{. Checks whether it starts a block."
+ "Point is before ?\\{. Return true if it starts a block."
;; No save-excursion! This is more a distinguisher of a block/hash ref...
(cperl-backward-to-noncomment (point-min))
(or (memq (preceding-char) (append ";){}$@&%\C-@" nil)) ; Or label! \C-@ at bobp
; Label may be mixed up with `$blah :'
(save-excursion (cperl-after-label))
+ ;; text with the 'attrib-group property is also covered by the
+ ;; next clause. We keep it because it is faster (for
+ ;; subroutines with attributes).
(get-text-property (cperl-1- (point)) 'attrib-group)
+ (save-excursion (cperl-block-declaration-p))
(and (memq (char-syntax (preceding-char)) '(?w ?_))
(progn
(backward-sexp)
;; sub {BLK}, print {BLK} $data, but NOT `bless', `return', `tr', `constant'
+ ;; a-zA-Z is fine here, these are Perl keywords
(or (and (looking-at "[a-zA-Z0-9_:]+[ \t\n\f]*[{#]") ; Method call syntax
(not (looking-at "\\(bless\\|return\\|q[wqrx]?\\|tr\\|[smy]\\|constant\\)\\>")))
;; sub bless::foo {}
@@ -4672,7 +4853,7 @@ the sections using `cperl-pod-head-face', `cperl-pod-face',
;; Moreover, one takes positive approach (looks for else,grep etc)
;; another negative (looks for bless,tr etc)
(defun cperl-after-block-p (lim &optional pre-block)
- "Return true if the preceding } (if PRE-BLOCK, following {) delimits a block.
+ "Return non-nil if the preceding } (if PRE-BLOCK, following {) delimits a block.
Would not look before LIM. Assumes that LIM is a good place to begin a
statement. The kind of block we treat here is one after which a new
statement would start; thus the block in ${func()} does not count."
@@ -4688,6 +4869,7 @@ statement would start; thus the block in ${func()} does not count."
(save-excursion (cperl-after-label))
;; sub :attr {}
(get-text-property (cperl-1- (point)) 'attrib-group)
+ (save-excursion (cperl-block-declaration-p))
(if (memq (char-syntax (preceding-char)) '(?w ?_)) ; else {}
(save-excursion
(forward-sexp -1)
@@ -4707,7 +4889,7 @@ statement would start; thus the block in ${func()} does not count."
(error nil))))
(defun cperl-after-expr-p (&optional lim chars test)
- "Return true if the position is good for start of expression.
+ "Return non-nil if the position is good for start of expression.
TEST is the expression to evaluate at the found position. If absent,
CHARS is a string that contains good characters to have before us (however,
`}' is treated \"smartly\" if it is not in the list)."
@@ -4803,7 +4985,8 @@ CHARS is a string that contains good characters to have before us (however,
(skip-chars-forward " \t"))
(defun cperl-after-block-and-statement-beg (lim)
- ;; We assume that we are after ?\}
+ "Return non-nil if the preceding ?} ends the statement."
+ ;; We assume that we are after ?\}
(and
(cperl-after-block-p lim)
(save-excursion
@@ -4861,7 +5044,11 @@ conditional/loop constructs."
cperl-maybe-white-and-comment-rex
"\\(state\\|my\\|local\\|our\\)\\)?"
cperl-maybe-white-and-comment-rex
- "\\$[_a-zA-Z0-9]+\\)?\\)\\>"))
+ (rx
+ (sequence
+ "$"
+ (eval cperl--basic-identifier-rx)))
+ "\\)?\\)\\>"))
(progn
(goto-char top)
(forward-sexp 1)
@@ -4955,7 +5142,14 @@ Returns some position at the last line."
;; Looking at:
;; foreach my $var (
(if (looking-at
- "[ \t]*\\<for\\(each\\)?[ \t]+\\(state\\|my\\|local\\|our\\)[ \t]*\\$[_a-zA-Z0-9]+\\(\t*\\|[ \t][ \t]+\\)[^ \t\n#]")
+ (rx (sequence (0+ blank) symbol-start
+ "for" (opt "each")
+ (1+ blank)
+ (or "state" "my" "local" "our")
+ (0+ blank)
+ "$" (eval cperl--basic-identifier-rx)
+ (1+ blank)
+ (not (in " \t\n#")))))
(progn
(forward-sexp 3)
(delete-horizontal-space)
@@ -4965,9 +5159,25 @@ Returns some position at the last line."
;; Looking at (with or without "}" at start, ending after "({"):
;; } foreach my $var () OR {
(if (looking-at
- "[ \t]*\\(}[ \t]*\\)?\\<\\(els\\(e\\|if\\)\\|continue\\|if\\|unless\\|while\\|for\\(each\\)?\\(\\([ \t]+\\(state\\|my\\|local\\|our\\)\\)?[ \t]*\\$[_a-zA-Z0-9]+\\)?\\|until\\)\\>\\([ \t]*(\\|[ \t\n]*{\\)\\|[ \t]*{")
+ (rx (sequence
+ (0+ blank)
+ (opt (sequence "}" (0+ blank) ))
+ symbol-start
+ (or "else" "elsif" "continue" "if" "unless" "while" "until"
+ (sequence (or "for" "foreach")
+ (opt
+ (opt (sequence (1+ blank)
+ (or "state" "my" "local" "our")))
+ (0+ blank)
+ "$" (eval cperl--basic-identifier-rx))))
+ symbol-end
+ (group-n 1
+ (or
+ (or (sequence (0+ blank) "(")
+ (sequence (eval cperl--ws*-rx) "{"))
+ (sequence (0+ blank) "{"))))))
(progn
- (setq ml (match-beginning 8)) ; "(" or "{" after control word
+ (setq ml (match-beginning 1)) ; "(" or "{" after control word
(re-search-forward "[({]")
(forward-char -1)
(setq p (point))
@@ -5279,8 +5489,12 @@ indentation and initial hashes. Behaves usually outside of comment."
;; Previous space could have gone:
(or (memq (preceding-char) '(?\s ?\t)) (insert " "))))))
+(defvar cperl-imenu-package-keywords '("package" "class" "role"))
+(defvar cperl-imenu-sub-keywords '("sub" "method" "function" "fun"))
+(defvar cperl-imenu-pod-keywords '("=head"))
+
(defun cperl-imenu--create-perl-index ()
- "Implement `imenu-create-index-function` for CPerl mode.
+ "Implement `imenu-create-index-function' for CPerl mode.
This function relies on syntaxification to exclude lines which
look like declarations but actually are part of a string, a
comment, or POD."
@@ -5297,20 +5511,21 @@ comment, or POD."
(current-package "(main)")
(current-package-end (point-max))) ; end of package scope
;; collect index entries
- (while (re-search-forward cperl--imenu-entries-regexp nil t)
+ (while (re-search-forward (rx (eval cperl--imenu-entries-rx)) nil t)
;; First, check whether we have left the scope of previously
;; recorded packages, and if so, eliminate them from the stack.
(while (< current-package-end (point))
(setq current-package (pop package-stack))
(setq current-package-end (pop package-stack)))
(let ((state (syntax-ppss))
+ (entry-type (match-string 1))
name marker) ; for the "current" entry
(cond
((nth 3 state) nil) ; matched in a string, so skip
- ((match-string 1) ; found a package name!
+ ((member entry-type cperl-imenu-package-keywords) ; package or class
(unless (nth 4 state) ; skip if in a comment
- (setq name (match-string-no-properties 1)
- marker (copy-marker (match-end 1)))
+ (setq name (match-string-no-properties 2)
+ marker (copy-marker (match-end 2)))
(if (string= (match-string 3) ";")
(setq current-package name) ; package NAME;
;; No semicolon, therefore we have: package NAME BLOCK.
@@ -5323,32 +5538,33 @@ comment, or POD."
(setq current-package-end (save-excursion
(goto-char (match-beginning 3))
(forward-sexp)
- (point)))
+ (point))))
(push (cons name marker) index-package-alist)
- (push (cons (concat "package " name) marker) index-unsorted-alist))))
- ((match-string 5) ; found a sub name!
+ (push (cons (concat entry-type " " name) marker) index-unsorted-alist)))
+ ((or (member entry-type cperl-imenu-sub-keywords) ; sub or method
+ (string-equal entry-type "")) ; named blocks
(unless (nth 4 state) ; skip if in a comment
- (setq name (match-string-no-properties 5)
- marker (copy-marker (match-end 5)))
+ (setq name (match-string-no-properties 2)
+ marker (copy-marker (match-end 2)))
;; Qualify the sub name with the package if it doesn't
;; already have one, and if it isn't lexically scoped.
;; "my" and "state" subs are lexically scoped, but "our"
;; are just lexical aliases to package subs.
(if (and (null (string-match "::" name))
- (or (null (match-string 4))
- (string-equal (match-string 4) "our")))
+ (or (null (match-string 3))
+ (string-equal (match-string 3) "our")))
(setq name (concat current-package "::" name)))
(let ((index (cons name marker)))
(push index index-alist)
(push index index-sub-alist)
(push index index-unsorted-alist))))
- ((match-string 6) ; found a POD heading!
- (when (get-text-property (match-beginning 6) 'in-pod)
+ ((member entry-type cperl-imenu-pod-keywords) ; POD heading
+ (when (get-text-property (match-beginning 2) 'in-pod)
(setq name (concat (make-string
- (* 3 (- (char-after (match-beginning 6)) ?1))
+ (* 3 (- (char-after (match-beginning 3)) ?1))
?\ )
- (match-string-no-properties 7))
- marker (copy-marker (match-beginning 7)))
+ (match-string-no-properties 2))
+ marker (copy-marker (match-beginning 2)))
(push (cons name marker) index-pod-alist)
(push (cons (concat "=" name) marker) index-unsorted-alist)))
(t (error "Unidentified match: %s" (match-string 0))))))
@@ -5371,7 +5587,11 @@ comment, or POD."
(setq lst index-sub-alist)
(while lst
(setq elt (car lst) lst (cdr lst))
- (cond ((string-match "\\(::\\|'\\)[_a-zA-Z0-9]+$" (car elt))
+ (cond ((string-match
+ (rx (sequence (or "::" "'")
+ (eval cperl--basic-identifier-rx)
+ string-end))
+ (car elt))
(setq pack (substring (car elt) 0 (match-beginning 0)))
(if (setq group (assoc pack hier-list))
(if (listp (cdr group))
@@ -5444,7 +5664,7 @@ comment, or POD."
(defvar cperl-font-lock-keywords nil
"Additional expressions to highlight in Perl mode. Default set.")
(defvar cperl-font-lock-keywords-2 nil
- "Additional expressions to highlight in Perl mode. Maximal set")
+ "Additional expressions to highlight in Perl mode. Maximal set.")
(defun cperl-load-font-lock-keywords ()
(or cperl-faces-init (cperl-init-faces))
@@ -5458,11 +5678,22 @@ comment, or POD."
(or cperl-faces-init (cperl-init-faces))
cperl-font-lock-keywords-2)
+(defun cperl-font-lock-syntactic-face-function (state)
+ "Apply faces according to their syntax type.
+In CPerl mode, this is used for here-documents which have been
+marked as c-style comments. For everything else, delegate to the
+default function."
+ (cond
+ ;; A c-style comment is a HERE-document. Fontify if requested.
+ ((and (eq 2 (nth 7 state))
+ cperl-pod-here-fontify)
+ cperl-here-face)
+ (t (funcall (default-value 'font-lock-syntactic-face-function) state))))
+
(defun cperl-init-faces ()
(condition-case errs
(progn
- (let (t-font-lock-keywords t-font-lock-keywords-1 font-lock-anchored)
- (setq font-lock-anchored t)
+ (let (t-font-lock-keywords t-font-lock-keywords-1)
(setq
t-font-lock-keywords
(list
@@ -5575,105 +5806,188 @@ comment, or POD."
(if (eq (char-after (cperl-1- (match-end 0))) ?\{ )
'font-lock-function-name-face
'font-lock-variable-name-face))))
- '("\\<\\(package\\|require\\|use\\|import\\|no\\|bootstrap\\)[ \t]+\\([a-zA-Z_][a-zA-Z_0-9:]*\\)[ \t;]" ; require A if B;
- 2 font-lock-function-name-face)
+ `(,(rx (sequence symbol-start
+ (or "package" "require" "use" "import"
+ "no" "bootstrap")
+ (eval cperl--ws+-rx)
+ (group-n 1 (eval cperl--normal-identifier-rx))
+ (any " \t;"))) ; require A if B;
+ 1 font-lock-function-name-face)
'("^[ \t]*format[ \t]+\\([a-zA-Z_][a-zA-Z_0-9:]*\\)[ \t]*=[ \t]*$"
1 font-lock-function-name-face)
- (cond (font-lock-anchored
- '("\\([]}\\%@>*&]\\|\\$[a-zA-Z0-9_:]*\\)[ \t]*{[ \t]*\\(-?[a-zA-Z0-9_:]+\\)[ \t]*}"
- (2 font-lock-string-face t)
- ("\\=[ \t]*{[ \t]*\\(-?[a-zA-Z0-9_:]+\\)[ \t]*}"
- nil nil
- (1 font-lock-string-face t))))
- (t '("\\([]}\\%@>*&]\\|\\$[a-zA-Z0-9_:]*\\)[ \t]*{[ \t]*\\(-?[a-zA-Z0-9_:]+\\)[ \t]*}"
- 2 font-lock-string-face t)))
- '("[[ \t{,(]\\(-?[a-zA-Z0-9_:]+\\)[ \t]*=>" 1
- font-lock-string-face t)
- '("^[ \t]*\\([a-zA-Z0-9_]+[ \t]*:\\)[ \t]*\\($\\|{\\|\\<\\(until\\|while\\|for\\(each\\)?\\|do\\)\\>\\)" 1
- font-lock-constant-face) ; labels
- '("\\<\\(continue\\|next\\|last\\|redo\\|break\\|goto\\)\\>[ \t]+\\([a-zA-Z0-9_:]+\\)" ; labels as targets
- 2 font-lock-constant-face)
+ ;; bareword hash key: $foo{bar}
+ `(,(rx (or (in "]}\\%@>*&") ; What Perl is this?
+ (sequence "$" (eval cperl--normal-identifier-rx)))
+ (0+ blank) "{" (0+ blank)
+ (group-n 1 (sequence (opt "-")
+ (eval cperl--basic-identifier-rx)))
+ (0+ blank) "}")
+;; '("\\([]}\\%@>*&]\\|\\$[a-zA-Z0-9_:]*\\)[ \t]*{[ \t]*\\(-?[a-zA-Z0-9_:]+\\)[ \t]*}"
+ (1 font-lock-string-face t)
+ ;; anchored bareword hash key: $foo{bar}{baz}
+ (,(rx point
+ (0+ blank) "{" (0+ blank)
+ (group-n 1 (sequence (opt "-")
+ (eval cperl--basic-identifier-rx)))
+ (0+ blank) "}")
+ ;; ("\\=[ \t]*{[ \t]*\\(-?[a-zA-Z0-9_:]+\\)[ \t]*}"
+ nil nil
+ (1 font-lock-string-face t)))
+ ;; hash element assignments with bareword key => value
+ `(,(rx (in "[ \t{,()")
+ (group-n 1 (sequence (opt "-")
+ (eval cperl--basic-identifier-rx)))
+ (0+ blank) "=>")
+ 1 font-lock-string-face t)
+;; '("[[ \t{,(]\\(-?[a-zA-Z0-9_:]+\\)[ \t]*=>" 1
+;; font-lock-string-face t)
+ ;; labels
+ `(,(rx
+ (sequence
+ (0+ space)
+ (group (eval cperl--label-rx))
+ (0+ space)
+ (or line-end "#" "{"
+ (sequence word-start
+ (or "until" "while" "for" "foreach" "do")
+ word-end))))
+ 1 font-lock-constant-face)
+ ;; labels as targets (no trailing colon!)
+ `(,(rx
+ (sequence
+ symbol-start
+ (or "continue" "next" "last" "redo" "break" "goto")
+ (1+ space)
+ (group (eval cperl--basic-identifier-rx))))
+ 1 font-lock-constant-face)
;; Uncomment to get perl-mode-like vars
;;; '("[$*]{?\\(\\sw+\\)" 1 font-lock-variable-name-face)
;;; '("\\([@%]\\|\\$#\\)\\(\\sw+\\)"
;;; (2 (cons font-lock-variable-name-face '(underline))))
- (cond (font-lock-anchored
;; 1=my_etc, 2=white? 3=(+white? 4=white? 5=var
- `(,(concat "\\<\\(state\\|my\\|local\\|our\\)"
- cperl-maybe-white-and-comment-rex
- "\\(("
- cperl-maybe-white-and-comment-rex
- "\\)?\\([$@%*]\\([a-zA-Z0-9_:]+\\|[^a-zA-Z0-9_]\\)\\)")
- (5 ,(if cperl-font-lock-multiline
- 'font-lock-variable-name-face
- '(progn (setq cperl-font-lock-multiline-start
- (match-beginning 0))
- 'font-lock-variable-name-face)))
- (,(concat "\\="
- cperl-maybe-white-and-comment-rex
- ","
- cperl-maybe-white-and-comment-rex
- "\\([$@%*]\\([a-zA-Z0-9_:]+\\|[^a-zA-Z0-9_]\\)\\)")
- ;; Bug in font-lock: limit is used not only to limit
- ;; searches, but to set the "extend window for
- ;; facification" property. Thus we need to minimize.
- ,(if cperl-font-lock-multiline
- '(if (match-beginning 3)
- (save-excursion
- (goto-char (match-beginning 3))
- (condition-case nil
- (forward-sexp 1)
- (error
- (condition-case nil
- (forward-char 200)
- (error nil)))) ; typeahead
- (1- (point))) ; report limit
- (forward-char -2)) ; disable continued expr
- '(if (match-beginning 3)
- (point-max) ; No limit for continuation
- (forward-char -2))) ; disable continued expr
- ,(if cperl-font-lock-multiline
- nil
- '(progn ; Do at end
- ;; "my" may be already fontified (POD),
- ;; so cperl-font-lock-multiline-start is nil
- (if (or (not cperl-font-lock-multiline-start)
- (> 2 (count-lines
- cperl-font-lock-multiline-start
- (point))))
- nil
- (put-text-property
- (1+ cperl-font-lock-multiline-start) (point)
- 'syntax-type 'multiline))
- (setq cperl-font-lock-multiline-start nil)))
- (3 font-lock-variable-name-face))))
- (t '("^[ \t{}]*\\(state\\|my\\|local\\|our\\)[ \t]*\\(([ \t]*\\)?\\([$@%*][a-zA-Z0-9_:]+\\)"
- 3 font-lock-variable-name-face)))
- '("\\<for\\(each\\)?\\([ \t]+\\(state\\|my\\|local\\|our\\)\\)?[ \t]*\\(\\$[a-zA-Z_][a-zA-Z_0-9]*\\)[ \t]*("
- 4 font-lock-variable-name-face)
+ `(,(rx (sequence (or "state" "my" "local" "our"))
+ (eval cperl--ws*-rx)
+ (opt (sequence "(" (eval cperl--ws*-rx)))
+ (group
+ (in "$@%*")
+ (or
+ (eval cperl--normal-identifier-rx)
+ (eval cperl--special-identifier-rx))
+ )
+ )
+ ;; (concat "\\<\\(state\\|my\\|local\\|our\\)"
+ ;; cperl-maybe-white-and-comment-rex
+ ;; "\\(("
+ ;; cperl-maybe-white-and-comment-rex
+ ;; "\\)?\\([$@%*]\\([a-zA-Z0-9_:]+\\|[^a-zA-Z0-9_]\\)\\)")
+ ;; (5 ,(if cperl-font-lock-multiline
+ (1 ,(if cperl-font-lock-multiline
+ 'font-lock-variable-name-face
+ '(progn (setq cperl-font-lock-multiline-start
+ (match-beginning 0))
+ 'font-lock-variable-name-face)))
+ (,(rx (sequence point
+ (eval cperl--ws*-rx)
+ ","
+ (eval cperl--ws*-rx)
+ (group
+ (in "$@%*")
+ (or
+ (eval cperl--normal-identifier-rx)
+ (eval cperl--special-identifier-rx))
+ )
+ )
+ )
+ ;; ,(concat "\\="
+ ;; cperl-maybe-white-and-comment-rex
+ ;; ","
+ ;; cperl-maybe-white-and-comment-rex
+ ;; "\\([$@%*]\\([a-zA-Z0-9_:]+\\|[^a-zA-Z0-9_]\\)\\)")
+ ;; Bug in font-lock: limit is used not only to limit
+ ;; searches, but to set the "extend window for
+ ;; facification" property. Thus we need to minimize.
+ ,(if cperl-font-lock-multiline
+ '(if (match-beginning 1)
+ (save-excursion
+ (goto-char (match-beginning 1))
+ (condition-case nil
+ (forward-sexp 1)
+ (error
+ (condition-case nil
+ (forward-char 200)
+ (error nil)))) ; typeahead
+ (1- (point))) ; report limit
+ (forward-char -2)) ; disable continued expr
+ '(if (match-beginning 1)
+ (point-max) ; No limit for continuation
+ (forward-char -2))) ; disable continued expr
+ ,(if cperl-font-lock-multiline
+ nil
+ '(progn ; Do at end
+ ;; "my" may be already fontified (POD),
+ ;; so cperl-font-lock-multiline-start is nil
+ (if (or (not cperl-font-lock-multiline-start)
+ (> 2 (count-lines
+ cperl-font-lock-multiline-start
+ (point))))
+ nil
+ (put-text-property
+ (1+ cperl-font-lock-multiline-start) (point)
+ 'syntax-type 'multiline))
+ (setq cperl-font-lock-multiline-start nil)))
+ (1 font-lock-variable-name-face)))
+ ;; foreach my $foo (
+ `(,(rx symbol-start "for" (opt "each")
+ (opt (sequence (1+ blank)
+ (or "state" "my" "local" "our")))
+ (0+ blank)
+ (group-n 1 (sequence "$"
+ (eval cperl--basic-identifier-rx)))
+ (0+ blank) "(")
+;; '("\\<for\\(each\\)?\\([ \t]+\\(state\\|my\\|local\\|our\\)\\)?[ \t]*\\(\\$[a-zA-Z_][a-zA-Z_0-9]*\\)[ \t]*("
+ 1 font-lock-variable-name-face)
;; Avoid $!, and s!!, qq!! etc. when not fontifying syntactically
'("\\(?:^\\|[^smywqrx$]\\)\\(!\\)" 1 font-lock-negation-char-face)
'("\\[\\(\\^\\)" 1 font-lock-negation-char-face prepend)))
(setq
t-font-lock-keywords-1
- '(
- ("\\(\\([@%]\\|\\$#\\)[a-zA-Z_:][a-zA-Z0-9_:]*\\)" 1
+ `(
+ ;; arrays and hashes. Access to elements is fixed below
+ (,(rx (group-n 1 (group-n 2 (or (in "@%") "$#"))
+ (eval cperl--normal-identifier-rx)))
+ 1
+;; ("\\(\\([@%]\\|\\$#\\)[a-zA-Z_:][a-zA-Z0-9_:]*\\)" 1
(if (eq (char-after (match-beginning 2)) ?%)
'cperl-hash-face
'cperl-array-face)
nil) ; arrays and hashes
- ("\\(\\([$@%]+\\)[a-zA-Z_:][a-zA-Z0-9_:]*\\)[ \t]*\\([[{]\\)"
+ ;; access to array/hash elements
+ (,(rx (group-n 1 (group-n 2 (in "$@%"))
+ (eval cperl--normal-identifier-rx))
+ (0+ blank)
+ (group-n 3 (in "[{")))
+;; ("\\(\\([$@%]+\\)[a-zA-Z_:][a-zA-Z0-9_:]*\\)[ \t]*\\([[{]\\)"
1
(if (= (- (match-end 2) (match-beginning 2)) 1)
(if (eq (char-after (match-beginning 3)) ?{)
'cperl-hash-face
'cperl-array-face) ; arrays and hashes
font-lock-variable-name-face) ; Just to put something
- t)
- ("\\(@\\|\\$#\\)\\(\\$+\\([a-zA-Z_:][a-zA-Z0-9_:]*\\|[^ \t\n]\\)\\)"
+ t) ; override previous
+ ;; @$ array dereferences, $#$ last array index
+ (,(rx (group-n 1 (or "@" "$#"))
+ (group-n 2 (sequence "$"
+ (or (eval cperl--normal-identifier-rx)
+ (not (in " \t\n"))))))
+ ;; ("\\(@\\|\\$#\\)\\(\\$+\\([a-zA-Z_:][a-zA-Z0-9_:]*\\|[^ \t\n]\\)\\)"
(1 'cperl-array-face)
(2 font-lock-variable-name-face))
- ("\\(%\\)\\(\\$+\\([a-zA-Z_:][a-zA-Z0-9_:]*\\|[^ \t\n]\\)\\)"
+ ;; %$ hash dereferences
+ (,(rx (group-n 1 "%")
+ (group-n 2 (sequence "$"
+ (or (eval cperl--normal-identifier-rx)
+ (not (in " \t\n"))))))
+ ;; ("\\(%\\)\\(\\$+\\([a-zA-Z_:][a-zA-Z0-9_:]*\\|[^ \t\n]\\)\\)"
(1 'cperl-hash-face)
(2 font-lock-variable-name-face))
;;("\\([smy]\\|tr\\)\\([^a-z_A-Z0-9]\\)\\(\\([^\n\\]*||\\)\\)\\2")
@@ -6048,7 +6362,7 @@ side-effect of memorizing only. Examples in `cperl-style-examples'."
(filename nodename &optional no-going-back strict-case))
(defun cperl-info-buffer (type)
- ;; Returns buffer with documentation. Creates if missing.
+ ;; Return buffer with documentation. Creates if missing.
;; If TYPE, this vars buffer.
;; Special care is taken to not stomp over an existing info buffer
(let* ((bname (if type "*info-perl-var*" "*info-perl*"))
@@ -6182,7 +6496,7 @@ Customized by setting variables `cperl-shrink-wrap-info-frame',
(declare-function imenu-choose-buffer-index "imenu" (&optional prompt alist))
(defun cperl-imenu-on-info ()
- "Shows imenu for Perl Info Buffer.
+ "Show imenu for Perl Info Buffer.
Opens Perl Info buffer if needed."
(interactive)
(require 'imenu)
@@ -6235,6 +6549,8 @@ Will not move the position at the start to the left."
(indent-region beg end nil)
(goto-char beg)
(setq col (current-column))
+ ;; Assuming that lineup is done on Perl syntax, this regexp
+ ;; doesn't need to be unicode aware -- haj, 2021-09-10
(if (looking-at "[a-zA-Z0-9_]")
(if (looking-at "\\<[a-zA-Z0-9_]+\\>")
(setq search
@@ -6272,6 +6588,9 @@ Will not move the position at the start to the left."
"Run etags with appropriate options for Perl files.
If optional argument ALL is `recursive', will process Perl files
in subdirectories too."
+ ;; Apparently etags doesn't support UTF-8 encoded sources, and usage
+ ;; of etags has been commented out in the menu since ... well,
+ ;; forever. So, let's just stick to ASCII here. -- haj, 2021-09-14
(interactive)
(let ((cmd "etags")
(args `("-l" "none" "-r"
@@ -6411,6 +6730,9 @@ Does not move point."
;; Search for the function
(progn ;;save-match-data
(while (re-search-forward
+ ;; FIXME: Should XS code be unicode aware? Recent C
+ ;; compilers (Gcc 10+) are, but I guess this isn't used
+ ;; much. -- haj, 2021-09-14
"^\\([ \t]*MODULE\\>[^\n]*\\<PACKAGE[ \t]*=[ \t]*\\([a-zA-Z_][a-zA-Z_0-9:]*\\)\\>\\|\\([a-zA-Z_][a-zA-Z_0-9]*\\)(\\|[ \t]*BOOT:\\)"
nil t)
(cond
@@ -6473,7 +6795,7 @@ Does not move point."
(setq lst
(mapcar
(lambda (elt)
- (cond ((string-match "^[_a-zA-Z]" (car elt))
+ (cond ((string-match (rx line-start (or alpha "_")) (car elt))
(goto-char (cdr elt))
(beginning-of-line) ; pos should be of the start of the line
(list (car elt)
@@ -6503,9 +6825,14 @@ Does not move point."
","
(number-to-string (1- (elt elt 1))) ; Char pos 0-based
"\n")
- (if (and (string-match "^[_a-zA-Z]+::" (car elt))
- (string-match (concat "^" cperl-sub-regexp "[ \t]+\\([_a-zA-Z]+\\)[^:_a-zA-Z]")
- (elt elt 3)))
+ (if (and (string-match (rx line-start
+ (eval cperl--basic-identifier-rx) "++")
+ (car elt))
+ (string-match (rx-to-string `(sequence line-start
+ (regexp ,cperl-sub-regexp)
+ (1+ (in " \t"))
+ ,cperl--normal-identifier-rx))
+ (elt elt 3)))
;; Need to insert the name without package as well
(setq lst (cons (cons (substring (elt elt 3)
(match-beginning 1)
@@ -6530,8 +6857,7 @@ Does not move point."
"Add to TAGS data for \"pure\" Perl files in the current directory and kids.
Use as
emacs -batch -q -no-site-file -l emacs/cperl-mode.el \\
- -f cperl-add-tags-recurse-noxs
-"
+ -f cperl-add-tags-recurse-noxs"
(cperl-write-tags nil nil t t nil t))
(defun cperl-add-tags-recurse-noxs-fullpath ()
@@ -6539,16 +6865,14 @@ Use as
Writes down fullpath, so TAGS is relocatable (but if the build directory
is relocated, the file TAGS inside it breaks). Use as
emacs -batch -q -no-site-file -l emacs/cperl-mode.el \\
- -f cperl-add-tags-recurse-noxs-fullpath
-"
+ -f cperl-add-tags-recurse-noxs-fullpath"
(cperl-write-tags nil nil t t nil t ""))
(defun cperl-add-tags-recurse ()
"Add to TAGS file data for Perl files in the current directory and kids.
Use as
emacs -batch -q -no-site-file -l emacs/cperl-mode.el \\
- -f cperl-add-tags-recurse
-"
+ -f cperl-add-tags-recurse"
(cperl-write-tags nil nil t t))
(defvar cperl-tags-file-name "TAGS"
@@ -6958,14 +7282,14 @@ Currently it is tuned to C and Perl syntax."
;;(concat "\\("
(mapconcat
#'identity
- '("[$@%*&][0-9a-zA-Z_:]+\\([ \t]*[[{]\\)?" ; Usual variable
+ '("[$@%*&][[:alnum:]_:]+\\([ \t]*[[{]\\)?" ; Usual variable
"[$@]\\^[a-zA-Z]" ; Special variable
"[$@][^ \n\t]" ; Special variable
"-[a-zA-Z]" ; File test
"\\\\[a-zA-Z0]" ; Special chars
"^=[a-z][a-zA-Z0-9_]*" ; POD sections
"[-!&*+,./<=>?\\^|~]+" ; Operator
- "[a-zA-Z_0-9:]+" ; symbol or number
+ "[[:alnum:]_:]+" ; symbol or number
"x="
"#!")
;;"\\)\\|\\("
@@ -6981,7 +7305,7 @@ Currently it is tuned to C and Perl syntax."
;; Does not save-excursion
;; Get to the something meaningful
(or (eobp) (eolp) (forward-char 1))
- (re-search-backward "[-a-zA-Z0-9_:!&*+,./<=>?\\^|~$%@]"
+ (re-search-backward "[-[:alnum:]_:!&*+,./<=>?\\^|~$%@]"
(point-at-bol)
'to-beg)
;; (cond
@@ -6990,8 +7314,8 @@ Currently it is tuned to C and Perl syntax."
;; (or (bobp) (backward-char 1))))
;; Try to backtrace
(cond
- ((looking-at "[a-zA-Z0-9_:]") ; symbol
- (skip-chars-backward "a-zA-Z0-9_:")
+ ((looking-at "[[:alnum:]_:]") ; symbol
+ (skip-chars-backward "[:alnum:]_:")
(cond
((and (eq (preceding-char) ?^) ; $^I
(eq (char-after (- (point) 2)) ?\$))
@@ -7002,7 +7326,7 @@ Currently it is tuned to C and Perl syntax."
(eq (current-column) 1))
(forward-char -1))) ; =head1
(if (and (eq (preceding-char) ?\<)
- (looking-at "\\$?[a-zA-Z0-9_:]+>")) ; <FH>
+ (looking-at "\\$?[[:alnum:]_:]+>")) ; <FH>
(forward-char -1)))
((and (looking-at "=") (eq (preceding-char) ?x)) ; x=
(forward-char -1))
@@ -7015,15 +7339,15 @@ Currently it is tuned to C and Perl syntax."
(not (eq (char-after (- (point) 2)) ?\$))) ; $-
(forward-char -1))
((and (eq (following-char) ?\>)
- (string-match "[a-zA-Z0-9_]" (char-to-string (preceding-char)))
+ (string-match "[[:alnum:]_]" (char-to-string (preceding-char)))
(save-excursion
(forward-sexp -1)
(and (eq (preceding-char) ?\<)
- (looking-at "\\$?[a-zA-Z0-9_:]+>")))) ; <FH>
+ (looking-at "\\$?[[:alnum:]_:]+>")))) ; <FH>
(search-backward "<"))))
((and (eq (following-char) ?\$)
(eq (preceding-char) ?\<)
- (looking-at "\\$?[a-zA-Z0-9_:]+>")) ; <$fh>
+ (looking-at "\\$?[[:alnum:]_:]+>")) ; <$fh>
(forward-char -1)))
(if (looking-at cperl-have-help-regexp)
(buffer-substring (match-beginning 0) (match-end 0))))
@@ -7532,11 +7856,10 @@ prototype \\&SUB Returns prototype of the function given a reference.
=begin formatname Start directly formatted region.
=end formatname End directly formatted region.
=for formatname text Paragraph in special format.
-=encoding encodingname Encoding of the document.
-")
+=encoding encodingname Encoding of the document.")
(defun cperl-switch-to-doc-buffer (&optional interactive)
- "Go to the perl documentation buffer and insert the documentation."
+ "Go to the Perl documentation buffer and insert the documentation."
(interactive "p")
(let ((buf (get-buffer-create cperl-doc-buffer)))
(if interactive
@@ -8195,7 +8518,7 @@ If a region is highlighted, restricts to the region."
beg end))))
(defun cperl-map-pods-heres (func &optional prop s end)
- "Executes a function over regions of pods or here-documents.
+ "Execute a function over regions of pods or here-documents.
PROP is the text-property to search for; default to `in-pod'. Stop when
function returns nil."
(let (pos posend has-prop (cont t))
@@ -8370,7 +8693,7 @@ Delay of auto-help controlled by `cperl-lazy-help-time'."
(remove-text-properties beg end '(face nil))))
(defun cperl-font-lock-fontify-region-function (beg end loudly)
- "Extends the region to safe positions, then calls the default function.
+ "Extend the region to safe positions, then call the default function.
Newer `font-lock's can do it themselves.
We unwind only as far as needed for fontification. Syntaxification may
do extra unwind via `cperl-unwind-to-safe'."