Is it possible to select some parts of a text and open it in a different buffer with a different mode?
Fore example, if I often work in ESS mode (syntax highlighting for R),
astring <- '<form>
<input type="checkbox" id="foo", value="bar">
</form>'
if the text within the single quotes is selected, I would like to edit it in a new buffer HTML mode (similar to org-src-lang-modes in orgmode).
What you're describing is called an 'indirect buffer'. You create one by calling M-x clone-indirect-buffer. This creates a second copy of the buffer you're editing. Any changes made in one buffer are mirrored in the other. However, both buffers can have different major modes active, so one can be in ESS mode, and the other in HTML (or whatever you like).
See the manual for details.
Here is one method of handling the issue using narrow-to-region -- the example contemplates that the point (cursor) will be somewhere between the single quotes when typing M-x narrow-to-single-quotes. A simple two-line function can be used to exit -- (widen) (ess-mode); or, you can get fancy with recursive-edit. Of course, this is not the same as opening the text in a new buffer. A similar functionality can also be used to copy the region to a new buffer, but I am assuming that the original poster may want to incorporate the edited text back into the primary buffer.
(defun narrow-to-single-quotes ()
"When cursor (aka point) is between single quotes, this function will narrow
the region to whatever is between the single quotes, and then change the
major mode to `html-mode`. To exit, just type `M-x widen` and then
`M-x [whatever-previous-major-mode-was-used]`."
(interactive)
(let* (
(init-pos (point))
beg
end)
(re-search-backward "'" nil t)
(forward-char 1)
(setq beg (point))
(re-search-forward "'" nil t)
(backward-char 1)
(setq end (point))
(narrow-to-region beg end)
(html-mode)
(goto-char init-pos)))
Related
I'm trying to find a way to automate/simplify the following use case:
I'm somewhere in the middle of a long file and I paste something.
Then I would like to send/move the current line (to the top header of the currently opened file). Most often this is some import/require function.
All of these ideally should happen without losing the relative position in a file (without setting markers manually).
How can I do this?
If it is important, I use Emacs primarily in evil-mode.
Here is the code:
(defun move-region-to-beginning-of-buffer (beg end)
"Move the region to the beginning of the buffer, then go back."
(interactive "r")
(let ((text (buffer-substring beg end)))
(save-excursion
(delete-region beg end)
(goto-char (point-min))
(insert text))))
Use it by selecting the text (actually, right after paste it is already marked) to be sent to the beginning of the buffer, then M-x move-region-to-beginning-of-buffer RET.
You can also bind this command to a key the usual way.
I'm creating a custom .emacs file (using GNU Emacs 24.3.1) and in it I have a custom function, bound to a custom shortcut, that copies the selected region "(kill-ring-save (region-beginning) (region-end))" then pastes it in a different location. Then I try to select a new region. But when I do, the region is no longer highlighted. The mark IS set, because I can copy/paste.
If I skip the paste step in my function, the new region is highlighted. It's just that editing the buffer in any way causes region highlighting to stop working.
From https://www.fnal.gov/docs/products/emacs/emacs/emacs_12.html :
"Any change to the buffer, such as inserting or deleting a character, deactivates the mark. This means any subsequent command that operates on a region will get an error and refuse to operate. You can make the region active again by typing C-x C-x. "
C-x C-x simply calls (exchange-point-and-mark), but if I call (exchange-point-and-mark) in my function, highlighting still does not turn on again. Why not?
Disclaimer: I am not that familiar with emacs, I'm just trial and erroring to some working code, but I just can't find a way to highlight the selected region after I edit the buffer. My workaround is to call a different function in my .emacs, bound to a different shortcut, that simply calls (exchange-point-and-mark) and the previously selected region is highlighted.
EDIT: adding representative code
(defun func1 ()
(interactive)
(set-mark (point))
(forward-char)
(forward-char) ; at this point two characters are highlighted
(set-mark (point))
(forward-char)
(forward-char) ; at this point two different characters are highlighted
)
(defun func2 ()
(interactive)
(set-mark (point))
(forward-char)
(forward-char) ; at this point two characters are highlighted
(insert "a")
(set-mark (point))
(forward-char)
(forward-char) ; at this point nothing is highlighted because of the insert, but the mark IS set
)
(defun func3 ()
; if I call this right after calling func2 the region is highlighted
(interactive)
(exchange-point-and-mark)
)
(global-set-key (kbd "<f5> x") 'func1)
(global-set-key (kbd "<f5> c") 'func2)
(global-set-key (kbd "<f5> v") 'func3)
[this is a re-edit after the discussion in the comments]
After making sure transient-mark-mode is enabled (it is what does the hilighting) there may not be much else that can be done to make this work.
My speculation is that transient-mark-mode works during the idle time between key inputs and it is perhaps simply testing whether the buffer was modified since the last time it ran or not, in which case any function which both attempts to set and activate the mark such that some hilighting is displayed, while at the same time modifying the buffer, will never succeed in triggering transient-mark-mode to hilight anything.
After doing a (re-search-forward str) in the current buffer, it would be nice in some cases to have an easy method to return to the previous buffer position. The behavior should be like (undo) for buffer changes. So if I do two searches forward, first from position A to B, and then from B to C, I would like to press a key to go back one step (from C to B), and pressing the key again would leave me at A..
If you are using re-search-forward in Lisp code (and you probably should be, if you are using it at all, even though it is a command), then do not set the mark in order to be able to return to your starting point.
Instead, simply save the starting position ((point)) as, say, variable beg, and then use goto-char beg.
See this paragraph in (elisp) The Mark:
Novice Emacs Lisp programmers often try to use the mark for the
wrong purposes. The mark saves a location for the user's
convenience. An editing command should not alter the mark unless
altering the mark is part of the user-level functionality of the
command. (And, in that case, this effect should be documented.)
To remember a location for internal use in the Lisp program, store
it in a Lisp variable. For example:
(let ((beg (point)))
(forward-line 1)
(delete-region beg (point))).
With this
(global-set-key
(kbd "M-p")
(lambda()(interactive) (set-mark-command 4)))
I can jump backwards one by one through a few C-M-s.
Note that this works for isearch-forward-regexp, not for plain
re-search-forward (this one doesn't set the mark).
But with elisp it's no problem - just call push-mark before
re-search-forward.
To sum up, the following seems to work:
(defun my-search-fun (str)
(interactive)
(push-mark)
(beginning-of-buffer)
(re-search-forward str))
(defun my-undo-search ()
(interactive)
(pop-mark)
(goto-char (mark))
I'm new to elisp, so please forgive me if the following approach is totally clumsy.
In the team I'm currently working with, there is an usual convention of closing python blocks with a pass statement (if they aren't ended by closing keywords like else or except or such). While unusual, this has the advantage that one can always recover the original indentation of the program if it is unintentionally changed (using emacs indent-region).
To get existing code in line with this convention, I wrote a small elisp function:
(defun python-check-indent ()
"Check if automatic indentation changes current indent, insert pass keyword if it does."
(interactive)
(move-beginning-of-line 1)
(skip-chars-forward " ")
(if
(< 0
(let (original)
(setq original (point))
(indent-for-tab-command)
(- (point) original)
)
)
(progn
(insert "pass")
(newline)
(indent-for-tab-command)
)
)
(next-line)
)
(global-set-key (kbd "C-`") 'python-check-indent)
The idea is simply to test whether hitting TAB would change the indentation, and insert a pass statement in that case. To facilitate processing longer blocks of code, it then advances to the next line.
When I run it using M-x python-check-indent, it does what I want (except that it moves around empty lines slightly), also when running it repeatedly to process several lines. However, when I run it repeatedly using the C-` keybinding, it starts messing up the code from the second invocation on.
So here are my questions: What is the difference between invoking a command with M-x ... and using its keybinding? And how could I change the function to be not affected by this difference?
emacs-version: GNU Emacs 23.3.1 (x86_64-apple-darwin, NS apple-appkit-1038.35) of 2011-03-10 on black.porkrind.org
(edit) current workaround: I'm now wrapping it inside a keyboard-macro, so it's "bound" to C-x e, and behaves properly.
The general rule is that it is best to avoid complex interactive
commands in your functions because they could be affected by all sorts
of options.
(defun python-check-indent ()
"Check if automatic indentation changes current indent, insert pass keyword if it does."
(interactive)
(goto-char (line-beginning-position))
(skip-chars-forward " ")
(when (< 0
(let (original)
(setq original (point))
(python-indent-line)
(- (point) original)))
(insert "pass\n")
(python-indent-line))
(forward-line))
However, even this is probably not good because python-indent-line's behavior depends on last-command and python-indent-trigger-commands. I think it would be best if you replaced the first invocation of python-indent-line with the code which computes the target indentation instead of actually indenting, something like (nth python-indent-current-level python-indent-levels).
PS. If you still have problems, I suggest that you use edebug and step through the function.
I am working on splitting code into smaller files and refactoring it a bit. Consider the following code below as the section I want to extract:
(require 'package)
(add-to-list 'package-archives
'("marmalade" . "http://marmalade-repo.org/packages/") t)
(package-initialize)
(when (not package-archive-contents)
(package-refresh-contents))
(defvar my-packages '(org magit)
"A list of packages to ensure are installed at launch.")
(dolist (p my-packages)
(when (not (package-installed-p p))
(package-install p)))
I want to take the section above and replace it with something like (require `file-name)
Then take the text replaced and place that in a new file in the current directory named file-name.el
And then add a line to the top of the file (provides `file-name)
It would be great if I could hit a keychord and then type a name and have this happen. If there is an easy way to do this then I would love to hear possible solutions.
Edit:
I'm starting a bounty because I think this applies to more types of code than Lisp and I would like to have something a little more general that I can expand upon.
I have considered yasnippet but I don't think it's powerful enough to perform the task at hand. Basically the ideal workflow would be marking the lines to be extracted, replacing that with an appropriate require or include directive and sending the text off to it's own file. Ideally one command and something that is aware of the type of file being edited or at least the major mode so the behavior can be customized, again yasnippet is good at performing different tasks when editing in different major modes however I would have no idea how to make that work or evaluate the possibility of making it work.
Let me know if you need any more information.
A general solution to this type of problem are keyboard macros (not to be confused with (Emacs) LISP macros). Basically Emacs allows you to record a sequence of keystrokes and "play them back" afterwards. This can be a very handy tool in situations where writing custom LISP code seems overkill.
For instance you could create the following keyboard macro (type the key combinations on the left hand side, the right hand side shows explanations for each key stroke):
C-x ( ; start recording a keyboard macro
C-x h ; mark whole buffer
C-w ; kill region
(require 'file-name) ; insert a require statement into the buffer
C-x C-s ; save buffer
C-x C-f ; find file
file-name.el <RET> ; specify the name of the file
M-< ; move to the beginning of the buffer
C-u C-y ; insert the previously killed text, leaving point where it is
(provide 'file-name) <RET> <RET> ; insert a provide statement into the buffer
C-x ) ; stop recording the keyboard macro
Now you can re-play that macro in some other buffer by typing C-x e, or save it for later use. You can also bind a macro to a shortcut just like a function.
However, there is one weakness with this approach: you want to be able to actually specify the file-name, and not just use the string "file-name" every time. That is a bit difficult - by default, keyboard macros provide no general facility for querying the user (except the very minimal C-x q, as documented here).
The Emacs Wiki has some work-arounds for that, however, instead of prompting the user in the minibuffer, it can sometimes be sufficient to start the macro by killing the current line and saving its text to a register.
C-x (
C-e C-<SPC> C-a ; mark current line
C-x r s T ; copy line to register T
C-k C-k ; kill current line
... ; actual macro
C-x )
Now when you want to use your macro, you would first write the desired file-name in an otherwise empty line, and then do C-x e in that line. Whenever the value of the file-name is needed in the macro you can retrieve it from the register T:
C-x r i T ; insert file-name into buffer
For instance, for the provide statement in the above macro, you could write: (provide ' C-x r i T ). Note that this technique (inserting) also works in the minibuffer, and of course you could save multiple lines to different registers.
May sound complicated, but is actually quite easy in practice.
Slightly tested:
(defun extract-to-package (name start end)
(interactive (list (read-string "Package name to create: ")
(region-beginning) (region-end)))
(let ((snip (buffer-substring start end)))
(delete-region start end)
(insert (format "(require '%s)\n" name))
(with-current-buffer (find-file-noselect (concat name ".el"))
(insert snip)
(insert (format "(provide '%s)\n" name))
(save-buffer))))
For a such thing I use the following snippet (with yasnippet):
;; `(buffer-name)`
;; Copyright (C) `(format-time-string "%Y")` name
;; Author: name <email>
;; This program is free software: you can redistribute it and/or
;; modify it under the terms of the GNU General Public License as
;; published by the Free Software Foundation, either version 3 of
;; the License, or (at your option) any later version.
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <http://www.gnu.org/licenses/>.
$0
(provide '`(subseq (buffer-name) 0 (- (length (buffer-name)) 3))`)
1st create the file C-xC-ffile-name.elRET
then insert the snippet with C-c&C-s
and add any piece of code you wish.
I've also the following hook:
(add-hook 'after-save-hook 'autocompile)
(defun autocompile ()
"Byte compile an elisp."
(interactive)
(require 'bytecomp)
(let ((filename (buffer-file-name)))
(if (string-match "\\.el$" filename)
(byte-compile-file filename))))
to produce an .elc whenever I save a .el.
(defun region-to-file+require (beg end file append)
"Move region text to FILE, and replace it with `(require 'FEATURE)'.
You are prompted for FILE, the name of an Emacs-Lisp file. If FILE
does not yet exist then it is created.
With a prefix argument, the region text is appended to existing FILE.
FEATURE is the relative name of FILE, minus the extension `.el'."
(interactive "#*r\nG\nP")
(when (string= (expand-file-name (buffer-file-name)) (expand-file-name file))
(error "Same file as current"))
(unless (string-match-p ".+[.]el$" file)
(error "File extension must be `.el' (Emacs-Lisp file)"))
(unless (or (region-active-p)
(y-or-n-p "Region is not active. Use it anyway? "))
(error "OK, canceled"))
(unless (> (region-end) (region-beginning)) (error "Region is empty"))
(unless (or (not append)
(and (file-exists-p file) (file-readable-p file))
(y-or-n-p (format "File `%s' does not exist. Create it? " file)))
(error "OK, canceled"))
(write-region beg end file append nil nil (not append))
(delete-region beg end)
(let ((feature (and (string-match "\\(.+\\)[.]el$" file)
(match-string 1 file))))
(when feature
(insert (format "(require '%s)\n" feature)))))