BIRKEY CONSULTING

ABOUT  RSS  ARCHIVE


Posts tagged "emacs":

01 Aug 2021

Why Eshell? - Part 5

One of the feature of Eshell that took me sometime to really appreciate is its built-in ability to emulate Plan9 smart Shell. It allows you to run a script or a command, run it again by just hitting enter key after you modify it, say you made a mistake or want to change part of it using Emacs editing power. You might say that you can do same thing in any terminal using your up arrow key and command line editing. But I challenge you to try it until you fully realize the advantage you have vs regular terminal. Below is a simple screen recording to show you what I mean:

eshell-plan9-smart-shell.gif

I recommend you read following post to learn more about smart shell and more about eshell including aliases: https://masteringemacs.org/article/complete-guide-mastering-eshell.

This concludes my `Why Eshell` series. Hope you find it useful and happy Eshelling!

Tags: eshell emacs
17 Jul 2021

Why Eshell? - part 4

Since eshell buffer is just a regular emacs buffer, we have all of the emacs power at our disposal. In part 3 of my post, I briefly alluded to multiple terminal management by just using few lines of elisp and all without using any third party packages. I am posting the main elisp function for posterity here:

(defun krun (cmd)
  (interactive
   (list
    (ido-completing-read
     "Enter cmd to run (append ##name for buffer name): "
     (let ((history nil))
       ;; We have to build up a list ourselves from the ring vector.
       (dotimes (index (ring-length keshell-history-global-ring))
	 (push (ring-ref keshell-history-global-ring index) history))
       ;; Show them most-recent-first.
       (setq history (nreverse history))))))
  (let* ((cmds (split-string cmd "##"))
	 (tag (or (-> cmds second)
		  "kshell"))
	 (buff-name (-> tag s-chomp s-trim)))
    (kshell cmd buff-name)))

The above function (along with those from the previous post) will allow you to do following:

Following demonstrates above cases I am talking about:

eshell-buffer-management.gif
Tags: eshell emacs
10 Jul 2021

Why Eshell? - Part 3

If you have been following my `Why Eshell` series, you might be wondering about interactive ido completion for eshell. Since Eshell buffer is just a regular emacs buffer, we can compose few emacs builtin functionalities to bend it to our command line work flow. Following is all the code you need. Note that it is heavily commented so you can understand what is happening.

(defun kshell-with-name (&optional name)
  ;; creates an eshell buffer with `kshell' as the default name. Take
  ;; optional name to override
  (let ((m (->> (or name "kshell")
		(format "*%s*"))))
    (if (get-buffer m)
	(pop-to-buffer m)
      (progn
	(eshell)
	(rename-buffer m)))))

(defun kshell (&optional cmd buff-name send-input)
  ;; interactive fn that you can call via M-x or hotkey. It detects
  ;; current project root to start eshell buffer if no `buff-name' is
  ;; given. You can control how the `cmd` gets insert only or insert
  ;; and executed using `send-input` flag. Ex. Useful for further
  ;; editing.
  (interactive)
  (let ((dir (projectile-project-root))) ;; you need projectile
    (if buff-name
	(kshell-with-name buff-name)
      (kshell-with-name))
    (eshell/clear-scrollback)
    (insert (format "cd %s" (or dir "~/")))
    (eshell-send-input)
    (insert (format "%s" cmd))
    (when send-input (eshell-send-input))))

(defun krun (cmd)
  ;; eshell command history with ido completion. Assign it to a hot
  ;; key say, F12 and you will get a search-able command history that
  ;; you can execute just by doing ido interactive search.
  (interactive
   (list
    (ido-completing-read
     "Enter cmd to run (append ##name for buffer name): "
     (let ((history nil))
       ;; We have to build up a list ourselves from the ring vector.
       (dotimes (index (ring-length keshell-history-global-ring))
	 (push (ring-ref keshell-history-global-ring index) history))
       ;; Show them most-recent-first.
       (setq history (nreverse history))))))
  (let* ((cmds (split-string cmd "##")) ;; you need s.el lib
	 (tag (or (-> cmds second)
		  "kshell"))
	 (buff-name (-> tag s-chomp s-trim)))
    (kshell cmd buff-name)))

Here is the screen recording in action:

eshell-ido-interactive.gif

I am using F12 to invoke `krun` and my cross session eshell history shows up in the minibuffer where I can use ido completion to select a command that I can run. As an added benefit, above code allows one to bring a eshell buffer with specific name by adding ##name at the end of the command if one wants to have the command run in a dedicated buffer.

