From 949f8273ffd87f0aeb0329e1db8e5d876c99dacb Mon Sep 17 00:00:00 2001 From: Konstantin Kharlamov Date: Sun, 4 May 2025 16:34:11 +0700 Subject: [PATCH 1/4] font-lock: highlight operators with "builtin" face What face is the most appropriate is arguable. Emacs 29.1 has "operator-face", but it's unavailable for older Emacs, and even for newer versions it's just "nil", so no highlight. Kinda pointless face. The current "variable-name" is definitely wrong though, because operators are far from being variables. And there's a separate "color-identifiers-mode" which uses this face to highlight similar variables, and that would work wrong for purescript-mode. Thus, replace the highlight to "builtin". --- purescript-font-lock.el | 2 +- tests/purescript-font-lock-tests.el | 116 ++++++++++++++-------------- 2 files changed, 59 insertions(+), 59 deletions(-) diff --git a/purescript-font-lock.el b/purescript-font-lock.el index 53fd8c9..7816984 100644 --- a/purescript-font-lock.el +++ b/purescript-font-lock.el @@ -128,7 +128,7 @@ ;; This is probably just wrong, but it used to use ;; `font-lock-function-name-face' with a result that was not consistent with ;; other major modes, so I just exchanged with `purescript-definition-face'. -(defvar purescript-operator-face 'font-lock-variable-name-face) +(defvar purescript-operator-face 'font-lock-builtin-face) (defvar purescript-default-face nil) (defvar purescript-literate-comment-face 'font-lock-doc-face "Face with which to fontify literate comments. diff --git a/tests/purescript-font-lock-tests.el b/tests/purescript-font-lock-tests.el index 1fd994d..b9a1064 100644 --- a/tests/purescript-font-lock-tests.el +++ b/tests/purescript-font-lock-tests.el @@ -55,13 +55,13 @@ import Data.Either (Either(..)) (53 58 font-lock-keyword-face) (60 70 font-lock-type-face) (73 78 font-lock-type-face) - (80 81 font-lock-variable-name-face)))) + (80 81 font-lock-builtin-face)))) (ert-deftest string () (purescript-test-ranges "foo = \"hello\"" '((1 3 font-lock-function-name-face) - (5 5 font-lock-variable-name-face) + (5 5 font-lock-builtin-face) (7 13 font-lock-string-face)))) (ert-deftest multiline-string () @@ -71,7 +71,7 @@ hello \"\"\" " '((1 3 font-lock-function-name-face) - (5 5 font-lock-variable-name-face) + (5 5 font-lock-builtin-face) (7 19 font-lock-string-face)))) (ert-deftest multiline-string-with-hash () @@ -85,7 +85,7 @@ hello \"\"\" " '((1 3 font-lock-function-name-face) - (5 5 font-lock-variable-name-face) + (5 5 font-lock-builtin-face) (7 114 font-lock-string-face)))) (ert-deftest multiline-string-with-embedded-strings () @@ -96,7 +96,7 @@ this = \"still a string\" \"\"\" " '((1 3 font-lock-function-name-face) - (5 5 font-lock-variable-name-face) + (5 5 font-lock-builtin-face) (7 37 font-lock-string-face)))) (ert-deftest docs-bar-comment-different-spacings () @@ -163,11 +163,11 @@ noncomment (5 5 nil) (6 14 font-lock-type-face) (15 21 nil) - (22 22 font-lock-variable-name-face) + (22 22 font-lock-builtin-face) (23 23 nil) (24 29 font-lock-type-face) (30 37 nil) - (38 39 font-lock-variable-name-face) + (38 39 font-lock-builtin-face) (40 40 nil) (41 43 font-lock-type-face) (44 45 nil)))) @@ -205,47 +205,47 @@ mkMyComponent = do " '((1 13 font-lock-function-name-face) (14 14 nil) - (15 16 font-lock-variable-name-face) + (15 16 font-lock-builtin-face) (17 17 nil) (18 26 font-lock-type-face) (27 30 nil) (31 43 font-lock-function-name-face) (44 44 nil) - (45 45 font-lock-variable-name-face) + (45 45 font-lock-builtin-face) (46 46 nil) (47 48 font-lock-keyword-face) (49 61 nil) - (62 63 font-lock-variable-name-face) + (62 63 font-lock-builtin-face) (64 65 nil) (66 72 font-lock-type-face) (73 73 nil) - (74 75 font-lock-variable-name-face) + (74 75 font-lock-builtin-face) (76 76 nil) (77 79 font-lock-type-face) (80 81 nil) - (82 83 font-lock-variable-name-face) + (82 83 font-lock-builtin-face) (84 104 nil) (105 119 font-lock-string-face) (120 120 nil) - (121 121 font-lock-variable-name-face) + (121 121 font-lock-builtin-face) (122 122 font-lock-keyword-face) (123 123 nil) - (124 125 font-lock-variable-name-face) + (124 125 font-lock-builtin-face) (126 126 nil) (127 131 font-lock-type-face) - (132 132 font-lock-variable-name-face) + (132 132 font-lock-builtin-face) (133 134 font-lock-keyword-face) (135 149 nil) - (150 151 font-lock-variable-name-face) + (150 151 font-lock-builtin-face) (152 152 nil) (153 159 font-lock-type-face) (160 160 nil) - (161 162 font-lock-variable-name-face) + (161 162 font-lock-builtin-face) (163 181 nil) - (182 182 font-lock-variable-name-face) + (182 182 font-lock-builtin-face) (183 183 nil) (184 184 font-lock-type-face) - (185 185 font-lock-variable-name-face) + (185 185 font-lock-builtin-face) (186 192 nil) (193 194 font-lock-type-face) (195 195 nil)))) @@ -264,24 +264,24 @@ instance semigroupNonEmptyList :: Semigroup (NonEmptyList a) where derive newtype instance foldableNonEmptyList :: Foldable NonEmptyList " '((1 8 font-lock-keyword-face) (9 28 nil) - (29 30 font-lock-variable-name-face) (31 31 nil) + (29 30 font-lock-builtin-face) (31 31 nil) (32 37 font-lock-type-face) (38 38 nil) (39 50 font-lock-type-face) (51 51 nil) (52 56 font-lock-keyword-face) (57 69 nil) - (70 70 font-lock-variable-name-face) (71 71 nil) + (70 70 font-lock-builtin-face) (71 71 nil) (72 83 font-lock-type-face) (84 85 nil) (86 86 font-lock-keyword-face) (87 87 nil) (88 89 font-lock-type-face) (90 95 nil) - (96 96 font-lock-variable-name-face) (97 101 nil) + (96 96 font-lock-builtin-face) (97 101 nil) (102 113 font-lock-type-face) (114 119 nil) (120 121 font-lock-type-face) (122 137 nil) (138 138 font-lock-type-face) (139 139 nil) (140 142 font-lock-type-face) (143 147 nil) (148 148 font-lock-type-face) (149 149 nil) (150 152 font-lock-type-face) (153 158 nil) - (159 159 font-lock-variable-name-face) (160 168 nil) + (159 159 font-lock-builtin-face) (160 168 nil) (169 173 font-lock-keyword-face) (174 196 nil) - (197 197 font-lock-variable-name-face) (198 203 nil) + (197 197 font-lock-builtin-face) (198 203 nil) (204 204 font-lock-type-face) (205 208 nil) (209 220 font-lock-type-face) (221 224 nil) (225 226 font-lock-type-face) (227 233 nil) @@ -289,18 +289,18 @@ derive newtype instance foldableNonEmptyList :: Foldable NonEmptyList (244 244 font-lock-type-face) (245 247 nil) (248 248 font-lock-type-face) (249 255 nil) (256 263 font-lock-keyword-face) (264 286 nil) - (287 288 font-lock-variable-name-face) (289 289 nil) + (287 288 font-lock-builtin-face) (289 289 nil) (290 298 font-lock-type-face) (299 300 nil) (301 312 font-lock-type-face) (313 316 nil) (317 321 font-lock-keyword-face) (322 332 nil) (333 344 font-lock-type-face) (345 348 nil) (349 350 font-lock-type-face) (351 360 nil) - (361 361 font-lock-variable-name-face) (362 366 nil) + (361 361 font-lock-builtin-face) (362 366 nil) (367 378 font-lock-type-face) (379 382 nil) (383 384 font-lock-type-face) (385 388 nil) - (389 390 font-lock-variable-name-face) (391 403 nil) + (389 390 font-lock-builtin-face) (391 403 nil) (404 409 font-lock-keyword-face) (410 448 nil) - (449 450 font-lock-variable-name-face) (451 451 nil) + (449 450 font-lock-builtin-face) (451 451 nil) (452 459 font-lock-type-face) (460 460 nil) (461 472 font-lock-type-face) (473 473 nil)))) @@ -319,7 +319,7 @@ foreign importinvalid (8 8 nil) (9 14 font-lock-keyword-face) (15 21 nil) - (22 23 font-lock-variable-name-face) + (22 23 font-lock-builtin-face) (24 24 nil) (25 30 font-lock-type-face) (31 31 nil) @@ -329,7 +329,7 @@ foreign importinvalid (43 43 nil) (44 49 font-lock-keyword-face) (50 58 nil) - (59 60 font-lock-variable-name-face) + (59 60 font-lock-builtin-face) (61 61 nil) (62 67 font-lock-type-face) (68 68 nil) @@ -339,7 +339,7 @@ foreign importinvalid (80 80 nil) (81 86 font-lock-keyword-face) (87 95 nil) - (96 97 font-lock-variable-name-face) + (96 97 font-lock-builtin-face) (98 98 nil) (99 104 font-lock-type-face) (105 105 nil) @@ -398,105 +398,105 @@ arr = 1 : [2,3] (24 25 nil) (26 30 font-lock-type-face) (31 31 nil) - (32 33 font-lock-variable-name-face) + (32 33 font-lock-builtin-face) (34 37 nil) (38 40 font-lock-comment-delimiter-face) (41 50 font-lock-comment-face) (51 52 font-lock-function-name-face) (53 53 nil) - (54 55 font-lock-variable-name-face) + (54 55 font-lock-builtin-face) (56 58 nil) - (59 60 font-lock-variable-name-face) + (59 60 font-lock-builtin-face) (61 63 nil) (64 65 font-lock-function-name-face) (66 66 nil) - (67 67 font-lock-variable-name-face) + (67 67 font-lock-builtin-face) (68 68 nil) - (69 69 font-lock-variable-name-face) + (69 69 font-lock-builtin-face) (70 71 nil) - (72 73 font-lock-variable-name-face) + (72 73 font-lock-builtin-face) (74 77 nil) (78 80 font-lock-comment-delimiter-face) (81 83 font-lock-comment-face) (84 87 font-lock-function-name-face) (88 88 nil) - (89 89 font-lock-variable-name-face) + (89 89 font-lock-builtin-face) (90 90 nil) (91 92 font-lock-keyword-face) (93 97 nil) - (98 99 font-lock-variable-name-face) + (98 99 font-lock-builtin-face) (100 118 nil) (119 121 font-lock-comment-delimiter-face) (122 130 font-lock-comment-face) (131 137 font-lock-function-name-face) (138 138 nil) - (139 140 font-lock-variable-name-face) + (139 140 font-lock-builtin-face) (141 141 nil) (142 145 font-lock-type-face) (146 148 nil) - (149 150 font-lock-variable-name-face) + (149 150 font-lock-builtin-face) (151 153 nil) - (154 155 font-lock-variable-name-face) + (154 155 font-lock-builtin-face) (156 156 nil) (157 162 font-lock-type-face) (163 163 nil) (164 170 font-lock-function-name-face) (171 171 nil) - (172 172 font-lock-variable-name-face) + (172 172 font-lock-builtin-face) (173 179 nil) (180 182 font-lock-comment-delimiter-face) (183 184 font-lock-comment-face) (185 193 font-lock-function-name-face) (194 195 nil) - (196 196 font-lock-variable-name-face) + (196 196 font-lock-builtin-face) (197 197 nil) (198 201 font-lock-type-face) (202 202 nil) (203 203 font-lock-keyword-face) (204 205 nil) - (206 206 font-lock-variable-name-face) + (206 206 font-lock-builtin-face) (207 210 nil) (211 213 font-lock-comment-delimiter-face) (214 221 font-lock-comment-face) (222 225 font-lock-function-name-face) (226 228 nil) - (229 229 font-lock-variable-name-face) + (229 229 font-lock-builtin-face) (230 232 nil) - (233 233 font-lock-variable-name-face) + (233 233 font-lock-builtin-face) (234 236 nil) - (237 237 font-lock-variable-name-face) + (237 237 font-lock-builtin-face) (238 238 nil) - (239 239 font-lock-variable-name-face) + (239 239 font-lock-builtin-face) (240 248 nil) - (249 249 font-lock-variable-name-face) + (249 249 font-lock-builtin-face) (250 260 nil) - (261 261 font-lock-variable-name-face) + (261 261 font-lock-builtin-face) (262 265 nil) (266 268 font-lock-comment-delimiter-face) (269 284 font-lock-comment-face) (285 296 font-lock-function-name-face) (297 297 nil) - (298 298 font-lock-variable-name-face) + (298 298 font-lock-builtin-face) (299 307 nil) - (308 308 font-lock-variable-name-face) + (308 308 font-lock-builtin-face) (309 309 nil) (310 319 font-lock-type-face) (320 322 nil) - (323 324 font-lock-variable-name-face) + (323 324 font-lock-builtin-face) (325 325 nil) (326 329 font-lock-type-face) (330 330 nil) - (331 332 font-lock-variable-name-face) + (331 332 font-lock-builtin-face) (333 335 nil) (336 347 font-lock-function-name-face) (348 348 nil) - (349 349 font-lock-variable-name-face) + (349 349 font-lock-builtin-face) (350 361 nil) (362 364 font-lock-comment-delimiter-face) (365 381 font-lock-comment-face) (382 384 font-lock-function-name-face) (385 385 nil) - (386 387 font-lock-variable-name-face) + (386 387 font-lock-builtin-face) (388 388 nil) (389 393 font-lock-type-face) (394 394 nil) @@ -504,7 +504,7 @@ arr = 1 : [2,3] (398 398 nil) (399 401 font-lock-function-name-face) (402 402 nil) - (403 403 font-lock-variable-name-face) + (403 403 font-lock-builtin-face) (404 406 nil) (407 407 font-lock-type-face) (408 414 nil)))) From 49e0faeca49dad987998080a743266af69ab4f6b Mon Sep 17 00:00:00 2001 From: Konstantin Kharlamov Date: Sun, 4 May 2025 15:21:47 +0700 Subject: [PATCH 2/4] font-lock: cover more operators than just the builtins For example, the tuple construction `1 /\ 2` was weirdly half-haighlighted before this commit, because it wasn't covered by the "operators regexp" and was getting caught by some other rule. Fix that. For inspiration, I looked at the regexp from the official Atom purescript highlight https://github.com/purescript-contrib/atom-language-purescript/blob/d17eee55b12c140e8a1a750cce7e5aa9b09653c2/src/purescript.coffee#L32 that one works a bit differently, it excludes chars from class. AFAIK Emacs can't do that, so instead I just enlisted the symbols. One thing I did differently though is including colon, because it is a valid operator in PureScript (for List), I think Atom has a bug in their regexp. --- purescript-font-lock.el | 10 ++++---- tests/purescript-font-lock-tests.el | 36 +++++++++++++++++++---------- 2 files changed, 28 insertions(+), 18 deletions(-) diff --git a/purescript-font-lock.el b/purescript-font-lock.el index 7816984..8c76689 100644 --- a/purescript-font-lock.el +++ b/purescript-font-lock.el @@ -165,12 +165,10 @@ Returns keywords suitable for `font-lock-keywords'." ;; be thrown for some reason by backslash's escape syntax. "\\(\\s_\\|\\\\\\)+") - ;; Reserved operations - (reservedsym + (operator (concat "\\S_" - ;; (regexp-opt '(".." "::" "=" "\\" "|" "<-" "->" - ;; "@" "~" "=>") t) - "\\(->\\|\\.\\.\\|::\\|∷\\|<-\\|=>\\|[=@\\|~]\\)" + ;; All punctuation, excluding (),;[]{}_"'` + "\\([!@#$%^&*+\\-./<=>?@|~:∷\\\\]+\\)" "\\S_")) ;; These are only keywords when appear at top-level, optionally with ;; indentation. They are not reserved and in other levels would represent @@ -210,7 +208,7 @@ Returns keywords suitable for `font-lock-keywords'." ;; (,toplevel-keywords 1 (symbol-value 'purescript-keyword-face)) (,reservedid 1 (symbol-value 'purescript-keyword-face)) - (,reservedsym 1 (symbol-value 'purescript-operator-face)) + (,operator 1 (symbol-value 'purescript-operator-face)) ;; Special case for `as', `hiding', `safe' and `qualified', which are ;; keywords in import statements but are not otherwise reserved. ("\\\\)[ \t]*\\)?\\(?:\\(qualified\\>\\)[ \t]*\\)?[^ \t\n()]+[ \t]*\\(?:\\(\\\\)[ \t]*[^ \t\n()]+[ \t]*\\)?\\(\\\\)?" diff --git a/tests/purescript-font-lock-tests.el b/tests/purescript-font-lock-tests.el index b9a1064..db20cdb 100644 --- a/tests/purescript-font-lock-tests.el +++ b/tests/purescript-font-lock-tests.el @@ -271,33 +271,33 @@ derive newtype instance foldableNonEmptyList :: Foldable NonEmptyList (70 70 font-lock-builtin-face) (71 71 nil) (72 83 font-lock-type-face) (84 85 nil) (86 86 font-lock-keyword-face) (87 87 nil) - (88 89 font-lock-type-face) (90 95 nil) + (88 89 font-lock-builtin-face) (90 95 nil) (96 96 font-lock-builtin-face) (97 101 nil) (102 113 font-lock-type-face) (114 119 nil) - (120 121 font-lock-type-face) (122 137 nil) - (138 138 font-lock-type-face) (139 139 nil) + (120 121 font-lock-builtin-face) (122 137 nil) + (138 138 font-lock-builtin-face) (139 139 nil) (140 142 font-lock-type-face) (143 147 nil) - (148 148 font-lock-type-face) (149 149 nil) + (148 148 font-lock-builtin-face) (149 149 nil) (150 152 font-lock-type-face) (153 158 nil) (159 159 font-lock-builtin-face) (160 168 nil) (169 173 font-lock-keyword-face) (174 196 nil) (197 197 font-lock-builtin-face) (198 203 nil) - (204 204 font-lock-type-face) (205 208 nil) + (204 204 font-lock-builtin-face) (205 208 nil) (209 220 font-lock-type-face) (221 224 nil) - (225 226 font-lock-type-face) (227 233 nil) - (234 234 font-lock-type-face) (235 243 nil) - (244 244 font-lock-type-face) (245 247 nil) - (248 248 font-lock-type-face) (249 255 nil) + (225 226 font-lock-builtin-face) (227 233 nil) + (234 234 font-lock-builtin-face) (235 243 nil) + (244 244 font-lock-builtin-face) (245 247 nil) + (248 248 font-lock-builtin-face) (249 255 nil) (256 263 font-lock-keyword-face) (264 286 nil) (287 288 font-lock-builtin-face) (289 289 nil) (290 298 font-lock-type-face) (299 300 nil) (301 312 font-lock-type-face) (313 316 nil) (317 321 font-lock-keyword-face) (322 332 nil) (333 344 font-lock-type-face) (345 348 nil) - (349 350 font-lock-type-face) (351 360 nil) + (349 350 font-lock-builtin-face) (351 360 nil) (361 361 font-lock-builtin-face) (362 366 nil) (367 378 font-lock-type-face) (379 382 nil) - (383 384 font-lock-type-face) (385 388 nil) + (383 384 font-lock-builtin-face) (385 388 nil) (389 390 font-lock-builtin-face) (391 403 nil) (404 409 font-lock-keyword-face) (410 448 nil) (449 450 font-lock-builtin-face) (451 451 nil) @@ -506,5 +506,17 @@ arr = 1 : [2,3] (402 402 nil) (403 403 font-lock-builtin-face) (404 406 nil) - (407 407 font-lock-type-face) + (407 407 font-lock-builtin-face) (408 414 nil)))) + +(ert-deftest tuple-and-cap-highlighted () + (purescript-test-ranges + "myTuple = 1 /\\ (2 ^ 3)" + '((1 7 font-lock-function-name-face) + (8 8 nil) + (9 9 font-lock-builtin-face) + (10 12 nil) + (13 14 font-lock-builtin-face) + (15 18 nil) + (19 19 font-lock-builtin-face) + (20 23 nil)))) From f52227bfb8de304f353fd124baef8c0563f7d3c0 Mon Sep 17 00:00:00 2001 From: Konstantin Kharlamov Date: Mon, 5 May 2025 11:24:31 +0700 Subject: [PATCH 3/4] font-lock: remove useless rule for (,) and (->) The (,) and (->) don't have much use in PureScript, nor in Haskell for that matter. In Haskell (,) was a prefix notation for tuple construction, but in PureScript this operator isn't a thing. Both trigger "syntax error" in PureScript (and (->) doesn't work even in Haskell). Indeed a debatable rule. Let's just remove it. --- purescript-font-lock.el | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/purescript-font-lock.el b/purescript-font-lock.el index 8c76689..9d2606a 100644 --- a/purescript-font-lock.el +++ b/purescript-font-lock.el @@ -230,8 +230,7 @@ Returns keywords suitable for `font-lock-keywords'." (,topdecl-sym (2 (symbol-value 'purescript-definition-face))) (,topdecl-sym2 (1 (symbol-value 'purescript-definition-face))) - ;; These four are debatable... - ("(\\(,*\\|->\\))" 0 (symbol-value 'purescript-constructor-face)) + ;; This one is debatable… ("\\[\\]" 0 (symbol-value 'purescript-constructor-face)) ;; Expensive. (,qvarid 0 (symbol-value 'purescript-default-face)) From 1a0e9398e4b8a42a4fec7464dfe6fb1bc5346480 Mon Sep 17 00:00:00 2001 From: Konstantin Kharlamov Date: Mon, 5 May 2025 22:16:24 +0700 Subject: [PATCH 4/4] font-lock: remove unused escaping from backtick-operator rule Backticks don't need to be escaped. --- purescript-font-lock.el | 2 +- tests/purescript-font-lock-tests.el | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/purescript-font-lock.el b/purescript-font-lock.el index 9d2606a..c024968 100644 --- a/purescript-font-lock.el +++ b/purescript-font-lock.el @@ -235,7 +235,7 @@ Returns keywords suitable for `font-lock-keywords'." ;; Expensive. (,qvarid 0 (symbol-value 'purescript-default-face)) (,qconid 0 (symbol-value 'purescript-constructor-face)) - (,(concat "\`" varid "\`") 0 (symbol-value 'purescript-operator-face)) + (,(concat "`" varid "`") 0 (symbol-value 'purescript-operator-face)) ;; Expensive. (,conid 0 (symbol-value 'purescript-constructor-face)) diff --git a/tests/purescript-font-lock-tests.el b/tests/purescript-font-lock-tests.el index db20cdb..b745c4c 100644 --- a/tests/purescript-font-lock-tests.el +++ b/tests/purescript-font-lock-tests.el @@ -520,3 +520,13 @@ arr = 1 : [2,3] (15 18 nil) (19 19 font-lock-builtin-face) (20 23 nil)))) + +(ert-deftest backtick-operator () + (purescript-test-ranges + "var = 1 `func` 2" + '((1 3 font-lock-function-name-face) + (4 4 nil) + (5 5 font-lock-builtin-face) + (6 8 nil) + (9 14 font-lock-builtin-face) + (15 17 nil))))