Skip to content

Commit

Permalink
Extended support for function shorthand notation (#1282)
Browse files Browse the repository at this point in the history
* Implement font-locking for functions defined or specified (anonymous)
using the backslash shorthand notation. Ensure extensibility by
creating variables `ess-R-keystrings' and `ess-r--non-fn-kstrs' as
complements to `ess-R-keywords' and `ess-r--non-fn-kwds'. Adjust
`ess-r--find-fl-keyword' accordingly. Fixes #1278.

Add question mark (shortcut to help) to both `ess-R-keystrings' and
`ess-r--non-fn-kstrs'.

* Function shorthand notation `\()' to trigger font-locking of function name

* Add R function shorthand notation `\()' to
`ess--r-s-function-pattern'. Allows function defined with the
shorthand notation to be recognized as functions by e.g.
`ess-r-beginning-of-function'.

* Make the ESS-R 'keystrings' variable private by applying the 'two hyphens' convention and remove reference made to it from the docstring of `ess-R-keywords'.
Set it to lowercase.

* Add R shorthand notation for `function' to font-locking/fontification tests, excluding test related to backquoted function definition fontification.

* Adjust `ess-r-font-lock-syntactic-face-function' so that it supports R function shorthand notation. Allows e.g. backquoted function names to be font-locked.

* Fix fontification of names of functions defined using R function shorthand notation.
  • Loading branch information
maxecharel committed Apr 10, 2024
1 parent e7b25b6 commit 8030e29
Show file tree
Hide file tree
Showing 5 changed files with 51 additions and 9 deletions.
9 changes: 9 additions & 0 deletions doc/newfeat.texi
@@ -1,6 +1,15 @@
@comment @itemize @w{}
@comment @item

Changes and New Features in development version:
@itemize @bullet

@item ESS[R]: The shorthand notation for lambda functions
and the question mark are now fontified as keywords.
Contributed by Maxime Pettinger.

@end itemize

Changes and New Features in 24.01.1:
@itemize @bullet

Expand Down
12 changes: 11 additions & 1 deletion lisp/ess-custom.el
Expand Up @@ -2052,6 +2052,15 @@ See also function `ess-create-object-name-db'.")
"recover" "browser")
"Reserved words or special functions in the R language.")

(defvar ess-r--keystrings
'("\\" "\?")
"Reserved non-word strings or special functions whose names
include special characters in the R language.
Similar font-locking usage as `ess-R-keywords', but dedicated to
strings that should not be treated as `words’ by `regexp-opt' in
`ess-r--find-fl-keyword'.")

(defvar ess-S-keywords
(append ess-R-keywords '("terminate")))

Expand Down Expand Up @@ -2088,7 +2097,8 @@ See also function `ess-create-object-name-db'.")
(defvar ess-R-function-name-regexp
(concat "\\(" "\\sw+" "\\)"
"[ \t]*" "\\(<-\\)"
"[ \t\n]*" "function\\b"))
"[ \t\n]*" "\\(function\\b\\|\\(\\\\[ \t\n(]+\\)\\)"))
;; "[ \t\n(]+" after "\\\\" since cannot use "\\b" to bound a non-word

