Emacs Bliss

Welcome to Emacs Bliss

17 Feb 2019

search replace with Emacs

Search (and replace) is something I do so much in emacs, I want to share my typical workflow for it.

search in buffer(s)

For this purpose, I like to use swiper 1 which I bind to C-s.

swiper searches inside current buffer and list all matches in minibuffer. To search in all open buffers, use swiper-all instead.

Sometimes I just prefer simple search like the classic vim way, then I use / which maps to evil-ex-search-forward from evil mode 2.

It behaves exactly like what you do with / in vim normal mode.

search in directories

rg

So far ripgrep(rg) 3 seems to be the fatest search tool around and many Emacs packages use it as backend.

By default rg respects .gitignore file but if you want it to search everything, you can use --no-ignore-vcs switch.

Sometimes it finds things from the file I don’t want, to prevent that, add a file with name .ignore at the root of git repo as described here 4:

/TAGS
*.min.js*
/Build/Output/
/ThirdParty/

counsel-rg

My favorite Emacs package for searching is counsel-rg 5 and I bind it to C-/. It uses rg as backend.

counsel-rg by default looks for a directory with .git and starts a search from there.

To limit the scope to current directory and its sub directories, not where .git is, use prefix argument before running counsel-rg then select which directory you want to run rg on.

deadgrep

deadgrep 6 is another package which uses rg as backend for searching.

It has many features and allow filtering to be done more easier than counsel-rg.

One notable feature is search on remote host:

If you’re editing a remote file with Tramp, deadgrep will search the remote machine. This requires rg to be installed on that machine.

replace in multiple files

For this purpose, this great blog post 7 describes it in detail. It use counsel-ag but same thing works for counsel-rg which is the one I use.

In short, workflow is something like this:

  • C-/ (counsel-rg)
  • type search pattern
  • C-c C-o (ivy-occur) which saves result into a buffer
  • C-x C-q (ivy-wgrep-change-to-wgrep-mode) in ivy-occur buffer
  • :%s/from/to/g to do the replace
  • C-c C-c (wgrep-finish-edit) save the changes

Footnotes:

Tags: Emacs
09 Sep 2018

Emacs is an awesome git client

Emacs is much more than just a text editor in many ways. It’s an excellent File Explorer(with dired), notes organizer and GTD tool (with Org mode) and much more.

Among so many roles Emacs can be,it’s also an awesome Git client across multiple platforms including macOS, Linux and Windows.

necessary packages

This is made possible via several Emacs packages:

Magit is magical

Among all these packages, Magit is the most critical one and does most of the heavy lifting. It alone enables me to perform all the git operations I can think of.

Magit website has an excellent guide to walk you through all the basic things like:

  • show the current repository status
  • stage/commit files/changes
  • show git log history and move through logs
  • rebase/merge/branching
  • diffing

There are a few things from Magit I particularly like because they’re MUCH easier with magit than the git CLI or even some other git tools.

Here’s my list:

  • stage a particular hunk of a file
  • quickly discard changes to files/hunks
  • list all the commits affecting a particular file
  • list all the commits affecting a particular function

Magit is really sweet for git, but as I’m so used to Vim editing mode(that’s where the evil package really shines), I want to use Vim keybinding for operating magit buffer (for eg, use hjkl for navigating). evil-magit makes me feel right at home.

time travelling

I realized there are times I want to see a particular version for a given file or just traverse through versions; git time machine is the package that helps me with that. However I need a little tweak so I can select the particular version as the starting point (other than current head).

After starting the git time machine, you can use p to go to the previous version while n goes to the next version. There is also a line at the top of the buffer to indicate which version you’re looking at along with other relevant information.

See it in action below: git-time-machine.gif

show indication of changes in real time

Sometimes we may also find it helpful that Emacs will show the indication that some lines are changed since the last commit on the fringe of the buffer for whatever reason, that’s what git gutter fringe package is for.

It will give you the visual indication on the left fringe of the current buffer if there is change, something like this:

Again, with a little tweak we can easily get a list of changes in the current buffer and jump to the one I’m interested in:

git-gutter.gif

my workflow

There are basically two workflows for me.

If I just want to do things like the following with the current file:

  • get diff for changes
  • get list of commits
  • get list of commits affect a function
  • stage current file

Then I just invoke the key binding corresponds to the magit command without openning magit status buffer.

However if I want to:

  • get the status of a repository
  • stage/unstage certain files
  • commit current staged changes
  • pull/push from remote
  • branching/merging

then I would use magit-status (I bound to SPC g s) to launch the magit status buffer and start from there.

Here is a demo showing some of the operations mentioned above:

emacs-git-client.gif

keybinding

I use space as leader key for Evil mode, and all git related functions are bound to g using package general:

