Easy Moving From Vscode To Emacs

If you have already decided to go with Emacs as your choice of editor, congratulations! This is going to be great. However, the part after this is what’s hard and takes time. You might already be missing some of the very basic functionalities like opening a project file without typing the full path or searching something project-wide that you are very accustomed to. These features usually comes out of the box or by installing some extensions that do not require any configuration, with most of graphics based editors like Atom, VS Code or Sublime. If that is the case, keep reading. When I first started using Emacs, I spent quite some time reading and trying out stuffs to make these things work and this article is exactly because of that – to make your transition smoother. The focus is NOT on being more productive with Emacs or to give you tricks and fancy lisp code that will make you Emacs ninja overnight (they exists btw). That is something you can focus on once you’re comfortable using Emacs. This is my attempt to make every tyro feel home, to give you a setup that will allow you to do all the stuff that you’re used to.

Emacs is built around the philosophy of extensibility and flexibility – being able to hook your own code at any event of your choice is what makes it awesome. And because of that, the huge community around the editor have highly opinionated setups based on what works for them. There exists modified versions of Emacs like Prelude, Spacemacs, etc. that are loaded with a lot of functionalities you wouldn’t find anywhere else but in my personal opinion, it’s good to know what is cake and what are the cherries.

Before we start, familiarize yourself with basic key combinations and terminology of Emacs. Built-in tutorial explains that very neatly which you can read by C-h t (hold control key and press h, then leave control key and press t). This awesome article from last week’s HN front page is also a great read for this.

Let’s start. After moving from Atom and VSCode to Emacs, I missed these functionalities:

  1. Being able to duplicate current line
  2. Move or drag a line up and down
  3. Use multiple cursors to edit at a time
  4. Auto-completion of variables and functions name
  5. Fuzzy file search i.e. Ctrl-P or Cmd-Shift-o in other editors
  6. Searching a term of string in the whole project
  7. A tree view of the project to explore file structure

    Few more things that I had installed extensions for in VSCode:

  8. Goto functions and class definitions
  9. Come back to previous cursor position
  10. Find all the reference of a function
  11. Markdown preview

Surprisingly, some of these are as easy as hooking a couple of lines of code to a keystroke, for some a little more than a couple of lines so we’ll use community developed packages for them. The latest version of Emacs ships with a package manager – package.el. We just need to add MELPA repository to it so it can search packages for us. Add this Emacs Lisp (ref) to your ~/.emacs.d/init.el file to do that.

(require 'package)
(setq package-archives
      '(("GNU ELPA"     . "https://elpa.gnu.org/packages/")
        ("MELPA Stable" . "https://stable.melpa.org/packages/")
        ("MELPA"        . "https://melpa.org/packages/"))
      package-archive-priorities
      '(("GNU ELPA"     . 10)
        ("MELPA Stable" . 5)
        ("MELPA"        . 0)))
(package-initialize)

This will also give priorities to repositories to avoid duplicate listing in case of certain packages being present in more than one. After adding this restart Emacs to reload init.el and do this: M-x package-refresh-contents. Now you are ready to install most of the packages. Note that you can also reload you init.el by doing M-x load-file and then providing file path.

