diff options
| author | Harald Jörg | 2023-06-30 23:41:06 +0200 |
|---|---|---|
| committer | Harald Jörg | 2023-06-30 23:41:06 +0200 |
| commit | a7ff8a76a52d316b27f05cd1267fe94cea9f35d1 (patch) | |
| tree | d767d5ff79405a6fb4066c20592f4e88faef318e | |
| parent | 361bf8a1132724516bf7e01d5f91510ffec83991 (diff) | |
| download | emacs-a7ff8a76a52d316b27f05cd1267fe94cea9f35d1.tar.gz emacs-a7ff8a76a52d316b27f05cd1267fe94cea9f35d1.zip | |
cperl-mode.el: Support subroutine signatures
Since Perl 5.20, subroutine signatures were available as an
experimental feature. With Perl 5.38, they will be always enabled in
the new object system.
* test/lisp/progmodes/cperl-mode-resources/cperl-bug-64190.pl:
* test/lisp/progmodes/cperl-mode-resources/cperl-bug-64364.pl: New
test resources.
* test/lisp/progmodes/cperl-mode-tests.el
(cperl-test-fontify-attrs-and-signatures): Add tests for
signatures.
(cperl-test-attribute-rx, cperl-test-attribute-list-rx)
(cperl-test-prototype-rx, cperl-test-signature-rx): Tests for the
new rx sequences.
(cperl-test-bug-64190): New test for multiline declarations.
(cperl-test-bug-64364): New test for indentation of declarations.
* lisp/progmodes/cperl-mode.el:
(toplevel): New rx sequences to match Perl variables and attributes.
(cperl-declaration-header-p): New function to identify declarations.
(cperl-block-declaration-p): Use the new function.
(cperl-mode): Use the rx sequences.
(cperl-get-state): Use the new function.
(cperl-sniff-for-indent): Use the new function.
(cperl-find-sub-attrs): Improve fontification of subroutine
prototypes and attributes while typing when jit-lock-mode is
active. Detect signatures, and distinguish them from prototypes.
(cperl-find-pods-heres): Use the rx sequences to detect subroutines.
(cperl-init-faces): Use the rx sequences for fontification.
| -rw-r--r-- | lisp/progmodes/cperl-mode.el | 388 | ||||
| -rw-r--r-- | test/lisp/progmodes/cperl-mode-resources/cperl-bug-64190.pl | 24 | ||||
| -rw-r--r-- | test/lisp/progmodes/cperl-mode-resources/cperl-bug-64364.pl | 26 | ||||
| -rw-r--r-- | test/lisp/progmodes/cperl-mode-tests.el | 143 |
4 files changed, 489 insertions, 92 deletions
diff --git a/lisp/progmodes/cperl-mode.el b/lisp/progmodes/cperl-mode.el index 66f01109e3f..fb636d0fb78 100644 --- a/lisp/progmodes/cperl-mode.el +++ b/lisp/progmodes/cperl-mode.el | |||
| @@ -1187,8 +1187,7 @@ The expansion is entirely correct because it uses the C preprocessor." | |||
| 1187 | "A regular expression for the name of a \"basic\" Perl variable. | 1187 | "A regular expression for the name of a \"basic\" Perl variable. |
| 1188 | Neither namespace separators nor sigils are included. As is, | 1188 | Neither namespace separators nor sigils are included. As is, |
| 1189 | this regular expression applies to labels,subroutine calls where | 1189 | this regular expression applies to labels,subroutine calls where |
| 1190 | the ampersand sigil is not required, and names of subroutine | 1190 | the ampersand sigil is not required, and names of attributes.") |
| 1191 | attributes.") | ||
| 1192 | 1191 | ||
| 1193 | (defconst cperl--label-rx | 1192 | (defconst cperl--label-rx |
| 1194 | `(sequence symbol-start | 1193 | `(sequence symbol-start |
| @@ -1225,6 +1224,30 @@ is a legal variable name).") | |||
| 1225 | (in "!\"$%&'()+,-./:;<=>?@\\]^_`|~")) ; $., $|, $", ... but not $^ or ${ | 1224 | (in "!\"$%&'()+,-./:;<=>?@\\]^_`|~")) ; $., $|, $", ... but not $^ or ${ |
| 1226 | "The list of Perl \"punctuation\" variables, as listed in perlvar.") | 1225 | "The list of Perl \"punctuation\" variables, as listed in perlvar.") |
| 1227 | 1226 | ||
| 1227 | (defconst cperl--basic-scalar-rx | ||
| 1228 | `(sequence "$" ,cperl--basic-identifier-rx) | ||
| 1229 | "Regular expression for a scalar (without package). | ||
| 1230 | This regexp intentionally does not support spaces (nor newlines | ||
| 1231 | and comments) between the sigil and the identifier, for | ||
| 1232 | educational reasons. So \"$foo\" will be matched, but \"$ foo\" | ||
| 1233 | or \"${ foo }\" will not.") | ||
| 1234 | |||
| 1235 | (defconst cperl--basic-array-rx | ||
| 1236 | `(sequence "@" ,cperl--basic-identifier-rx) | ||
| 1237 | "Regular expression for an array variable (without package). | ||
| 1238 | This regexp intentionally does not support spaces (nor newlines | ||
| 1239 | and comments) between the sigil and the identifier, for | ||
| 1240 | educational reasons. So \"@foo\" will be matched, but \"@ foo\" | ||
| 1241 | or \"@{ foo }\" will not.") | ||
| 1242 | |||
| 1243 | (defconst cperl--basic-hash-rx | ||
| 1244 | `(sequence "%" ,cperl--basic-identifier-rx) | ||
| 1245 | "Regular expression for a hash variable (without package). | ||
| 1246 | This regexp intentionally does not support spaces (nor newlines | ||
| 1247 | and comments) between the sigil and the identifier, for | ||
| 1248 | educational reasons. So \"%foo\" will be matched, but \"% foo\" | ||
| 1249 | or \"%{ foo }\" will not.") | ||
| 1250 | |||
| 1228 | (defconst cperl--ws-rx | 1251 | (defconst cperl--ws-rx |
| 1229 | '(sequence (or space "\n")) | 1252 | '(sequence (or space "\n")) |
| 1230 | "Regular expression for a single whitespace in Perl.") | 1253 | "Regular expression for a single whitespace in Perl.") |
| @@ -1246,6 +1269,27 @@ is a legal variable name).") | |||
| 1246 | `(1+ ,cperl--ws-or-comment-rx) | 1269 | `(1+ ,cperl--ws-or-comment-rx) |
| 1247 | "Regular expression for a sequence of whitespace and comments in Perl.") | 1270 | "Regular expression for a sequence of whitespace and comments in Perl.") |
| 1248 | 1271 | ||
| 1272 | (defconst cperl--basic-variable-rx | ||
| 1273 | `(sequence (in "$@%") ,cperl--basic-identifier-rx) | ||
| 1274 | "Regular expression for a Perl variable (scalar, array or hash). | ||
| 1275 | This regexp intentionally does not support spaces (nor newlines | ||
| 1276 | and comments) between the sigil and the identifier, for | ||
| 1277 | educational reasons. So \"$foo\" will be matched, but \"$ foo\" | ||
| 1278 | or \"${ foo }\" will not.") | ||
| 1279 | |||
| 1280 | (defconst cperl--variable-list-rx | ||
| 1281 | `(sequence "(" | ||
| 1282 | (optional (sequence | ||
| 1283 | ,cperl--ws*-rx | ||
| 1284 | ,cperl--basic-variable-rx | ||
| 1285 | (0+ (sequence | ||
| 1286 | ,cperl--ws*-rx | ||
| 1287 | "," | ||
| 1288 | ,cperl--ws*-rx | ||
| 1289 | ,cperl--basic-variable-rx)) | ||
| 1290 | ,cperl--ws*-rx))) | ||
| 1291 | "Regular expression for a list of Perl variables for declarations.") | ||
| 1292 | |||
| 1249 | ;; This is left as a string regexp. There are many version schemes in | 1293 | ;; This is left as a string regexp. There are many version schemes in |
| 1250 | ;; the wild, so people might want to fiddle with this variable. | 1294 | ;; the wild, so people might want to fiddle with this variable. |
| 1251 | (defconst cperl--version-regexp | 1295 | (defconst cperl--version-regexp |
| @@ -1260,6 +1304,54 @@ is a legal variable name).") | |||
| 1260 | (optional (sequence "_" (1+ word)))))) | 1304 | (optional (sequence "_" (1+ word)))))) |
| 1261 | "A sequence for recommended version number schemes in Perl.") | 1305 | "A sequence for recommended version number schemes in Perl.") |
| 1262 | 1306 | ||
| 1307 | (defconst cperl--single-attribute-rx | ||
| 1308 | `(sequence ,cperl--basic-identifier-rx | ||
| 1309 | (optional (sequence "(" | ||
| 1310 | (0+ (not (in ")"))) | ||
| 1311 | ")"))) | ||
| 1312 | "A regular expression for a single attribute, without leading colon. | ||
| 1313 | It may have parameters in parens, but parens within the | ||
| 1314 | parameter's value are not supported.. This regexp does not have | ||
| 1315 | capture groups.") | ||
| 1316 | |||
| 1317 | (defconst cperl--attribute-list-rx | ||
| 1318 | `(sequence ":" | ||
| 1319 | (0+ (sequence | ||
| 1320 | ,cperl--ws*-rx | ||
| 1321 | ,cperl--single-attribute-rx | ||
| 1322 | ,cperl--ws*-rx | ||
| 1323 | (optional ":")))) | ||
| 1324 | "A regular expression for an attribute list. | ||
| 1325 | Attribute lists may only occur in certain declarations. A colon | ||
| 1326 | is required before the first attribute but optional between | ||
| 1327 | subsequent attributes. This regexp does not have capture groups.") | ||
| 1328 | |||
| 1329 | (defconst cperl--prototype-rx | ||
| 1330 | `(sequence "(" | ||
| 1331 | (0+ (any "$@%&*;\\[]")) | ||
| 1332 | ")") | ||
| 1333 | "A regular expression for a subroutine prototype. Not as strict as the actual prototype syntax, but good enough to distinguish prototypes from signatures.") | ||
| 1334 | |||
| 1335 | (defconst cperl--signature-rx | ||
| 1336 | `(sequence "(" | ||
| 1337 | (optional | ||
| 1338 | (sequence | ||
| 1339 | (0+ (sequence ,cperl--ws*-rx | ||
| 1340 | ,cperl--basic-scalar-rx | ||
| 1341 | ,cperl--ws*-rx | ||
| 1342 | ",")) | ||
| 1343 | ,cperl--ws*-rx | ||
| 1344 | (or ,cperl--basic-scalar-rx | ||
| 1345 | ,cperl--basic-array-rx | ||
| 1346 | ,cperl--basic-hash-rx))) | ||
| 1347 | (optional (sequence ,cperl--ws*-rx) "," ) | ||
| 1348 | ,cperl--ws*-rx | ||
| 1349 | ")") | ||
| 1350 | "A regular expression for a subroutine signature. | ||
| 1351 | These are a bit more restricted than \"my\" declaration lists | ||
| 1352 | because they allow only one slurpy variable, and only in the last | ||
| 1353 | place.") | ||
| 1354 | |||
| 1263 | (defconst cperl--package-rx | 1355 | (defconst cperl--package-rx |
| 1264 | `(sequence (group "package") | 1356 | `(sequence (group "package") |
| 1265 | ,cperl--ws+-rx | 1357 | ,cperl--ws+-rx |
| @@ -1327,6 +1419,15 @@ Covers packages, subroutines, and POD headings.") | |||
| 1327 | ) | 1419 | ) |
| 1328 | 1420 | ||
| 1329 | 1421 | ||
| 1422 | (defun cperl-declaration-header-p (pos) | ||
| 1423 | "Return t if POS is in the header of a declaration. | ||
| 1424 | Perl syntax can have various constructs between a | ||
| 1425 | keyword (e.g. \"sub\") and its associated block of code, and | ||
| 1426 | these can span several lines. These blocks are identified and | ||
| 1427 | marked with a text-property in `cperl-find-pods-heres'. This | ||
| 1428 | function tests that property." | ||
| 1429 | (equal (get-text-property pos 'syntax-type) 'sub-decl)) | ||
| 1430 | |||
| 1330 | (defun cperl-block-declaration-p () | 1431 | (defun cperl-block-declaration-p () |
| 1331 | "Test whether the following ?\\{ opens a declaration block. | 1432 | "Test whether the following ?\\{ opens a declaration block. |
| 1332 | Returns the column where the declarating keyword is found, or nil | 1433 | Returns the column where the declarating keyword is found, or nil |
| @@ -1345,6 +1446,9 @@ statement, so there's no semicolon." | |||
| 1345 | ((looking-at (rx (eval cperl--block-declaration-rx))) | 1446 | ((looking-at (rx (eval cperl--block-declaration-rx))) |
| 1346 | (setq is-block-declaration (current-column) | 1447 | (setq is-block-declaration (current-column) |
| 1347 | continue-searching nil)) | 1448 | continue-searching nil)) |
| 1449 | ((cperl-declaration-header-p (point)) | ||
| 1450 | (setq is-block-declaration (current-column) | ||
| 1451 | continue-searching nil)) | ||
| 1348 | ;; Another brace means this is no block declaration | 1452 | ;; Another brace means this is no block declaration |
| 1349 | ((looking-at "{") | 1453 | ((looking-at "{") |
| 1350 | (setq continue-searching nil)) | 1454 | (setq continue-searching nil)) |
| @@ -1710,6 +1814,8 @@ or as help on variables `cperl-tips', `cperl-problems', | |||
| 1710 | (concat "^[ \t]*\\(" | 1814 | (concat "^[ \t]*\\(" |
| 1711 | cperl-sub-regexp | 1815 | cperl-sub-regexp |
| 1712 | (cperl-after-sub-regexp 'named 'attr-groups) | 1816 | (cperl-after-sub-regexp 'named 'attr-groups) |
| 1817 | (rx (eval cperl--ws*-rx)) | ||
| 1818 | (rx (optional (eval cperl--signature-rx))) | ||
| 1713 | "\\|" ; per toke.c | 1819 | "\\|" ; per toke.c |
| 1714 | "\\(BEGIN\\|UNITCHECK\\|CHECK\\|INIT\\|END\\|AUTOLOAD\\|DESTROY\\)" | 1820 | "\\(BEGIN\\|UNITCHECK\\|CHECK\\|INIT\\|END\\|AUTOLOAD\\|DESTROY\\)" |
| 1715 | "\\)" | 1821 | "\\)" |
| @@ -2553,6 +2659,9 @@ PRESTART is the position basing on which START was found." | |||
| 2553 | (<= parse-start start-point)) | 2659 | (<= parse-start start-point)) |
| 2554 | (goto-char parse-start) | 2660 | (goto-char parse-start) |
| 2555 | (beginning-of-defun) | 2661 | (beginning-of-defun) |
| 2662 | (when (cperl-declaration-header-p (point)) | ||
| 2663 | (goto-char (cperl-beginning-of-property (point) 'syntax-type)) | ||
| 2664 | (beginning-of-line)) | ||
| 2556 | (setq start-state nil)) | 2665 | (setq start-state nil)) |
| 2557 | (setq prestart (point)) | 2666 | (setq prestart (point)) |
| 2558 | (if start-state nil | 2667 | (if start-state nil |
| @@ -2759,12 +2868,15 @@ Will not look before LIM." | |||
| 2759 | (if (not (or (eq (1- (point)) containing-sexp) | 2868 | (if (not (or (eq (1- (point)) containing-sexp) |
| 2760 | (and cperl-indent-parens-as-block | 2869 | (and cperl-indent-parens-as-block |
| 2761 | (not is-block)) | 2870 | (not is-block)) |
| 2762 | (save-excursion (cperl-block-declaration-p)) | 2871 | (and (looking-at "{") |
| 2872 | (save-excursion (cperl-block-declaration-p))) | ||
| 2763 | (memq (preceding-char) | 2873 | (memq (preceding-char) |
| 2764 | (append (if is-block " ;{" " ,;{") '(nil))) | 2874 | (append (if is-block " ;{" " ,;{") '(nil))) |
| 2765 | (and (eq (preceding-char) ?\}) | 2875 | (and (eq (preceding-char) ?\}) |
| 2766 | (cperl-after-block-and-statement-beg | 2876 | (cperl-after-block-and-statement-beg |
| 2767 | containing-sexp)) | 2877 | containing-sexp)) |
| 2878 | (and (cperl-declaration-header-p indent-point) | ||
| 2879 | (not (cperl-declaration-header-p char-after-pos))) | ||
| 2768 | (get-text-property (point) 'first-format-line))) | 2880 | (get-text-property (point) 'first-format-line))) |
| 2769 | ;; This line is continuation of preceding line's statement; | 2881 | ;; This line is continuation of preceding line's statement; |
| 2770 | ;; indent `cperl-continued-statement-offset' more than the | 2882 | ;; indent `cperl-continued-statement-offset' more than the |
| @@ -2843,12 +2955,11 @@ Will not look before LIM." | |||
| 2843 | ;; anonymous sub in a hash. | 2955 | ;; anonymous sub in a hash. |
| 2844 | (if (and;; Is it a sub in group starting on this line? | 2956 | (if (and;; Is it a sub in group starting on this line? |
| 2845 | cperl-indent-subs-specially | 2957 | cperl-indent-subs-specially |
| 2846 | (cond ((get-text-property (point) 'attrib-group) | 2958 | (cond |
| 2847 | (goto-char (cperl-beginning-of-property | 2959 | ((cperl-declaration-header-p (point)) |
| 2848 | (point) 'attrib-group))) | 2960 | (goto-char |
| 2849 | ((eq (preceding-char) ?b) | 2961 | (cperl-beginning-of-property (point) |
| 2850 | (forward-sexp -1) | 2962 | 'syntax-type)))) |
| 2851 | (looking-at (concat cperl-sub-regexp "\\>")))) | ||
| 2852 | (setq p (nth 1 ; start of innermost containing list | 2963 | (setq p (nth 1 ; start of innermost containing list |
| 2853 | (parse-partial-sexp | 2964 | (parse-partial-sexp |
| 2854 | (line-beginning-position) | 2965 | (line-beginning-position) |
| @@ -2992,6 +3103,9 @@ and closing parentheses and brackets." | |||
| 2992 | (goto-char (elt i 1)) ; statement-start | 3103 | (goto-char (elt i 1)) ; statement-start |
| 2993 | (+ (if (or (memq (elt i 2) (append "}])" nil)) ; char-after | 3104 | (+ (if (or (memq (elt i 2) (append "}])" nil)) ; char-after |
| 2994 | (eq 'continuation ; do not stagger continuations | 3105 | (eq 'continuation ; do not stagger continuations |
| 3106 | ;; FIXME: This clobbers the syntax state in parse-data | ||
| 3107 | ;; for the *following* lines and makes the state | ||
| 3108 | ;; useless for indent-region -- haj 2023-06-30 | ||
| 2995 | (elt (cperl-sniff-for-indent parse-data) 0))) | 3109 | (elt (cperl-sniff-for-indent parse-data) 0))) |
| 2996 | 0 ; Closing parenthesis or continuation of a continuation | 3110 | 0 ; Closing parenthesis or continuation of a continuation |
| 2997 | cperl-continued-statement-offset) | 3111 | cperl-continued-statement-offset) |
| @@ -3467,22 +3581,37 @@ Should be called with the point before leading colon of an attribute." | |||
| 3467 | "L%d: attribute `%s': %s" | 3581 | "L%d: attribute `%s': %s" |
| 3468 | (count-lines (point-min) (point)) | 3582 | (count-lines (point-min) (point)) |
| 3469 | (and start1 end1 (buffer-substring start1 end1)) b) | 3583 | (and start1 end1 (buffer-substring start1 end1)) b) |
| 3470 | (setq start nil))) | 3584 | ; (setq start nil) I'd like to keep trying -- haj 2023-06-26 |
| 3471 | (and start | 3585 | )) |
| 3472 | (progn | 3586 | (cond |
| 3473 | (put-text-property start (point) | 3587 | ;; Allow for a complete signature and trailing spaces here |
| 3474 | 'attrib-group (if (looking-at "{") t 0)) | 3588 | ((search-forward-regexp (rx (sequence point |
| 3475 | (and pos | 3589 | (eval cperl--ws*-rx) |
| 3476 | (< 1 (count-lines (+ 3 pos) (point))) ; end of `sub' | 3590 | (eval cperl--signature-rx) |
| 3477 | ;; Apparently, we do not need `multiline': faces added now | 3591 | (eval cperl--ws*-rx))) |
| 3478 | (put-text-property (+ 3 pos) (cperl-1+ (point)) | 3592 | nil |
| 3479 | 'syntax-type 'sub-decl)) | 3593 | t)) ; NOERROR |
| 3480 | (and b-fname ; Fontify here: the following condition | 3594 | ((looking-at (rx "(")) |
| 3481 | (cperl-postpone-fontification ; is too hard to determine by | 3595 | ;; We might be in the process of typing a prototype or |
| 3482 | b-fname e-fname 'face ; a REx, so do it here | 3596 | ;; signature. These start with a left paren, so we want this to |
| 3483 | (if (looking-at "{") | 3597 | ;; be included into the area marked as sub-decl. |
| 3484 | font-lock-function-name-face | 3598 | nil) |
| 3485 | font-lock-variable-name-face))))) | 3599 | ;; Else, we are in no mans land. Just keep trying. |
| 3600 | (t | ||
| 3601 | )) | ||
| 3602 | (when (looking-at (rx (in ";{"))) | ||
| 3603 | ;; A semicolon ends the declaration, an opening brace begins the | ||
| 3604 | ;; BLOCK. Neither is part of the declaration. | ||
| 3605 | (backward-char)) | ||
| 3606 | (when start | ||
| 3607 | (put-text-property start (point) | ||
| 3608 | 'attrib-group (if (looking-at "{") t 0)) | ||
| 3609 | (and pos | ||
| 3610 | (progn | ||
| 3611 | (< 1 (count-lines (+ 3 pos) (point))) ; end of `sub' | ||
| 3612 | ;; Apparently, we do not need `multiline': faces added now | ||
| 3613 | (put-text-property (+ 3 pos) (cperl-1+ (point)) | ||
| 3614 | 'syntax-type 'sub-decl)))) | ||
| 3486 | ;; now restore the initial state | 3615 | ;; now restore the initial state |
| 3487 | (if st | 3616 | (if st |
| 3488 | (progn | 3617 | (progn |
| @@ -3773,8 +3902,10 @@ recursive calls in starting lines of here-documents." | |||
| 3773 | max)) | 3902 | max)) |
| 3774 | (search | 3903 | (search |
| 3775 | (concat | 3904 | (concat |
| 3776 | "\\(\\`\n?\\|^\n\\)=" ; POD | 3905 | ;; -------- POD using capture group 1 |
| 3906 | "\\(\\`\n?\\|^\n\\)=" | ||
| 3777 | "\\|" | 3907 | "\\|" |
| 3908 | ;; -------- HERE-document capture groups 2-7 | ||
| 3778 | ;; One extra () before this: | 3909 | ;; One extra () before this: |
| 3779 | "<<\\(~?\\)" ; HERE-DOC, indented-p = capture 2 | 3910 | "<<\\(~?\\)" ; HERE-DOC, indented-p = capture 2 |
| 3780 | "\\(" ; 2 + 1 | 3911 | "\\(" ; 2 + 1 |
| @@ -3790,38 +3921,49 @@ recursive calls in starting lines of here-documents." | |||
| 3790 | ;; "\\([^= \t0-9$@%&]\\|[ \t]+[^ \t\n0-9$@%&]\\)" ; 6 + 1 | 3921 | ;; "\\([^= \t0-9$@%&]\\|[ \t]+[^ \t\n0-9$@%&]\\)" ; 6 + 1 |
| 3791 | "\\)" | 3922 | "\\)" |
| 3792 | "\\|" | 3923 | "\\|" |
| 3924 | ;; -------- format capture groups 8-9 | ||
| 3793 | ;; 1+6 extra () before this: | 3925 | ;; 1+6 extra () before this: |
| 3794 | "^[ \t]*\\(format\\)[ \t]*\\([a-zA-Z0-9_]+\\)?[ \t]*=[ \t]*$" ;FRMAT | 3926 | "^[ \t]*\\(format\\)[ \t]*\\([a-zA-Z0-9_]+\\)?[ \t]*=[ \t]*$" |
| 3795 | (if cperl-use-syntax-table-text-property | 3927 | (if cperl-use-syntax-table-text-property |
| 3796 | (concat | 3928 | (concat |
| 3797 | "\\|" | 3929 | "\\|" |
| 3930 | ;; -------- quoted constructs and regexps, group 10 | ||
| 3798 | ;; 1+6+2=9 extra () before this: | 3931 | ;; 1+6+2=9 extra () before this: |
| 3799 | "\\<\\(q[wxqr]?\\|[msy]\\|tr\\)\\>" ; QUOTED CONSTRUCT | 3932 | "\\<\\(q[wxqr]?\\|[msy]\\|tr\\)\\>" |
| 3800 | "\\|" | 3933 | "\\|" |
| 3934 | ;; -------- "bare" regex or glob, group 11 | ||
| 3801 | ;; 1+6+2+1=10 extra () before this: | 3935 | ;; 1+6+2+1=10 extra () before this: |
| 3802 | "\\([/<]\\)" ; /blah/ or <file*glob> | 3936 | "\\([/<]\\)" ; /blah/ or <file*glob> |
| 3803 | "\\|" | 3937 | "\\|" |
| 3938 | ;; -------- subroutine declarations, groups 12-17 | ||
| 3804 | ;; 1+6+2+1+1=11 extra () before this | 3939 | ;; 1+6+2+1+1=11 extra () before this |
| 3805 | "\\<" cperl-sub-regexp "\\>" ; sub with proto/attr | 3940 | (rx (sequence |
| 3806 | "\\(" | 3941 | word-start |
| 3807 | cperl-white-and-comment-rex | 3942 | (group (regexp cperl-sub-regexp)) ; #12 |
| 3808 | (rx (opt (group (eval cperl--normal-identifier-rx)))) | 3943 | (eval cperl--ws+-rx) |
| 3809 | "\\)" | 3944 | (opt (group (eval cperl--normal-identifier-rx))) ; #13 |
| 3810 | "\\(" | 3945 | (eval cperl--ws*-rx) |
| 3811 | cperl-maybe-white-and-comment-rex | 3946 | (group (or (group (eval cperl--prototype-rx)) ; #14,#15 |
| 3812 | "\\(([^()]*)\\|:[^:]\\)\\)" ; prototype or attribute start | 3947 | ;; (group (eval cperl--signature-rx)) ; #16 |
| 3948 | (group unmatchable) ; #16 | ||
| 3949 | (group (or anything buffer-end)))))) ; #17 | ||
| 3813 | "\\|" | 3950 | "\\|" |
| 3814 | ;; 1+6+2+1+1+6=17 extra () before this: | 3951 | ;; -------- weird variables, capture group 18 |
| 3952 | ;; FIXME: We don't need that group -- haj 2023-06-21 | ||
| 3953 | ;; 1+6+2+1+1+6=17 extra () before this | ||
| 3815 | "\\$\\(['{]\\)" ; $' or ${foo} | 3954 | "\\$\\(['{]\\)" ; $' or ${foo} |
| 3816 | "\\|" | 3955 | "\\|" |
| 3956 | ;; -------- old-style ' as package separator, group 19 | ||
| 3817 | ;; 1+6+2+1+1+6+1=18 extra () before this (old pack'var syntax; | 3957 | ;; 1+6+2+1+1+6+1=18 extra () before this (old pack'var syntax; |
| 3818 | ;; we do not support intervening comments...): | 3958 | ;; we do not support intervening comments...): |
| 3819 | "\\(\\<" cperl-sub-regexp "[ \t\n\f]+\\|[&*$@%]\\)[a-zA-Z0-9_]*'" | 3959 | "\\(\\<" cperl-sub-regexp "[ \t\n\f]+\\|[&*$@%]\\)[a-zA-Z0-9_]*'" |
| 3820 | ;; 1+6+2+1+1+6+1+1=19 extra () before this: | ||
| 3821 | "\\|" | 3960 | "\\|" |
| 3961 | ;; -------- __END__ and __DATA__ tokens, group 20 | ||
| 3962 | ;; 1+6+2+1+1+6+1+1=19 extra () before this: | ||
| 3822 | "__\\(END\\|DATA\\)__" ; __END__ or __DATA__ | 3963 | "__\\(END\\|DATA\\)__" ; __END__ or __DATA__ |
| 3823 | ;; 1+6+2+1+1+6+1+1+1=20 extra () before this: | 3964 | ;; 1+6+2+1+1+6+1+1+1=20 extra () before this: |
| 3824 | "\\|" | 3965 | "\\|" |
| 3966 | ;; -------- backslash-escaped stuff, don't interpret it | ||
| 3825 | "\\\\\\(['`\"($]\\)") ; BACKWACKED something-hairy | 3967 | "\\\\\\(['`\"($]\\)") ; BACKWACKED something-hairy |
| 3826 | ""))) | 3968 | ""))) |
| 3827 | warning-message) | 3969 | warning-message) |
| @@ -4691,28 +4833,28 @@ recursive calls in starting lines of here-documents." | |||
| 4691 | 'REx-part2 t))))) | 4833 | 'REx-part2 t))))) |
| 4692 | (if (> (point) max) | 4834 | (if (> (point) max) |
| 4693 | (setq tmpend tb)))) | 4835 | (setq tmpend tb)))) |
| 4694 | ((match-beginning 17) ; sub with prototype or attribute | 4836 | ((match-beginning 14) ; sub with prototype or attribute |
| 4695 | ;; 1+6+2+1+1=11 extra () before this (sub with proto/attr): | 4837 | ;; 1+6+2+1+1=11 extra () before this (sub with proto/attr): |
| 4696 | ;;"\\<sub\\>\\(" ;12 | 4838 | ;; match-string 12: Keyword "sub" |
| 4697 | ;; cperl-white-and-comment-rex ;13 | 4839 | ;; match-string 13: Name of the subroutine (optional) |
| 4698 | ;; "\\([a-zA-Z_:'0-9]+\\)\\)?" ; name ;14 | 4840 | ;; match-string 14: Indicator for proto/attr/signature |
| 4699 | ;;"\\(" cperl-maybe-white-and-comment-rex ;15,16 | 4841 | ;; match-string 15: Prototype |
| 4700 | ;; "\\(([^()]*)\\|:[^:]\\)\\)" ; 17:proto or attribute start | 4842 | ;; match-string 16: unused |
| 4701 | (setq b1 (match-beginning 14) e1 (match-end 14)) | 4843 | ;; match-string 17: Distinguish declaration/definition |
| 4844 | (setq b1 (match-beginning 13) e1 (match-end 13)) | ||
| 4702 | (if (memq (char-after (1- b)) | 4845 | (if (memq (char-after (1- b)) |
| 4703 | '(?\$ ?\@ ?\% ?\& ?\*)) | 4846 | '(?\$ ?\@ ?\% ?\& ?\*)) |
| 4704 | nil | 4847 | nil ;; we found $sub or @sub etc |
| 4705 | (goto-char b) | 4848 | (goto-char b) |
| 4706 | (if (eq (char-after (match-beginning 17)) ?\( ) | 4849 | (if (match-beginning 15) ; a complete prototype |
| 4707 | (progn | 4850 | (progn |
| 4708 | (cperl-commentify ; Prototypes; mark as string | 4851 | (cperl-commentify ; Prototypes; mark as string |
| 4709 | (match-beginning 17) (match-end 17) t) | 4852 | (match-beginning 15) (match-end 15) t) |
| 4710 | (goto-char (match-end 0)) | 4853 | (goto-char (match-end 0)) |
| 4711 | ;; Now look for attributes after prototype: | 4854 | ;; Now look for attributes after prototype: |
| 4712 | (forward-comment (buffer-size)) | 4855 | (forward-comment (buffer-size)) |
| 4713 | (and (looking-at ":[^:]") | 4856 | (cperl-find-sub-attrs st-l b1 e1 b)) |
| 4714 | (cperl-find-sub-attrs st-l b1 e1 b))) | 4857 | ;; treat attributes without prototype and incomplete stuff |
| 4715 | ;; treat attributes without prototype | ||
| 4716 | (goto-char (match-beginning 17)) | 4858 | (goto-char (match-beginning 17)) |
| 4717 | (cperl-find-sub-attrs st-l b1 e1 b)))) | 4859 | (cperl-find-sub-attrs st-l b1 e1 b)))) |
| 4718 | ;; 1+6+2+1+1+6+1=18 extra () before this: | 4860 | ;; 1+6+2+1+1+6+1=18 extra () before this: |
| @@ -5313,6 +5455,10 @@ conditional/loop constructs." | |||
| 5313 | (let ((comment-column new-comm-indent)) | 5455 | (let ((comment-column new-comm-indent)) |
| 5314 | (indent-for-comment))) | 5456 | (indent-for-comment))) |
| 5315 | (progn | 5457 | (progn |
| 5458 | ;; FIXME: It would be nice to keep indent-info, but this | ||
| 5459 | ;; doesn not work if the region contains continuation | ||
| 5460 | ;; lines (see `cperl-calculate-indent') -- haj 2023-06-30 | ||
| 5461 | (setq indent-info (list nil nil nil)) | ||
| 5316 | (setq i (cperl-indent-line indent-info)) | 5462 | (setq i (cperl-indent-line indent-info)) |
| 5317 | (or comm | 5463 | (or comm |
| 5318 | (not i) | 5464 | (not i) |
| @@ -5668,7 +5814,11 @@ default function." | |||
| 5668 | (setq | 5814 | (setq |
| 5669 | t-font-lock-keywords | 5815 | t-font-lock-keywords |
| 5670 | (list | 5816 | (list |
| 5817 | ;; -------- trailing spaces -> use invalid-face as a warning | ||
| 5818 | ;; (matcher subexp facespec) | ||
| 5671 | `("[ \t]+$" 0 ',cperl-invalid-face t) | 5819 | `("[ \t]+$" 0 ',cperl-invalid-face t) |
| 5820 | ;; -------- flow control | ||
| 5821 | ;; (matcher . subexp) font-lock-keyword-face by default | ||
| 5672 | (cons | 5822 | (cons |
| 5673 | (concat | 5823 | (concat |
| 5674 | "\\(^\\|[^$@%&\\]\\)\\<\\(" | 5824 | "\\(^\\|[^$@%&\\]\\)\\<\\(" |
| @@ -5688,6 +5838,8 @@ default function." | |||
| 5688 | "\\)\\>") 2) ; was "\\)[ \n\t;():,|&]" | 5838 | "\\)\\>") 2) ; was "\\)[ \n\t;():,|&]" |
| 5689 | ; In what follows we use `type' style | 5839 | ; In what follows we use `type' style |
| 5690 | ; for overwritable builtins | 5840 | ; for overwritable builtins |
| 5841 | ;; -------- builtin functions | ||
| 5842 | ;; (matcher subexp facespec) | ||
| 5691 | (list | 5843 | (list |
| 5692 | (concat | 5844 | (concat |
| 5693 | "\\(^\\|[^$@%&\\]\\)\\<\\(" | 5845 | "\\(^\\|[^$@%&\\]\\)\\<\\(" |
| @@ -5730,6 +5882,10 @@ default function." | |||
| 5730 | 2 'font-lock-type-face) | 5882 | 2 'font-lock-type-face) |
| 5731 | ;; In what follows we use `other' style | 5883 | ;; In what follows we use `other' style |
| 5732 | ;; for nonoverwritable builtins | 5884 | ;; for nonoverwritable builtins |
| 5885 | ;; This is a bit shaky because the status | ||
| 5886 | ;; "nonoverwritable" can change between Perl versions. | ||
| 5887 | ;; -------- "non overridable" functions | ||
| 5888 | ;; (matcher subexp facespec) | ||
| 5733 | (list | 5889 | (list |
| 5734 | (concat | 5890 | (concat |
| 5735 | "\\(^\\|[^$@%&\\]\\)\\<\\(" | 5891 | "\\(^\\|[^$@%&\\]\\)\\<\\(" |
| @@ -5750,33 +5906,69 @@ default function." | |||
| 5750 | ;; '("#endif" "#else" "#ifdef" "#ifndef" "#if" | 5906 | ;; '("#endif" "#else" "#ifdef" "#ifndef" "#if" |
| 5751 | ;; "#include" "#define" "#undef") | 5907 | ;; "#include" "#define" "#undef") |
| 5752 | ;; "\\|") | 5908 | ;; "\\|") |
| 5909 | ;; -------- -X file tests | ||
| 5910 | ;; (matcher subexp facespec) | ||
| 5753 | '("-[rwxoRWXOezsfdlpSbctugkTBMAC]\\>\\([ \t]+_\\>\\)?" 0 | 5911 | '("-[rwxoRWXOezsfdlpSbctugkTBMAC]\\>\\([ \t]+_\\>\\)?" 0 |
| 5754 | font-lock-function-name-face keep) ; Not very good, triggers at "[a-z]" | 5912 | font-lock-function-name-face keep) ; Not very good, triggers at "[a-z]" |
| 5755 | ;; This highlights declarations and definitions differently. | 5913 | ;; This highlights declarations and definitions differently. |
| 5756 | ;; We do not try to highlight in the case of attributes: | 5914 | ;; We do not try to highlight in the case of attributes: |
| 5757 | ;; it is already done by `cperl-find-pods-heres' | 5915 | ;; it is already done by `cperl-find-pods-heres' |
| 5916 | ;; -------- function definition _and_ declaration | ||
| 5917 | ;; (matcher (subexp facespec)) | ||
| 5918 | ;; facespec is evaluated depending on whether the | ||
| 5919 | ;; statement ends in a "{" (definition) or ";" | ||
| 5920 | ;; (declaration without body) | ||
| 5758 | (list (concat "\\<" cperl-sub-regexp | 5921 | (list (concat "\\<" cperl-sub-regexp |
| 5759 | cperl-white-and-comment-rex ; whitespace/comments | 5922 | (rx |
| 5760 | "\\([^ \n\t{;()]+\\)" ; 2=name (assume non-anonymous) | 5923 | (sequence (eval cperl--ws+-rx) |
| 5761 | "\\(" | 5924 | (group (optional (eval cperl--normal-identifier-rx))))) |
| 5762 | cperl-maybe-white-and-comment-rex ;whitespace/comments? | 5925 | ;; "\\([^ \n\t{;()]+\\)" ; 2=name (assume non-anonymous) |
| 5763 | "([^()]*)\\)?" ; prototype | 5926 | (rx |
| 5764 | cperl-maybe-white-and-comment-rex ; whitespace/comments? | 5927 | (optional |
| 5928 | (group (sequence (group (eval cperl--ws*-rx)) | ||
| 5929 | (eval cperl--prototype-rx))))) | ||
| 5930 | ;; "\\(" | ||
| 5931 | ;; cperl-maybe-white-and-comment-rex ;whitespace/comments? | ||
| 5932 | ;; "([^()]*)\\)?" ; prototype | ||
| 5933 | (rx (optional (sequence (eval cperl--ws*-rx) | ||
| 5934 | (eval cperl--attribute-list-rx)))) | ||
| 5935 | ; cperl-maybe-white-and-comment-rex ; whitespace/comments? | ||
| 5936 | (rx (group-n 3 | ||
| 5937 | (optional (sequence(eval cperl--ws*-rx) | ||
| 5938 | (eval cperl--signature-rx))))) | ||
| 5939 | (rx (eval cperl--ws*-rx)) | ||
| 5765 | "[{;]") | 5940 | "[{;]") |
| 5766 | 2 '(if (eq (char-after (cperl-1- (match-end 0))) ?\{ ) | 5941 | '(1 (if (eq (char-after (cperl-1- (match-end 0))) ?\{ ) |
| 5767 | 'font-lock-function-name-face | 5942 | 'font-lock-function-name-face |
| 5768 | 'font-lock-variable-name-face)) | 5943 | 'font-lock-variable-name-face) |
| 5944 | t ;; override | ||
| 5945 | t) ;; laxmatch in case of anonymous subroutines | ||
| 5946 | ;; -------- anchored: Signature | ||
| 5947 | `(,(rx (or (eval cperl--basic-scalar-rx) | ||
| 5948 | (eval cperl--basic-array-rx) | ||
| 5949 | (eval cperl--basic-hash-rx))) | ||
| 5950 | (progn | ||
| 5951 | (goto-char (match-beginning 3)) ; pre-match: Back to sig | ||
| 5952 | (match-end 3)) | ||
| 5953 | |||
| 5954 | nil | ||
| 5955 | (0 font-lock-variable-name-face))) | ||
| 5956 | ;; -------- various stuff calling for a package name | ||
| 5957 | ;; (matcher subexp facespec) | ||
| 5769 | `(,(rx (sequence symbol-start | 5958 | `(,(rx (sequence symbol-start |
| 5770 | (or "package" "require" "use" "import" | 5959 | (or "package" "require" "use" "import" |
| 5771 | "no" "bootstrap") | 5960 | "no" "bootstrap") |
| 5772 | (eval cperl--ws+-rx) | 5961 | (eval cperl--ws+-rx) |
| 5773 | (group-n 1 (eval cperl--normal-identifier-rx)) | 5962 | (group-n 1 (eval cperl--normal-identifier-rx)) |
| 5774 | (any " \t;"))) ; require A if B; | 5963 | (any " \t\n;"))) ; require A if B; |
| 5775 | 1 font-lock-function-name-face) | 5964 | 1 font-lock-function-name-face) |
| 5965 | ;; -------- formats | ||
| 5966 | ;; (matcher subexp facespec) | ||
| 5776 | '("^[ \t]*format[ \t]+\\([a-zA-Z_][a-zA-Z_0-9:]*\\)[ \t]*=[ \t]*$" | 5967 | '("^[ \t]*format[ \t]+\\([a-zA-Z_][a-zA-Z_0-9:]*\\)[ \t]*=[ \t]*$" |
| 5777 | 1 font-lock-function-name-face) | 5968 | 1 font-lock-function-name-face) |
| 5778 | ;; bareword hash key: $foo{bar} | 5969 | ;; -------- bareword hash key: $foo{bar}, $foo[1]{bar} |
| 5779 | `(,(rx (or (in "]}\\%@>*&") ; What Perl is this? | 5970 | ;; (matcher (subexp facespec) ... |
| 5971 | `(,(rx (or (in "]}\\%@>*&") | ||
| 5780 | (sequence "$" (eval cperl--normal-identifier-rx))) | 5972 | (sequence "$" (eval cperl--normal-identifier-rx))) |
| 5781 | (0+ blank) "{" (0+ blank) | 5973 | (0+ blank) "{" (0+ blank) |
| 5782 | (group-n 1 (sequence (opt "-") | 5974 | (group-n 1 (sequence (opt "-") |
| @@ -5784,24 +5976,27 @@ default function." | |||
| 5784 | (0+ blank) "}") | 5976 | (0+ blank) "}") |
| 5785 | ;; '("\\([]}\\%@>*&]\\|\\$[a-zA-Z0-9_:]*\\)[ \t]*{[ \t]*\\(-?[a-zA-Z0-9_:]+\\)[ \t]*}" | 5977 | ;; '("\\([]}\\%@>*&]\\|\\$[a-zA-Z0-9_:]*\\)[ \t]*{[ \t]*\\(-?[a-zA-Z0-9_:]+\\)[ \t]*}" |
| 5786 | (1 font-lock-string-face t) | 5978 | (1 font-lock-string-face t) |
| 5787 | ;; anchored bareword hash key: $foo{bar}{baz} | 5979 | ;; -------- anchored bareword hash key: $foo{bar}{baz} |
| 5980 | ;; ... (anchored-matcher pre-form post-form subex-highlighters) | ||
| 5788 | (,(rx point | 5981 | (,(rx point |
| 5789 | (0+ blank) "{" (0+ blank) | 5982 | (0+ blank) "{" (0+ blank) |
| 5790 | (group-n 1 (sequence (opt "-") | 5983 | (group-n 1 (sequence (opt "-") |
| 5791 | (eval cperl--basic-identifier-rx))) | 5984 | (eval cperl--basic-identifier-rx))) |
| 5792 | (0+ blank) "}") | 5985 | (0+ blank) "}") |
| 5793 | ;; ("\\=[ \t]*{[ \t]*\\(-?[a-zA-Z0-9_:]+\\)[ \t]*}" | 5986 | ;; ("\\=[ \t]*{[ \t]*\\(-?[a-zA-Z0-9_:]+\\)[ \t]*}" |
| 5794 | nil nil | 5987 | nil nil |
| 5795 | (1 font-lock-string-face t))) | 5988 | (1 font-lock-string-face t))) |
| 5796 | ;; hash element assignments with bareword key => value | 5989 | ;; -------- hash element assignments with bareword key => value |
| 5797 | `(,(rx (in "[ \t{,()") | 5990 | ;; (matcher subexp facespec) |
| 5798 | (group-n 1 (sequence (opt "-") | 5991 | `(,(rx (in "[ \t{,()") |
| 5799 | (eval cperl--basic-identifier-rx))) | 5992 | (group-n 1 (sequence (opt "-") |
| 5800 | (0+ blank) "=>") | 5993 | (eval cperl--basic-identifier-rx))) |
| 5801 | 1 font-lock-string-face t) | 5994 | (0+ blank) "=>") |
| 5802 | ;; '("[[ \t{,(]\\(-?[a-zA-Z0-9_:]+\\)[ \t]*=>" 1 | 5995 | 1 font-lock-string-face t) |
| 5803 | ;; font-lock-string-face t) | 5996 | ;; '("[[ \t{,(]\\(-?[a-zA-Z0-9_:]+\\)[ \t]*=>" 1 |
| 5804 | ;; labels | 5997 | ;; font-lock-string-face t) |
| 5998 | ;; -------- labels | ||
| 5999 | ;; (matcher subexp facespec) | ||
| 5805 | `(,(rx | 6000 | `(,(rx |
| 5806 | (sequence | 6001 | (sequence |
| 5807 | (0+ space) | 6002 | (0+ space) |
| @@ -5812,7 +6007,8 @@ default function." | |||
| 5812 | (or "until" "while" "for" "foreach" "do") | 6007 | (or "until" "while" "for" "foreach" "do") |
| 5813 | word-end)))) | 6008 | word-end)))) |
| 5814 | 1 font-lock-constant-face) | 6009 | 1 font-lock-constant-face) |
| 5815 | ;; labels as targets (no trailing colon!) | 6010 | ;; -------- labels as targets (no trailing colon!) |
| 6011 | ;; (matcher subexp facespec) | ||
| 5816 | `(,(rx | 6012 | `(,(rx |
| 5817 | (sequence | 6013 | (sequence |
| 5818 | symbol-start | 6014 | symbol-start |
| @@ -5824,10 +6020,12 @@ default function." | |||
| 5824 | ;;; '("[$*]{?\\(\\sw+\\)" 1 font-lock-variable-name-face) | 6020 | ;;; '("[$*]{?\\(\\sw+\\)" 1 font-lock-variable-name-face) |
| 5825 | ;;; '("\\([@%]\\|\\$#\\)\\(\\sw+\\)" | 6021 | ;;; '("\\([@%]\\|\\$#\\)\\(\\sw+\\)" |
| 5826 | ;;; (2 (cons font-lock-variable-name-face '(underline)))) | 6022 | ;;; (2 (cons font-lock-variable-name-face '(underline)))) |
| 5827 | ;; 1=my_etc, 2=white? 3=(+white? 4=white? 5=var | 6023 | ;; 1=my_etc, 2=white? 3=(+white? 4=white? 5=var |
| 6024 | ;; -------- variable declarations | ||
| 6025 | ;; (matcher (subexp facespec) ... | ||
| 5828 | `(,(rx (sequence (or "state" "my" "local" "our")) | 6026 | `(,(rx (sequence (or "state" "my" "local" "our")) |
| 5829 | (eval cperl--ws*-rx) | 6027 | (eval cperl--ws*-rx) |
| 5830 | (opt (sequence "(" (eval cperl--ws*-rx))) | 6028 | (opt (group (sequence "(" (eval cperl--ws*-rx)))) |
| 5831 | (group | 6029 | (group |
| 5832 | (in "$@%*") | 6030 | (in "$@%*") |
| 5833 | (or | 6031 | (or |
| @@ -5840,7 +6038,8 @@ default function." | |||
| 5840 | ;; "\\((" | 6038 | ;; "\\((" |
| 5841 | ;; cperl-maybe-white-and-comment-rex | 6039 | ;; cperl-maybe-white-and-comment-rex |
| 5842 | ;; "\\)?\\([$@%*]\\([a-zA-Z0-9_:]+\\|[^a-zA-Z0-9_]\\)\\)") | 6040 | ;; "\\)?\\([$@%*]\\([a-zA-Z0-9_:]+\\|[^a-zA-Z0-9_]\\)\\)") |
| 5843 | (1 font-lock-variable-name-face) | 6041 | (2 font-lock-variable-name-face) |
| 6042 | ;; ... (anchored-matcher pre-form post-form subex-highlighters) | ||
| 5844 | (,(rx (sequence point | 6043 | (,(rx (sequence point |
| 5845 | (eval cperl--ws*-rx) | 6044 | (eval cperl--ws*-rx) |
| 5846 | "," | 6045 | "," |
| @@ -5861,7 +6060,7 @@ default function." | |||
| 5861 | ;; Bug in font-lock: limit is used not only to limit | 6060 | ;; Bug in font-lock: limit is used not only to limit |
| 5862 | ;; searches, but to set the "extend window for | 6061 | ;; searches, but to set the "extend window for |
| 5863 | ;; facification" property. Thus we need to minimize. | 6062 | ;; facification" property. Thus we need to minimize. |
| 5864 | '(if (match-beginning 1) | 6063 | (if (match-beginning 1) ; list declaration |
| 5865 | (save-excursion | 6064 | (save-excursion |
| 5866 | (goto-char (match-beginning 1)) | 6065 | (goto-char (match-beginning 1)) |
| 5867 | (condition-case nil | 6066 | (condition-case nil |
| @@ -5874,7 +6073,8 @@ default function." | |||
| 5874 | (forward-char -2)) ; disable continued expr | 6073 | (forward-char -2)) ; disable continued expr |
| 5875 | nil | 6074 | nil |
| 5876 | (1 font-lock-variable-name-face))) | 6075 | (1 font-lock-variable-name-face))) |
| 5877 | ;; foreach my $foo ( | 6076 | ;; ----- foreach my $foo ( |
| 6077 | ;; (matcher subexp facespec) | ||
| 5878 | `(,(rx symbol-start "for" (opt "each") | 6078 | `(,(rx symbol-start "for" (opt "each") |
| 5879 | (opt (sequence (1+ blank) | 6079 | (opt (sequence (1+ blank) |
| 5880 | (or "state" "my" "local" "our"))) | 6080 | (or "state" "my" "local" "our"))) |
| @@ -5885,12 +6085,18 @@ default function." | |||
| 5885 | ;; '("\\<for\\(each\\)?\\([ \t]+\\(state\\|my\\|local\\|our\\)\\)?[ \t]*\\(\\$[a-zA-Z_][a-zA-Z_0-9]*\\)[ \t]*(" | 6085 | ;; '("\\<for\\(each\\)?\\([ \t]+\\(state\\|my\\|local\\|our\\)\\)?[ \t]*\\(\\$[a-zA-Z_][a-zA-Z_0-9]*\\)[ \t]*(" |
| 5886 | 1 font-lock-variable-name-face) | 6086 | 1 font-lock-variable-name-face) |
| 5887 | ;; Avoid $!, and s!!, qq!! etc. when not fontifying syntactically | 6087 | ;; Avoid $!, and s!!, qq!! etc. when not fontifying syntactically |
| 6088 | ;; -------- ! as a negation char like $false = !$true | ||
| 6089 | ;; (matcher subexp facespec) | ||
| 5888 | '("\\(?:^\\|[^smywqrx$]\\)\\(!\\)" 1 font-lock-negation-char-face) | 6090 | '("\\(?:^\\|[^smywqrx$]\\)\\(!\\)" 1 font-lock-negation-char-face) |
| 6091 | ;; -------- ^ as a negation char in character classes m/[^abc]/ | ||
| 6092 | ;; (matcher subexp facespec) | ||
| 5889 | '("\\[\\(\\^\\)" 1 font-lock-negation-char-face prepend))) | 6093 | '("\\[\\(\\^\\)" 1 font-lock-negation-char-face prepend))) |
| 5890 | (setq | 6094 | (setq |
| 5891 | t-font-lock-keywords-1 | 6095 | t-font-lock-keywords-1 |
| 5892 | `( | 6096 | `( |
| 5893 | ;; arrays and hashes. Access to elements is fixed below | 6097 | ;; -------- arrays and hashes. Access to elements is fixed below |
| 6098 | ;; (matcher subexp facespec) | ||
| 6099 | ;; facespec is an expression to distinguish between arrays and hashes | ||
| 5894 | (,(rx (group-n 1 (group-n 2 (or (in "@%") "$#")) | 6100 | (,(rx (group-n 1 (group-n 2 (or (in "@%") "$#")) |
| 5895 | (eval cperl--normal-identifier-rx))) | 6101 | (eval cperl--normal-identifier-rx))) |
| 5896 | 1 | 6102 | 1 |
| @@ -5898,8 +6104,10 @@ default function." | |||
| 5898 | (if (eq (char-after (match-beginning 2)) ?%) | 6104 | (if (eq (char-after (match-beginning 2)) ?%) |
| 5899 | 'cperl-hash-face | 6105 | 'cperl-hash-face |
| 5900 | 'cperl-array-face) | 6106 | 'cperl-array-face) |
| 5901 | nil) ; arrays and hashes | 6107 | nil) |
| 5902 | ;; access to array/hash elements | 6108 | ;; -------- access to array/hash elements |
| 6109 | ;; (matcher subexp facespec) | ||
| 6110 | ;; facespec is an expression to distinguish between arrays and hashes | ||
| 5903 | (,(rx (group-n 1 (group-n 2 (in "$@%")) | 6111 | (,(rx (group-n 1 (group-n 2 (in "$@%")) |
| 5904 | (eval cperl--normal-identifier-rx)) | 6112 | (eval cperl--normal-identifier-rx)) |
| 5905 | (0+ blank) | 6113 | (0+ blank) |
| @@ -5912,7 +6120,8 @@ default function." | |||
| 5912 | 'cperl-array-face) ; arrays and hashes | 6120 | 'cperl-array-face) ; arrays and hashes |
| 5913 | font-lock-variable-name-face) ; Just to put something | 6121 | font-lock-variable-name-face) ; Just to put something |
| 5914 | t) ; override previous | 6122 | t) ; override previous |
| 5915 | ;; @$ array dereferences, $#$ last array index | 6123 | ;; -------- @$ array dereferences, $#$ last array index |
| 6124 | ;; (matcher (subexp facespec) (subexp facespec)) | ||
| 5916 | (,(rx (group-n 1 (or "@" "$#")) | 6125 | (,(rx (group-n 1 (or "@" "$#")) |
| 5917 | (group-n 2 (sequence "$" | 6126 | (group-n 2 (sequence "$" |
| 5918 | (or (eval cperl--normal-identifier-rx) | 6127 | (or (eval cperl--normal-identifier-rx) |
| @@ -5920,7 +6129,8 @@ default function." | |||
| 5920 | ;; ("\\(@\\|\\$#\\)\\(\\$+\\([a-zA-Z_:][a-zA-Z0-9_:]*\\|[^ \t\n]\\)\\)" | 6129 | ;; ("\\(@\\|\\$#\\)\\(\\$+\\([a-zA-Z_:][a-zA-Z0-9_:]*\\|[^ \t\n]\\)\\)" |
| 5921 | (1 'cperl-array-face) | 6130 | (1 'cperl-array-face) |
| 5922 | (2 font-lock-variable-name-face)) | 6131 | (2 font-lock-variable-name-face)) |
| 5923 | ;; %$ hash dereferences | 6132 | ;; -------- %$ hash dereferences |
| 6133 | ;; (matcher (subexp facespec) (subexp facespec)) | ||
| 5924 | (,(rx (group-n 1 "%") | 6134 | (,(rx (group-n 1 "%") |
| 5925 | (group-n 2 (sequence "$" | 6135 | (group-n 2 (sequence "$" |
| 5926 | (or (eval cperl--normal-identifier-rx) | 6136 | (or (eval cperl--normal-identifier-rx) |
diff --git a/test/lisp/progmodes/cperl-mode-resources/cperl-bug-64190.pl b/test/lisp/progmodes/cperl-mode-resources/cperl-bug-64190.pl new file mode 100644 index 00000000000..c7621e1c47b --- /dev/null +++ b/test/lisp/progmodes/cperl-mode-resources/cperl-bug-64190.pl | |||
| @@ -0,0 +1,24 @@ | |||
| 1 | # Example 1 | ||
| 2 | |||
| 3 | my ($var1, | ||
| 4 | $var2, | ||
| 5 | $var3); | ||
| 6 | |||
| 7 | # Example 2 | ||
| 8 | |||
| 9 | package Foo | ||
| 10 | 0.1; | ||
| 11 | |||
| 12 | # Example 3 (intentionally incomplete, body is inserted by test) | ||
| 13 | |||
| 14 | sub do_stuff | ||
| 15 | |||
| 16 | # Example 4 | ||
| 17 | |||
| 18 | sub do_more_stuff ($param1, | ||
| 19 | $param2) | ||
| 20 | { | ||
| 21 | ...; | ||
| 22 | } | ||
| 23 | |||
| 24 | sub oops { ...; } | ||
diff --git a/test/lisp/progmodes/cperl-mode-resources/cperl-bug-64364.pl b/test/lisp/progmodes/cperl-mode-resources/cperl-bug-64364.pl new file mode 100644 index 00000000000..af188cbedac --- /dev/null +++ b/test/lisp/progmodes/cperl-mode-resources/cperl-bug-64364.pl | |||
| @@ -0,0 +1,26 @@ | |||
| 1 | # This resource file can be run with cperl--run-testcases from | ||
| 2 | # cperl-tests.el and works with both perl-mode and cperl-mode. | ||
| 3 | |||
| 4 | # -------- Bug#64364: input ------- | ||
| 5 | package P { | ||
| 6 | sub way { ...; } | ||
| 7 | # | ||
| 8 | sub bus | ||
| 9 | :lvalue | ||
| 10 | ($sig,$na,@ture) | ||
| 11 | { | ||
| 12 | ...; | ||
| 13 | } | ||
| 14 | } | ||
| 15 | # -------- Bug#64364: expected output ------- | ||
| 16 | package P { | ||
| 17 | sub way { ...; } | ||
| 18 | # | ||
| 19 | sub bus | ||
| 20 | :lvalue | ||
| 21 | ($sig,$na,@ture) | ||
| 22 | { | ||
| 23 | ...; | ||
| 24 | } | ||
| 25 | } | ||
| 26 | # -------- Bug#64364: end ------- | ||
diff --git a/test/lisp/progmodes/cperl-mode-tests.el b/test/lisp/progmodes/cperl-mode-tests.el index 9bd250a38b5..99d5a51b3ea 100644 --- a/test/lisp/progmodes/cperl-mode-tests.el +++ b/test/lisp/progmodes/cperl-mode-tests.el | |||
| @@ -177,14 +177,18 @@ attributes, prototypes and signatures." | |||
| 177 | (should (equal (get-text-property (1+ (match-beginning 0)) 'face) | 177 | (should (equal (get-text-property (1+ (match-beginning 0)) 'face) |
| 178 | 'font-lock-string-face))) | 178 | 'font-lock-string-face))) |
| 179 | (goto-char start-of-sub) | 179 | (goto-char start-of-sub) |
| 180 | ;; Attributes with their optional parameters | ||
| 180 | (when (search-forward-regexp "\\(:[a-z]+\\)\\((.*?)\\)?" end-of-sub t) | 181 | (when (search-forward-regexp "\\(:[a-z]+\\)\\((.*?)\\)?" end-of-sub t) |
| 181 | (should (equal (get-text-property (match-beginning 1) 'face) | 182 | (should (equal (get-text-property (match-beginning 1) 'face) |
| 182 | 'font-lock-constant-face)) | 183 | 'font-lock-constant-face)) |
| 183 | (when (match-beginning 2) | 184 | (when (match-beginning 2) |
| 184 | (should (equal (get-text-property (match-beginning 2) 'face) | 185 | (should (equal (get-text-property (match-beginning 2) 'face) |
| 185 | 'font-lock-string-face)))) | 186 | 'font-lock-string-face)))) |
| 186 | (goto-char end-of-sub))) | 187 | (goto-char end-of-sub) |
| 187 | 188 | ;; Subroutine signatures | |
| 189 | (when (search-forward "$bar" end-of-sub t) | ||
| 190 | (should (equal (get-text-property (match-beginning) 'face) | ||
| 191 | 'font-lock-variable-name-face))))) | ||
| 188 | ;; Anonymous subroutines | 192 | ;; Anonymous subroutines |
| 189 | (while (search-forward-regexp "= sub" nil t) | 193 | (while (search-forward-regexp "= sub" nil t) |
| 190 | (let ((start-of-sub (match-beginning 0)) | 194 | (let ((start-of-sub (match-beginning 0)) |
| @@ -201,7 +205,11 @@ attributes, prototypes and signatures." | |||
| 201 | (when (match-beginning 2) | 205 | (when (match-beginning 2) |
| 202 | (should (equal (get-text-property (match-beginning 2) 'face) | 206 | (should (equal (get-text-property (match-beginning 2) 'face) |
| 203 | 'font-lock-string-face)))) | 207 | 'font-lock-string-face)))) |
| 204 | (goto-char end-of-sub)))))) | 208 | (goto-char end-of-sub) |
| 209 | ;; Subroutine signatures | ||
| 210 | (when (search-forward "$bar" end-of-sub t) | ||
| 211 | (should (equal (get-text-property (match-beginning) 'face) | ||
| 212 | 'font-lock-variable-name-face)))))))) | ||
| 205 | 213 | ||
| 206 | (ert-deftest cperl-test-fontify-special-variables () | 214 | (ert-deftest cperl-test-fontify-special-variables () |
| 207 | "Test fontification of variables like $^T or ${^ENCODING}. | 215 | "Test fontification of variables like $^T or ${^ENCODING}. |
| @@ -428,6 +436,62 @@ Also includes valid cases with whitespace in strange places." | |||
| 428 | (cperl-test--validate-regexp (rx (eval cperl--basic-identifier-rx)) | 436 | (cperl-test--validate-regexp (rx (eval cperl--basic-identifier-rx)) |
| 429 | valid invalid))) | 437 | valid invalid))) |
| 430 | 438 | ||
| 439 | (ert-deftest cperl-test-attribute-rx () | ||
| 440 | "Test attributes and attribute lists" | ||
| 441 | (skip-unless (eq cperl-test-mode #'cperl-mode)) | ||
| 442 | (let ((valid | ||
| 443 | '("foo" "bar()" "baz(quux)")) | ||
| 444 | (invalid | ||
| 445 | '("+foo" ; not an identifier | ||
| 446 | "foo::bar" ; no package qualifiers allowed | ||
| 447 | "(no-identifier)" ; no attribute name | ||
| 448 | "baz (quux)"))) ; no space allowed before "(" | ||
| 449 | (cperl-test--validate-regexp (rx (eval cperl--single-attribute-rx)) | ||
| 450 | valid invalid))) | ||
| 451 | |||
| 452 | (ert-deftest cperl-test-attribute-list-rx () | ||
| 453 | "Test attributes and attribute lists" | ||
| 454 | (skip-unless (eq cperl-test-mode #'cperl-mode)) | ||
| 455 | (let ((valid | ||
| 456 | '(":" ":foo" ": bar()" ":baz(quux):" | ||
| 457 | ":isa(Foo)does(Bar)" ":isa(Foo):does(Bar)" ":isa(Foo):does(Bar):" | ||
| 458 | ": isa(Foo::Bar) : does(Bar)")) | ||
| 459 | (invalid | ||
| 460 | '(":foo + bar" ; not an identifier | ||
| 461 | ": foo(bar : : baz" ; too many colons | ||
| 462 | ": baz (quux)"))) ; no space allowed before "(" | ||
| 463 | (cperl-test--validate-regexp (rx (eval cperl--attribute-list-rx)) | ||
| 464 | valid invalid))) | ||
| 465 | |||
| 466 | (ert-deftest cperl-test-prototype-rx () | ||
| 467 | "Test subroutine prototypes" | ||
| 468 | (skip-unless (eq cperl-test-mode #'cperl-mode)) | ||
| 469 | (let ((valid | ||
| 470 | ;; Examples from perldoc perlsub | ||
| 471 | '("($$)" "($$$)" "($$;$)" "($$$;$)" "(@)" "($@)" "(\\@)" "(\\@$$@)" | ||
| 472 | "(\\[%@])" "(*;$)" "(**)" "(&@)" "(;$)" "()")) | ||
| 473 | (invalid | ||
| 474 | '("$" ; missing paren | ||
| 475 | "($self)" ; a variable, -> subroutine signature | ||
| 476 | "(!$)" ; not all punctuation is permitted | ||
| 477 | "{$$}"))) ; wrong type of paren | ||
| 478 | (cperl-test--validate-regexp (rx (eval cperl--prototype-rx)) | ||
| 479 | valid invalid))) | ||
| 480 | |||
| 481 | (ert-deftest cperl-test-signature-rx () | ||
| 482 | "Test subroutine signatures." | ||
| 483 | (skip-unless (eq cperl-test-mode #'cperl-mode)) | ||
| 484 | (let ((valid | ||
| 485 | '("()" "( )" "($self, %params)" "(@params)")) | ||
| 486 | (invalid | ||
| 487 | '("$self" ; missing paren | ||
| 488 | "($)" ; a subroutine signature | ||
| 489 | "($!)" ; globals not permitted in a signature | ||
| 490 | "(@par,%options)" ; two slurpy parameters | ||
| 491 | "{$self}"))) ; wrong type of paren | ||
| 492 | (cperl-test--validate-regexp (rx (eval cperl--signature-rx)) | ||
| 493 | valid invalid))) | ||
| 494 | |||
| 431 | ;;; Test unicode identifier in various places | 495 | ;;; Test unicode identifier in various places |
| 432 | 496 | ||
| 433 | (defun cperl--test-unicode-setup (code string) | 497 | (defun cperl--test-unicode-setup (code string) |
| @@ -1145,6 +1209,79 @@ as a regex." | |||
| 1145 | (funcall cperl-test-mode) | 1209 | (funcall cperl-test-mode) |
| 1146 | (should-not (nth 3 (syntax-ppss 3))))) | 1210 | (should-not (nth 3 (syntax-ppss 3))))) |
| 1147 | 1211 | ||
| 1212 | (ert-deftest cperl-test-bug-64190 () | ||
| 1213 | "Verify correct fontification of multiline declarations" | ||
| 1214 | (skip-unless (eq cperl-test-mode #'cperl-mode)) | ||
| 1215 | (let ((file (ert-resource-file "cperl-bug-64190.pl"))) | ||
| 1216 | (with-temp-buffer | ||
| 1217 | (insert-file-contents file) | ||
| 1218 | (goto-char (point-min)) | ||
| 1219 | (cperl-mode) | ||
| 1220 | (font-lock-ensure) | ||
| 1221 | ;; Example 1 | ||
| 1222 | (while (search-forward "var" nil t) | ||
| 1223 | (should (equal (get-text-property (point) 'face) | ||
| 1224 | 'font-lock-variable-name-face))) | ||
| 1225 | ;; Example 2 | ||
| 1226 | (search-forward "package F") | ||
| 1227 | (should (equal (get-text-property (point) 'face) | ||
| 1228 | 'font-lock-function-name-face)) | ||
| 1229 | |||
| 1230 | ;; Example 3 and 4 can't be directly tested because jit-lock and | ||
| 1231 | ;; batch tests don't play together well. But we can approximate | ||
| 1232 | ;; the behavior by calling the the fontification for the same | ||
| 1233 | ;; region which would be used by jit-lock. | ||
| 1234 | ;; Example 3 | ||
| 1235 | (search-forward "sub do_stuff") | ||
| 1236 | (let ((start-change (point))) | ||
| 1237 | (insert "\n{") | ||
| 1238 | (cperl-font-lock-fontify-region-function start-change | ||
| 1239 | (point-max) | ||
| 1240 | nil) ; silent | ||
| 1241 | (font-lock-ensure start-change (point-max)) | ||
| 1242 | (goto-char (1- start-change)) ; between the "ff" in "stuff" | ||
| 1243 | (should (equal (get-text-property (point) 'face) | ||
| 1244 | 'font-lock-function-name-face)) | ||
| 1245 | (search-forward "{") | ||
| 1246 | (insert "}")) ; make it legal again | ||
| 1247 | |||
| 1248 | ;; Example 4 | ||
| 1249 | (search-forward "$param2") | ||
| 1250 | (beginning-of-line) | ||
| 1251 | (let ((start-change (point))) | ||
| 1252 | (insert " ") | ||
| 1253 | (cperl-font-lock-fontify-region-function start-change | ||
| 1254 | (point-max) | ||
| 1255 | nil) ; silent | ||
| 1256 | (font-lock-ensure start-change (point-max)) | ||
| 1257 | (goto-char (1+ start-change)) | ||
| 1258 | (should (equal (get-text-property (point) 'face) | ||
| 1259 | 'font-lock-variable-name-face)) | ||
| 1260 | (re-search-forward (rx (group "sub") " " (group "oops"))) | ||
| 1261 | (should (equal (get-text-property (match-beginning 1) 'face) | ||
| 1262 | 'font-lock-keyword-face)) | ||
| 1263 | (should (equal (get-text-property (match-beginning 2) 'face) | ||
| 1264 | 'font-lock-function-name-face)))))) | ||
| 1265 | |||
| 1266 | (ert-deftest cperl-test-bug-64364 () | ||
| 1267 | "Check that multi-line subroutine declarations indent correctly." | ||
| 1268 | (cperl-set-style "PBP") ; make cperl-mode use the same settings as perl-mode | ||
| 1269 | (cperl--run-test-cases | ||
| 1270 | (ert-resource-file "cperl-bug-64364.pl") | ||
| 1271 | (indent-region (point-min) (point-max))) | ||
| 1272 | (cperl--run-test-cases | ||
| 1273 | (ert-resource-file "cperl-bug-64364.pl") | ||
| 1274 | (let ((tab-function | ||
| 1275 | (if (equal cperl-test-mode 'perl-mode) | ||
| 1276 | #'indent-for-tab-command | ||
| 1277 | #'cperl-indent-command))) | ||
| 1278 | (goto-char (point-min)) | ||
| 1279 | (while (null (eobp)) | ||
| 1280 | (funcall tab-function) | ||
| 1281 | (forward-line 1)))) | ||
| 1282 | (cperl-set-style-back)) | ||
| 1283 | |||
| 1284 | |||
| 1148 | (ert-deftest test-indentation () | 1285 | (ert-deftest test-indentation () |
| 1149 | (ert-test-erts-file (ert-resource-file "cperl-indents.erts"))) | 1286 | (ert-test-erts-file (ert-resource-file "cperl-indents.erts"))) |
| 1150 | 1287 | ||