(general-define-key
 :states '(normal visual insert emacs)
 :keymaps 'override
 :prefix "SPC"
 :non-normal-prefix "M-m"

 "g" '(:ignore t :which-key "Git")
 "gs" '(magit-status :which-key "Git status")
 "ga" '(magit-stage-file :which-key "stage this file")
 "gb" '(magit-blame :which-key "Git blame")
 "gc" '(magit-commit :which-key "Git commit")
 "gd" '(magit-diff-buffer-file :which-key "Git diff")
 ;; list commits affect current function
 "gf" '(magit-log-trace-definition :which-key "show commits for this function")
 ;; list commits affect current file
 "gl" '(magit-log-buffer-file :which-key "show commits for this file")
 "gg" '(hydra-git/body :which-key "Git gutter")
 "gp" '(magit-push-current :which-key "Git push")
 "gt" '(my-git-timemachine :which-key "Git time machine")
)

So pressing Space g will show a list of available keys:

git-keybinding.png

There is also a nice cheatsheet from excellent API documentation browser software: Dash.

Conclusion

Hopefully by now you have seen how awesome Emacs is as a git client.

It’s pretty much a no-brainer for anyone who already uses Emacs day to day to adopt those packages (IF they have not done so).

But even for non-Emacs user, if you need to work with git, I would still recommend it even this is the ONLY thing you use Emacs for.

With all these packages and a little bit of customization Emacs turns into a top-notch and FREE (also across platform) git client.

Tags: Emacs Git
02 Sep 2018

annoyance with paste in Evil visual mode

I like Evil mode and use it all the time with my Emacs configuration.

One thing I often do is copy some text with y and select some text in visual mode followed by using p or P to replace it with what I have just copied.

However what really annoys me is that every time after I press p or P , the content(ie. visual selection) being replaced is saved to the top of kill-ring which ruined my next paste operation with p or P since kill-ring has been changed.

After consulting the helpful, it shows that command invoked for pasting in visual mode is evil-visual-paste defined in evil-commands.el.

Skimming through the function, this line is what I’m looking for:

(when evil-kill-on-visual-paste
        (kill-new new-kill))

Basically it will save whatever in visual selection and push it to the top of kill-ring if variable evil-kill-on-visual-paste is true.

This variable is defined here as following (in evil-vars.el):

(defcustom evil-kill-on-visual-paste t
  "Whether ~evil-visual-paste' adds the replaced text to the kill
ring, making it the default for the next paste. The default, t,
replicates the default vim behavior."
  :type 'boolean
  :group 'evil)

So by setting this variable to false that annoyance goes away:

(setq evil-kill-on-visual-paste nil)
Tags: Emacs Evil-mode
03 Aug 2018

Go to column in Emacs

I was looking for a command/function to jump to a specific column in emacs. It turned out there is already one built in emacs: move-to-column and is bind to M-g Tab.

Even better Evil has already defined a command evil-goto-column and by default it’s bind to |.

So to jump to column 10 in evil normal mode, do this:

10 |

NOTE: column number is 0 based

Tags: Emacs Evil-mode
25 Apr 2018

clear iOS camera roll with Pythonista

Sometimes I find the need to clear the camera roll on my iPhone after I have backed up to free up the storage. Interestingly Apple doesn’t offer a simple way to do it and it would become a drag if I need to select all the photos by tapping on each of them.

There appears to be few ways to achieve this. One approach requires connect iPhone to computer which I rarely do if never.

Another approach requires using 3rd party app like Gemini Photos which seems to be ok.

However given I have purchased a fantastic app Pythonista 3 and I like using python, so I figured if I can achieve this with the app.

And answer is a sure yes.

Pythonista photos module

Pythonista provides a module called photos which can be used to do a LOT of things to your photos on your ios device.

Before we diving into the final code, I would like to explain few concepts involved really quick to help understand the code.

AssetCollection is the corresponding python type for a photo album. It holds a list of Asset object which contains two pieces information: metadata and image data for a photo.

clear the camera roll

photos module provides get_albums and get_smart_albums method to retrieve an photo album which is AssetCollection object as aforementioned.

Since I want to clear the Camera Roll and only get_smart_albums return would include it, so we will be using it to begin with.

Next step is straightforward, get the Camera Roll from return result of get_smart_albums by checking the title of an album.

After that we can just delete all the photos from the album by invoking photos.batch_delete(). That method requires a list of Asset object and AssetCollection.assets property is what we need.

That’s it!

Here’s all the code:

import photos
# NOTE: return of this will not include camera roll
# a = photos.get_albums()

# return of this will include camera roll
albums = photos.get_smart_albums()
camera_roll = [a for a in albums if a.title == 'Camera Roll'][0]
photos.batch_delete(camera_roll.assets)

# NOTE: this will not work! see photos module documentation
#camera_roll.remove_assets(camera_roll.assets)

action

After you run the script, iOS will popup a confirmation dialog before it actually deletes all the photos.

Once you tap Delete button, all photos are gone. I figure it’s nice and easy enough for me, if you own the Pythonista already, it might be worth a try.

This is tested with my iPhone6 (running iOS 11.3).

photos module provides much more functionality than this, you can explore its documentation to find out other use cases.

Tags: python iOS
Other posts
Other posts
Creative Commons License
EmacsBliss by EmacsBliss is licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License.