I am starting to learn Emacs Lisp, and as a first project I would like to improve the fortran mode in Emacs. I would like to mark the name of a sub routine in the buffer, and then press a shortcut key. To bring up a buffer with all lines in the given source where the name of the subroutine is mentioned.
I found that I can get the marked text using:
(defun get-selected-text (beg end)
(interactive
(if (use-region-p)
(list (region-beginning) (region-end))
(list nil nil)))
(message "%s" (if (and beg end)
(buffer-substring-no-properties beg end) "")))
and can store the line numbers of the subroutines using:
(defun get-line-numbers (str)
(interactive "sEnter string: ")
(save-excursion
(goto-char 0)
(let (( sok 1) (list nil) pp)
(while sok
(setq pp (search-forward str nil t))
(if pp (push (line-number-at-pos pp) list)
(setq sok nil)))
(message "%s" list))))
I would now like to open a new buffer similar to when I use Ctrl-x Ctrl-b to execute list-buffers and then display each line number, together with the text on the line, and the user can select a given line, and press Enter to goto the given line in the original buffer..
Just wanted to show you my version of occur-dwim.
I remember spending some time to find out about the regexp-history variable.
The first function is similar to your get-selected-text.
(defun region-str-or-symbol ()
"Return the contents of region or current symbol."
(if (region-active-p)
(buffer-substring-no-properties
(region-beginning)
(region-end))
(thing-at-point 'symbol)))
(defun occur-dwim ()
"Call `occur' with a sane default."
(interactive)
(push (region-str-or-symbol) regexp-history)
(call-interactively 'occur))
To display the list-buffer you use get-buffer-create and clear it with erase-buffer (it might be that it already extisted).
To output the lines you search in the current buffer save the line in a string and put it into the list buffer via with-current-buffer and insert.
To make return special on the text or to make it clickable put a text-property with a local keymap on it.
With this guide you should be able to find everything you need in the elisp-manual.
Regarding your code, you get the beginning and end of the current region with (interactive "r"). Therewith you also get the error message if there is no active region.
Related
I write a elisp function to copy the current line if no region has be selected, but it does not work on emacs 24.5. When I hit the "M-w" keystrokes , there comes a message "Mark set" in the minibuffer. Did I miss something?
(defun copy-region-or-current-line (beg end)
"copy current if no region selected, copy the region otherwise"
(interactive "r")
(let ((cur-pos (point)))
(if (region-active-p)
(kill-ring-save beg end)
(progn
(kill-whole-line)
(yank)
(goto-char cur-pos)))))
(global-set-key (kbd "M-w") 'copy-region-or-current-line)
Your function works: You're calling yank and that command sets the mark; hence the message.
That's a side effect you undoubtedly don't want, though, and the kill+yank sequence isn't necessary.
You already know about kill-ring-save, so just use that with (line-beginning-position) and (line-end-position).
FYI, on account of the optional REGION argument to kill-ring-save, you could rewrite this as:
(defun copy-region-or-current-line ()
"Copy the active region or the current line to the kill ring."
(interactive)
(if (region-active-p)
(kill-ring-save nil nil t)
(kill-ring-save (line-beginning-position) (line-end-position))))
My aim is to get the output from a process in Emacs.
For example, M-x run-python gives me a python shell *Python* that I can send python code to. If I send print "hello world" to *Python*, I hope Emacs can know the result once the execution is finished and echo it in the mini-buffer.
Is it possible to add something like a callback?
Thanks to the comments from #lawlist , I solved my problem by creating the following filter function and assigning it to the process (*MozRepl* in my case) with (set-process-filter (get-buffer-process "*MozRepl*") 'moz-controller-repl-filter)
(defun moz-controller-repl-filter (proc string)
"Filter function of *MozRepl*.
It gets the useful output of *MozRepl*, store it in `moz-controller-repl-output` and `kill-ring`"
(when (buffer-live-p (process-buffer proc))
(unless (string= string "repl> ") ; ignore empty output (page up, page down, etc)
(setq moz-controller-repl-output
(replace-regexp-in-string "\"\\(.+\\)\"\nrepl> " "\\1" string))
(kill-new moz-controller-repl-output) ; append to kill-ring
(message moz-controller-repl-output) ; show the copied content in echo area
)
(with-current-buffer (process-buffer proc)
(let ((moving (= (point) (process-mark proc))))
(save-excursion
;; Insert the text, advancing the process marker.
(goto-char (process-mark proc))
(insert string)
(set-marker (process-mark proc) (point)))
(if moving (goto-char (process-mark proc)))))))
In Eclipse, highlighting multiple rows and pressing Ctrl+/ comments each of the lines of the selection.
Emacs has a function comment-or-uncomment-region that is close what I want, but behaves differently if the region only partially covers the lines I'm trying to comment.
Is there any way I make a function similar to comment-or-uncomment-region, but have it comment each of the lines of the region regardless of how the region is selected?
In other words, I want the function to act as though the region occupies the whole line as long as the region includes that line, so it behaves as Eclipse's selection commenting does.
EDIT: I am actually using the comment-or-uncomment-region-or-line function mentioned as an answer instead of the function comment-or-uncomment-region that comes with Emacs.
I feel as though this is worth mentioning because the former seems to reflect how the line commenting works in Eclipse more. That is, the line the point is on is commented if no region exists.
I ended up combining parts from juanleon's and Ehvince's answers to get something just a little more like Eclipse's commenting.
Here is the final product:
(defun comment-eclipse ()
(interactive)
(let ((start (line-beginning-position))
(end (line-end-position)))
(when (or (not transient-mark-mode) (region-active-p))
(setq start (save-excursion
(goto-char (region-beginning))
(beginning-of-line)
(point))
end (save-excursion
(goto-char (region-end))
(end-of-line)
(point))))
(comment-or-uncomment-region start end)))
Please let me know if anything is wrong with it.
Note that emacs 25 has a new function comment-line bound to C-x C-;.
Here you have a function that do what you are describing:
(defun comment-or-uncomment-region-eclipse-style (beg end &optional arg)
(interactive "*r\nP")
(comment-or-uncomment-region
(save-excursion
(goto-char beg)
(beginning-of-line)
(point))
(save-excursion
(goto-char end)
(end-of-line)
(point)) arg))
FWIW, I don't use comment-or-uncomment-region. I use comment-region instead. It's similar, but it lets you decide whether to uncomment or comment. It lets you nest comments, instead of automatically uncommenting the region if it is already commented out. With a numeric prefix arg it uses that many comment-start chars (e.g., ;, ;;, ;;;,... in Lisp). With a plain C-u prefix arg it uncomments. I bind it to C-x C-;.
Anyway, I think this does what you want, using comment-region (see that for the general behavior):
(defun comment-region-lines (beg end &optional arg)
"Like `comment-region', but comment/uncomment whole lines."
(interactive "*r\nP")
(if (> beg end) (let (mid) (setq mid beg beg end end mid)))
(let ((bol (save-excursion (goto-char beg) (line-beginning-position)))
(eol (save-excursion (goto-char end) (line-end-position))))
(comment-region bol end arg)))
;; Suggested binding
(define-key ctl-x-map [(control ?\;)] 'comment-region-lines)
This saves and restores the region. And it works if only part of a single line is selected. I might even use it myself (which is saying quite a bit, since I have pretty set habits for this kind of thing).
Compared to Juanleon's solution, mine adds the fact that if you don't select a region it will (un)comment the current line and go the next line (instead of doing something based on marks you don't see):
(defun comment-or-uncomment-region-or-line ()
"Comments or uncomments the region or the current line if there's no active region."
(interactive)
(let (beg end)
(if (region-active-p)
(setq beg (region-beginning) end (region-end))
(setq beg (line-beginning-position) end (line-end-position)))
(comment-or-uncomment-region beg end)
(next-line)))
;; bind it to F7:
(global-set-key (kbd "<f7>")'comment-or-uncomment-region-or-line)
taken from: Emacs comment/uncomment current line
There is a file which provides the following
(defun ar-comment-or-uncomment-lor (&optional copy beg end)
"Comment line or region, unless it's already commented:
uncomment then.
..."
...
Afterwards cursor is at next line, which permits repeated execution.
With C-u the current line is copied and inserted as comment above - thus reminding the previous state when editing.
Get it here:
https://github.com/andreas-roehler/werkstatt/blob/master/ar-comment-lor.el
Here's a slight change to Ehvince's function which only advances to the next line if text was commented out. i.e., if uncommenting text, you usually want the cursor to remain.
(defun comment-or-uncomment-region-or-line ()
"Comments or uncomments the region or the current line if there's no active region."
(interactive)
(let (beg end)
(if (region-active-p)
(setq beg (region-beginning) end (region-end))
(setq beg (line-beginning-position) end (line-end-position)))
(comment-or-uncomment-region beg end)
(when (comment-only-p beg end)
(next-logical-line))))
I use M-q for fill-paragraph, can I do the un-fill-paragraph in auto-fill-mode?
With org mode, I sometimes enter [[Very long HTML][Name with spaces]], and for the 'Name with spaces' the auto-fill mode break the whole line based on the inserted space, which makes it very ugly.
Is there a command something like un-fill-paragraph? Or, is there a way disable auto-fill-mode temporarily/locally?
Emacs does not record what was your line before calling fill-paragraph. So the only thing you can do is C-_ which runs the command undo. It can undo your fill-paragraph command but only if it is the preceding command call.
If you want to put a multi-line paragraph on one line you could do like this :
Select the region
C-M-% C-q C-j RET SPACE RET !
Xah Lee has updated his code since monotux's answer, and I refactored it somewhat for readability:
(defun my-toggle-fill-paragraph ()
;; Based on http://xahlee.org/emacs/modernization_fill-paragraph.html
"Fill or unfill the current paragraph, depending upon the current line length.
When there is a text selection, act on the region.
See `fill-paragraph' and `fill-region'."
(interactive)
;; We set a property 'currently-filled-p on this command's symbol
;; (i.e. on 'my-toggle-fill-paragraph), thus avoiding the need to
;; create a variable for remembering the current fill state.
(save-excursion
(let* ((deactivate-mark nil)
(line-length (- (line-end-position) (line-beginning-position)))
(currently-filled (if (eq last-command this-command)
(get this-command 'currently-filled-p)
(< line-length fill-column)))
(fill-column (if currently-filled
most-positive-fixnum
fill-column)))
(if (region-active-p)
(fill-region (region-beginning) (region-end))
(fill-paragraph))
(put this-command 'currently-filled-p (not currently-filled)))))
To remake a long line out of a paragraph in Org mode, I gave myself a new command. Here is the associated Emacs Lisp code:
(defun fp-unfill-paragraph (&optional justify region)
(interactive (progn
(barf-if-buffer-read-only)
(list (if current-prefix-arg 'full) t)))
(interactive)
(let ((fill-column 100000))
(fill-paragraph justify region)))
(global-set-key "\C-ceu" 'fp-unfill-paragraph)
Of course, you adjust the command keybinding as you see fit!
I use the following snippet to fill and un-fill paragraphs (using only M-q), it is really, really handy. I borrowed it from Xah Lee, but removed some comments and whitespace in order to make it fit in here. The link in the first comment goes to his original code.
;; http://xahlee.org/emacs/modernization_fill-paragraph.html
(defun compact-uncompact-block ()
"Remove or add line endings on the current block of text.
This is similar to a toggle for fill-paragraph and unfill-paragraph
When there is a text selection, act on the region.
When in text mode, a paragraph is considered a block. When in programing
language mode, the block defined by between empty lines.
Todo: The programing language behavior is currently not done.
Right now, the code uses fill* functions, so does not work or work well
in programing lang modes. A proper implementation to compact is replacing
newline chars by space when the newline char is not inside string.
"
(interactive)
(let (bds currentLineCharCount currentStateIsCompact
(bigFillColumnVal 4333999) (deactivate-mark nil))
(save-excursion
(setq currentLineCharCount
(progn
(setq bds (bounds-of-thing-at-point 'line))
(length (buffer-substring-no-properties (car bds) (cdr bds)))))
(setq currentStateIsCompact
(if (eq last-command this-command)
(get this-command 'stateIsCompact-p)
(if (> currentLineCharCount fill-column) t nil)))
(if (and transient-mark-mode mark-active)
(if currentStateIsCompact
(fill-region (region-beginning) (region-end))
(let ((fill-column bigFillColumnVal))
(fill-region (region-beginning) (region-end)))
)
(if currentStateIsCompact
(fill-paragraph nil)
(let ((fill-column bigFillColumnVal))
(fill-paragraph nil))))
(put this-command 'stateIsCompact-p
(if currentStateIsCompact
nil t)))))
(global-set-key (kbd "M-q") 'compact-uncompact-block)
My problem is I am opening a buffer using (set-buffer (find-tag-noselect (current-word))) and then I try to copy some text out of that buffer. The text that I get back has only the properties (fontified nil). find-tag-noselect automatically opens the buffer found in the TAGS file but it seems it does not run the font lock mode over it. When I manually switch to this buffer after it has been opened and then run the function again when it copies the text it has all the correct text properties attached. So what do I need to do to have this buffer completely initialized so that the correct syntax highlighting will be copied in?
(defvar newline-string "
")
(defun get-initial-indent-regexp-python()
"Gets the initial amount of spaces for the function we are looking at, does not account for tabs"
(concat "^" (get-current-indent-string) (concat "[^ #" newline-string "]")))
(defun get-end-of-function-python(spaces-regex)
"Gets the point at the end of a python block"
(save-excursion
(forward-line 1)
(while (and (not (looking-at spaces-regex)) (equal (forward-line 1) 0)))
(point)))
(defun get-point-at-end-of-function ()
"This might be better served checking the major mode."
(setq extension (file-name-extension (buffer-file-name)))
(if (equal extension "py")
(get-end-of-function-python (get-initial-indent-regexp-python))))
(defun inline-function ()
"Must change to overlays, be able to toggle visibility"
(interactive)
(let (text indent-string)
; clean all overlays without attached buffer
(save-excursion
(set-buffer (find-tag-noselect (current-word)))
(setq text (buffer-substring (point) (get-point-at-end-of-function))))
(setq text (concat newline-string text))
(save-excursion
(move-end-of-line nil)
(let (overlay)
(setq overlay (make-overlay (point) (+ (point) 1) (current-buffer)))
(overlay-put overlay 'display text)
(setq inline-func-overlays (cons overlay inline-func-overlays))))))
What's happening is that font-lock is done on-the-fly, so only the displayed parts of the buffer get "fontified". If you want/need to overrule this optimization, you need different functions depending on the circumstance (depending on how font-lock happens to be configured). We should add a new font-lock-ensure-fontified function for that, but in the mean time, you can take ps-print-.el as an example:
(defun ps-print-ensure-fontified (start end)
(cond ((and (boundp 'jit-lock-mode) (symbol-value 'jit-lock-mode))
(jit-lock-fontify-now start end))
((and (boundp 'lazy-lock-mode) (symbol-value 'lazy-lock-mode))
(lazy-lock-fontify-region start end))))
I'm not exactly sure what you're trying to do, but set-buffer does not display the buffer, so its effect ends when the current command terminates. It's generally useful only for temporary buffer switches inside a function and I guess this is the reason it doesn't run font-lock on the buffer. When you manually go to the buffer you're probably using a different function - switch-to-buffer.
Try explicitly calling 'font-lock-fontify-buffer'.