Rest of the article is about enabling above mentioned functionalities one by one.

  1. Add this code to same init.el

     (defun duplicate-line ()
       (interactive)
       (save-mark-and-excursion
         (beginning-of-line)
         (insert (thing-at-point 'line t))))
    
     (global-set-key (kbd "C-S-d") 'duplicate-line)
    

    Now, you can use C-S-d (Control-Shift-d) to duplicate current line. Change these key combination to whatever suits you.

  2. Add these lines for using C-S-j and C-S-k to move a line up or down one line.

     (defun move-line-down ()
       (interactive)
       (let ((col (current-column)))
         (save-excursion
           (forward-line)
           (transpose-lines 1))
         (forward-line)
         (move-to-column col)))
    
     (defun move-line-up ()
       (interactive)
       (let ((col (current-column)))
         (save-excursion
           (forward-line)
           (transpose-lines -1))
         (forward-line -1)
         (move-to-column col)))
    
     (global-set-key (kbd "C-S-j") 'move-line-down)
     (global-set-key (kbd "C-S-k") 'move-line-up)
    
  3. To get mutiple cursor, I use multiple-cursor package, which can be installed by M-x package-install <RET> multiple-cursor <RET>. Add these key bindings to easily use it.

     (require 'multiple-cursors)
     (global-set-key (kbd "C-|") 'mc/edit-lines)
     (global-set-key (kbd "C->") 'mc/mark-next-like-this)
     (global-set-key (kbd "C-<") 'mc/mark-previous-like-this)
     (global-set-key (kbd "C-c C-<") 'mc/mark-all-like-this)
     (global-set-key (kbd "C-S-<mouse-1>") 'mc/add-cursor-on-click)
     (define-key mc/keymap (kbd "<return>") nil)
    

    These commands do exactly what they look like. To get out of multiple cursors, use C-g, the last line in above code prevents using <RETURN> key to do same. You can read more about it in the project doc page.

  4. To let Emacs auto-complete function names and variables, I use company, which can be installed by M-x package-install <RET> company-mode <RET>. And can be activated by adding this line to init.el:

     (add-hook 'after-init-hook 'global-company-mode)
    
  5. I use the combination of projectile and helm package to deal with fuzzy file search. Configurations for helm can be a bit overwhelming so just start with following.

     (require 'projectile)
     (setq projectile-indexing-method 'alien)
     (setq projectile-enable-caching t)
     (projectile-global-mode)
    
     (require 'helm)
     (require 'helm-config)
     (global-set-key (kbd "C-c h") 'helm-command-prefix)
     (global-unset-key (kbd "C-x c"))
     (helm-autoresize-mode 1)
     (global-set-key (kbd "M-x") 'helm-M-x)
     (setq helm-M-x-fuzzy-match t)
     (global-set-key (kbd "C-x C-f") 'helm-find-files)
     (helm-mode 1)
    

    This will allow you to use C-c p f for opening your project files just by typing file name and not the whole path. And when you use C-x C-f to open a file that is outside the project directory, you can type any part of the directory or file name or even non contiguous parts separated by space to narrow down the suggestions e.g. if you want to select controllers out of controllers and contol folder name, you can type co s. Use C-j (not TAB) to choose highlighted option.

  6. projectile can also be used to search for something in the whole project: C-c p s s. This command used ag system package, which should definitely have and a ag.el Emacs package. I don’t generally use project wild search and replace but if that’s your thing, this answer from Stackexchange explains a great way of doing so.

  7. Built-in speedbar provides good enough interactive tree view of project. It can be started with M-x speedbar. It’s easier to bind it to some key like F8 using

     (global-set-key (kbd "<f8>") 'speedbar)
    
  8. I use dumb-jump package to jump to definitions using C-M-g. Add this line to auto enable it every time:

     (dumb-jump-mode)
    
  9. You can jump back to function call by C-M-p.

  10. To find all the references of a function, priviously mentioned project-wide search with projectile works well for me.

  11. I use fymd package to real time markdown preview, with this key binding:

    (global-set-key (kbd "<f9>") 'flymd-flyit)
    

Most of the snippets on this page are taken from emacsrocks.com, whattheemacsd.com and official documentation of mentioned packages. Both of these resources are great places to learn more about Emacs. Here is my .emacs.d directory, if don’t feel like doing this all by yourself, feel free to clone mine. I have all the defuns defined in ~/.emacs.d/user-lisp/defuns.el file which is imported in init.el by:

(add-to-list 'load-path "~/.emacs.d/user-lisp")
(require 'defuns)

I keep all the references to these defuns in init.el so all the key bindings remain in one file.

I would highly appreciate any feedback you may have or to listen about any other features you miss from your old editor and should be in this list.

[Edit]

Code snippet for duplicating line (first point) has been updated with current cleaner version suggested by Philip K., earlier it was:

(defun duplicate-line ()
   (interactive)
   (let ((col (current-column)))
     (move-beginning-of-line 1)
     (kill-line)
     (yank)
     (newline)
     (yank)
     (move-to-column col)))

@krsoninikhil

What Is That Options Request Before Post And Cross Origin Resource Sharing

I was working on project that was sending a POST request with some data from a web client to a service hosted on a different domain. The service’s logs were saying no support for OPTIONS request, which made sence because it expects a POST request and that’s what it supports. So who is sending this OPTIONS request when I’m tring to sending a POST from my browser client? I checked the network tab in browser inspector, browser indeed was sending an OPTIONS request, that too with an empty body and I didn’t see any POST request. Weird!

If you have ever wrote any ajax call, there are high chances you would have encountered problems related to cross-domain, saying you’re not allowed to access resources that doesn’t have same origin. This happens when you try to call services from different hosts other than your own server through Javascript. And I knew the reason is browser’s Same Origin policy and what that is, what I didn’t knew was how it exactly works?!

What is Same Origin policy, well, the moment you navigate to an URL, your browser allows the website developer to execute theirs JavaScript on your machine. To limit what that script can do, like accessing data from another web page opened in another tab or cookies created by another websites, browser uses this Same Origin policy, which allow one domain to access resources that belongs to that domain only i.e. resources with same origin. This applies only on what already loaded JS can access through DOM or by making a Ajax calls and not on web page itself embedding static content e.g. images, CSS or Javascipt files itself.

When Javascipt of some website tries to access resources from other sites i.e. make a cross-origin request, the browser adds a Origin header with the request and checks the response header for specific headers like Access-Control-Allow-Origin which signals browser that server allows current origin to access its data. This happens if request does not modify data e.g. GET or HEAD request. For requests that modify data like requests with POST method or content type as application/json, browser first asks the other host if it allows requesting host to access their resource. If other host says yes, it goes ahead with your request.

Browser implements this by first sending something called a preflight request which is a HTTP request with OPTIONS method, containing a header Origin and other Access-Control-* headers describing original request’s method and content type, which server can respond with proper headers describing methods it supports for request from this origin and header it needs. Browser can cache this information and might not verify every time.

You can see this in browser’s Developer Tools. Open Inspect window for current or any web page other than example.com and execute following JS in console:

fetch("https://example.com").then(res => console.log(res))

You should seen error saying Cross-Origin Request Blocked and if you check Network tab, browser did made a GET request as specified above along with a Origin header but since the response header did not have any Access-Control-* headers, browser will not return the response to the client and hence you only saw error in console. Now try executing this JS:

fetch("https://example.com", {
  method: 'POST',
  body: JSON.stringify({"foo": "bar"}),
  headers:{
    'Content-Type': 'application/json'
  }
}).then(res => console.log(res));

You’ll get same error in console and if you check Network tab again, you should see a OPTIONS request and not a POST request. Because the response of OPTIONS request does not have headers that say –allow current webpage to access it’s data, browser never sent the real request.

The OPTIONS request I was getting was this preflight request and since I was not handling this request, I never received the POST request which browser would have sent after receiving response from it’s first request. Handling this is what is called CORS i.e. Cross Origin Resource Sharing. From client side, this is relaxation to Same Origin Policy provided by browser. From server side, most of the API frameworks provides a library to handle this. You can specify which origin domain you want to allow and these libraries will handle the OPTIONS request for you and will respond with proper headers set.

This is good resource to read more about CORS.

When Your Sudo And Pacman Breaks

I needed to install Mongodb for a project I was working on, as usual I did:

$ sudo pacman -S mongodb

Now starting the mongodb.service with systemctl was failing without any specific error logs:

$ journalctl -u mongodb.service -b
-- Logs begin at Thu 2017-06-01 13:13:02 IST, end at Sun 2018-01-07 12:14:08 IST. --
Jan 07 12:05:56 TheBlackPearl systemd[1]: Started High-performance, schema-free document-oriented da
Jan 07 12:05:56 TheBlackPearl systemd[1]: mongodb.service: Main process exited, code=exited, status=
Jan 07 12:05:56 TheBlackPearl systemd[1]: mongodb.service: Unit entered failed state.
Jan 07 12:05:56 TheBlackPearl systemd[1]: mongodb.service: Failed with result 'exit-code'.
Jan 07 12:09:46 TheBlackPearl systemd[1]: Started High-performance, schema-free document-oriented da
Jan 07 12:09:46 TheBlackPearl systemd[1]: mongodb.service: Main process exited, code=exited, status=
Jan 07 12:09:46 TheBlackPearl systemd[1]: mongodb.service: Unit entered failed state.
Jan 07 12:09:46 TheBlackPearl systemd[1]: mongodb.service: Failed with result 'exit-code'.
lines 1-9/9 (END)

And running mongo and mongodb was throwing following error:

mongo: error while loading shared libraries: libboost_program_options.so.1.65.1: cannot open shared object file: No such file or directory

I seached this a bit and then installed boost package.

$ sudo pacman -S boost

And now running mongo was throwing different error:

mongo: error while loading shared libraries: libssl.so.1.1: cannot open shared object file: No such file or directory

I tries installing mongo-tools package which also installed openssl-1.0 along with it. And running mongo was still throwing same error. So I installed openssl which got me openssl-1.1. That’s where almost everything broke. Running sudo:

$ sudo pacman
sudo: error in /etc/sudo.conf, line 0 while loading plugin "sudoers_policy"
sudo: unable to load /usr/lib/sudo/sudoers.so: libssl.so.1.0.0: cannot open shared object file: No such file or directory
sudo: fatal error, unable to load plugins

Since sudo was not an option now, I switched to root by su only to realize that even pacman is broken:

$ pacman -Ss openssl
pacman: error while loading shared libraries: libcrypto.so.1.0.0: cannot open shared object file: No such file or directory

Now, this was scary. How do fix broken packages without my package manager?

Searching for this I found grism’s answer to similar problem. Turns out current version of sudo and pacman depends on openssl-1.0 and I upgraded it to 1.1. To downgrade it, I didn’t need to download the 1.0 version package from web as installing mongodb-tools also installed openssl-1.0, pkg.tar.xz of which can be found at /var/cache/pacman/pkg. As per the above link, I extracted it in /tmp and made a symlink for libssl-1.0.0 and libcrypto-1.0.0 at /var/lib/:

# cd /tmp && tar xf /var/cache/pacman/pkg/openssl-1.0-1.0.2.n-1-x86_64.pkg.tar.xz
# ln -s /tmp/usr/lib/libcrypto.so.1.0.0 /usr/lib/libcrypto.so.1.0.0
# ln -s /tmp/usr/lib/libssl.so.1.0.0 /usr/lib/libssl.so.1.0.0

That is some terrible advice here, as symlinking one version to another can break mulitple things, but other solutions were suggesting to reinstall OS. Now, Arch Linux comes with absolute minimum built in packages which means reinstalling it would require reinstalling all the packages which I had installed over the span of 2-3 years since I’m using Arch. So obiously, I took the above advice and well that fixed my pacman. So first thing first, I need my sudo back, so I installed openssl-1.0 from downloaded .pkg.tar.xz:

# pacman -U /var/cache/pacman/pkg/openssl-1.0-1.0.2.n-1-x86_64.pkg.tar.xz

Exit root and try:

$ sudo ls
[sudo] password for nks:

That’s better. Back to MongoDB problem.

Lesson learnt – when they say upgrading a single package in Arch Linux in not recommended, they mean it. Infact they mean, never ever do that, instead do a full system upgrade like

$ sudo pacman -Syu

@krsoninikhil

Troubleshooting

In “Corporate World” there is a lot of focus on documenting things, which I didn’t used to do much in college. And doing it made me realize it’s importance. While working on any project, now I’m in a habit of writing things down, what worked, what didn’t, everything, which most of the time involve troubleshooting.

And looking back last 2-3 year, I’ve spent countless hours on installing and fixing things, sometimes even on repeated stuff and other than StackOverflow, blogs of other people helped me more than anything. Because of these reasons I’ve started to write about breaking and fixing things publicly. Here is the list of the posts on same:

@krsoninikhil