(defvar ess-S-function-name-regexp
ess-R-function-name-regexp)
Expand Down
29 changes: 25 additions & 4 deletions lisp/ess-r-mode.el
Expand Up @@ -372,7 +372,7 @@ namespace.")
(ess-goto-char string-end)
(ess-looking-at "<-")
(ess-goto-char (match-end 0))
(ess-looking-at "function\\b" t)))
(ess-looking-at "function\\b\\|\\\\" t)))
font-lock-function-name-face)
((save-excursion
(and (cdr (assq 'ess-fl-keyword:fun-calls ess-R-font-lock-keywords))
Expand All @@ -394,23 +394,44 @@ namespace.")
font-lock-comment-face))

(defvar ess-r--non-fn-kwds
'("in" "else" "break" "next" "repeat"))
'("in" "else" "break" "next" "repeat")
"Reserved words that should not be treated as names of special
functions by `ess-r--find-fl-keyword'; such reserved words do
not need to be followed by an open parenthesis to trigger
font-locking.
See also `ess-r--non-fn-kstrs'.")

(defvar ess-r--non-fn-kstrs
'("\?")
"Reserved non-word strings that should not be treated as names of
special functions by `ess-r--find-fl-keyword'; such reserved
strings do not need to be followed by an open parenthesis to
trigger font-locking.
See also `ess-r--non-fn-kwds'.")

(defvar-local ess-r--keyword-regexp nil)
(defun ess-r--find-fl-keyword (limit)
"Search for R keyword and set the match data.
"Search for R keyword or keystring and set the match data.
To be used as part of `font-lock-defaults' keywords."
(unless ess-r--keyword-regexp
(let (fn-kwds non-fn-kwds)
(let (fn-kwds non-fn-kwds fn-kstrs non-fn-kstrs)
(dolist (kw ess-R-keywords)
(if (member kw ess-r--non-fn-kwds)
(push kw non-fn-kwds)
(push kw fn-kwds)))
(dolist (kw ess-r--keystrings)
(if (member kw ess-r--non-fn-kstrs)
(push kw non-fn-kstrs)
(push kw fn-kstrs)))
(setq ess-r--keyword-regexp
(concat "\\("
(regexp-opt non-fn-kwds 'words)
"\\|"
(regexp-opt non-fn-kstrs t)
"\\)\\|\\("
(regexp-opt fn-kwds 'words)
"\\|"
(regexp-opt fn-kstrs t)
"\\)"))))
(let (out)
(while (and (not out)
Expand Down
2 changes: 1 addition & 1 deletion lisp/ess-utils.el
Expand Up @@ -807,7 +807,7 @@ Copied almost verbatim from gnus-utils.el (but with test for mac added)."

"\\(" space "\\s<.*\\s>\\)*" ; whitespace, comment
;; FIXME: in principle we should skip 'definition *= *' here
space "function\\s-*(" ; whitespace, function keyword, parenthesis
space "\\(function\\|\\\\\\)\\s-*(" ; whitespace, function keyword, parenthesis
)))
`(,part-1 ,part-2))
"Partial regex for matching functions.
Expand Down
8 changes: 5 additions & 3 deletions test/ess-test-r-fontification.el
Expand Up @@ -57,6 +57,7 @@
¶while ¶for ¶if ¶switch ¶function ¶return ¶on.exit ¶stop
¶tryCatch ¶withRestarts ¶invokeRestart ¶recover ¶browser
¶message ¶warning ¶signalCondition ¶withCallingHandlers
\\
"
(should (not (face-at-point))))

Expand All @@ -66,7 +67,7 @@
¶while() ¶for() ¶if() ¶function()
¶switch() ¶return() ¶on.exit() ¶stop() ¶tryCatch()
¶withRestarts() ¶invokeRestart() ¶recover() ¶browser()
¶.Defunct()
¶.Defunct()\\(\\\\\\)()
"
(should (eq (face-at-point) 'ess-keyword-face))

Expand All @@ -75,7 +76,7 @@
while¶() for¶() if¶() function¶()
switch¶() return¶() on.exit¶() stop¶() tryCatch¶()
withRestarts¶() invokeRestart¶() recover¶() browser¶()
.Defunct¶()
.Defunct¶() \\(\\\\\\)¶()
"
(should (not (face-at-point)))

Expand All @@ -95,7 +96,7 @@ message¶() warning¶() signalCondition¶() withCallingHandlers¶()

(etest-deftest ess-test-r-fontification-keywords-simple-test ()
"Simple keywords are always fontified."
:case "¶else ¶break ¶next ¶repeat"
:case "¶else ¶break ¶next ¶repeat\?"
(should (eq (face-at-point) 'ess-keyword-face)))

(etest-deftest ess-test-r-fontification-keywords-in-test ()
Expand Down Expand Up @@ -154,6 +155,7 @@ message¶() warning¶() signalCondition¶() withCallingHandlers¶()
¶`[.foo` <- function(...) NULL
\"[.foo\" <- function(...) NULL
"

(should (eq (face-at-point) 'font-lock-function-name-face))

(with-ess-disabled-font-lock-keyword 'ess-R-fl-keyword:fun-defs
Expand Down

0 comments on commit 8030e29

Please sign in to comment.