Tags: eshell emacs
27 Jun 2021

Why Eshell? - Part 2

Among many reasons of why I use eshell as my main terminal, I listed following in part 1 of my `Why Eshell?` series blog post. I am listing it here for posterity:

  1. Long running command notification and time
  2. Cross session history
  3. Interactive ido completion
  4. Unified interface (shell prompt buffer as regular emacs buffer)
  5. Plan9 Style Shell prompt (Think of it as bash REPL)
  6. Multiple terminal management
  7. Super charge bash with elisp
  8. Eshell aliases that puts bash aliases to shame :)

I addressed first point here: notification and time. Let me address second point, which is sharing eshell history across many terminal sessions.

(defvar keshell-history-global-ring nil
  "The history ring shared across Eshell sessions.")

(defun keshell-hist-use-global-history ()
  "Make Eshell history shared across different sessions."
  (unless keshell-history-global-ring
    (when eshell-history-file-name
      (eshell-read-history nil t))
    (setq keshell-history-global-ring
	  (or eshell-history-ring (make-ring eshell-history-size))))
  (setq eshell-history-ring keshell-history-global-ring))
;; Following hook enables it
(add-hook 'eshell-mode-hook 'keshell-hist-use-global-history)
;; Following removes the hook
(remove-hook 'eshell-mode-hook 'keshell-hist-use-global-history)

After evaluating above 12 lines of elisp, you will have a list that holds all the eshell entries across sessions, which you can persist into a history file (just uses bash history file) and can even share it across machines over network. This opens up a whole new possibilities of completions, deduplication and multiple eshell buffer management goodness, which I will cover in the next few parts of `Why Eshell?` posts. So stay tuned and happy eshelling!

Tags: eshell emacs
20 Jun 2021

Why Eshell? - Part 1

From time to time, I got asked why I use eshell as my main terminal. There are multiple reasons I do so and covering all of them in one post would be too long. Instead, I would like to start with a high level bullet points and address each one in a separate blog post with working code examples where I can point my coworkers and friends to them so they can pick and choose as they wish.

  1. Long running command notification and time
  2. Cross session history
  3. Interactive ido completion
  4. Unified interface (shell prompt buffer as regular emacs buffer)
  5. Plan9 Style Shell prompt (Think of it as bash REPL)
  6. Multiple terminal management
  7. Super charge bash with elisp
  8. Eshell aliases that puts bash aliases to shame :)

Let us start with how you can accomplish the first item from the list above. Following 18 lines of elisp will give you the super power of:

;; eshell time and notification
(defvar-local eshell-current-command-start-time nil)

(defun eshell-current-command-start ()
  (setq eshell-current-command-start-time (current-time)))

(defun eshell-current-command-stop ()
  (when eshell-current-command-start-time
    (let ((elapsed-time (float-time
			 (time-subtract (current-time)
					eshell-current-command-start-time))))
      (if (> elapsed-time 30)
	  (tooltip-show (format "Finished in: %.0fs" elapsed-time))
	(eshell-interactive-print
	 (format "Time: %.0fs\n" elapsed-time))))
    (setq eshell-current-command-start-time nil)))

(defun eshell-current-command-time-track ()
  (add-hook 'eshell-pre-command-hook #'eshell-current-command-start nil t)
  (add-hook 'eshell-post-command-hook #'eshell-current-command-stop nil t))

;; This line below installs time tracking and notification
(add-hook 'eshell-mode-hook #'eshell-current-command-time-track)
;; Once you eval above snippet in emacs, fire up M-x eshell, and type:
sleep 40
;; You can switch away from emacs and will be notified that above
;; command took 40s to run
;; To uninstall
;; (remove-hook 'eshell-mode-hook #'eshell-current-command-time-track)

Try doing above with some bashrc voodoo or plug-in that you have no control over. I have been there and done that and it is one of many reasons why I use eshell now. I hope someone finds it useful to his or her command line work flow. Happy eshelling!

Tags: emacs eshell
04 Aug 2019

Why Emacs

I have been thinking about writing up my experience of using Emacs and its positive influence on me as a Software Craftsman over the last 10 years or so. Over the weekend, I came across a a blog post by the CTO of a company, who put it very well that have really resonated with me. It is a great read, not too long not too short, that I strongly recommend you go ahead read and come back here to continue with the rest of what I had to say. Here is the link: Two Years With Emacs as a CEO (and now CTO).

