Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Extended support for function shorthand notation #1282

Merged
merged 8 commits into from Apr 10, 2024
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