In GNU Emacs, I could use something like a hypothetical "flyexist.el" - I have a buffer with absolute (Unix) file names in it (plus some additional text around them). Most of those files exist, but some are missing. I would like to run a function that highlights me the missing files (maybe with a red overlay). This function would need to figure out which of the text in the buffer looks like a file name (some false positives are okay) and then work on it with file-exists-p.
For example, assume that my buffer contains
Some random text mentioning /file/that/does/exist.txt,
some more random text, and a /file/that/does/not-exist.txt
I want to have the second file highlighted.
Does something like this exist already?
I am new to emacs hacking... Here is my "minor-mode" version.
(defvar filehi-path-re "\\([/$][[:alnum:]$-_.]+\\)+"
"Regexp used for path matching.")
(defface filehi-file-existing
'((t (:foreground "White" :background "Green")))
"Face for existing files.")
(defface filehi-file-missing
'((t (:foreground "Yellow" :background "Red")))
"Face for missing files.")
(defun filehi-check-and-highlight (start end)
"Check if substring is existing file path and highlight it."
(remove-overlays start end 'name 'filehi-highlight)
(let ((overlay (make-overlay start end)))
(overlay-put overlay 'name 'filehi-highlight)
(overlay-put overlay 'face (if (file-exists-p (substitute-in-file-name
(buffer-substring start end)))
'filehi-file-existing
'filehi-file-missing))))
(defun filehi-highlight-file-paths (&optional start end _ignore)
"Run through the buffer and highliht file paths."
(save-excursion
(save-match-data ; fixes problem with dabbrev (and may be more...)
(remove-overlays (point-min) end 'name 'filehi-highlight)
(let ((prev-end (point-min)))
(goto-char (point-min)) ; FIXME use something like greedy
; search-backward
(while (and (<= (point) end)
(re-search-forward filehi-path-re nil t))
(filehi-check-and-highlight (match-beginning 0) (match-end 0)))))))
(define-minor-mode filehi-mode
"Minor mode for highlighting existing file paths.
May conflict with other modes..."
nil " Filehi" nil
(if filehi-mode
(progn ; enable mode
(make-local-hook 'after-change-functions)
(filehi-highlight-file-paths (point-min) (point-max))
(add-hook 'after-change-functions 'filehi-highlight-file-paths nil t))
; disable mode
(remove-hook 'after-change-functions 'filehi-highlight-file-paths t)
(remove-overlays (point-min) (point-max) 'name 'filehi-highlight)))
Try with this (you have to trigger it manually though, or incorporate it into some other periodical routine):
(defun x-mark-missing-files ()
(interactive)
(save-excursion
(while (search-forward-regexp "~?/[A-Za-z./-]+")
(when (not (file-exists-p (match-string 0)))
(overlay-put
(make-overlay (match-beginning 0) (match-end 0))
'face '(:background "red"))))))
Play a little with the filename regexp to get it right how you want it.
Related
Trying to get the line numbering right aligned with a separator space and the same background color as the line highlighting in Spacemacs was quite complicated. Specialy when it came to do both at the same time in linum and linum-relative.
I don't know if this code is ok, but it manages so far:
(defun dotspacemacs/user-config ()
"Configuration function for user code.
This function is called at the very end of Spacemacs initialization after
layers configuration. You are free to put any user code."
(global-linum-mode t)
(unless window-system
(add-hook 'linum-before-numbering-hook
(lambda ()
(setq-local my-linum-format-fmt
(let ((w (length (number-to-string
(count-lines (point-min) (point-max))))))
(concat "%" (number-to-string w) "d"))))
(set-face-attribute 'linum nil
:background (face-background 'hl-line nil t))))
(defface my-linum-hl
`((t :inherit linum :background ,(face-background 'hl-line nil t)))
"Face for the current line number."
:group 'linum)
(defun my-linum-format-func (line)
(concat
(propertize (format my-linum-format-fmt line) 'face 'my-linum-hl)
(propertize " " 'face 'my-linum-hl)))
(unless window-system
(setq linum-format 'my-linum-format-func))
;; linum-relative
(linum-relative-toggle)
(unless window-system
(setq-local my-linum-relative-format-fmt
(let ((w (length (number-to-string
(count-lines (point-min) (point-max))))))
(concat "%" (number-to-string w) "s "))))
(unless window-system
(setq linum-relative-format my-linum-relative-format-fmt))
)
Problem is: The numbers background color don't change to the correct one when I change the theme while inside Emacs. The color remains the same. How do I make emacs update the linum and linum-relative background color after a color theme change?
I'm writing a minor mode for emacs which, at the very least, will calculate a numeric value for each line in a buffer. I want to display this visually, preferable neatly before each line.
I know some minor modes draw to the fringe, and I know overlays are an option too (are these related?), but I can't find a good example of what I want anywhere.
Basically, I want to have something like the line numbers from linum-mode, but they will need to change every time the buffer is modified (actually, only whenever the line they're on changes). Something like a character counter for each line would be a good example. And I'd like it to not break linum-mode, but not depend on it, etc, if possible.
Here is a quick example of one way to put an overlay after linum-mode numbers and before the line of text. I will need to give some thought about right-alignment of the character count.
NOTE: This method contemplates that the linum-mode numbers are generated before the code that follows in this example. If the post-command-hook or the widow-scroll-functions hook is used to implement this proposed method, then those additions to the hooks would need to follow in time subsequently to the linum-mode functions attached to those same hooks.
The following example could be implemented with the post-command-hook and the window-scroll-functions hook. See the following link for an example of how to determine window-start and window-end before a redisplay occurs: https://stackoverflow.com/a/24216247/2112489
EDIT: Added right-alignment of character count -- contemplates a maximum of three digits (i.e., up to 999 characters per line). The text after the character count overlays are now left-aligned.
(save-excursion
(let* (
(window-start (window-start))
(window-end (window-end)))
(goto-char window-end)
(while (re-search-backward "\n" window-start t)
(let* (
(pbol (point-at-bol))
(peol (point-at-eol))
(raw-char-count (abs (- peol pbol)))
(starting-column
(propertize (char-to-string ?\uE001)
'display
`((space :align-to 1)
(space :width 0))))
(colored-char-count
(propertize (number-to-string raw-char-count)
'face '(:background "gray50" :foreground "black")
'cursor t))
(one-spacer
(propertize (char-to-string ?\uE001)
'display
`((space :width 1))))
(two-spacers
(propertize (char-to-string ?\uE001)
'display
`((space :width 2))))
(final-char-count
(cond
((and
(< raw-char-count 100)
(> raw-char-count 9))
(concat one-spacer colored-char-count))
((< raw-char-count 10)
(concat two-spacers colored-char-count))
(t colored-char-count))) )
(overlay-put (make-overlay pbol pbol)
'before-string
(concat starting-column final-char-count two-spacers) )))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; M-x char-count-mode
(defvar char-count-p nil
"When `char-count-p` is non-`nil`, the overlays are present.")
(make-variable-buffer-local 'char-count-p)
(defvar char-count-this-command nil
"This local variable is set within the `post-command-hook`; and,
is also used by the `window-scroll-functions` hook.")
(make-variable-buffer-local 'char-count-this-command)
(defvar char-count-overlay-list nil
"List used to store overlays until they are removed.")
(make-variable-buffer-local 'char-count-overlay-list)
(defun char-count-post-command-hook ()
"Doc-string."
(setq char-count-this-command this-command)
(character-count-function))
(defun character-count-window-scroll-functions (win _start)
"Doc-string."
(character-count-function))
(defun equal-including-properties--remove-overlays (beg end name val)
"Remove the overlays using `equal`, instead of `eq`."
(when (and beg end name val)
(overlay-recenter end)
(dolist (o (overlays-in beg end))
(when (equal-including-properties (overlay-get o name) val)
(delete-overlay o)))))
(defun character-count-function ()
"Doc-string for the character-count-function."
(when
(and
char-count-mode
char-count-this-command
(window-live-p (get-buffer-window (current-buffer)))
(not (minibufferp))
(pos-visible-in-window-p (point)
(get-buffer-window (current-buffer) (selected-frame)) t) )
(remove-char-count-overlays)
(save-excursion
(let* (
counter
(selected-window (selected-window))
(window-start (window-start selected-window))
(window-end (window-end selected-window t)) )
(goto-char window-end)
(catch 'done
(while t
(when counter
(re-search-backward "\n" window-start t))
(when (not counter)
(setq counter t))
(let* (
(pbol (point-at-bol))
(peol (point-at-eol))
(raw-char-count (abs (- peol pbol)))
(starting-column
(propertize (char-to-string ?\uE001)
'display
`((space :align-to 1) (space :width 0))))
(colored-char-count
(propertize (number-to-string raw-char-count)
'face '(:background "gray50" :foreground "black")))
(one-spacer
(propertize (char-to-string ?\uE001)
'display
`((space :width 1))))
(two-spacers
(propertize (char-to-string ?\uE001)
'display
`((space :width 2))))
(final-char-count
(cond
((and
(< raw-char-count 100)
(> raw-char-count 9))
(concat one-spacer colored-char-count))
((< raw-char-count 10)
(concat two-spacers colored-char-count))
(t colored-char-count)))
(ov-string (concat starting-column final-char-count two-spacers)) )
(push ov-string char-count-overlay-list)
(overlay-put (make-overlay pbol pbol) 'before-string ov-string)
(when (<= pbol window-start)
(throw 'done nil)) )))
(setq char-count-p t)))
(setq char-count-this-command nil) ))
(defun remove-char-count-overlays ()
(when char-count-p
(require 'cl)
(setq char-count-overlay-list
(remove-duplicates char-count-overlay-list
:test (lambda (x y) (or (null y) (equal-including-properties x y)))
:from-end t))
(dolist (description char-count-overlay-list)
(equal-including-properties--remove-overlays (point-min) (point-max) 'before-string description))
(setq char-count-p nil) ))
(defun turn-off-char-count-mode ()
(char-count-mode -1))
(define-minor-mode char-count-mode
"A minor-mode that places the character count at the beginning of the line."
:init-value nil
:lighter " Char-Count"
:keymap nil
:global nil
:group nil
(cond
(char-count-mode
(setq scroll-conservatively 101)
(add-hook 'post-command-hook 'char-count-post-command-hook t t)
(add-hook 'window-scroll-functions
'character-count-window-scroll-functions t t)
(add-hook 'change-major-mode-hook 'turn-off-char-count-mode nil t)
(message "Turned ON `char-count-mode`."))
(t
(remove-char-count-overlays)
(remove-hook 'post-command-hook 'char-count-post-command-hook t)
(remove-hook 'window-scroll-functions
'character-count-window-scroll-functions t)
(remove-hook 'change-major-mode-hook 'turn-off-char-count-mode t)
(kill-local-variable 'scroll-conservatively)
(message "Turned OFF `char-count-mode`.") )))
(provide 'char-count)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
Is it possible to calculate a new window-start/end without a redisplay occurring? If so, then an example would be greatly appreciated. If not, then what is the best way to approximate it?
Example: We want to move to a new area of the buffer somewhere off screen, and place overlays when we get there. We might be using page-down or scroll-down or paragraph-down or end-of-buffer. When we get to that new point, we want to calculate the new window-start and the new window-end. However, we want to avoid a momentary naked looking buffer without any overlays. Ideally, the redisplay would occur once those overlays are added. I want to restrict new overlays to the new region based upon the new window-start/end.
Point-Min: point = 1
Old Window Start: point = 1000
Old Window End: point = 1500
New Window Start: point = 3500
New Window End: point = 4000
Point-Max: point = 6000
Problem: When using the post-command-hook to try and calculate the new window-start and new window-end, the previous display positions are being used instead -- i.e., the old window-start and the old window-end.
Here is a sample of the project I am working on. Absent fixing the window-start \ window-end problem, I get the following error:
Error in post-command-hook (my-eol-ruler-function):
(error "Invalid search bound (wrong side of point)")`.
The error happens when going from (point-min) to the end of the buffer with the interactive function end-of-buffer. In the context of this error, (point-max) is beyond the old window-end.
EDIT: Updated code to include a message: (message "point: %s | window-start: %s | window-end: %s | point-max: %s" (point) (window-start) (window-end) (point-max) ). The message is used to demonstrate that the new window-start and new window-end are not calculated within the post-command-hook because a redisplay has not yet occurred. However, I am trying to avoid a redisplay until after the new overlays have been placed -- otherwise, a naked buffer without overlays is visible for a split second.
(defvar my-eol-ruler nil
"A horizontal ruler stretching from eol (end of line) to the window edge.")
(make-variable-buffer-local 'my-eol-ruler)
(defvar my-eol-pilcrow nil
"A pilcrow symbol placed at the end of every line except the current line.")
(make-variable-buffer-local 'my-eol-pilcrow)
(defun my-eol-ruler-function ()
(let* (
(opoint (point))
(window-width (window-width))
(window-start (window-start))
(window-end (window-end))
(col-eovl
(save-excursion
(vertical-motion 1)
(skip-chars-backward " \r\n" (- (point) 1))
(- (current-column) (progn (vertical-motion 0) (current-column)))))
(my-current-line-length (- (- window-width col-eovl) 3))
(pilcrow
(propertize (char-to-string ?\u00B6)
'face '(:foreground "white")
'cursor t))
(pilcrow-underlined
(propertize (char-to-string ?\u00B6)
'face '(:foreground "white" :underline "yellow")
'cursor t))
(underline (propertize (char-to-string ?\u2009)
'display `(space :width ,my-current-line-length)
'face '(:underline "yellow")
'cursor t)))
(when (or my-eol-ruler my-eol-pilcrow)
(dolist (description `(
,my-eol-ruler
,my-eol-pilcrow ))
(remove-overlays (point-min) (point-max)
'after-string description)) )
(setq my-eol-ruler (concat pilcrow-underlined underline))
(setq my-eol-pilcrow pilcrow)
(save-excursion
(end-of-line)
(overlay-put (make-overlay (point) (point))
'after-string my-eol-ruler ) )
(message "point: %s | window-start: %s | window-end: %s | point-max: %s"
(point)
(window-start)
(window-end)
(point-max) )
(save-excursion
(goto-char window-end)
(while (re-search-backward "\n" window-start t)
(let* (
(pbol (point-at-bol))
(pbovl (save-excursion (vertical-motion 0) (point)))
(peol (point))
(peol-pbol-region-p
(if (region-active-p)
(= peol pbol)))
(eol-inside-region-p
(if (region-active-p)
(and
(<= reg-beg peol)
(> reg-end peol))))
(col-eovl
(save-excursion
(vertical-motion 1)
(skip-chars-backward " \r\n" (- (point) 1))
(- (current-column) (progn (vertical-motion 0) (current-column)))))
(my-last-column (current-column))
(window-width-bug-p (= my-last-column (- window-width 1)))
(shazbot-pbol
(save-excursion
(end-of-line)
(re-search-backward "\s\\|\t" pbol t) (+ (point) 1)))
(wrapped-window-width-bug-p (= col-eovl (- window-width 1))) )
(when
(or
(< opoint pbol)
(> opoint peol))
(overlay-put (make-overlay peol peol) 'after-string my-eol-pilcrow))))) ))
(add-hook 'post-command-hook 'my-eol-ruler-function)
Beginning of the buffer, before the error occurs.
End of the buffer -- the error occurs when executing the interactive function end-of-buffer from a point at the beginning of the buffer.
Error in post-command-hook (my-eol-ruler-function):
(error "Invalid search bound (wrong side of point)")
See also Emacs bug tracker feature request #22404 (which has not yet been implemented, but the mailing archive contains a rough draft rudimentary patch that creates a new hook for this specific issue): https://debbugs.gnu.org/cgi/bugreport.cgi?bug=22404
Minor-mode for testing window-start and window-end BEFORE visual redisplay.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; test-mode
;; A minor-mode for testing `window-start` / `window-end` BEFORE visual redisplay.
(defvar test-this-command nil
"This local variable is set within the `post-command-hook`; and,
is also used by the `window-scroll-functions` hook.")
(make-variable-buffer-local 'test-this-command)
(defun test-post-command-hook-fn ()
"A function attached to the `post-command-hook`."
(setq test-this-command this-command)
(test-demo-fn))
(defun test-window-scroll-functions-fn (win _start)
"A function attached to the `window-scroll-functions` hook."
(test-demo-fn))
(defun test-demo-fn ()
"This is a test-mode demonstration function."
(when
(and
test-mode
test-this-command
(window-live-p (get-buffer-window (current-buffer)))
(not (minibufferp))
(pos-visible-in-window-p (point)
(get-buffer-window (current-buffer) (selected-frame)) t))
(let* (
(selected-window (selected-window))
(window-start (window-start selected-window))
(window-end (window-end selected-window t)) )
(message "window-start: %s | window-end: %s" window-start window-end)
(setq test-this-command nil) )))
(define-minor-mode test-mode
"A minor-mode for testing `window-start` / `window-end` BEFORE visual redisplay."
:init-value nil
:lighter " TEST"
:keymap nil
:global nil
:group nil
(cond
(test-mode
(set (make-local-variable 'scroll-conservatively) 101)
(add-hook 'post-command-hook 'test-post-command-hook-fn nil t)
(add-hook 'window-scroll-functions 'test-window-scroll-functions-fn nil t)
(when (called-interactively-p 'any)
(message "Turned ON `test-mode`.")))
(t
(kill-local-variable 'scroll-conservatively)
(kill-local-variable 'test-this-command)
(remove-hook 'post-command-hook 'test-post-command-hook-fn t)
(remove-hook 'window-scroll-functions 'test-window-scroll-functions-fn t)
(when (called-interactively-p 'any)
(message "Turned OFF `test-mode`.") ))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
Offhand, I'd say that the error is raised because you pass a BOUND arg to a search function. For example:
(re-search-backward "\n" window-start t)
(re-search-backward "\s\\|\t" pbol t)
Check your values of window-start and pbol. Remember that when you search backward the bound must not be greater than the current position (point).
I think you want to use jit-lock-register instead of post-command-hook. This way, the redisplay code will call you back once it has decided of a window-start and you'll be able to add the overlays you want before the buffer's content is displayed.
What is the proper way, please, to remove after-string overlays with variable values?
When using C-u C-x =, it only shows up as after-string without stating what the value is.
For example, once I lay an overlay using (overlay-put (make-overlay (point) (point)) 'after-string my-concatenated-string), I would like to be able to delete it without programming Emacs to remember every single my-concatenated-string that was previously used in the buffer -- there might be a few different ones on every line?
Is it sufficient to use?: (remove-overlays (window-start) (window-end)) 'after-string)
Or, is it better to use?: (remove-overlays (window-start) (window-end)) 'after-string t)
Or, is there another method to get them all?
EDIT (March 17, 2014): My confusion is apparently coming from a misunderstanding between an object and a property.
In general, an overlay property is created as follows:
(overlay-put (make-overlay (point) (point)) 'my-property 'property-number-one )
In general, an overlay object is created as follows:
(overlay-put (make-overlay (point) (+ (point) 1))
'face '(:background "gray50" :foreground "black"))
Here is a unique situation where an 'after-string smells-like an object. My assumption is: if it smells-like an object, then perhaps a value needs to be included when attempting to remove it so that I'm not left with a disconnected 'after-string:
(save-excursion
(end-of-line)
(let ((eol-floating-column (+ (current-column) 10)))
(overlay-put (make-overlay (point) (point))
'after-string
(concat
(propertize (char-to-string ?\uE001)
'display
`((space :align-to ,eol-floating-column)
(space :width 0)))
(propertize (char-to-string ?\u00B6)
'face '(:background "gray50" :foreground "black")
'cursor t) ))))
The way the code is written, if you omit the last parameter, it only removes an overlay if the value is `nil' (which it doesn't appear to be in your case).
As you don't know the value of the property, I don't think you can use the function. However, you can simply write something like (assuming the value of the after-string is never nil):
(dolist (o (overlays-in (window-start) (window-end)))
(when (overlay-get o 'after-string)
(delete-overlay o))
Also note that if you do this from a post-command hook, window-end might not reflect the true value. To be safe you can do (window-end nil t), however this could be a bit slower.
When you put the overlay, add another property (like (overlay-put ol 'lawlist t), for example), after which you can remove those overlays with (remove-overlays BEG END 'lawlist t).
(defun lawlist-remove-overlays (beg end name val)
"Remove the overlays."
;; DEBUGGING
;; (unless (and beg end name val)
;; (message "ERROR -- beg: %s | end: %s | name: %s | val: %s" beg end name val))
(let* (
(point-max (point-max))
(point-min (point-min))
(narrowed-p (not (equal (- point-max point-min) (buffer-size))))
(beg (if beg beg point-min))
(end
(cond
((and
(not narrowed-p)
end)
end)
((and
(not narrowed-p)
(null end))
point-max)
((and
narrowed-p
end
(< end point-max))
end)
((and
narrowed-p
end
(= end point-max))
(1+ end))
((and
narrowed-p
(null end))
(1+ point-max)) )))
(when (and beg end name val)
(overlay-recenter end)
(dolist (o (overlays-in beg end))
(when (eq (overlay-get o name) val)
(delete-overlay o))))))
(dolist (description `(
,fci-pre-limit-string
,fci-pre-limit-active-region-string
,fci-at-limit-string
,fci-post-limit-string
,fci-wrapped-limit-string
,fci-cursor-at-eol-string
,fci-tab-text-left
,fci-tab-text-right
,fci-tab-sandwiched))
(lawlist-remove-overlays nil nil 'after-string description))
See also this related thread which deals with targeting overlays with values containing text properties:
https://emacs.stackexchange.com/a/9847/2287
Currently my buffer with linum mode and white space mode enabled looks like this:
How do I configure the linum region to not render the whitespace symbols?
Observation: There is no need for spaces to the right of the line numbers (as shown in the question), because the fringe width can be used to control separation between the line numbers and the body.
(setq-default left-fringe-width 10)
(setq-default right-fringe-width 0)
(set-face-attribute 'fringe nil :background "black")
Option # 1: However, this is not flush-right.
(setq linum-format "%d")
Option # 2: Use leading zeros -- is flush-right.
(eval-after-load 'linum
'(progn
(defface linum-leading-zero
`((t :inherit 'linum
:foreground ,(face-attribute 'linum :background nil t)))
"Face for displaying leading zeroes for line numbers in display margin."
:group 'linum)
(defun linum-format-func (line)
(let ((w (length
(number-to-string (count-lines (point-min) (point-max))))))
(concat
(propertize (make-string (- w (length (number-to-string line))) ?0)
'face 'linum-leading-zero)
(propertize (number-to-string line) 'face 'linum))))
(setq linum-format 'linum-format-func)))
You could improve #lawlists second solution, but instead of using 0s as a space replacement, you can use some exotic whitespace like the en-space which would be "\u2002". Since we aren't using proportional fonts that will look just like a space, but whitespace won't mess with it.
I'm actually using linum-relative-mode where you can conveniently just advice linum-relative:
(advice-add 'linum-relative :filter-return
(lambda (num)
(if (not (get-text-property 0 'invisible num))
(propertize (replace-regexp-in-string " " "\u2002" num)
'face (get-text-property 0 'face num)))))