Posts Tagged elisp

Reload buffer when file changes in Emacs

I’ve started learning typescript and one of the cool feature of compiler is –watch options that recompile the file when it change it’s content. In Emacs if file is changed the buffer still contain old file and if you try to edit that file you’re ask if you really want to ed it that file because it change content.

It found that it would be nice to have reload when file changes, so I wrote this little bit of Emacs Lisp:

(defun reload ()
  "Revert buffer, then restore previous position."
  (let ((pt  (point)))
    (revert-buffer t t)
    (goto-char pt)))

;; two helpers to work with alists

(defun add-to-alist (symbol name value)
  "Add new pair to ALIST"
  (let* ((list (symbol-value symbol))
         (pair (assoc name list)))
    (if pair
        (setcdr pair value)
      (let ((pair (cons name value)))
        (if list
            (setf (symbol-value symbol) (cons pair list))
          (setf (symbol-value symbol) (cons pair nil)))))
    (symbol-value symbol)))

(defun remove-alist (symbol name)
  "Remove pair from ALIST"
  (let* ((list (symbol-value symbol))
         (pair (assoc name list)))
    (if pair
        (let ((ptr list))
          (if (eq (caar ptr) name)
              (setf (symbol-value symbol) '())
            (while ptr
              (if (eq (caadr ptr) name)
                    (setcdr ptr (cddr ptr))
                    (setq ptr nil))
                (setq ptr (cdr ptr))))

(require 'filenotify)

(setq file-watchers '())

(defun watch ()
  "Function watch file changes and reload the file from buffer"
  (let* ((fname (buffer-file-name))
         (pair (assoc fname file-watchers)))
    (if pair
          (file-notify-rm-watch (cdr pair))
          (remove-alist 'file-watchers descritor)))

    (let ((descriptor (file-notify-add-watch fname
                                             (lexical-let ((filename fname))
                                               (lambda (event)
                                                 (with-current-buffer (get-file-buffer filename)
                                                   (if (not (buffer-modified-p))
                                                         (message "update %s" filename)
      (add-to-alist 'file-watchers fname descriptor))))

Then to watch the file You just need to invoke the function using M-x watch. The function will not reload the buffer if you have modifications. In that case you need to invoke reload function using M-x reload.

This will probably don’t work on windows but I’m not sure, here is documentation for file-notify-* functions.




Show code coverage from jest framework in emacs

I recently switched from jasmine/istanbul to jest framework for my project jQuery Terminal and I’ve got coverage drop from 80-81% to about 69% with some update of the code and commented out code that I was working on.

So I thought it would be cool to highlight the lines that where covered and not covered by tests and since I use emacs as my editor I thought that I write a function that will do that.

Here it is:

(defun root-git-repo ()
  (replace-regexp-in-string "\n"
                            (shell-command-to-string "git rev-parse --show-toplevel")))

(defun line-pos-at-line (line)
    (goto-line line)

(defun coverage-mark-buffer ()
  (let* ((dir (root-git-repo))
         (json-object-type 'hash-table)
         (json-array-type 'list)
         (json-key-type 'string)
         (json (json-read-file (concat dir "/coverage/coverage-final.json")))
         (filename (buffer-file-name (current-buffer)))
         (coverage (gethash filename json))
         (statments (gethash "statementMap" coverage)))
      (maphash (lambda (key value)
                 (let* ((statment (gethash key statments))
                        (start (gethash "start" statment))
                        (end (gethash "end" statment))
                        (start-line-pos (line-pos-at-line (gethash "line" start)))
                        (start-pos (+ start-line-pos (gethash "column" start)))
                        (end-line-pos (line-pos-at-line (gethash "line" start)))
                        (end-pos (+ end-line-pos (gethash "column" end)))
                        (color (if (= value 0) "dark red" "dark green"))
                        (face `((t (:background ,color)))))
                    (hlt-highlight-region start-pos end-pos face)))
               (gethash "s" coverage)))))

The function is using hlt-highlight-region from highlight.el by Drew Adams.

The function don’t check if coverage file exists. It assume that you’re opening from from git repo and that coverage file is in coverage directory (default for jest) in git root.

The function is quite slow, and it need to process a lot, if the file is big. You can see how it look like in this page, generated by Emacs library htmlize.

If you want to clear the buffer you can use this function:

(defun coverage-clear-buffer ()
    (hlt-unhighlight-region 0 (point))))

Clear is much faster.

If you’re iterested in code coverage in Emacs you can take a look at jest-coverage minor mode that I’ve created based on this solution.

, , ,

Leave a comment

Async shell command execution in GNU Emacs

In GNU Emacs if you execute shell command for instance using exec function the whole Editor freeze until shell command is finished. So I’ve written asynchronous version of exec:

(defun async-exec-command (command &rest success)
  (interactive "MExecute command: ")
  (let* ((buffer-name (generate-new-buffer-name "**shell**"))
         (buffer (get-buffer-create buffer-name))
         (process (apply #'start-process
                         (append (list buffer-name buffer)
                                 (split-string command " ")))))
    (lexical-let ((buffer buffer) (success (car success)) (command command))
      (set-process-sentinel process
                            (if success
                                (lambda (process str)
                                    (set-buffer buffer)
                                    (let ((content (buffer-string)))
                                      (kill-buffer buffer)
                                      (if (or (string= str "finished\n")
                                              (string-match "exited abnormally" str))
                                          (funcall success content)
                                        (message content)))))
                              (lambda (proces str)
                                (kill-buffer buffer)))))
    (concat "execute: " command)))

, ,

Leave a comment

Faster Emacs Window Switching Inside a Frame

I needed something to speed up my work when I have few windows, I was looking at switch-window.el, but it kind of silly that the content of the window disappear. So I’ve written this simple 2 functions which act similar to my other sollution Faster buffer bookmarking in Emacs. But you mark window to which you want to jump.

It set Keyboard C-c C-0 for mark and C-C C-<1-9> to jump.

(setq window-alist '())

(defun mark-window (number)
  "Give current window a number to select it later using `switch-to-number-window'"
  (interactive "nNumber Your Window: ")
  (let ((pair (assoc number window-alist))
        (window (get-buffer-window)))
    (if pair
        (setf (cdr pair) window)
      (add-to-list 'window-alist (cons number window)))))

(defun switch-to-number-window (number)
  "Jump to the window  marked with a `mark-window' function"
  (interactive "nNumber Your Window: ")
  (let ((pair (assoc number window-alist)))
    (if pair
        (select-window (cdr pair))
      (message "Invalid Number"))))

;; create 9 keyboard shortcuts using closure (forced using lexical-let)
(dolist (i (map #'1+ (range 9)))
    (global-set-key (read-kbd-macro (concat "C-c C-"
                                            (number-to-string i)))
                    (lexical-let ((i i))
                      (lambda ()
                        (switch-to-number-window i)))))

(global-set-key (kbd "C-c C-0") 'mark-window)

It use range helper function (same as in python)

(defun range (n &optional list)
  "function return list of numbers from 1 to n"
  (if (eq n 0)
    (let ((n (- n 1)))
      (range n (cons n list)))))

EDIT: I just found windmove commands which are much better.

, ,

Leave a comment

ERC notifications on channels where there was activity after some inactivity

I need to monitor few IRC channels and I use Emacs so I write simple elisp function that I append to erc-insert-pre-hook and it notify me when there is some activity on those channels. I made this mainly because I what to know if someone visit #openclipart channel (because people where visiting ask question and leave after few minutes, there is no much activity on this channel)

(setq inactivity-buffer-alist '(("#openclipart" (inactivity . 900))
                                ("#hackerrank" (inactivity . 900))
                                ("#aiki" (inactivity . 900))))

(defun channel-activity (string &rest ignore)
  "notification when there is activity on a erc channel after inactivity"
  (let* ((buffer (buffer-name))
         (buffer-alist-pair (assoc buffer inactivity-buffer-alist))
         (buffer-alist (cdr buffer-alist-pair))
         (current-time (current-time)))
    (if (not (null buffer-alist))
        (let ((last-time-pair (assoc 'last-time buffer-alist))
              (inactivity (cdr (assoc 'inactivity buffer-alist))))
          (if (not (and (string-match "^\\*\\*\\*" string)
                        (string-match "[freenode-info]" string)))
                (if (or (null last-time-pair)
                        (> (float-time (time-subtract current-time
                                                      (cdr last-time-pair)))
                    (async-exec-command "mpg123 -q /home/kuba/Pobrane/beep-6.mp3"))
                (if (null last-time-pair)
                    (setf (cdr buffer-alist-pair)
                          (append buffer-alist
                                  (list (cons 'last-time current-time))))
                  (setf (cdr last-time-pair) current-time))))))))

(add-hook 'erc-insert-pre-hook 'channel-activity)

You can add your channels to inactivity-buffer-alist along with time of inactivity (in miliseconds)

The function I use for notification is play sound using (async-exec-command "mpg123 -q /home/kuba/Pobrane/beep-6.mp3") – normal shell command was stoping execution of Emacs for few seconds

The code for this function is as follow

(defun async-exec-command (command &rest success)
  (let* ((buffer-name (generate-new-buffer-name "**shell**"))
         (buffer (get-buffer-create buffer-name))
         (process (apply #'start-process
                         (append (list buffer-name buffer)
                                 (split-string command " ")))))
    (lexical-let ((buffer buffer) (success (car success)) (command command))
      (set-process-sentinel process
                            (if success (lambda (process str)
                                          (if (string= str "finished\n")
                                                (set-buffer buffer)
                                                (let ((content (buffer-string)))
                                                  (kill-buffer buffer)
                                                  (funcall success content)))))
                              (lambda (proces str)
                                (kill-buffer buffer)))))
    (concat "execute: " command)))

, , ,

Leave a comment

%d bloggers like this: