diff options
| author | Dmitry Gutov | 2013-11-02 05:10:10 +0400 |
|---|---|---|
| committer | Dmitry Gutov | 2013-11-02 05:10:10 +0400 |
| commit | 7ffd37219b311035fd346a126d7799cb53c6c73d (patch) | |
| tree | 019bd2432af865a67831367d25fda06f84e94a19 | |
| parent | a3996a2eba1a85e33da320f7bcec0e714ea1d6d6 (diff) | |
| download | emacs-7ffd37219b311035fd346a126d7799cb53c6c73d.tar.gz emacs-7ffd37219b311035fd346a126d7799cb53c6c73d.zip | |
* lisp/progmodes/ruby-mode.el Use `syntax-propertize-function'
unconditionally. Remove now unnecessary forward declarations.
Remove XEmacs-specific setup.
(ruby-here-doc-end-re, ruby-here-doc-beg-match)
(ruby-font-lock-syntactic-keywords)
(ruby-comment-beg-syntax, ruby-in-here-doc-p)
(ruby-here-doc-find-end, ruby-here-doc-beg-syntax)
(ruby-here-doc-end-syntax): Remove.
(ruby-mode): Don't check whether `syntax-propertize-rules' is
defined as function.
| -rw-r--r-- | lisp/ChangeLog | 13 | ||||
| -rw-r--r-- | lisp/progmodes/ruby-mode.el | 521 |
2 files changed, 185 insertions, 349 deletions
diff --git a/lisp/ChangeLog b/lisp/ChangeLog index dc8cc973423..3de7afba477 100644 --- a/lisp/ChangeLog +++ b/lisp/ChangeLog | |||
| @@ -1,3 +1,16 @@ | |||
| 1 | 2013-11-02 Dmitry Gutov <dgutov@yandex.ru> | ||
| 2 | |||
| 3 | * progmodes/ruby-mode.el Use `syntax-propertize-function' | ||
| 4 | unconditionally. Remove now unnecessary forward declarations. | ||
| 5 | Remove XEmacs-specific setup. | ||
| 6 | (ruby-here-doc-end-re, ruby-here-doc-beg-match) | ||
| 7 | (ruby-font-lock-syntactic-keywords) | ||
| 8 | (ruby-comment-beg-syntax, ruby-in-here-doc-p) | ||
| 9 | (ruby-here-doc-find-end, ruby-here-doc-beg-syntax) | ||
| 10 | (ruby-here-doc-end-syntax): Remove. | ||
| 11 | (ruby-mode): Don't check whether `syntax-propertize-rules' is | ||
| 12 | defined as function. | ||
| 13 | |||
| 1 | 2013-11-02 Bozhidar Batsov <bozhidar@batsov.com> | 14 | 2013-11-02 Bozhidar Batsov <bozhidar@batsov.com> |
| 2 | 15 | ||
| 3 | * progmodes/ruby-mode.el (ruby-mode-variables, ruby-mode): Use `setq-local'. | 16 | * progmodes/ruby-mode.el (ruby-mode-variables, ruby-mode): Use `setq-local'. |
diff --git a/lisp/progmodes/ruby-mode.el b/lisp/progmodes/ruby-mode.el index c8d7169ca58..75c59ebe1fd 100644 --- a/lisp/progmodes/ruby-mode.el +++ b/lisp/progmodes/ruby-mode.el | |||
| @@ -1515,349 +1515,182 @@ If the result is do-end block, it will always be multiline." | |||
| 1515 | (ruby-do-end-to-brace beg end))) | 1515 | (ruby-do-end-to-brace beg end))) |
| 1516 | (goto-char start)))) | 1516 | (goto-char start)))) |
| 1517 | 1517 | ||
| 1518 | (declare-function ruby-syntax-propertize-heredoc "ruby-mode" (limit)) | 1518 | (eval-and-compile |
| 1519 | (declare-function ruby-syntax-enclosing-percent-literal "ruby-mode" (limit)) | 1519 | (defconst ruby-percent-literal-beg-re |
| 1520 | (declare-function ruby-syntax-propertize-percent-literal "ruby-mode" (limit)) | 1520 | "\\(%\\)[qQrswWxIi]?\\([[:punct:]]\\)" |
| 1521 | ;; Unusual code layout confuses the byte-compiler. | 1521 | "Regexp to match the beginning of percent literal.") |
| 1522 | (declare-function ruby-syntax-propertize-expansion "ruby-mode" ()) | 1522 | |
| 1523 | (declare-function ruby-syntax-expansion-allowed-p "ruby-mode" (parse-state)) | 1523 | (defconst ruby-syntax-methods-before-regexp |
| 1524 | (declare-function ruby-syntax-propertize-function "ruby-mode" (start end)) | 1524 | '("gsub" "gsub!" "sub" "sub!" "scan" "split" "split!" "index" "match" |
| 1525 | 1525 | "assert_match" "Given" "Then" "When") | |
| 1526 | (if (eval-when-compile (fboundp #'syntax-propertize-rules)) | 1526 | "Methods that can take regexp as the first argument. |
| 1527 | ;; New code that works independently from font-lock. | ||
| 1528 | (progn | ||
| 1529 | (eval-and-compile | ||
| 1530 | (defconst ruby-percent-literal-beg-re | ||
| 1531 | "\\(%\\)[qQrswWxIi]?\\([[:punct:]]\\)" | ||
| 1532 | "Regexp to match the beginning of percent literal.") | ||
| 1533 | |||
| 1534 | (defconst ruby-syntax-methods-before-regexp | ||
| 1535 | '("gsub" "gsub!" "sub" "sub!" "scan" "split" "split!" "index" "match" | ||
| 1536 | "assert_match" "Given" "Then" "When") | ||
| 1537 | "Methods that can take regexp as the first argument. | ||
| 1538 | It will be properly highlighted even when the call omits parens.") | 1527 | It will be properly highlighted even when the call omits parens.") |
| 1539 | 1528 | ||
| 1540 | (defvar ruby-syntax-before-regexp-re | 1529 | (defvar ruby-syntax-before-regexp-re |
| 1541 | (concat | 1530 | (concat |
| 1542 | ;; Special tokens that can't be followed by a division operator. | 1531 | ;; Special tokens that can't be followed by a division operator. |
| 1543 | "\\(^\\|[[=(,~;<>]" | 1532 | "\\(^\\|[[=(,~;<>]" |
| 1544 | ;; Distinguish ternary operator tokens. | 1533 | ;; Distinguish ternary operator tokens. |
| 1545 | ;; FIXME: They don't really have to be separated with spaces. | 1534 | ;; FIXME: They don't really have to be separated with spaces. |
| 1546 | "\\|[?:] " | 1535 | "\\|[?:] " |
| 1547 | ;; Control flow keywords and operators following bol or whitespace. | 1536 | ;; Control flow keywords and operators following bol or whitespace. |
| 1548 | "\\|\\(?:^\\|\\s \\)" | 1537 | "\\|\\(?:^\\|\\s \\)" |
| 1549 | (regexp-opt '("if" "elsif" "unless" "while" "until" "when" "and" | 1538 | (regexp-opt '("if" "elsif" "unless" "while" "until" "when" "and" |
| 1550 | "or" "not" "&&" "||")) | 1539 | "or" "not" "&&" "||")) |
| 1551 | ;; Method name from the list. | 1540 | ;; Method name from the list. |
| 1552 | "\\|\\_<" | 1541 | "\\|\\_<" |
| 1553 | (regexp-opt ruby-syntax-methods-before-regexp) | 1542 | (regexp-opt ruby-syntax-methods-before-regexp) |
| 1554 | "\\)\\s *") | 1543 | "\\)\\s *") |
| 1555 | "Regexp to match text that can be followed by a regular expression.")) | 1544 | "Regexp to match text that can be followed by a regular expression.")) |
| 1556 | 1545 | ||
| 1557 | (defun ruby-syntax-propertize-function (start end) | 1546 | (defun ruby-syntax-propertize-function (start end) |
| 1558 | "Syntactic keywords for Ruby mode. See `syntax-propertize-function'." | 1547 | "Syntactic keywords for Ruby mode. See `syntax-propertize-function'." |
| 1559 | (let (case-fold-search) | 1548 | (let (case-fold-search) |
| 1560 | (goto-char start) | 1549 | (goto-char start) |
| 1561 | (remove-text-properties start end '(ruby-expansion-match-data)) | 1550 | (remove-text-properties start end '(ruby-expansion-match-data)) |
| 1562 | (ruby-syntax-propertize-heredoc end) | 1551 | (ruby-syntax-propertize-heredoc end) |
| 1563 | (ruby-syntax-enclosing-percent-literal end) | 1552 | (ruby-syntax-enclosing-percent-literal end) |
| 1564 | (funcall | 1553 | (funcall |
| 1565 | (syntax-propertize-rules | 1554 | (syntax-propertize-rules |
| 1566 | ;; $' $" $` .... are variables. | 1555 | ;; $' $" $` .... are variables. |
| 1567 | ;; ?' ?" ?` are character literals (one-char strings in 1.9+). | 1556 | ;; ?' ?" ?` are character literals (one-char strings in 1.9+). |
| 1568 | ("\\([?$]\\)[#\"'`]" | 1557 | ("\\([?$]\\)[#\"'`]" |
| 1569 | (1 (unless (save-excursion | 1558 | (1 (unless (save-excursion |
| 1570 | ;; Not within a string. | 1559 | ;; Not within a string. |
| 1571 | (nth 3 (syntax-ppss (match-beginning 0)))) | 1560 | (nth 3 (syntax-ppss (match-beginning 0)))) |
| 1572 | (string-to-syntax "\\")))) | 1561 | (string-to-syntax "\\")))) |
| 1573 | ;; Regular expressions. Start with matching unescaped slash. | 1562 | ;; Regular expressions. Start with matching unescaped slash. |
| 1574 | ("\\(?:\\=\\|[^\\]\\)\\(?:\\\\\\\\\\)*\\(/\\)" | 1563 | ("\\(?:\\=\\|[^\\]\\)\\(?:\\\\\\\\\\)*\\(/\\)" |
| 1575 | (1 (let ((state (save-excursion (syntax-ppss (match-beginning 1))))) | 1564 | (1 (let ((state (save-excursion (syntax-ppss (match-beginning 1))))) |
| 1576 | (when (or | 1565 | (when (or |
| 1577 | ;; Beginning of a regexp. | 1566 | ;; Beginning of a regexp. |
| 1578 | (and (null (nth 8 state)) | 1567 | (and (null (nth 8 state)) |
| 1579 | (save-excursion | 1568 | (save-excursion |
| 1580 | (forward-char -1) | 1569 | (forward-char -1) |
| 1581 | (looking-back ruby-syntax-before-regexp-re | 1570 | (looking-back ruby-syntax-before-regexp-re |
| 1582 | (point-at-bol)))) | 1571 | (point-at-bol)))) |
| 1583 | ;; End of regexp. We don't match the whole | 1572 | ;; End of regexp. We don't match the whole |
| 1584 | ;; regexp at once because it can have | 1573 | ;; regexp at once because it can have |
| 1585 | ;; string interpolation inside, or span | 1574 | ;; string interpolation inside, or span |
| 1586 | ;; several lines. | 1575 | ;; several lines. |
| 1587 | (eq ?/ (nth 3 state))) | 1576 | (eq ?/ (nth 3 state))) |
| 1588 | (string-to-syntax "\"/"))))) | 1577 | (string-to-syntax "\"/"))))) |
| 1589 | ;; Expression expansions in strings. We're handling them | 1578 | ;; Expression expansions in strings. We're handling them |
| 1590 | ;; here, so that the regexp rule never matches inside them. | 1579 | ;; here, so that the regexp rule never matches inside them. |
| 1591 | (ruby-expression-expansion-re | 1580 | (ruby-expression-expansion-re |
| 1592 | (0 (ignore (ruby-syntax-propertize-expansion)))) | 1581 | (0 (ignore (ruby-syntax-propertize-expansion)))) |
| 1593 | ("^=en\\(d\\)\\_>" (1 "!")) | 1582 | ("^=en\\(d\\)\\_>" (1 "!")) |
| 1594 | ("^\\(=\\)begin\\_>" (1 "!")) | 1583 | ("^\\(=\\)begin\\_>" (1 "!")) |
| 1595 | ;; Handle here documents. | 1584 | ;; Handle here documents. |
| 1596 | ((concat ruby-here-doc-beg-re ".*\\(\n\\)") | 1585 | ((concat ruby-here-doc-beg-re ".*\\(\n\\)") |
| 1597 | (7 (unless (or (nth 8 (save-excursion | 1586 | (7 (unless (or (nth 8 (save-excursion |
| 1598 | (syntax-ppss (match-beginning 0)))) | 1587 | (syntax-ppss (match-beginning 0)))) |
| 1599 | (ruby-singleton-class-p (match-beginning 0))) | 1588 | (ruby-singleton-class-p (match-beginning 0))) |
| 1600 | (put-text-property (match-beginning 7) (match-end 7) | 1589 | (put-text-property (match-beginning 7) (match-end 7) |
| 1601 | 'syntax-table (string-to-syntax "\"")) | 1590 | 'syntax-table (string-to-syntax "\"")) |
| 1602 | (ruby-syntax-propertize-heredoc end)))) | 1591 | (ruby-syntax-propertize-heredoc end)))) |
| 1603 | ;; Handle percent literals: %w(), %q{}, etc. | 1592 | ;; Handle percent literals: %w(), %q{}, etc. |
| 1604 | ((concat "\\(?:^\\|[[ \t\n<+(,=]\\)" ruby-percent-literal-beg-re) | 1593 | ((concat "\\(?:^\\|[[ \t\n<+(,=]\\)" ruby-percent-literal-beg-re) |
| 1605 | (1 (prog1 "|" (ruby-syntax-propertize-percent-literal end))))) | 1594 | (1 (prog1 "|" (ruby-syntax-propertize-percent-literal end))))) |
| 1606 | (point) end))) | 1595 | (point) end))) |
| 1607 | 1596 | ||
| 1608 | (defun ruby-syntax-propertize-heredoc (limit) | 1597 | (defun ruby-syntax-propertize-heredoc (limit) |
| 1609 | (let ((ppss (syntax-ppss)) | 1598 | (let ((ppss (syntax-ppss)) |
| 1610 | (res '())) | 1599 | (res '())) |
| 1611 | (when (eq ?\n (nth 3 ppss)) | 1600 | (when (eq ?\n (nth 3 ppss)) |
| 1612 | (save-excursion | 1601 | (save-excursion |
| 1613 | (goto-char (nth 8 ppss)) | 1602 | (goto-char (nth 8 ppss)) |
| 1614 | (beginning-of-line) | ||
| 1615 | (while (re-search-forward ruby-here-doc-beg-re | ||
| 1616 | (line-end-position) t) | ||
| 1617 | (unless (ruby-singleton-class-p (match-beginning 0)) | ||
| 1618 | (push (concat (ruby-here-doc-end-match) "\n") res)))) | ||
| 1619 | (save-excursion | ||
| 1620 | ;; With multiple openers on the same line, we don't know in which | ||
| 1621 | ;; part `start' is, so we have to go back to the beginning. | ||
| 1622 | (when (cdr res) | ||
| 1623 | (goto-char (nth 8 ppss)) | ||
| 1624 | (setq res (nreverse res))) | ||
| 1625 | (while (and res (re-search-forward (pop res) limit 'move)) | ||
| 1626 | (if (null res) | ||
| 1627 | (put-text-property (1- (point)) (point) | ||
| 1628 | 'syntax-table (string-to-syntax "\"")))) | ||
| 1629 | ;; End up at bol following the heredoc openers. | ||
| 1630 | ;; Propertize expression expansions from this point forward. | ||
| 1631 | )))) | ||
| 1632 | |||
| 1633 | (defun ruby-syntax-enclosing-percent-literal (limit) | ||
| 1634 | (let ((state (syntax-ppss)) | ||
| 1635 | (start (point))) | ||
| 1636 | ;; When already inside percent literal, re-propertize it. | ||
| 1637 | (when (eq t (nth 3 state)) | ||
| 1638 | (goto-char (nth 8 state)) | ||
| 1639 | (when (looking-at ruby-percent-literal-beg-re) | ||
| 1640 | (ruby-syntax-propertize-percent-literal limit)) | ||
| 1641 | (when (< (point) start) (goto-char start))))) | ||
| 1642 | |||
| 1643 | (defun ruby-syntax-propertize-percent-literal (limit) | ||
| 1644 | (goto-char (match-beginning 2)) | ||
| 1645 | ;; Not inside a simple string or comment. | ||
| 1646 | (when (eq t (nth 3 (syntax-ppss))) | ||
| 1647 | (let* ((op (char-after)) | ||
| 1648 | (ops (char-to-string op)) | ||
| 1649 | (cl (or (cdr (aref (syntax-table) op)) | ||
| 1650 | (cdr (assoc op '((?< . ?>)))))) | ||
| 1651 | parse-sexp-lookup-properties) | ||
| 1652 | (save-excursion | ||
| 1653 | (condition-case nil | ||
| 1654 | (progn | ||
| 1655 | (if cl ; Paired delimiters. | ||
| 1656 | ;; Delimiter pairs of the same kind can be nested | ||
| 1657 | ;; inside the literal, as long as they are balanced. | ||
| 1658 | ;; Create syntax table that ignores other characters. | ||
| 1659 | (with-syntax-table (make-char-table 'syntax-table nil) | ||
| 1660 | (modify-syntax-entry op (concat "(" (char-to-string cl))) | ||
| 1661 | (modify-syntax-entry cl (concat ")" ops)) | ||
| 1662 | (modify-syntax-entry ?\\ "\\") | ||
| 1663 | (save-restriction | ||
| 1664 | (narrow-to-region (point) limit) | ||
| 1665 | (forward-list))) ; skip to the paired character | ||
| 1666 | ;; Single character delimiter. | ||
| 1667 | (re-search-forward (concat "[^\\]\\(?:\\\\\\\\\\)*" | ||
| 1668 | (regexp-quote ops)) limit nil)) | ||
| 1669 | ;; Found the closing delimiter. | ||
| 1670 | (put-text-property (1- (point)) (point) 'syntax-table | ||
| 1671 | (string-to-syntax "|"))) | ||
| 1672 | ;; Unclosed literal, do nothing. | ||
| 1673 | ((scan-error search-failed))))))) | ||
| 1674 | |||
| 1675 | (defun ruby-syntax-propertize-expansion () | ||
| 1676 | ;; Save the match data to a text property, for font-locking later. | ||
| 1677 | ;; Set the syntax of all double quotes and backticks to punctuation. | ||
| 1678 | (let* ((beg (match-beginning 2)) | ||
| 1679 | (end (match-end 2)) | ||
| 1680 | (state (and beg (save-excursion (syntax-ppss beg))))) | ||
| 1681 | (when (ruby-syntax-expansion-allowed-p state) | ||
| 1682 | (put-text-property beg (1+ beg) 'ruby-expansion-match-data | ||
| 1683 | (match-data)) | ||
| 1684 | (goto-char beg) | ||
| 1685 | (while (re-search-forward "[\"`]" end 'move) | ||
| 1686 | (put-text-property (match-beginning 0) (match-end 0) | ||
| 1687 | 'syntax-table (string-to-syntax ".")))))) | ||
| 1688 | |||
| 1689 | (defun ruby-syntax-expansion-allowed-p (parse-state) | ||
| 1690 | "Return non-nil if expression expansion is allowed." | ||
| 1691 | (let ((term (nth 3 parse-state))) | ||
| 1692 | (cond | ||
| 1693 | ((memq term '(?\" ?` ?\n ?/))) | ||
| 1694 | ((eq term t) | ||
| 1695 | (save-match-data | ||
| 1696 | (save-excursion | ||
| 1697 | (goto-char (nth 8 parse-state)) | ||
| 1698 | (looking-at "%\\(?:[QWrxI]\\|\\W\\)"))))))) | ||
| 1699 | |||
| 1700 | (defun ruby-syntax-propertize-expansions (start end) | ||
| 1701 | (save-excursion | ||
| 1702 | (goto-char start) | ||
| 1703 | (while (re-search-forward ruby-expression-expansion-re end 'move) | ||
| 1704 | (ruby-syntax-propertize-expansion)))) | ||
| 1705 | ) | ||
| 1706 | |||
| 1707 | ;; For Emacsen where syntax-propertize-rules is not (yet) available, | ||
| 1708 | ;; fallback on the old font-lock-syntactic-keywords stuff. | ||
| 1709 | |||
| 1710 | (defconst ruby-here-doc-end-re | ||
| 1711 | "^\\([ \t]+\\)?\\(.*\\)\\(\n\\)" | ||
| 1712 | "Regexp to match the end of heredocs. | ||
| 1713 | |||
| 1714 | This will actually match any line with one or more characters. | ||
| 1715 | It's useful in that it divides up the match string so that | ||
| 1716 | `ruby-here-doc-beg-match' can search for the beginning of the heredoc.") | ||
| 1717 | |||
| 1718 | (defun ruby-here-doc-beg-match () | ||
| 1719 | "Return a regexp to find the beginning of a heredoc. | ||
| 1720 | |||
| 1721 | This should only be called after matching against `ruby-here-doc-end-re'." | ||
| 1722 | (let ((contents (concat | ||
| 1723 | (regexp-quote (concat (match-string 2) (match-string 3))) | ||
| 1724 | (if (string= (match-string 3) "_") "\\B" "\\b")))) | ||
| 1725 | (concat "<<" | ||
| 1726 | (let ((match (match-string 1))) | ||
| 1727 | (if (and match (> (length match) 0)) | ||
| 1728 | (concat "\\(?:-\\([\"']?\\)\\|\\([\"']\\)" | ||
| 1729 | (match-string 1) "\\)" | ||
| 1730 | contents "\\(\\1\\|\\2\\)") | ||
| 1731 | (concat "-?\\([\"']\\|\\)" contents "\\1")))))) | ||
| 1732 | |||
| 1733 | (defconst ruby-font-lock-syntactic-keywords | ||
| 1734 | `( | ||
| 1735 | ;; the last $', $", $` in the respective string is not variable | ||
| 1736 | ;; the last ?', ?", ?` in the respective string is not ascii code | ||
| 1737 | ("\\(^\\|[\[ \t\n<+\(,=]\\)\\(['\"`]\\)\\(\\\\.\\|\\2\\|[^'\"`\n\\\\]\\)*?\\\\?[?$]\\(\\2\\)" | ||
| 1738 | (2 (7 . nil)) | ||
| 1739 | (4 (7 . nil))) | ||
| 1740 | ;; $' $" $` .... are variables | ||
| 1741 | ;; ?' ?" ?` are ascii codes | ||
| 1742 | ("\\(^\\|[^\\\\]\\)\\(\\\\\\\\\\)*[?$]\\([#\"'`]\\)" 3 (1 . nil)) | ||
| 1743 | ;; regexps | ||
| 1744 | ("\\(^\\|[[=(,~?:;<>]\\|\\(^\\|\\s \\)\\(if\\|elsif\\|unless\\|while\\|until\\|when\\|and\\|or\\|&&\\|||\\)\\|g?sub!?\\|scan\\|split!?\\)\\s *\\(/\\)[^/\n\\\\]*\\(\\\\.[^/\n\\\\]*\\)*\\(/\\)" | ||
| 1745 | (4 (7 . ?/)) | ||
| 1746 | (6 (7 . ?/))) | ||
| 1747 | ("^=en\\(d\\)\\_>" 1 "!") | ||
| 1748 | ;; Percent literal. | ||
| 1749 | ("\\(^\\|[[ \t\n<+(,=]\\)\\(%[xrqQwW]?\\([^<[{(a-zA-Z0-9 \n]\\)[^\n\\\\]*\\(\\\\.[^\n\\\\]*\\)*\\(\\3\\)\\)" | ||
| 1750 | (3 "\"") | ||
| 1751 | (5 "\"")) | ||
| 1752 | ("^\\(=\\)begin\\_>" 1 (ruby-comment-beg-syntax)) | ||
| 1753 | ;; Currently, the following case is highlighted incorrectly: | ||
| 1754 | ;; | ||
| 1755 | ;; <<FOO | ||
| 1756 | ;; FOO | ||
| 1757 | ;; <<BAR | ||
| 1758 | ;; <<BAZ | ||
| 1759 | ;; BAZ | ||
| 1760 | ;; BAR | ||
| 1761 | ;; | ||
| 1762 | ;; This is because all here-doc beginnings are highlighted before any endings, | ||
| 1763 | ;; so although <<BAR is properly marked as a beginning, when we get to <<BAZ | ||
| 1764 | ;; it thinks <<BAR is part of a string so it's marked as well. | ||
| 1765 | ;; | ||
| 1766 | ;; This may be fixable by modifying ruby-in-here-doc-p to use | ||
| 1767 | ;; ruby-in-non-here-doc-string-p rather than syntax-ppss-context, | ||
| 1768 | ;; but I don't want to try that until we've got unit tests set up | ||
| 1769 | ;; to make sure I don't break anything else. | ||
| 1770 | (,(concat ruby-here-doc-beg-re ".*\\(\n\\)") | ||
| 1771 | ,(+ 1 (regexp-opt-depth ruby-here-doc-beg-re)) | ||
| 1772 | (ruby-here-doc-beg-syntax)) | ||
| 1773 | (,ruby-here-doc-end-re 3 (ruby-here-doc-end-syntax))) | ||
| 1774 | "Syntactic keywords for Ruby mode. See `font-lock-syntactic-keywords'.") | ||
| 1775 | |||
| 1776 | (defun ruby-comment-beg-syntax () | ||
| 1777 | "Return the syntax cell for a the first character of a =begin. | ||
| 1778 | See the definition of `ruby-font-lock-syntactic-keywords'. | ||
| 1779 | |||
| 1780 | This returns a comment-delimiter cell as long as the =begin | ||
| 1781 | isn't in a string or another comment." | ||
| 1782 | (when (not (nth 3 (syntax-ppss))) | ||
| 1783 | (string-to-syntax "!"))) | ||
| 1784 | |||
| 1785 | (defun ruby-in-here-doc-p () | ||
| 1786 | "Return whether or not the point is in a heredoc." | ||
| 1787 | (save-excursion | ||
| 1788 | (let ((old-point (point)) (case-fold-search nil)) | ||
| 1789 | (beginning-of-line) | 1603 | (beginning-of-line) |
| 1790 | (catch 'found-beg | 1604 | (while (re-search-forward ruby-here-doc-beg-re |
| 1791 | (while (and (re-search-backward ruby-here-doc-beg-re nil t) | 1605 | (line-end-position) t) |
| 1792 | (not (ruby-singleton-class-p))) | 1606 | (unless (ruby-singleton-class-p (match-beginning 0)) |
| 1793 | (if (not (or (ruby-in-ppss-context-p 'anything) | 1607 | (push (concat (ruby-here-doc-end-match) "\n") res)))) |
| 1794 | (ruby-here-doc-find-end old-point))) | 1608 | (save-excursion |
| 1795 | (throw 'found-beg t))))))) | 1609 | ;; With multiple openers on the same line, we don't know in which |
| 1796 | 1610 | ;; part `start' is, so we have to go back to the beginning. | |
| 1797 | (defun ruby-here-doc-find-end (&optional limit) | 1611 | (when (cdr res) |
| 1798 | "Expects the point to be on a line with one or more heredoc openers. | 1612 | (goto-char (nth 8 ppss)) |
| 1799 | Returns the buffer position at which all heredocs on the line | 1613 | (setq res (nreverse res))) |
| 1800 | are terminated, or nil if they aren't terminated before the | 1614 | (while (and res (re-search-forward (pop res) limit 'move)) |
| 1801 | buffer position `limit' or the end of the buffer." | 1615 | (if (null res) |
| 1802 | (save-excursion | 1616 | (put-text-property (1- (point)) (point) |
| 1803 | (beginning-of-line) | 1617 | 'syntax-table (string-to-syntax "\"")))) |
| 1804 | (catch 'done | 1618 | ;; End up at bol following the heredoc openers. |
| 1805 | (let ((eol (point-at-eol)) | 1619 | ;; Propertize expression expansions from this point forward. |
| 1806 | (case-fold-search nil) | 1620 | )))) |
| 1807 | ;; Fake match data such that (match-end 0) is at eol | 1621 | |
| 1808 | (end-match-data (progn (looking-at ".*$") (match-data))) | 1622 | (defun ruby-syntax-enclosing-percent-literal (limit) |
| 1809 | beg-match-data end-re) | 1623 | (let ((state (syntax-ppss)) |
| 1810 | (while (re-search-forward ruby-here-doc-beg-re eol t) | 1624 | (start (point))) |
| 1811 | (setq beg-match-data (match-data)) | 1625 | ;; When already inside percent literal, re-propertize it. |
| 1812 | (setq end-re (ruby-here-doc-end-match)) | 1626 | (when (eq t (nth 3 state)) |
| 1813 | 1627 | (goto-char (nth 8 state)) | |
| 1814 | (set-match-data end-match-data) | 1628 | (when (looking-at ruby-percent-literal-beg-re) |
| 1815 | (goto-char (match-end 0)) | 1629 | (ruby-syntax-propertize-percent-literal limit)) |
| 1816 | (unless (re-search-forward end-re limit t) (throw 'done nil)) | 1630 | (when (< (point) start) (goto-char start))))) |
| 1817 | (setq end-match-data (match-data)) | 1631 | |
| 1818 | 1632 | (defun ruby-syntax-propertize-percent-literal (limit) | |
| 1819 | (set-match-data beg-match-data) | 1633 | (goto-char (match-beginning 2)) |
| 1820 | (goto-char (match-end 0))) | 1634 | ;; Not inside a simple string or comment. |
| 1821 | (set-match-data end-match-data) | 1635 | (when (eq t (nth 3 (syntax-ppss))) |
| 1822 | (goto-char (match-end 0)) | 1636 | (let* ((op (char-after)) |
| 1823 | (point))))) | 1637 | (ops (char-to-string op)) |
| 1824 | 1638 | (cl (or (cdr (aref (syntax-table) op)) | |
| 1825 | (defun ruby-here-doc-beg-syntax () | 1639 | (cdr (assoc op '((?< . ?>)))))) |
| 1826 | "Return the syntax cell for a line that may begin a heredoc. | 1640 | parse-sexp-lookup-properties) |
| 1827 | See the definition of `ruby-font-lock-syntactic-keywords'. | 1641 | (save-excursion |
| 1828 | 1642 | (condition-case nil | |
| 1829 | This sets the syntax cell for the newline ending the line | 1643 | (progn |
| 1830 | containing the heredoc beginning so that cases where multiple | 1644 | (if cl ; Paired delimiters. |
| 1831 | heredocs are started on one line are handled correctly." | 1645 | ;; Delimiter pairs of the same kind can be nested |
| 1832 | (save-excursion | 1646 | ;; inside the literal, as long as they are balanced. |
| 1833 | (goto-char (match-beginning 0)) | 1647 | ;; Create syntax table that ignores other characters. |
| 1834 | (unless (or (ruby-in-ppss-context-p 'non-heredoc) | 1648 | (with-syntax-table (make-char-table 'syntax-table nil) |
| 1835 | (ruby-in-here-doc-p)) | 1649 | (modify-syntax-entry op (concat "(" (char-to-string cl))) |
| 1836 | (string-to-syntax "\"")))) | 1650 | (modify-syntax-entry cl (concat ")" ops)) |
| 1837 | 1651 | (modify-syntax-entry ?\\ "\\") | |
| 1838 | (defun ruby-here-doc-end-syntax () | 1652 | (save-restriction |
| 1839 | "Return the syntax cell for a line that may end a heredoc. | 1653 | (narrow-to-region (point) limit) |
| 1840 | See the definition of `ruby-font-lock-syntactic-keywords'." | 1654 | (forward-list))) ; skip to the paired character |
| 1841 | (let ((pss (syntax-ppss)) (case-fold-search nil)) | 1655 | ;; Single character delimiter. |
| 1842 | ;; If we aren't in a string, we definitely aren't ending a heredoc, | 1656 | (re-search-forward (concat "[^\\]\\(?:\\\\\\\\\\)*" |
| 1843 | ;; so we can just give up. | 1657 | (regexp-quote ops)) limit nil)) |
| 1844 | ;; This means we aren't doing a full-document search | 1658 | ;; Found the closing delimiter. |
| 1845 | ;; every time we enter a character. | 1659 | (put-text-property (1- (point)) (point) 'syntax-table |
| 1846 | (when (ruby-in-ppss-context-p 'heredoc pss) | 1660 | (string-to-syntax "|"))) |
| 1661 | ;; Unclosed literal, do nothing. | ||
| 1662 | ((scan-error search-failed))))))) | ||
| 1663 | |||
| 1664 | (defun ruby-syntax-propertize-expansion () | ||
| 1665 | ;; Save the match data to a text property, for font-locking later. | ||
| 1666 | ;; Set the syntax of all double quotes and backticks to punctuation. | ||
| 1667 | (let* ((beg (match-beginning 2)) | ||
| 1668 | (end (match-end 2)) | ||
| 1669 | (state (and beg (save-excursion (syntax-ppss beg))))) | ||
| 1670 | (when (ruby-syntax-expansion-allowed-p state) | ||
| 1671 | (put-text-property beg (1+ beg) 'ruby-expansion-match-data | ||
| 1672 | (match-data)) | ||
| 1673 | (goto-char beg) | ||
| 1674 | (while (re-search-forward "[\"`]" end 'move) | ||
| 1675 | (put-text-property (match-beginning 0) (match-end 0) | ||
| 1676 | 'syntax-table (string-to-syntax ".")))))) | ||
| 1677 | |||
| 1678 | (defun ruby-syntax-expansion-allowed-p (parse-state) | ||
| 1679 | "Return non-nil if expression expansion is allowed." | ||
| 1680 | (let ((term (nth 3 parse-state))) | ||
| 1681 | (cond | ||
| 1682 | ((memq term '(?\" ?` ?\n ?/))) | ||
| 1683 | ((eq term t) | ||
| 1684 | (save-match-data | ||
| 1847 | (save-excursion | 1685 | (save-excursion |
| 1848 | (goto-char (nth 8 pss)) ; Go to the beginning of heredoc. | 1686 | (goto-char (nth 8 parse-state)) |
| 1849 | (let ((eol (point))) | 1687 | (looking-at "%\\(?:[QWrxI]\\|\\W\\)"))))))) |
| 1850 | (beginning-of-line) | ||
| 1851 | (if (and (re-search-forward (ruby-here-doc-beg-match) eol t) ; If there is a heredoc that matches this line... | ||
| 1852 | (not (ruby-in-ppss-context-p 'anything)) ; And that's not inside a heredoc/string/comment... | ||
| 1853 | (progn (goto-char (match-end 0)) ; And it's the last heredoc on its line... | ||
| 1854 | (not (re-search-forward ruby-here-doc-beg-re eol t)))) | ||
| 1855 | (string-to-syntax "\""))))))) | ||
| 1856 | 1688 | ||
| 1857 | (unless (functionp 'syntax-ppss) | 1689 | (defun ruby-syntax-propertize-expansions (start end) |
| 1858 | (defun syntax-ppss (&optional pos) | 1690 | (save-excursion |
| 1859 | (parse-partial-sexp (point-min) (or pos (point))))) | 1691 | (goto-char start) |
| 1860 | ) | 1692 | (while (re-search-forward ruby-expression-expansion-re end 'move) |
| 1693 | (ruby-syntax-propertize-expansion)))) | ||
| 1861 | 1694 | ||
| 1862 | (defun ruby-in-ppss-context-p (context &optional ppss) | 1695 | (defun ruby-in-ppss-context-p (context &optional ppss) |
| 1863 | (let ((ppss (or ppss (syntax-ppss (point))))) | 1696 | (let ((ppss (or ppss (syntax-ppss (point))))) |
| @@ -1880,14 +1713,6 @@ See the definition of `ruby-font-lock-syntactic-keywords'." | |||
| 1880 | "context name `" (symbol-name context) "' is unknown")))) | 1713 | "context name `" (symbol-name context) "' is unknown")))) |
| 1881 | t))) | 1714 | t))) |
| 1882 | 1715 | ||
| 1883 | (if (featurep 'xemacs) | ||
| 1884 | (put 'ruby-mode 'font-lock-defaults | ||
| 1885 | '((ruby-font-lock-keywords) | ||
| 1886 | nil nil nil | ||
| 1887 | beginning-of-line | ||
| 1888 | (font-lock-syntactic-keywords | ||
| 1889 | . ruby-font-lock-syntactic-keywords)))) | ||
| 1890 | |||
| 1891 | (defvar ruby-font-lock-syntax-table | 1716 | (defvar ruby-font-lock-syntax-table |
| 1892 | (let ((tbl (copy-syntax-table ruby-mode-syntax-table))) | 1717 | (let ((tbl (copy-syntax-table ruby-mode-syntax-table))) |
| 1893 | (modify-syntax-entry ?_ "w" tbl) | 1718 | (modify-syntax-entry ?_ "w" tbl) |
| @@ -2082,9 +1907,7 @@ The variable `ruby-indent-level' controls the amount of indentation. | |||
| 2082 | (setq-local font-lock-keywords ruby-font-lock-keywords) | 1907 | (setq-local font-lock-keywords ruby-font-lock-keywords) |
| 2083 | (setq-local font-lock-syntax-table ruby-font-lock-syntax-table) | 1908 | (setq-local font-lock-syntax-table ruby-font-lock-syntax-table) |
| 2084 | 1909 | ||
| 2085 | (if (eval-when-compile (fboundp 'syntax-propertize-rules)) | 1910 | (setq-local syntax-propertize-function #'ruby-syntax-propertize-function)) |
| 2086 | (setq-local syntax-propertize-function #'ruby-syntax-propertize-function) | ||
| 2087 | (setq-local font-lock-syntactic-keywords ruby-font-lock-syntactic-keywords))) | ||
| 2088 | 1911 | ||
| 2089 | ;;; Invoke ruby-mode when appropriate | 1912 | ;;; Invoke ruby-mode when appropriate |
| 2090 | 1913 | ||