0

Suppose I am trying to write a major mode. In this mode, certain ordinary characters should have special "electric" behavior when typed, but only in some contexts (e.g. in code but not in strings or comments). The definition of "some contexts" is very complicated, but it's neatly summarized by the syntax highlighting: if the character x, inserted at point, would be given a particular face by font-lock, then the characters shouldn't have electric behavior, otherwise they should.

So I'm imagining that I could somehow write

(defun mymode-maybe-electric-squiggle ()
  (interactive)
  (if (or (not mymode-electricity)      ; user option
          (memq (faces-of-hypothetically-inserted-character ?x)
             'mymode-literal-face))
      (call-interactively 'self-insert-command)
      ;; else do the electric thing
))

But I can't figure out how to write faces-of-hypothetically-inserted-character. The x shouldn't go on the undo list, and the buffer shouldn't visibly have an x in it for a moment while I give font-lock a chance to run, and how would I give font-lock a chance to run in the middle of a command, anyway?

(For purposes of this question, please assume that there isn't any other feasible way to make the decision.)

  • I'll recommend you don't rebind the key to a new command, but that you instead use post-self-insert-hook and put there a function which checks if x was just inserted (with the additional conditions you want). This will be more likely to correctly interact with other forms of electricity! – Stefan Sep 1 '18 at 3:29
  • @Stefan I'm not trying to make something special happen when x is inserted. I'm trying to make something special happen, upon insertion of several different characters none of which is x, when x would be given a particular face if it had been the character inserted. – zwol Sep 2 '18 at 21:18
  • Indeed, sorry: please replace x in my comment with ~ (or whatever is the "squigle" you're trying to electrify). I posted it as a comment because it is not an answer to your question, only a side remark about the definition you posted. – Stefan Sep 3 '18 at 13:02
  • @Stefan The trouble is that ~ (or whatever) has syntactic significance in itself and will probably be given a different face than the one x would be. – zwol Sep 3 '18 at 14:00
  • @Stefan However, thank you for telling me about post-self-insert-hook, I'm going to look at whether it will help. – zwol Sep 3 '18 at 14:01
3

I don't think "inserting x and seeing the resulting font-lock highlighting" is a good approach in general. You'll likely be better off calling asking directly the code that performs this font-lock highlighting (which should be able to make this decision directly).

But FWIW, here's how you could do it "by asking font-lock" like you suggest:

(add-hook 'post-self-insert-hook #'my-electric-squiggle nil t)

(defun my-electric-squiggle ()
  (when (and mymode-electricity
             (eq last-command-event ?~)
             (eq last-command-event (char-before))
             (not (memq (my-hypothetical-x-faces)
                        '(mymode-literal-face))))
    (do-some-thing)))

(defun my-hypothetical-x-faces ()
  (catch 'my-undo
    (atomic-change-group
      ;; Remove the squiggle that was just inserted
      (delete-region (1- (point)) (point))
      (insert "X")
      (font-lock-ensure (1- (point)) (point))
      (throw 'my-undo (get-text-property (1- (point)) 'face)))))

[ Note: 100% guaranteed untested. ]

Beware: Having to temporarily modify the buffer and then undo it is rather ugly and inefficient, and it may have undesired consequences.

  • 1
    Nice answer (I didn't know about atomic-change-group)! Just one thing: the face' property can be a single face or a list of faces (in addition to naked face attributes). This mean that a slightly more complex test than `memq' is needed. – Lindydancer Sep 3 '18 at 18:48
  • @Lindydancer Yeah, I tripped over that earlier myself. It seems like there should be a built-in function equivalent to (lambda (x) (if (listp x) x (list x)) but I can't find one... – zwol Sep 3 '18 at 21:37

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.