diff options
| author | Stefan Monnier | 2020-10-16 14:03:59 -0400 |
|---|---|---|
| committer | Stefan Monnier | 2020-10-16 14:03:59 -0400 |
| commit | 3b3274a85c2f5df21d76d82e0d7740005aa84fdf (patch) | |
| tree | c0840a1dc9f882bc389fed4396cd36209e75f801 /lisp/progmodes/python.el | |
| parent | c41c1ac410423243f575e910aef9a1c7645ec216 (diff) | |
| download | emacs-3b3274a85c2f5df21d76d82e0d7740005aa84fdf.tar.gz emacs-3b3274a85c2f5df21d76d82e0d7740005aa84fdf.zip | |
* lisp/progmodes/python.el: Teach f-strings to `font-lock`
(python--f-string-p, python--font-lock-f-strings): New functions.
(python-font-lock-keywords-maximum-decoration): Use them.
Diffstat (limited to 'lisp/progmodes/python.el')
| -rw-r--r-- | lisp/progmodes/python.el | 55 |
1 files changed, 52 insertions, 3 deletions
diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 76baa4469c7..d1871c93a78 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el | |||
| @@ -501,6 +501,52 @@ The type returned can be `comment', `string' or `paren'." | |||
| 501 | font-lock-string-face) | 501 | font-lock-string-face) |
| 502 | font-lock-comment-face)) | 502 | font-lock-comment-face)) |
| 503 | 503 | ||
| 504 | (defun python--f-string-p (ppss) | ||
| 505 | "Return non-nil if the pos where PPSS was found is inside an f-string." | ||
| 506 | (and (nth 3 ppss) | ||
| 507 | (let ((spos (1- (nth 8 ppss)))) | ||
| 508 | (and (memq (char-after spos) '(?f ?F)) | ||
| 509 | (or (< (point-min) spos) | ||
| 510 | (not (memq (char-syntax (char-before spos)) '(?w ?_)))))))) | ||
| 511 | |||
| 512 | (defun python--font-lock-f-strings (limit) | ||
| 513 | "Mark {...} holes as being code. | ||
| 514 | Remove the (presumably `font-lock-string-face') `face' property from | ||
| 515 | the {...} holes that appear within f-strings." | ||
| 516 | ;; FIXME: This will fail to properly highlight strings appearing | ||
| 517 | ;; within the {...} of an f-string. | ||
| 518 | ;; We could presumably fix it by running | ||
| 519 | ;; `font-lock-fontify-syntactically-region' (as is done in | ||
| 520 | ;; `sm-c--cpp-fontify-syntactically', for example) after removing | ||
| 521 | ;; the `face' property, but I'm not sure it's worth the effort and | ||
| 522 | ;; the risks. | ||
| 523 | (let ((ppss (syntax-ppss))) | ||
| 524 | (while | ||
| 525 | (progn | ||
| 526 | (while (and (not (python--f-string-p ppss)) | ||
| 527 | (re-search-forward "\\<f['\"]" limit 'move)) | ||
| 528 | (setq ppss (syntax-ppss))) | ||
| 529 | (< (point) limit)) | ||
| 530 | (cl-assert (python--f-string-p ppss)) | ||
| 531 | (let ((send (save-excursion | ||
| 532 | (goto-char (nth 8 ppss)) | ||
| 533 | (condition-case nil | ||
| 534 | (progn (let ((forward-sexp-function nil)) | ||
| 535 | (forward-sexp 1)) | ||
| 536 | (min limit (1- (point)))) | ||
| 537 | (scan-error limit))))) | ||
| 538 | (while (re-search-forward "{" send t) | ||
| 539 | (if (eq ?\{ (char-after)) | ||
| 540 | (forward-char 1) ;Just skip over {{ | ||
| 541 | (let ((beg (match-beginning 0)) | ||
| 542 | (end (condition-case nil | ||
| 543 | (progn (up-list 1) (min send (point))) | ||
| 544 | (scan-error send)))) | ||
| 545 | (goto-char end) | ||
| 546 | (put-text-property beg end 'face nil)))) | ||
| 547 | (goto-char (min limit (1+ send))) | ||
| 548 | (setq ppss (syntax-ppss)))))) | ||
| 549 | |||
| 504 | (defvar python-font-lock-keywords-level-1 | 550 | (defvar python-font-lock-keywords-level-1 |
| 505 | `((,(rx symbol-start "def" (1+ space) (group (1+ (or word ?_)))) | 551 | `((,(rx symbol-start "def" (1+ space) (group (1+ (or word ?_)))) |
| 506 | (1 font-lock-function-name-face)) | 552 | (1 font-lock-function-name-face)) |
| @@ -567,7 +613,8 @@ This is the medium decoration level, including everything in | |||
| 567 | builtins.") | 613 | builtins.") |
| 568 | 614 | ||
| 569 | (defvar python-font-lock-keywords-maximum-decoration | 615 | (defvar python-font-lock-keywords-maximum-decoration |
| 570 | `(,@python-font-lock-keywords-level-2 | 616 | `((python--font-lock-f-strings) |
| 617 | ,@python-font-lock-keywords-level-2 | ||
| 571 | ;; Constants | 618 | ;; Constants |
| 572 | (,(rx symbol-start | 619 | (,(rx symbol-start |
| 573 | (or | 620 | (or |
| @@ -575,7 +622,8 @@ builtins.") | |||
| 575 | ;; copyright, license, credits, quit and exit are added by the site | 622 | ;; copyright, license, credits, quit and exit are added by the site |
| 576 | ;; module and they are not intended to be used in programs | 623 | ;; module and they are not intended to be used in programs |
| 577 | "copyright" "credits" "exit" "license" "quit") | 624 | "copyright" "credits" "exit" "license" "quit") |
| 578 | symbol-end) . font-lock-constant-face) | 625 | symbol-end) |
| 626 | . font-lock-constant-face) | ||
| 579 | ;; Decorators. | 627 | ;; Decorators. |
| 580 | (,(rx line-start (* (any " \t")) (group "@" (1+ (or word ?_)) | 628 | (,(rx line-start (* (any " \t")) (group "@" (1+ (or word ?_)) |
| 581 | (0+ "." (1+ (or word ?_))))) | 629 | (0+ "." (1+ (or word ?_))))) |
| @@ -609,7 +657,8 @@ builtins.") | |||
| 609 | ;; OS specific | 657 | ;; OS specific |
| 610 | "VMSError" "WindowsError" | 658 | "VMSError" "WindowsError" |
| 611 | ) | 659 | ) |
| 612 | symbol-end) . font-lock-type-face) | 660 | symbol-end) |
| 661 | . font-lock-type-face) | ||
| 613 | ;; assignments | 662 | ;; assignments |
| 614 | ;; support for a = b = c = 5 | 663 | ;; support for a = b = c = 5 |
| 615 | (,(lambda (limit) | 664 | (,(lambda (limit) |