The colour goes on the language, and the program is left plain
This commit is contained in:
parent
d9bb882bbe
commit
612411021c
@ -203,7 +203,14 @@ reason and is the odd one — it is legal only as the last item of a `def' or a
|
||||
(1 font-lock-keyword-face)
|
||||
(2 font-lock-function-name-face nil t))
|
||||
(,(concat "(" (regexp-opt flan--special t) "\\_>") 1 font-lock-keyword-face)
|
||||
(,(concat "(" (regexp-opt flan--builtins t) "\\_>") 1 font-lock-builtin-face)
|
||||
;; The builtins are deliberately not drawn. clojure-mode leaves `map',
|
||||
;; `filter' and the rest of the core library plain and spends
|
||||
;; `font-lock-builtin-face' only on the earmuffed dynamic variables, and a
|
||||
;; buffer reads better for it: `at', `push' and `println' are the ordinary
|
||||
;; vocabulary of a program, and colouring the ordinary leaves nothing for
|
||||
;; the unusual. `flan--builtins' is still the list completion and the
|
||||
;; dynamic rules check against, so nothing else has to know this.
|
||||
;;
|
||||
;; Ahead of the qualified-name rule below, which would otherwise take
|
||||
;; `context/' in `context/allocator' for a package alias. It is not one:
|
||||
;; there is no package called `context', and the slash is part of the name.
|
||||
@ -213,12 +220,11 @@ reason and is the odd one — it is legal only as the last item of a `def' or a
|
||||
;; constant rather than as a string. `:where', the one key a `defn''s
|
||||
;; constraint map accepts, is covered by the same rule.
|
||||
("\\_<:\\(?:\\sw\\|\\s_\\)+" . font-lock-constant-face)
|
||||
;; A field. The label in a struct literal — `{.x 1.0}' — and the accessor
|
||||
;; `(.x v)' are the same name and are drawn the same way. Without this
|
||||
;; rule every field label in the corpus is unfontified, which is what the
|
||||
;; colon-to-dot change left behind: the keyword rule above used to cover
|
||||
;; them and no longer does, because the colon belongs to keywords now.
|
||||
("\\_<\\.\\(?:\\sw\\|\\s_\\)+" . font-lock-constant-face)
|
||||
;; A field — the label in `{.x 1.0}' and the accessor in `(.x v)' — is left
|
||||
;; plain, for the reason the builtins are: it is part of reading the
|
||||
;; program, not part of reading the language. clojure-mode draws Java
|
||||
;; interop the same way.
|
||||
;;
|
||||
;; The package half of a qualified name — the `rl/' of `rl/draw-text'.
|
||||
;; Drawn as a type the way clojure-mode draws a namespace, so the eye can
|
||||
;; split the package from the name without reading either. The leading
|
||||
@ -243,9 +249,10 @@ reason and is the odd one — it is legal only as the last item of a `def' or a
|
||||
. font-lock-type-face)
|
||||
;; A type variable, `$t', which is what a generic `defn' names its
|
||||
;; parameter types with and what `{:where (ordered? $t)}' constrains.
|
||||
("\\_<\\$\\(?:\\sw\\|\\s_\\)*" . font-lock-type-face)
|
||||
("\\_<\\(?:0x[0-9a-fA-F]+\\|-?[0-9]+\\(?:\\.[0-9]+\\)?\\)\\_>"
|
||||
. font-lock-constant-face))
|
||||
("\\_<\\$\\(?:\\sw\\|\\s_\\)*" . font-lock-type-face))
|
||||
;; A number is not drawn. clojure-mode has no rule for one either: a
|
||||
;; literal is already unmistakable by shape, and a colour on every integer
|
||||
;; in a program full of indices is noise.
|
||||
"Font lock for `flan-mode'.
|
||||
Every rule here is about the language itself, so a file that has never been
|
||||
near a running program is drawn completely. What a *particular program*
|
||||
|
||||
@ -1798,21 +1798,28 @@ other, and KIND is what tells it apart where that matters.")
|
||||
;; buffer when the cache is dropped, so a file opened with no session looks
|
||||
;; exactly as it did before any of this existed.
|
||||
|
||||
(defcustom flan-font-lock-dynamically t
|
||||
"Whether to colour names by what the running program says they are.
|
||||
(defcustom flan-font-lock-dynamically '(macro)
|
||||
"Which kinds of name to colour by what the running program says they are.
|
||||
|
||||
With a session connected, a macro the program defines is drawn as a macro, a
|
||||
function as a function, a global as a global and a struct, union, data type,
|
||||
enum or alias as a type. Names the program has never heard of are left
|
||||
alone, which is what makes a typo visible.
|
||||
A list of kinds drawn from `flan--dynamic-faces': `macro', `fn', `var',
|
||||
`const', `struct', `data', `union', `enum', `alias', `extern', `builtin'.
|
||||
t draws all of them and nil draws none.
|
||||
|
||||
The default is macros alone, which is the one kind a reader cannot work out
|
||||
from the call itself — a macro does not evaluate its arguments, so the shape
|
||||
of the call is its own and worth marking. A function drawn as a function
|
||||
says only that the name exists, which most names in a program do, and the
|
||||
buffer reads better for leaving them plain. cider-mode's default is the same
|
||||
shape and for the same reason.
|
||||
|
||||
This never changes how the language itself is drawn: `if' and `push' and
|
||||
`i64' keep the colours `flan-mode' gives them whatever the program defines.
|
||||
|
||||
Set to nil to leave every name to `flan-mode''s own rules. Nothing else
|
||||
changes — completion, eldoc and \\[xref-find-definitions] read the same
|
||||
answer and go on working."
|
||||
:type 'boolean
|
||||
Nothing else changes with this setting — completion, eldoc and
|
||||
\\[xref-find-definitions] read the same answer and go on working."
|
||||
:type '(choice (const :tag "Every kind" t)
|
||||
(const :tag "None" nil)
|
||||
(repeat :tag "These kinds" symbol))
|
||||
:group 'flan
|
||||
:set (lambda (sym val)
|
||||
(set-default sym val)
|
||||
@ -1871,6 +1878,14 @@ false where an undrawn one says only that this editor is older.")
|
||||
"Name to face, built from `flan--defs'.
|
||||
Rebuilt when the cache is, and read once per symbol per redisplay.")
|
||||
|
||||
(defun flan--dynamic-kind-wanted-p (kind)
|
||||
"Non-nil when KIND, a string from the `defs' op, is one to draw.
|
||||
The setting is written with symbols because that is what a user types; the
|
||||
wire answers with strings."
|
||||
(cond ((eq flan-font-lock-dynamically t) t)
|
||||
((null flan-font-lock-dynamically) nil)
|
||||
(t (and (memq (intern kind) flan-font-lock-dynamically) t))))
|
||||
|
||||
(defvar flan--dynamic-face nil
|
||||
"The face `flan--dynamic-match' found, read by the font-lock rule after it.")
|
||||
|
||||
@ -1884,7 +1899,8 @@ Rebuilt when the cache is, and read once per symbol per redisplay.")
|
||||
;; `draw'.
|
||||
(tails (make-hash-table :test #'equal)))
|
||||
(dolist (d flan--defs)
|
||||
(let ((face (cdr (assoc (nth 1 d) flan--dynamic-faces))))
|
||||
(let ((face (and (flan--dynamic-kind-wanted-p (nth 1 d))
|
||||
(cdr (assoc (nth 1 d) flan--dynamic-faces)))))
|
||||
(when face
|
||||
(puthash (car d) face tbl)
|
||||
(let ((slash (string-search "/" (car d))))
|
||||
@ -1934,7 +1950,12 @@ Leaves its face in `flan--dynamic-face' for the rule that calls this."
|
||||
|
||||
(defun flan--dynamic-wanted-p ()
|
||||
"Non-nil when there is something to draw and permission to draw it."
|
||||
(and flan-font-lock-dynamically flan--defs t))
|
||||
;; The table, not the setting: a setting naming only kinds this program has
|
||||
;; none of draws nothing, and installing a rule that can never match is a
|
||||
;; cost for no picture.
|
||||
(and flan-font-lock-dynamically flan--defs
|
||||
(> (hash-table-count flan--dynamic-table) 0)
|
||||
t))
|
||||
|
||||
(defun flan--dynamic-install ()
|
||||
"Add or remove the dynamic rules in the current buffer, and redraw it.
|
||||
|
||||
@ -329,8 +329,9 @@
|
||||
|
||||
;;; Font lock
|
||||
|
||||
;; The colon-to-dot change left every field label in the corpus unfontified:
|
||||
;; the keyword rule used to cover them, and the colon belongs to keywords now.
|
||||
;; What is drawn is the language; what is drawn plain is the program. A field
|
||||
;; label, a builtin and a number are all part of reading a program and none of
|
||||
;; them is coloured, which is how clojure-mode treats the same three.
|
||||
|
||||
(defun test-flan-mode--face-at (text needle)
|
||||
"The face on the first character of NEEDLE in TEXT, under `flan-mode'."
|
||||
@ -344,14 +345,22 @@
|
||||
|
||||
(message "\n-- font lock")
|
||||
|
||||
;; The keyword rule is what is left of the constants: an enum member resolves
|
||||
;; against a type and is worth marking.
|
||||
(test-flan--check "an enum member is a constant"
|
||||
(eq (test-flan-mode--face-at
|
||||
"(rl/mouse-button-down? :mouse-left)" ":mouse-left")
|
||||
'font-lock-constant-face))
|
||||
|
||||
(dolist (case '(("(rl/Vector2 {.x 1.0 .y 2.0})" ".x" "a struct literal's field label")
|
||||
("(rl/Vector2 {.x 1.0 .y 2.0})" ".y" "and the second one")
|
||||
("(set total (.bytes c))" ".bytes" "a field accessor")
|
||||
;; The keyword rule is still there: an enum member is one.
|
||||
("(rl/mouse-button-down? :mouse-left)" ":mouse-left" "an enum member")))
|
||||
(test-flan--check (format "%s is a constant" (nth 2 case))
|
||||
(eq (test-flan-mode--face-at (nth 0 case) (nth 1 case))
|
||||
'font-lock-constant-face)))
|
||||
("(push v 42)" "42" "a number")
|
||||
("(set mask 0xFF)" "0xFF" "a hex number")
|
||||
("(push v x)" "push" "a builtin")
|
||||
("(println (length xs))" "length" "and another")))
|
||||
(test-flan--check (format "%s is plain" (nth 2 case))
|
||||
(null (test-flan-mode--face-at (nth 0 case) (nth 1 case)))))
|
||||
|
||||
;; A dot inside a number is not a label, and neither is the dot in a name that
|
||||
;; has one in the middle.
|
||||
@ -407,15 +416,10 @@
|
||||
font-lock-keyword-face "array-fill")
|
||||
("(array-gen [n] f)" "array-gen"
|
||||
font-lock-keyword-face "array-gen")
|
||||
;; A builtin is a call, not a form the parser knows, and it is
|
||||
;; drawn as one.
|
||||
("(push v 1)" "push" font-lock-builtin-face "a builtin")
|
||||
("(slice xs 0 4)" "slice" font-lock-builtin-face "and another")
|
||||
("(bytes-view b)" "bytes-view" font-lock-builtin-face
|
||||
"bytes-view")
|
||||
("(length xs)" "length" font-lock-builtin-face "length")
|
||||
("(class-of x)" "class-of" font-lock-builtin-face "class-of")
|
||||
("(filled xs 0)" "filled" font-lock-builtin-face "filled")
|
||||
;; A builtin is a call and not a form the parser knows, and it
|
||||
;; is left plain — the rows for that are above, with the field
|
||||
;; labels and the numbers.
|
||||
;;
|
||||
;; Types.
|
||||
("(defn f [x dyn] dyn x)" "dyn" font-lock-type-face "dyn")
|
||||
("(def v (Vec u8))" "Vec" font-lock-type-face "Vec")
|
||||
@ -508,23 +512,47 @@
|
||||
|
||||
(message "\n-- font lock from the program")
|
||||
|
||||
(dolist (case '(("(settle 1 2)" "settle" flan-function-face "a function")
|
||||
("(set ticks 0)" "ticks" flan-global-face "a global")
|
||||
("(print gravity)" "gravity" flan-global-face "a constant")
|
||||
("(with-retry (go))" "with-retry" flan-macro-face "a macro")
|
||||
("(def p (Vec Pixel))" "Pixel" flan-type-face "a struct")
|
||||
("(defn f [s Shape] () 1)" "Shape" flan-type-face "a data type")
|
||||
("(def k Key)" "Key" flan-type-face "an enum")))
|
||||
(test-flan--check (format "%s the program defines is drawn as one" (nth 3 case))
|
||||
(eq (test-flan-mode--dyn-face (nth 0 case) (nth 1 case))
|
||||
(nth 2 case))))
|
||||
;; `flan-font-lock-dynamically' is a list of kinds and defaults to macros
|
||||
;; alone, so a macro is drawn and everything else the program defines is not.
|
||||
;; A macro is the one kind a reader cannot work out from the call, because its
|
||||
;; arguments are not evaluated; a function drawn as a function says only that
|
||||
;; the name exists. cider-mode's default is the same shape.
|
||||
(test-flan--check
|
||||
"a macro the program defines is drawn as one"
|
||||
(eq (test-flan-mode--dyn-face "(with-retry (go))" "with-retry")
|
||||
'flan-macro-face))
|
||||
|
||||
(dolist (case '(("(settle 1 2)" "settle" "a function")
|
||||
("(set ticks 0)" "ticks" "a global")
|
||||
("(print gravity)" "gravity" "a constant")
|
||||
("(def p (Vec Pixel))" "Pixel" "a struct")
|
||||
("(defn f [s Shape] () 1)" "Shape" "a data type")
|
||||
("(def k Key)" "Key" "an enum")))
|
||||
(test-flan--check
|
||||
(format "%s the program defines is left plain" (nth 2 case))
|
||||
(null (test-flan-mode--dyn-face (nth 0 case) (nth 1 case)))))
|
||||
|
||||
;; Asked for by name, the other kinds still draw: the default is a default and
|
||||
;; not a removal.
|
||||
(test-flan--check
|
||||
"a function is drawn when the setting asks for one"
|
||||
(let ((flan-font-lock-dynamically '(macro fn)))
|
||||
(eq (test-flan-mode--dyn-face "(settle 1 2)" "settle")
|
||||
'flan-function-face)))
|
||||
|
||||
(test-flan--check
|
||||
"and t still draws every kind"
|
||||
(let ((flan-font-lock-dynamically t))
|
||||
(eq (test-flan-mode--dyn-face "(set ticks 0)" "ticks")
|
||||
'flan-global-face)))
|
||||
|
||||
;; A macro and a function must not look alike — the whole point — so this is
|
||||
;; asserted as a difference and not only as two faces.
|
||||
(test-flan--check
|
||||
"a macro and a function are not drawn the same"
|
||||
(not (eq (test-flan-mode--dyn-face "(with-retry (go))" "with-retry")
|
||||
(test-flan-mode--dyn-face "(settle 1 2)" "settle"))))
|
||||
(let ((flan-font-lock-dynamically '(macro fn)))
|
||||
(not (eq (test-flan-mode--dyn-face "(with-retry (go))" "with-retry")
|
||||
(test-flan-mode--dyn-face "(settle 1 2)" "settle")))))
|
||||
|
||||
;; A macro is compiled, so it has a body to disassemble and to lower — and the
|
||||
;; lists that offer "a thing with a body" used to filter on kind `fn' alone.
|
||||
@ -546,8 +574,9 @@
|
||||
;; the program can speak for and the case half is left alone.
|
||||
(test-flan--check
|
||||
"a constructor's type half is drawn"
|
||||
(eq (test-flan-mode--dyn-face "(match s (Shape.Dot) 1)" "Shape")
|
||||
'flan-type-face))
|
||||
(let ((flan-font-lock-dynamically t))
|
||||
(eq (test-flan-mode--dyn-face "(match s (Shape.Dot) 1)" "Shape")
|
||||
'flan-type-face)))
|
||||
|
||||
(test-flan--check
|
||||
"and its case half is not"
|
||||
@ -561,28 +590,27 @@
|
||||
"and so is a kind this editor does not know"
|
||||
(null (test-flan-mode--dyn-face "(somenewthing)" "somenewthing")))
|
||||
|
||||
;; The static rules win, and they win in both directions: a program function
|
||||
;; called `length' keeps the builtin colour, and one called `if' keeps the
|
||||
;; keyword colour.
|
||||
;; The static rules win. `length' is not the test it once was — a builtin is
|
||||
;; drawn plain now, so both answers are plain and the row proves nothing — so
|
||||
;; the claim rests on `if', which the language draws and the program may not
|
||||
;; repaint. Asked for every kind, so the program's own rule is live.
|
||||
(test-flan--check
|
||||
"a program function named length is still drawn as the builtin"
|
||||
(eq (test-flan-mode--dyn-face "(length xs)" "length")
|
||||
'font-lock-builtin-face))
|
||||
|
||||
(test-flan--check
|
||||
"and one named if is still drawn as the special form"
|
||||
(eq (test-flan-mode--dyn-face "(if a 1 2)" "if") 'font-lock-keyword-face))
|
||||
"a program function named if is still drawn as the special form"
|
||||
(let ((flan-font-lock-dynamically t))
|
||||
(eq (test-flan-mode--dyn-face "(if a 1 2)" "if") 'font-lock-keyword-face)))
|
||||
|
||||
;; A buffer inside a package writes `step' for what the program calls
|
||||
;; `sim/step'. One package offers it, so it resolves — the rule `flan--lookup'
|
||||
;; already follows for eldoc and `M-.'.
|
||||
(test-flan--check
|
||||
"an unqualified name that one package offers is drawn"
|
||||
(eq (test-flan-mode--dyn-face "(step)" "step") 'flan-function-face))
|
||||
(let ((flan-font-lock-dynamically '(macro fn)))
|
||||
(eq (test-flan-mode--dyn-face "(step)" "step") 'flan-function-face)))
|
||||
|
||||
(test-flan--check
|
||||
"and one that two packages offer is not, because that would be a guess"
|
||||
(null (test-flan-mode--dyn-face "(draw)" "draw")))
|
||||
(let ((flan-font-lock-dynamically '(macro fn)))
|
||||
(null (test-flan-mode--dyn-face "(draw)" "draw"))))
|
||||
|
||||
;; With no session there is nothing to say, and the buffer has to be drawn
|
||||
;; exactly as `flan-mode' alone draws it.
|
||||
@ -612,7 +640,10 @@
|
||||
(push (get-text-property (1+ i) 'face) acc))
|
||||
(nreverse acc)))))
|
||||
|
||||
(let ((text "(defn settle [row i32] ()\n (let [vel gravity]\n (set ticks vel)))\n"))
|
||||
;; The text carries a macro call, because the default draws macros and nothing
|
||||
;; else: a body of plain functions and globals is the same picture either way
|
||||
;; and the last of the three claims below would be asserting nothing.
|
||||
(let ((text "(defn settle [row i32] ()\n (let [vel gravity]\n (with-retry (set ticks vel))))\n"))
|
||||
(test-flan--check
|
||||
"a buffer with no session is drawn exactly as the mode alone draws it"
|
||||
(equal (test-flan-mode--faces text 'none)
|
||||
|
||||
@ -504,19 +504,24 @@ already rely on it — so nothing here is a stand-in for the real thing."
|
||||
;; And the buffer is redrawn for it. No refresh command is run here on
|
||||
;; purpose: the claim is that evaluating is enough.
|
||||
(font-lock-ensure)
|
||||
;; Asked for every kind, because the default draws macros alone and this
|
||||
;; claim is about the redraw and not about which kinds are wanted.
|
||||
(test-flan--check
|
||||
"a name installed now is drawn now, with nothing else asked for"
|
||||
(save-excursion
|
||||
(goto-char (point-max))
|
||||
(insert "\n(freshly-added)\n")
|
||||
(font-lock-ensure)
|
||||
(prog1 (eq (get-text-property (- (point) (length "(freshly-added)\n") -1)
|
||||
'face)
|
||||
'flan-function-face)
|
||||
(delete-region (- (point) (length "\n(freshly-added)\n")) (point)))))
|
||||
(let ((flan-font-lock-dynamically t))
|
||||
(flan--dynamic-sync)
|
||||
(save-excursion
|
||||
(goto-char (point-max))
|
||||
(insert "\n(freshly-added)\n")
|
||||
(font-lock-ensure)
|
||||
(prog1 (eq (get-text-property (- (point) (length "(freshly-added)\n") -1)
|
||||
'face)
|
||||
'flan-function-face)
|
||||
(delete-region (- (point) (length "\n(freshly-added)\n")) (point))))))
|
||||
(flan--dynamic-sync)
|
||||
|
||||
(test-flan--check
|
||||
"and a macro is not drawn like a function"
|
||||
"and a macro is drawn, which the default asks for on its own"
|
||||
(save-excursion
|
||||
(goto-char (point-max))
|
||||
(insert "\n(twice 1)\n")
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user