How do I display ANSI color codes in emacs for any mode? - emacs

I have a log file that uses ANSI escape color codes to format the text. The mode is fundamental. There are other answered questions that address this issue but I'm not sure how to apply it to this mode or any other mode. I know the solution has something to do with configuring ansi-color in some way.
ANSI codes in shell mode
ANSI codes in gdb mode

You could use code below
(require 'ansi-color)
(defun display-ansi-colors ()
(interactive)
(ansi-color-apply-on-region (point-min) (point-max)))
Then you can execute display-ansi-colors via M-x, via a key-binding of your choosing, or via some programmatic condition (maybe your log files have a extension or name that matches some regexp)
If you want to do this with read-only buffers (log files, grep results), you may use inhibit-read-only, so the function will be:
(defun display-ansi-colors ()
(interactive)
(let ((inhibit-read-only t))
(ansi-color-apply-on-region (point-min) (point-max))))

User defined function:
(defun my-ansi-color (&optional beg end)
"Interpret ANSI color esacape sequence by colorifying cotent.
Operate on selected region on whole buffer."
(interactive
(if (use-region-p)
(list (region-beginning) (region-end))
(list (point-min) (point-max))))
(ansi-color-apply-on-region beg end))
For buffers that uses comint/compilation use filter:
(ignore-errors
(require 'ansi-color)
(defun my-colorize-compilation-buffer ()
(when (eq major-mode 'compilation-mode)
(ansi-color-apply-on-region compilation-filter-start (point-max))))
(add-hook 'compilation-filter-hook 'my-colorize-compilation-buffer))

Gavenkoa's and Juanleon's solutions worked for me, but were not satisfying as they were modifying the contents of the file I was reading.
To colorize without modifying the contents of the file, download tty-format.el and add the following to your .emacs:
(add-to-list 'load-path "path/to/your/tty-format.el/")
(require 'tty-format)
;; M-x display-ansi-colors to explicitly decode ANSI color escape sequences
(defun display-ansi-colors ()
(interactive)
(format-decode-buffer 'ansi-colors))
;; decode ANSI color escape sequences for *.txt or README files
(add-hook 'find-file-hooks 'tty-format-guess)
;; decode ANSI color escape sequences for .log files
(add-to-list 'auto-mode-alist '("\\.log\\'" . display-ansi-colors))
tty-format is based on ansi-color.el which is only shipped natively with recent versions of emacs.

On large files, performance of ansi-color-apply-on-region is slow. Here's a solution that colors the current region and works with read-only buffers.
(require 'ansi-color)
(defun ansi-color-region ()
"Color the ANSI escape sequences in the acitve region.
Sequences start with an escape \033 (typically shown as \"^[\")
and end with \"m\", e.g. this is two sequences
^[[46;1mTEXT^[[0m
where the first sequence says to diplay TEXT as bold with
a cyan background and the second sequence turns it off.
This strips the ANSI escape sequences and if the buffer is saved,
the sequences will be lost."
(interactive)
(if (not (region-active-p))
(message "ansi-color-region: region is not active"))
(if buffer-read-only
;; read-only buffers may be pointing a read-only file system, so don't mark the buffer as
;; modified. If the buffer where to become modified, a warning will be generated when emacs
;; tries to autosave.
(let ((inhibit-read-only t)
(modified (buffer-modified-p)))
(ansi-color-apply-on-region (region-beginning) (region-end))
(set-buffer-modified-p modified))
(ansi-color-apply-on-region (region-beginning) (region-end))))
The one downside is that ansi-color-apply-on-region removes the ANSI escape sequence characters from the buffer, so when you save, they are lost. I wonder if there's a way to hide the characters instead of stripping them?

Related

Emacs ESS key bindings

I am trying to set some key bindings for ESS. I read one way is:
(eval-after-load "ess-mode"
'(define-key ess-mode-map (kbd "<f5>") 'myfunc))
But this works only inside the code blocks delimited by <<>>, #.
Another problem is that I would like to use the same key both for plain LaTeX mode (a .tex file) and LaTeX as part of Noweb (a .rnw file) and so I can't just define the key twice for LaTeX mode and ESS mode.
While there is a LaTeX-mode-hook, I don't see something like `ess-noweb-mode-hook.
ess-noweb mode is just a wrapper and it loads latex-mode and R-mode in corresponding chunks. So if you define a shortcut in latex-mode-map it should be available in .rnw buffers as well.
After some trial and error, I found the answer.
If we are in a LaTeX only buffer this is true: (and (equal (symbol-name major-mode) "latex-mode") (not ess-noweb-mode))).
If we are outside chunks of an ESS buffer this is true: ess-noweb-mode.
You may be interested to the following convenience functions.
;Check ESS related modes
(defun is-pure-latex ()
"The buffer is in LaTeX mode, but not in ESS mode."
(and (equal (symbol-name major-mode) "latex-mode") (not ess-noweb-mode)))
(defun is-ess ()
"The buffer is in ESS mode."
ess-noweb-mode)
(defun is-ess-doc ()
"The buffer is in ESS mode and insertion point is outside a chunk."
(and ess-noweb-mode (equal (symbol-name major-mode) "latex-mode")))
(defun is-ess-chunk ()
"The buffer is in ESS mode an insertion point is inside a chunk."
(equal (symbol-name major-mode) "ess-mode"))
(defun is-ess-inf ()
"The buffer is in inferior ESS mode"
(equal (symbol-name major-mode) "inferior-ess-mode"))

In emacs, how do I save without running save hooks?

I have various things set up in my 'before-save-hook. For example, I run 'delete-trailing-whitespace. This is what I want in almost all occasions.
But sometimes, I'm working on files that are shared with other people, and the file already has a bunch of trailing whitespace. If I save the file, I'll get a big diff that's pretty confusing, as my change is buried in dozens or hundreds of meaningless changes. Yes, everyone could just tell their diff tool to not show whitespace changes, but that's something that everyone has to do every time they look at the diff. I'd rather not even have the whitespace change.
Is there anything I can do to save the file without the whitespace changes, short of starting a new instance of Emacs with no init.el file, or with a modified init.el that doesn't have the hook?
Here is how I save without triggering delete-trailing-whitespace:
C-x C-q C-x C-s C-x C-q: read-only, save, revert read-only
A simpler solution I came up with is that my fundamental-mode has no hooks installed, because I want it to be as plain as possible. Thus if I want to save a file without running hooks, I temporarily switch to fundamental-mode.
Based on a comment discussion with #Stefan, here are two possible (untested) solutions:
Use let:
(defun save-buffer-without-dtw ()
(interactive)
(let ((b (current-buffer))) ; memorize the buffer
(with-temp-buffer ; new temp buffer to bind the global value of before-save-hook
(let ((before-save-hook (remove 'delete-trailing-whitespace before-save-hook)))
(with-current-buffer b ; go back to the current buffer, before-save-hook is now buffer-local
(let ((before-save-hook (remove 'delete-trailing-whitespace before-save-hook)))
(save-buffer)))))))
Use unwind-protect:
(defun save-buffer-without-dtw ()
(interactive)
(let ((restore-global
(memq 'delete-trailing-whitespace (default-value before-save-hook)))
(restore-local
(and (local-variable-p 'before-save-hook)
(memq 'delete-trailing-whitespace before-save-hook))))
(unwind-protect
(progn
(when restore-global
(remove-hook 'before-save-hook 'delete-trailing-whitespace))
(when restore-local
(remove-hook 'before-save-hook 'delete-trailing-whitespace t))
(save-buffer))
(when restore-global
(add-hook 'before-save-hook 'delete-trailing-whitespace))
(when restore-local
(add-hook 'before-save-hook 'delete-trailing-whitespace nil t)))))
The problem with the second solution is that the order of functions in the before-save-hook may change.
Here's another solution:
(defvar my-inhibit-dtw nil)
(defun my-delete-trailing-whitespace ()
(unless my-inhibit-dtw (delete-trailing-whitespace)))
(add-hook 'before-save-hook 'my-delete-trailing-whitespace)
and then
(defun my-inhibit-dtw ()
(interactive)
(set (make-local-variable 'my-inhibit-dtw) t))
so you can M-x my-inhibit-dtw RET in the buffers where you don't want to trim whitespace.
I wrote a command inspired by Nicholas Douma's solution.
(defun olav-save-buffer-as-is ()
"Save file \"as is\", that is in read-only-mode."
(interactive)
(if buffer-read-only
(save-buffer)
(read-only-mode 1)
(save-buffer)
(read-only-mode 0)))
What we need to do is remove 'delete-trailing-whitespace from before-save-hook, save the buffer, then add it back.
This code will do that, but only remove and add it if it's there to begin with.
;; save the buffer, removing and readding the 'delete-trailing-whitespace function
;; to 'before-save-hook if it's there
(defun save-buffer-no-delete-trailing-whitespace ()
(interactive)
(let ((normally-should-delete-trailing-whitespace (memq 'delete-trailing-whitespace before-save-hook)))
(when normally-should-delete-trailing-whitespace
(remove-hook 'before-save-hook 'delete-trailing-whitespace))
(save-buffer)
(when normally-should-delete-trailing-whitespace
(add-hook 'before-save-hook 'delete-trailing-whitespace))))
(global-set-key (kbd "C-c C-s") 'save-buffer-no-delete-trailing-whitespace)
It also binds the command to (kbd C-c C-s), for convenience.

.emacs .txt file visual-line-mode simultaneously with muse-mode

I indicate my muse-mode files (usually named with .txt suffix) as being muse-mode by starting them with a "#title". To do this, I have
;; muse-mode on *.txt files, if a #title or sect. header is on top 4 lines
(add-hook 'text-mode-hook
(lambda ()
(unless (or (eq major-mode 'muse-mode)
(not (stringp buffer-file-truename)))
(when (equal (file-name-extension buffer-file-truename) "txt")
(save-excursion
(goto-line 5)
(if (re-search-backward "\* [A-Z][a-z]+.*\\|#title " 1 t)
(muse-mode)))))))
If I also have
(add-to-list 'auto-mode-alist '("\\.txt$" . visual-line-mode))
in the .emacs (following the code above), then muse-mode no longer works. Though if I invoke visual-line-mode with Meta-x from within emacs on a muse file, it doesn't mess things up.
Ideally, I would like to have visual-line-mode working on all .txt files, but without messing up muse. Or else, I would like to start all .txt files in visual-line-mode except when they are muse files.
The variable 'auto-mode-alist chooses the major mode.
visual-line-mode is a minor mode, and by adding it to the 'auto-mode-alist you're making it act like a major mode, which replaces the text-mode you were starting with.
Instead, add turn-on-visual-line-mode-in-txt to the text-mode-hook like so:
(add-hook `text-mode-hook 'turn-on-visual-line-mode)
(defun turn-on-visual-line-mode-in-txt ()
(when (and (buffer-file-name)
(string-match ".txt$" (buffer-file-name)))
(turn-on-visual-line-mode)))
For more information on the differences, read the manual for major and minor modes.
I think #treyJackson identified the problem, but here are some extra comments:
BTW, your use of a text-mode-hook to switch to muse-mode will misbehave in various circumstances (because you first switch to text-mode, then halfway through you activate muse-mode, after which the end of the text-mode activation (usually, not much left to do, but there could be more functions on the text-mode-hook to run) will still be performed). A more robust approach might be to do:
(add-to-list 'auto-mode-alist '("\\.txt\\'" . text-or-muse-mode))
(defun text-or-muse-mode ()
(if (save-excursion
(goto-line 5)
(re-search-backward "\\* [A-Z][a-z]+.*\\|#title " 1 t))
(muse-mode)
(text-mode)))
Of course, you could also use a -*- muse -*- on the first line, or rely on magic-mode-alist instead.

Why doesn't font-lock-fontify-buffer work from elisp when it works from the minibuffer?

So I'm hacking up some elisp to test a web service, and I'm running into trouble with syntax highlighting. I'm using url-retrieve-synchronously to get an HTTP response, then editing the text to get down to just the XML I need to see. Unfortunately, syntax highlighting doesn't work in the returned buffer, even though I've set it to nxml-mode and used "font-lock-fontify-buffer" in the script. However, if I do "M-x font-lock-fontify-buffer", the highlighting works as I would expect. Is there some difference between using it in elisp and from inside emacs?
Here are the relevant parts of the script I'm putting together. I admit up front that this is the first elisp scripting I've ever done, and I'm probably doing things in a ludicrously incorrect manner, but it's all worked thus far.
(defun modality-http-request (url args request-type)
(let ((url-request-method request-type)
(url-request-extra-headers '(("Content-Type" . "application/x-www-form-urlencoded")))
(url-request-data
(mapconcat (lambda (arg)
(concat (url-hexify-string (car arg))
"="
(url-hexify-string (cdr arg))))
args
"&")))
(url-retrieve-synchronously url)))
(defun modality-http-get (url args)
(modality-http-request url args "GET"))
(defun modality-http-post (url args)
(modality-http-request url args "POST"))
(defun test-modality (test)
(interactive "s\Test: ")
(let ((buffer (modality-http-get (concat (get-modality-path) test) nil)))
(set-buffer buffer)
(setq modality-beginning (point))
(forward-paragraph)
(next-line)
(beginning-of-line)
(setq modality-end (point))
(delete-region modality-beginning modality-end)
(bf-pretty-print-xml-region)
(switch-to-buffer buffer)
(font-lock-fontify-buffer)))
(defun bf-pretty-print-xml-region ()
"Pretty format XML markup in region. You need to have nxml-mode
http://www.emacswiki.org/cgi-bin/wiki/NxmlMode installed to do
this. The function inserts linebreaks to separate tags that have
nothing but whitespace between them. It then indents the markup
by using nxml's indentation rules."
(interactive "r")
(save-excursion
(nxml-mode)
(goto-char (point-min))
(while (search-forward-regexp "\>[ \\t]*\<" nil t)
(backward-char) (insert "\n"))
(indent-region (point-min) (point-max))
))
URL uses temporary/internal buffers (recognized by the fact that their name starts with a space). They're plain normal, but some functions treat them specially: font-lock will not be activated, and the buffer will usually not be shown to the user (e.g. C-x b TAB will not show those buffers).
So either rename the buffer before enabling font-lock, or copy the text you need into another buffer whose name doesn't start with a space.

File path to clipboard in Emacs

What is the most simple way to send current full file name with file path to clipboard?
What I am using now is messages buffer: I copy file name that appears there after saving a file. But, I suppose, there should be much more simple way.
Why no one tell the simple solution.
Just go to your dired buffer then press 0 w or C-u 0 w.
This will call dired-copy-filename-as-kill which gives you full path of a file. If you want current dir, just delete the file at the end of it or you can use the function below, then bind it to any key you like.
(defun my/dired-copy-dirname-as-kill ()
"Copy the current directory into the kill ring."
(interactive)
(kill-new default-directory))
PS: personally I go to current directory from file buffer using dired-jump
I use this:
(defun my-put-file-name-on-clipboard ()
"Put the current file name on the clipboard"
(interactive)
(let ((filename (if (equal major-mode 'dired-mode)
default-directory
(buffer-file-name))))
(when filename
(with-temp-buffer
(insert filename)
(clipboard-kill-region (point-min) (point-max)))
(message filename))))
In Emacs Prelude I use:
(defun prelude-copy-file-name-to-clipboard ()
"Copy the current buffer file name to the clipboard."
(interactive)
(let ((filename (if (equal major-mode 'dired-mode)
default-directory
(buffer-file-name))))
(when filename
(kill-new filename)
(message "Copied buffer file name '%s' to the clipboard." filename))))
If you want to write the name/path of the current buffer you can type C-u M-: and then either (buffer-file-name) - for the full path - or (buffer-name) for the buffer name.
That is:
M-: + ellisp expression evaluates an ellisp expression in the mini-buffer
C-u write the output to the current buffer
Does not exactly answer to the question but could be useful if someone use this or other function sporadically, and prefers to not initialize the function at every startup.
In the Spacemacs distribution, you can press Spacefyy to display the buffer name in the minibuffer and copy it to the kill ring.
The function spacemacs/show-and-copy-buffer-filename seems to originate from this blog post: Emacs: Show Buffer File Name.
(defun camdez/show-buffer-file-name ()
"Show the full path to the current file in the minibuffer."
(interactive)
(let ((file-name (buffer-file-name)))
(if file-name
(progn
(message file-name)
(kill-new file-name))
(error "Buffer not visiting a file"))))
There's a buffer-extension - and it has copy-buffer-file-name-as-kill function. It even asks You what to copy: name, full name or a directory name.
Edit:
I use modified version of copy-buffer-file-name-as-kill from buffer-extension.el:
(defun copy-buffer-file-name-as-kill (choice)
"Copyies the buffer {name/mode}, file {name/full path/directory} to the kill-ring."
(interactive "cCopy (b) buffer name, (m) buffer major mode, (f) full buffer-file path, (d) buffer-file directory, (n) buffer-file basename")
(let ((new-kill-string)
(name (if (eq major-mode 'dired-mode)
(dired-get-filename)
(or (buffer-file-name) ""))))
(cond ((eq choice ?f)
(setq new-kill-string name))
((eq choice ?d)
(setq new-kill-string (file-name-directory name)))
((eq choice ?n)
(setq new-kill-string (file-name-nondirectory name)))
((eq choice ?b)
(setq new-kill-string (buffer-name)))
((eq choice ?m)
(setq new-kill-string (format "%s" major-mode)))
(t (message "Quit")))
(when new-kill-string
(message "%s copied" new-kill-string)
(kill-new new-kill-string))))
If you use Doom Emacs, it can be done with SPC f y.
To paste the current file path in the buffer, the most simple way I see is to do: C-u M-! pwd (this might not work on Windows systems though).
Alternatively, you can use C-x C-b to show the file paths of all opened buffers.
This is what has worked for me on MacOS 10.15.7, GNU Emacs 27.1
(defun copy-current-buffer-file-name ()
(interactive)
(shell-command (concat "echo " (buffer-file-name) " | pbcopy")))
set keybinding to "C-x M-f":
(global-set-key (kbd "C-x M-f") 'copy-current-buffer-file-name)
FYI: For a real beginner reading this, you need to add those lines to your init.el file.
Lots of good answers here, though I think for the "most simple way", as described in the question, there's room for improvement. Here's what I came up with (with thanks to other answers for the bits and pieces):
M-: (kill-new (buffer-file-name)) RET
This does precisely what you asked for -- takes the filename of the current buffer, and puts it in the "kill ring" and, depending on your settings, also the system clipboard. (See emacswiki/CopyAndPaste for more details on that part.)
If you want to do this regularly, then setting up a function like listed in the other answers, and binding it to an available key sequence, would make it easier to do frequently. But the above works with no prior setup, which I'm interpreting to be more "simple".