How emacs js2-mode jump to definition open in other window? - emacs

In js2-mode, M-. runs the command js2-jump-to-definition, this works great. But I want it go to the definition in other window. I find the code js2-jump-to-definition in Github.
What is the easiest way to make it open in other window?
(defun js2-jump-to-definition (&optional arg)
"Jump to the definition of an object's property, variable or function."
(interactive "P")
(if (eval-when-compile (fboundp 'xref-push-marker-stack))
(xref-push-marker-stack)
(ring-insert find-tag-marker-ring (point-marker)))
(js2-reparse)
(let* ((node (js2-node-at-point))
(parent (js2-node-parent node))
(names (if (js2-prop-get-node-p parent)
(reverse (let ((temp (js2-compute-nested-prop-get parent)))
(cl-loop for n in temp
with result = '()
do (push n result)
until (equal node n)
finally return result)))))
node-init)
(unless (and (js2-name-node-p node)
(not (js2-var-init-node-p parent))
(not (js2-function-node-p parent)))
(error "Node is not a supported jump node"))
(push (or (and names (pop names))
(unless (and (js2-object-prop-node-p parent)
(eq node (js2-object-prop-node-left parent)))
node)) names)
(setq node-init (js2-search-scope node names))
;; todo: display list of results in buffer
;; todo: group found references by buffer
(unless node-init
(switch-to-buffer
(catch 'found
(unless arg
(mapc (lambda (b)
(with-current-buffer b
(when (derived-mode-p 'js2-mode)
(setq node-init (js2-search-scope js2-mode-ast names))
(if node-init
(throw 'found b)))))
(buffer-list)))
nil)))
(setq node-init (if (listp node-init) (car node-init) node-init))
(unless node-init
(pop-tag-mark)
(error "No jump location found"))
(goto-char (js2-node-abs-pos node-init))))

Related

How to extract XML processing instructions in Emacs Lisp?

I would like to extract the processing instructions (particularly xml-model) from an XML file; yet both (n)xml-parse-file as well as libxml-parse-xml-region do not recognize processing instructions.
Is there a clean way to extract processing instructions or do I have to regex search for PIs?
edit: Here is a first draft of the functionality I was looking for:
(cl-defun extract-processing-instructions (&rest processing-instructions)
"Extracts all/only the specified xml processing instructions from the current buffer and returns them as a list of string."
(interactive)
(let ((pi-re
(format "<\\?\\(%s\\).*\\?>" (string-join processing-instructions "\\|")))
(result))
(save-excursion
(goto-char (point-min))
(while (re-search-forward pi-re nil t)
(push (match-string 0) result)))
(nreverse result)))
(cl-defun pi-str2sexp (pi-str)
"Takes a processing instruction as a string and transforms it to a sexp-structure (in the style of xml-parse-*)."
(let (sexp attr-alist)
(save-match-data
;; get and push pi-element-name
;; (string-match "<\\?\\([[:alnum:]-]*\\)" pi-str)
(string-match "<\\?\\([[:alnum:]-]*\\)" pi-str)
(push (make-symbol (match-string 1 pi-str)) sexp)
;; construct attribute alist
(while (string-match "\\([[:alnum:]-]*\\)=\"\\([^ ]*\\)\""
pi-str (match-end 0))
(push (cons (make-symbol (match-string 1 pi-str))
(match-string 2 pi-str))
attr-alist)))
;; finally: push attr alist and return sexp
(push (nreverse attr-alist) sexp)
(nreverse sexp)))
edit 2: Turns out advicing/generally building upon xml-parse-* in this matter (like suggested by #Tom Regner) is a huge pain. :(
The thing I came up with was a context manager, the idea was to use it to around-advice string-parse-tag-1 (which is at the heart of xml-parse-* (of course stand-alone use is also an option):
(cl-defmacro --replace-first-group (regex-replace-alist)
`(save-excursion
(dolist (expression ,regex-replace-alist)
(goto-char (point-min))
(replace-regexp (car expression) (cadr expression)))))
(cl-defmacro with-parsable-pi (buffer &body body)
"Context manager that treats xml processing instructions in BUFFER as normal elements."
(declare (indent defun))
`(let ((old-buffer ,buffer))
(with-temp-buffer
(insert-buffer-substring old-buffer)
(goto-char (point-min))
(--replace-first-group '(("\\(\\?\\)>" "/>") ("<\\(\\?\\)" "<")))
,#body)))
This e.g. allows calls like
(with-parsable-pi (current-buffer)
(xml-parse-tag-1))
so it is at least possible to get an element at a time; but since the XML exposed in the context manager isn't actually valid and xml-parse-* (rightfully) errors if invalid XML is encountered, it isn't possible to process more than one element at a time.
I was thinking of maybe introducing a pseudo root element or something, but the kludge spiral is ghastly enough as it is.
Another idea of course would be to run an xpath query to extract processing instructions. If there only was a solid xpath solution in Emacs Lisp..
Ok, I think I found a satisfactory solution: xmltok-forward-prolog!
So here is the code I came up with for extracting processing instructions:
(cl-defun filter-xmltok-prolog (&optional (buffer (current-buffer))
(filter-re "processing-instruction-.*"))
"Filters the output of xmltok-forward-prolog (i.e. index 0 ('type') of each array) run in the context of BUFFER against FILTER-RE. Returns a list of vectors."
(with-current-buffer buffer
(save-excursion
(goto-char (point-min))
(let ((raw-prolog-data (xmltok-forward-prolog)))
(seq-filter
#'(lambda (x)
(string-match filter-re (symbol-name (aref x 0))))
raw-prolog-data)))))
(cl-defun --merge-pi-data (pi-data)
"Meant to operate on data filtered with filter-xmltok-prolog against 'processing-instruction-.*'.
Merges processing-instruction-left/-right and returns a list of vectors holding the start/end coordinates of a processing instruction at index 1 and 2."
(let ((left (car pi-data))
(right (cadr pi-data)))
(cond
((null pi-data) nil)
(t (cons
(vector 'processing-instruction
(aref left 1) (aref right 2))
(--merge-pi-data (cddr pi-data)))))))
;; test
(--merge-pi-data '([processing-instruction-left 40 51] [processing-instruction-right 52 126]))
(cl-defun pi-str2s-exp (pi-str)
"Takes a processing instruction as a string and transforms it into a sexp structure (in the style of xml-parse-*)."
(let (sexp attr-alist)
(save-match-data
;; get and push pi-element-name
(string-match "<\\?\\([[:alnum:]-]*\\)" pi-str)
(push (make-symbol (match-string 1 pi-str)) sexp)
;; construct attribute alist
(while (string-match "\\([[:alnum:]-]*\\)=\"\\([^ ]*\\)\""
pi-str (match-end 0))
(push (cons (make-symbol (match-string 1 pi-str))
(match-string 2 pi-str))
attr-alist)))
;; finally: push attr alist and return sexp
(push (nreverse attr-alist) sexp)
(nreverse sexp)))
(cl-defun get-processing-instructions (&optional (buffer (current-buffer)))
"Extracts processing instructions from BUFFER and returns a list of sexp representations in the style of xml-parse-*."
(save-excursion
(mapcar #'pi-str2s-exp
(mapcar #'(lambda (v)
(buffer-substring (aref v 1) (aref v 2)))
(--merge-pi-data (filter-xmltok-prolog buffer))))))
(cl-defun test/get-pis-from-file (file)
(with-temp-buffer
(insert-file-contents file)
(get-processing-instructions)))
(test/get-pis-from-file "~/some/xml/file.xml")
I'm not at all an Emacs Lisp expert and this isn't at all tested thoroughly, but it works for now! :)

How can I cycle through only those buffers which are in a given major mode (such as Python-mode ) ?

How can I cycle through only those buffers which are in a given major mode (such as Python-mode ) ?
Currently I am using C-X-Left/Right arrows, but these also show all sorts of irrelevant (i.e. non source code) buffers, any idea how can I restrict the buffer switching only to a specific type of buffer (with a given major mode) ?
I could not find something ready-made. However, it is not very hard to make the suitable commands.
(defun buffer-mode-alist ()
"Returns a list of (<buffer-name> . <major-mode>) pairs."
(let ((all-buffers (buffer-list))
(rv nil))
(while all-buffers
(let* ((this (car all-buffers))
(name (buffer-name this)))
(setq all-buffers (cdr all-buffers))
(when name
(setq rv (cons (cons name (with-current-buffer this major-mode)) rv)))))
rv))
(defun buffers-with-major-mode (the-major-mode)
(let ((buffer-alist (buffer-mode-alist))
(rv nil))
(while buffer-alist
(let ((this (car buffer-alist)))
(setq buffer-alist (cdr buffer-alist))
(if (eql (cdr this) the-major-mode)
(setq rv (cons (car this) rv)))))
(sort rv #'string<)))
(defun spin-buffers (buffer-list current)
(cond ((not (member current buffer-list)) buffer-list)
((string= current (car buffer-list)) buffer-list)
(t (spin-buffers (append (cdr buffer-list)
(list (car buffer-list)))
current))))
(defvar next-buffer-mode nil)
(defun choose-next-buffer-mode (mode)
"Ask for what major mode should be used as a selector for next-buffer-with-mode."
(interactive "aMajor Mode: ")
(setq next-buffer-mode mode))
(defun next-buffer-with-mode (set)
"Switches to the 'next' buffer with a given mode. If the mode is not set,
require it to be set, by calling choose-next-buffer-mode. If any prefix
argument is passed, also call choose-next-buffer-mode."
(interactive "P")
(when (or (not next-buffer-mode)
set)
(call-interactively 'choose-next-buffer-mode))
(let ((buffers (spin-buffers (buffers-with-major-mode next-buffer-mode)
(buffer-name))))
(when (cdr buffers)
(switch-to-buffer (cadr buffers)))))
(defun prev-buffer-with-mode (set)
"Switches to the 'previous' buffer with a given mode. If the mode is not set,
require it to be set, by calling choose-next-buffer-mode. If any prefix
argument is passed, also call choose-next-buffer-mode."
(interactive "P")
(when (or (not next-buffer-mode)
set)
(call-interactively 'choose-next-buffer-mode))
(let ((buffers (spin-buffers (buffers-with-major-mode next-buffer-mode)
(buffer-name))))
(when buffers
(switch-to-buffer (car (last buffers))))))

emacs ido-buffer : group buffers by directories

Is it possible to view the open buffers grouped by directories in emacs ido-buffer mode, in some sort of tree representation?
"emacs ido-buffer mode" ??
Did you mean ibuffer? If so...
It's not grouping1, but sorting by filename is a fairly useful approximation, and is available by default.
sf
Unfortunately (to my mind) dired buffers aren't included. You might fix that by defining a variant of the sorter which includes them, and then remapping the binding:
(eval-after-load 'ibuffer
'(progn
(define-ibuffer-sorter filename/directory/process
"Sort the buffers by their file name/process name."
(:description "file name")
(string-lessp
(or (buffer-file-name (car a))
(let ((dir (buffer-local-value 'dired-directory (car a))))
(if (consp dir) (car dir) dir))
(let ((pr-a (get-buffer-process (car a))))
(and (processp pr-a) (process-name pr-a))))
(or (buffer-file-name (car b))
(let ((dir (buffer-local-value 'dired-directory (car b))))
(if (consp dir) (car dir) dir))
(let ((pr-b (get-buffer-process (car b))))
(and (processp pr-b) (process-name pr-b))))))
(define-key ibuffer-mode-map
[remap ibuffer-do-sort-by-filename/process]
'ibuffer-do-sort-by-filename/directory/process)))
1 A function to dynamically group by directory would be nifty.

In emacs, can I set up the *Messages* buffer so that it tails?

Basically I want the *Messages* buffer to always scroll to the bottom when a new message arrives.
Can I do that?
I found auto-revert-tail-mode but that works for buffers that are visiting files.
When I tried it in the Messages buffer, it popped an error:
auto-revert-tail-mode: This buffer is not visiting a file
For multiple frames you probably want:
(defadvice message (after message-tail activate)
"goto point max after a message"
(with-current-buffer "*Messages*"
(goto-char (point-max))
(walk-windows (lambda (window)
(if (string-equal (buffer-name (window-buffer window)) "*Messages*")
(set-window-point window (point-max))))
nil
t)))
Just put point at the end of the buffer M->. If you don't manually move it it will stay there -- IOW, you will always see the tail.
This code seems a bit overkill, but a the simple (goto-char (point-max)) wasn't working for me:
(defadvice message (after message-tail activate)
"goto point max after a message"
(with-current-buffer "*Messages*"
(goto-char (point-max))
(let ((windows (get-buffer-window-list (current-buffer) nil t)))
(while windows
(set-window-point (car windows) (point-max))
(setq windows (cdr windows))))))
Here's an implementation that uses the new advice style.
(defun message-buffer-goto-end-of-buffer (&rest args)
(let* ((win (get-buffer-window "*Messages*"))
(buf (and win (window-buffer win))))
(and win (not (equal (current-buffer) buf))
(set-window-point
win (with-current-buffer buf (point-max))))))
(advice-add 'message :after 'message-buffer-goto-end-of-buffer)
i run 23.3 and there were still way too many occasions where the built-in 'solution' and the orginal defadvice on the message function just didn't cut it, so i wrapped that code in a list / toggle / timer set up and it's working beautifully - no more frustration when debugging!
it's generic, so works on any buffer, although i only really use it for..
(toggle-buffer-tail "*Messages*" "on")
..hope it's useful to someone.
;alist of 'buffer-name / timer' items
(defvar buffer-tail-alist nil)
(defun buffer-tail (name)
"follow buffer tails"
(cond ((or (equal (buffer-name (current-buffer)) name)
(string-match "^ \\*Minibuf.*?\\*$" (buffer-name (current-buffer)))))
((get-buffer name)
(with-current-buffer (get-buffer name)
(goto-char (point-max))
(let ((windows (get-buffer-window-list (current-buffer) nil t)))
(while windows (set-window-point (car windows) (point-max))
(with-selected-window (car windows) (recenter -3)) (setq windows (cdr windows))))))))
(defun toggle-buffer-tail (name &optional force)
"toggle tailing of buffer NAME. when called non-interactively, a FORCE arg of 'on' or 'off' can be used to to ensure a given state for buffer NAME"
(interactive (list (cond ((if name name) (read-from-minibuffer
(concat "buffer name to tail"
(if buffer-tail-alist (concat " (" (caar buffer-tail-alist) ")") "") ": ")
(if buffer-tail-alist (caar buffer-tail-alist)) nil nil
(mapcar '(lambda (x) (car x)) buffer-tail-alist)
(if buffer-tail-alist (caar buffer-tail-alist)))) nil)))
(let ((toggle (cond (force force) ((assoc name buffer-tail-alist) "off") (t "on")) ))
(if (not (or (equal toggle "on") (equal toggle "off")))
(error "invalid 'force' arg. required 'on'/'off'")
(progn
(while (assoc name buffer-tail-alist)
(cancel-timer (cdr (assoc name buffer-tail-alist)))
(setq buffer-tail-alist (remove* name buffer-tail-alist :key 'car :test 'equal)))
(if (equal toggle "on")
(add-to-list 'buffer-tail-alist (cons name (run-at-time t 1 'buffer-tail name))))
(message "toggled 'tail buffer' for '%s' %s" name toggle)))))
edit: changed functionality to display tail at the bottom of the window
Here's an amendment over Peter's / Trey's solutions
(defun modi/messages-auto-tail (&rest _)
"Make *Messages* buffer auto-scroll to the end after each message."
(let* ((buf-name "*Messages*")
;; Create *Messages* buffer if it does not exist
(buf (get-buffer-create buf-name)))
;; Activate this advice only if the point is _not_ in the *Messages* buffer
;; to begin with. This condition is required; otherwise you will not be
;; able to use `isearch' and other stuff within the *Messages* buffer as
;; the point will keep moving to the end of buffer :P
(when (not (string= buf-name (buffer-name)))
;; Go to the end of buffer in all *Messages* buffer windows that are
;; *live* (`get-buffer-window-list' returns a list of only live windows).
(dolist (win (get-buffer-window-list buf-name nil :all-frames))
(with-selected-window win
(goto-char (point-max))))
;; Go to the end of the *Messages* buffer even if it is not in one of
;; the live windows.
(with-current-buffer buf
(goto-char (point-max))))))
(advice-add 'message :after #'modi/messages-auto-tail)

ELIM/garak in Emacs configuration

Does someone know how to setup ELIM in emacs?
There is no information about adding accounts etc.
when I run
/add-account
here comes a message
setq: Symbol's function definition is
void: garak-read-protocol
Thank you
This was fixed in commit d3c2f467ebf606fbe6406b2aac783aa68aa91019, which may not have made it into a release yet. You can try checking out from the git repo, or just monkeypatch in this definition:
(defun garak-read-protocol (proc)
(let ((available (mapcar 'car (elim-protocol-alist proc))))
(completing-read "protocol: " available nil t) ))
Privet, Dmitry!
(Ununtu 9.04, GNU Emacs 23.2.1 (i686-pc-linux-gnu, GTK+ Version 2.16.1) of 2010-06-21 on jonesbook)
Ya ustanovil ego tak:
1) u menia uge bil ustanovlen libpurple + pidgin (ver. 2.5.5, some old)
2) git clone elim source from github into dir ~/.emacs.d/elim and make it. OK. "elim-client" is here
3) Add link in .emacs to dir ~/.emacs.d/elim/elisp, e.g.
(add-to-list 'load-path "~/.emacs.d/elim/elisp")
(load-library "garak")
4) Optional. (emacs must compiled with --dbus). Install todochiku.el and put
(require 'todochiku)
Then, M-x garak
command
/add-account
work without such message: "Symbol definition is void: garak-read-protocol"
See garak.el
(add-account . garak-account-update )
...
(add-account . garak-cmd-add-account )
(defun garak-cmd-add-account (args)
(let (items user proto pass options elim errval)
(setq items (split-string args)
user (car items)
proto (cadr items)
items (cddr items))
(setq elim garak-elim-process)
(when (= (length proto) 0) (setq proto (garak-read-protocol elim)))
(when (= (length user ) 0) (setq user (garak-read-username elim proto)))
(when (and (car items) (not (string-match "=" (car items))))
(setq pass (car items) items (cdr items)))
(when (= (length pass ) 0) (setq pass (garak-read-password elim proto)))
;; options not supported yet:
;;(mapcar
;; (lambda (O) (setq options (nconc options (split-string "=" O)))) items)
;; (message "(elim-add-account PROC %S %S %S %S)" user proto pass nil)
(elim-add-account elim user proto pass options)
(format "/add-account %s" args) ))
(defun garak-account-update (proc name id status args)
"This function handles updating the garak ui when the state of one of your
accounts changes. Typically this is as a result of elim-account-status-changed
elim-connection-state or elim-connection-progress, but any call can be handled as long as an \"account-uid\" entry is present in the ARGS alist."
(let (buffer auid where-widget point end icon-name
icon conn kids node tag proto iname alt atag aname)
(setq buffer (elim-fetch-process-data proc :blist-buffer)
auid (elim-avalue "account-uid" args)
status nil)
;; update any account conversation buffers with _our_ new status
(garak-update-account-conversations proc auid)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; proceed to updating the blist ui buffer
(when (buffer-live-p buffer)
(with-current-buffer buffer
(setq where-widget (garak-ui-find-node auid :account))
;; set the conn or status data (whichever is appropriate)
(cond ((eq name 'elim-account-status-changed) (setq status args))
((eq name 'elim-connection-state ) (setq conn args))
((eq name 'elim-connection-progress ) (setq conn args)))
;; fetch any data we did not receive:
(when (not conn ) (setq conn (elim-account-connection proc auid)))
(when (not status) (setq status (elim-account-status proc auid)))
;; pick the most suitable status icon
(if (eq name 'elim-exit)
(setq icon-name ":offline")
(setq icon-name (garak-account-list-choose-icon conn status)))
;;(message "CHOSE ICON: %S" icon-name)
;; widget not found or removing an account => refresh the parent node.
;; otherwise => update node icon
(if (or (eq 'remove-account name) (not where-widget))
;; refreshing parent node:
(when (setq where-widget (garak-ui-find-node :accounts :garak-type)
point (car where-widget))
(setq node (widget-at point)
kids (garak-tree-widget-apply node :expander))
(garak-tree-widget-set node :args kids)
(when (garak-tree-widget-get node :open)
(widget-apply node :action)
(widget-apply node :action)))
;; updating node icon:
(setq point (car where-widget)
end (next-single-char-property-change point 'display)
tag (elim-avalue icon-name garak-icon-tags)
adata (elim-account-data proc auid)
proto (elim-avalue :proto adata)
aname (elim-avalue :name adata)
iname (format ":%s" proto)
atag (or (elim-avalue iname garak-icon-tags) " ?? ")
alt (format "[%-4s]%s%s" atag tag aname)
icon (tree-widget-find-image icon-name))
(let ((inhibit-read-only t) old)
(setq widget (widget-at point)
old (widget-get widget :tag))
(if (eq (cdr where-widget) 'menu-choice)
(widget-put widget :tag alt)
(widget-put widget :tag tag))
(if (and icon (tree-widget-use-image-p))
(put-text-property point end 'display icon) ;; widgets w images
(when tag
(setq end (+ (length old) point))
(save-excursion
(goto-char point)
(setq old (make-string (length old) ?.))
(when (search-forward-regexp old end t)
(if (eq (cdr where-widget) 'menu-choice)
(replace-match alt nil t)
(replace-match tag nil t))) )) )) )) )))
In these functions there are many calls (setq ...) and i don't know - where to search problem.
Your should give more information about error. Commands stack, for example.