So I am not going to repeat what he said about Emacs and why he still loves to use it to get things done. I am going to add following points on top of what he wrote there:

Emacs is a great workbench

I see Emacs as an extremely well designed workbench that will evolve with you as your work environment, technology and paradigms change. Emacs is a live programming environment where you can change every aspects of its functionalities, be it a simple text editing to a complex work flow where you can interact with many external systems. One of a startup that I worked had a mode where we have interacted with our live system written in Clojure via its CLI interface using `comint-mode`, which you should really checkout if you are not familiar with it. You can use Emacs as the client with uniform interface to many of the CLI, API and even for ABI. For example `EXWM`, which is an Window Manager, is a great example.

Emacs is a great structured and unstructured text manipulation library

This is what I personally like a lot about Emacs. As a programmer, we work with manipulating text all the time. Yes, there are great text Editors out there, which by the way I have used all of them myself before seeing the light of using Emacs. But I see all other editors/IDEs as a framework for the things they set out to do rather than a library that I can compose to solve a particular problem at hand. For example, I can use `smartparens` package for structured text editing of Clojure code.

Emacs helps you focus

In this day of our age where everything tries to grab your constant attention, Emacs stands out as the most distraction free environment to be in. One might argue that Emacs user spends way more time configuration management, which is definitely true when you are just getting started with any new toolbox, I found I spend very little time with configuration or keeping it up to date. Once you are familiar with Emacs help system and built in Documentation, You can be pretty much on your own when it comes to how much you spend on customization. It is this freedom you get from using Emacs where you are in charge as to how much customization you would like. The reason I say Emacs helps you focus is due to the fact that you have the freedom to make it an distraction free writing/coding/communication environment to get done what matters the most to you and have a lot of fun along the way.

Emacs has a learning curve

Yes, I do acknowledge that Emacs has a learning curve. However, most of it is due to the wrong approach we tend to take when encountering an unknown. On hindsight, I wish I had following approach, which I do now all the time:

  1. Start with getting familiar with Emacs terminology such as windows, buffers and frames etc. The builtin documentation is great for that and it is just C-h i (Hit Control and letter `h` at the same time, then hit `i`, which stands for info). To read Emacs' manual just enter `m` and select `Emacs` from minibuffer. Hmm, you might be wondering what is minibuffer, let us use this approach to find out. Press following C-h i m key sequence and type `Minibuffer` and hit enter. Voila, you are reading all about `Minibuffer` from the official Emacs manual. No other application that I have used comes close to the level of Emacs in terms of self documentation.
  2. Start with the goal of getting something done. I have two example that every programmer will benefit from learning: `Magit` and `Org-mode`. While you need to install `magit-mode`, which is a Emacs interface to git CLI, `org-mode` is built in. You will be surprised to find that it makes your git journey so much fun or your note organization so much enjoyable. I made the mistake of trying to memorize hot keys as much as I can before learning the hard truth of learning one key at a time as needed bases. I strongly recommend the only hot key you need at the beginning is `M-x`, where you just type a command so Emacs can execute for you. For Example, if you would like open (it is `visit` in Emacs speak) a file, just type `M-x` and type `file` then press tab key, select `find-file` in the completion buffer, now you can choose a file name from within the directory that you are in. It is just an example but knowing the fact that every action you perform in Emacs invokes an command, which is just an elisp function, is a very powerful realization. That means you can write an elisp function to have Emacs do whatever you like. You can also lookup what a command does using C-h f, then typing the name of the command. For example, C-h f then, type `org-insert-link` then enter and read on…
  3. I advise against starting with Emacs using other's configuration including Emacs that comes with pre-configuration. As I have mentioned earlier, Emacs is a great library where you can pick and choose to fit your work style. You will loose this great aspects if you start with other's way of configuring/composing it. If you had to just start with a minimal config, put this in your `init.el` file and start changing/adding/organizing configs as you see fit. For example:
