Search code examples
emacselisp

Emacs -- How to create a vertical strike-through effect


I'm looking for some suggestions, please, of how to create the visual effect of a vertical strike-through (with the character on top of the vertical bar ("\u007C") still being visible). If layering is possible, then the vertical bar should be underneath so that the letter is mostly visible.

I searched for a method of superimposing characters, layering overlays, and I looked at some xpm images, however, I have not found anything remotely close to what I'm looking for. The goal is to have a true cross-hairs effect similar to the image below -- the gray background would be replaced with a yellow vertical strike-through so that the letters would still be visible.

Example

Example


Solution

  • Feature Request #17684 (crosshairs) [ https://debbugs.gnu.org/cgi/bugreport.cgi?bug=17684 ] and Feature Request 22873 (multiple fake cursors) [ https://debbugs.gnu.org/cgi/bugreport.cgi?bug=22873 ] are a work in progress. Although it is unknown whether these features will ever be incorporated into the official Emacs, there is a working draft proof concept that has been posted to both of the above-mentioned feature requests. The features require modifications to both the C and Lisp internals prior to building a GUI version of Emacs from the master branch -- X11, Windows or NS.


    EXAMPLE 1 of 3:

    The following code snippet works with some fonts on some operating systmes -- e.g., it works out of the box on Windows; however, it does not work for this author on OSX 10.6.8 Snow Leopard or Snow Leopard Server 10.6.8. [See bug report number 20537 -- https://debbugs.gnu.org/cgi/bugreport.cgi?bug=20537 -- even though the bug report has been officially closed by the Emacs development team, this author has never been able to get that feature working on Snow Leopard subsequent to the "bug fix". :( ] The user may wish to experiment with the glyph reference points, and the variable reference-point-alist in composite.el has a detailed doc-string in relation thereto. The user may also wish to experiment with using an after-string or before-string display property. The library vline.el at http://www.emacswiki.org/emacs/vline.el has a variable named vline-style, which can be set to 'face, 'compose, or 'mixed -- the latter two settings cause vline to use a slight variation of this code snippet [see lines 362 to 370 of said library].

    (let* (
        (point-min (point-min))
        (point-max (point-max))
        (pt (point))
        (pt+1 (1+ pt))
        (char (char-after pt))
        (line-char (string-to-char "|"))
        (str (when char (compose-chars char '(tc . tc) line-char))) ) 
      (overlay-put (make-overlay pt pt+1) 'display str)
      (sit-for 2)
      (remove-overlays point-min point-max 'display str))
    

    EXAMPLE 2 of 3:

    The following code snippet uses a unicode character known as a zero-with space (i.e., uFEFF). Because a space cannot receive a foreground color, a background color is used to give the zero-width space the apparance of a thin vertical line that stretches the entire line height. As used in the context of this code snippet, the zero-width space has a width of 1 pixel and the concatenated charcter is reduced in size so that the two combined characters have a width equal to the frame-char-width. Testing of this snippet was done on an OSX operating system using the default font of -*-Monaco-normal-normal-normal-*-12-*-*-*-m-0-iso10646-1. The cursor is removed for a short duration during this example so that the overlay can be seen more clearly.

    (let* (
        (point-min (point-min))
        (point-max (point-max))
        (pt (point))
        (pt+1 (1+ pt))
        (char (char-after pt))
        (char-str (when char (char-to-string char)))
        (ln-char-str (char-to-string ?\uFEFF))
        (str (when char
          (concat
            (propertize ln-char-str 'face '(:background "red"))
            (propertize char-str 'face '(:height 100))))) )
      (internal-show-cursor nil nil)
      (overlay-put (make-overlay pt pt+1) 'display str)
      (sit-for 2)
      (internal-show-cursor nil t)
      (remove-overlays point-min point-max 'display str))
    

    EXAMPLE 3 of 3:

    The following is an example using an overlay with an xpm image for graphical Emacs versions that support xpm image format. It is 11 pixels wide; 20 pixels high; and has 4 preselected colors. I am on a Mac running Snow Leopard 10.6.8 and the font I prefer when using Emacs is -*-Courier-normal-normal-normal-*-18-*-*-*-m-0-iso10646-1 -- the frame-char-width is 11 and the frame-char-height is 20. I have added a thin vertical yellow line to the left of the capital letter "A" as an example of how to draw custom images. Substitution of the character at point can be made programmatically using (char-after (point)) and taking that number -- which in this case is 65 for the capital letter "A" -- and substituting the appropriate variable -- e.g., (cond ((eq (char-after (point)) 65) cap-ltr-a-xpm) . . . -- and using that variable in the the overlay placement -- e.g., (overlay-put (make-overlay (point) (1+ (point))) 'display cap-ltr-a-xpm). This works very nicely for both truncated buffers and also with word-wrap because the display overlay property on a character in the middle of a word does not cause word-wrap to think that the first part of the word belongs at the end of the previous line. Naturally, it will take time to create a custom library of favorite xpm images. The cursor is removed for a short duration during this example so that the overlay can be seen more clearly.

    ImageMagick is capable of producing a semi-accurate xpm of a particular character based on a specific font family and size, but it was not as precise as I had hoped -- here is a link to instructions for using that external utility: https://stackoverflow.com/a/14168154/2112489 In a nutshell, the user should be prepared to spend time customizing the xpm images to his / her liking.

    (let* (
        (pt (point))
        (pt+1 (1+ pt))
        (point-min (point-min))
        (point-max (point-max))
        (cap-ltr-a-xpm `(image :type xpm :mask nil :ascent center :data
          "/* XPM */
          static char * letters_xpm[] = {
          /* columns rows colors chars-per-pixel */
          /* columns = 1 pixel in width -- see also (frame-char-width) */
          /* rows = 1 pixel in height -- see also (frame-char-height) */
          \"11 20 4 1\",
          \". c #000000\",
          \"+ c #FF0000\",
          \"@ c #7F0000\",
          \"% c yellow\",
          \"%..........\",
          \"%....++....\",
          \"%....++....\",
          \"%..++..++..\",
          \"%..++..++..\",
          \"%++......++\",
          \"%++......++\",
          \"%++......++\",
          \"%++......++\",
          \"%++......++\",
          \"%++......++\",
          \"%++++++++++\",
          \"%++++++++++\",
          \"%++......++\",
          \"%++......++\",
          \"%++......++\",
          \"%++......++\",
          \"%++......++\",
          \"%++......++\",
          \"%..........\"};"))  )
      (internal-show-cursor nil nil)
      (overlay-put (make-overlay pt pt+1) 'display cap-ltr-a-xpm)
      (sit-for 2)
      (internal-show-cursor nil t)
      (remove-overlays point-min point-max 'display cap-ltr-a-xpm))
    

    The following code snippet is similar to the one above, except it uses the after-string display property. The cursor is removed for a short duration during this function so that the overlay can be seen more clearly.

    (let* (
        (point-min (point-min))
        (point-max (point-max))
        (peol (point-at-eol))
        (pilcrow `(image :type xpm :mask nil :ascent center :data
          "/* XPM */
          static char * pilcrow_xpm[] = {
          \"11 20 4 1\",
          \". c #000000\",
          \"+ c orange\",
          \"@ c #7F0000\",
          \"% c yellow\",
          \"%..........\",
          \"%..........\",
          \"%..........\",
          \"%..........\",
          \"%..++++++..\",
          \"%.++++.+...\",
          \"%.++++.+...\",
          \"%.++++.+...\",
          \"%..+++.+...\",
          \"%....+.+...\",
          \"%....+.+...\",
          \"%....+.+...\",
          \"%....+.+...\",
          \"%....+.+...\",
          \"%..........\",
          \"%..........\",
          \"%..........\",
          \"%..........\",
          \"%..........\",
          \"%..........\"};"))
        (pilcrow-str (propertize " " 'display pilcrow)) )
      (overlay-put (make-overlay peol peol) 'after-string pilcrow-str)
      (sit-for 2)
      (remove-overlays point-min point-max 'after-string pilcrow-str))
    

    The following screenshot was made using the second example.

    Example