;; Emacs reads init.el file located in ~/.emacs.d at startup
;; bootstrap el-get
(add-to-list 'load-path "~/.emacs.d/el-get/el-get")
(unless (require 'el-get nil 'noerror)
  (with-current-buffer
      (url-retrieve-synchronously
       "https://raw.githubusercontent.com/dimitri/el-get/master/el-get-install.el")
    (goto-char (point-max))
    (eval-print-last-sexp)))

;; Initialize available packages first
(package-refresh-contents t)

;; The only package you need to get started
(el-get-bundle smex
  (progn
    (require 'smex)
    (global-set-key (kbd "M-x") 'smex)))

Above snippets sets you up to use `El-get`, a great package manager that I came to rely over the years and have never failed me. It also pulls in the only package you had to have before getting started, `smex` that makes the only command `M-x` you need much more intuitive. Then say if you want to try `magit`, just type M-x el-get-install, then `magit`. El-get will download it and install it so you can starting using it in your git projects. Once you can find your way around Emacs, you can start your journey of how best organize your config/customization as you go along. I use just one org file for it and you might or might not like it. Here is the file in its entirety if you are interested: My config snapshot

Tags: emacs
19 Sep 2018

Blogging Made Simple

After taking some time off from work to focus on projects that I like to take on, I set out to find a simple way to use git and emacs workflow to start blogging again. Among plethora of options on the web, I came across this project - Org Static Blog - that I resonated with. It took me no time to set it up and start blogging. Below is my setup:

(use-package org-static-blog
  :straight t
  :config
  ;; -----------------------------------------------------------------------------
  ;; Set up blogging in Emacs
  ;; -----------------------------------------------------------------------------

  (setq org-static-blog-publish-title "BirkeyCo")
  (setq org-static-blog-publish-url "https://www.birkey.co/")
  (setq org-static-blog-publish-directory "~/github/oneness.github.io/")
  (setq org-static-blog-posts-directory "~/github/oneness.github.io/posts/")
  (setq org-static-blog-drafts-directory "~/github/oneness.github.io/drafts/")
  ;;(setq org-static-blog-enable-tags t)
  (setq org-export-with-toc nil)
  (setq org-export-with-section-numbers nil)

  (setq org-static-blog-page-header
	"<meta name=\"author\" content=\"Kasim Tuman\">
<meta name=\"referrer\" content=\"no-referrer\">
<link href= \"static/style.css\" rel=\"stylesheet\" type=\"text/css\" />
<link rel=\"icon\" href=\"static/favicon.ico\">
<link rel=\"apple-touch-icon-precomposed\" href=\"static/birkey_logo.png\">
<link rel=\"msapplication-TitleImage\" href=\"static/birkey_logo.png\">
<link rel=\"msapplication-TitleColor\" href=\"#0141ff\">
<script src=\"static/katex.min.js\"></script>
<script src=\"static/auto-render.min.js\"></script>
<link rel=\"stylesheet\" href=\"static/katex.min.css\">
<script>document.addEventListener(\"DOMContentLoaded\", function() { renderMathInElement(document.body); });</script>
<meta http-equiv=\"content-type\" content=\"application/xhtml+xml; charset=UTF-8\">
<meta name=\"viewport\" content=\"initial-scale=1,width=device-width,minimum-scale=1\">")

  (setq org-static-blog-page-preamble
	"<div class=\"header\">
  <a href=\"https://birkey.co\">Code, Data and Network</a>
  <div class=\"sitelinks\">
    <a href=\"https://twitter.com/KasimTuman\">Twitter</a> | <a href=\"https://github.com/oneness\">Github</a>
  </div>
</div>")

  (setq org-static-blog-page-postamble
	"<div id=\"archive\">
  <a href=\"https://www.birkey.co/archive.html\">Other posts</a>
</div>
<center><a rel=\"license\" href=\"https://creativecommons.org/licenses/by-sa/3.0/\"><img alt=\"Creative Commons License\" style=\"border-width:0\" src=\"https://i.creativecommons.org/l/by-sa/3.0/88x31.png\" /></a><br /><span xmlns:dct=\"https://purl.org/dc/terms/\" href=\"https://purl.org/dc/dcmitype/Text\" property=\"dct:title\" rel=\"dct:type\">birkey.co</span> by <a xmlns:cc=\"https://creativecommons.org/ns#\" href=\"https://www.birkey.co\" property=\"cc:attributionName\" rel=\"cc:attributionURL\">Kasim Tuman</a> is licensed under a <a rel=\"license\" href=\"https://creativecommons.org/licenses/by-sa/3.0/\">Creative Commons Attribution-ShareAlike 3.0 Unported License</a>.</center>")

  ;; This slows down org-publish to a crawl, and it is not needed since
  ;; I use magit anyway.
  (remove-hook 'find-file-hooks 'vc-find-file-hook))
Tags: emacs blogging
Other posts