Posts

  • Supercharge Your Bash History

    For some weird reason I’ve decided to abandon my insane ZShell setup for a while and switch to a vanilla Bash. Guess I needed a bit of simplicity in my life. One thing about my initial Bash experience that drove me nuts is the way it handles the history out-of-the-box:

    • Each shell session has its own history and once you terminate a session it history is dumped to .bash_history, overwriting whatever was there in the past. For me this pretty much kills the value of persistent shell history. Note that if you don’t exit cleanly your history will just be lost.
    • As a corollary - you can’t really share history between different shell sessions. Each shell loads the history file on startup and augments it whatever commands were executed in it, after loading the file. If you ran a build command in shell A you can’t easily recall it in shell B.
    • It keeps duplicated entries in the history.
    • By default it doesn’t store many history entries (only the last 500 commands).

    Obviously there’s no right way to do shell history and what you consider right or optimal depends on how exactly are using your shell. I know many people who hate sharing data between shell sessions, as they want to keep them isolated for various reason.

    On the other hand - I don’t know a single person who likes their shell history to be constantly overwritten. Let’s teach Bash to append to the history file instead of overwriting it! Just add the following to your .bashrc:

    # append to the history file, don't overwrite it
    shopt -s histappend
    

    Note that some Linux distros (e.g. Ubuntu) might be enabling this shell option in the default .bashrc they copy to each user’s home folder.

    Now, let’s increase the history size and teach Bash to ignore duplicate entries in the history:

    # don't put duplicate lines or lines starting with space in the history.
    # See bash(1) for more options
    HISTCONTROL=ignoreboth
    
    # for setting history length see HISTSIZE and HISTFILESIZE in bash(1)
    HISTSIZE=100000
    HISTFILESIZE=10000000
    

    You can obviously go really big here, but unless you have a very fast SSD I would not recommend it, as reading the history can add a bit of latency to new shell sessions.

    Now, we’re moving to the crux of it - let’s teach Bash to update the history after each command we invoke and to reload it. The reloading is what ensures that different shell sessions are synced in terms of history.

    # append and reload the history after each command
    PROMPT_COMMAND="history -a; history -n"
    

    history -a writes to the history file and history -n reloads the history from the file, but with a twist - it loads only the new entries that were added there. This makes it way more efficient than another similar (and quite popular) approach - namely using history -a; history -c; history -r. Full reloads of a huge history file would exhibit themselves as slight delays after each command you run. The solution I’ve suggested should largely avoid them.

    You might also want to remove the use of certain commands from your history, whether for privacy or readability reasons. This can be done with the $HISTIGNORE variable. It’s common to use this to exclude ls (and similar) calls, job control built-ins like bg and fg, and calls to history itself:

    HISTIGNORE='ls:ll:cd:pwd:bg:fg:history'
    

    Feel free to add here any other commands that you don’t want to store in the history. Note that here you’re specifying exact matches for exclusion - the above config would exclude ls, but it won’t exclude ls projects. Most of the time you’d be using this with commands invoked without arguments.

    So, putting it all together, that’s my magic recipe to supercharge Bash’s history:

    # place this in your .bashrc
    
    # don't put duplicate lines or lines starting with space in the history.
    # See bash(1) for more options
    HISTCONTROL=ignoreboth
    
    # append to the history file, don't overwrite it
    shopt -s histappend
    # append and reload the history after each command
    PROMPT_COMMAND="history -a; history -n"
    
    # ignore certain commands from the history
    HISTIGNORE="ls:ll:cd:pwd:bg:fg:history"
    
    # for setting history length see HISTSIZE and HISTFILESIZE in bash(1)
    HISTSIZE=100000
    HISTFILESIZE=10000000
    

    That’s all I have for you today! Keep hacking!

  • A Safer RuboCop, Part Deux

    A while ago I wrote an article about our recent efforts to make RuboCop safer in terms of the code changes that it suggests and performs in auto-correct mode. Today I’ll follow up with a small update on the latest developments in that area.

    In preparation for the long-awaited RuboCop 1.0, RuboCop’s team spent a lot of time recently adding auto-correction to more cops, polishing the internal auto-correction logic, and marking cops with unsafe auto-correction accordingly. Recently we reached another important milestone in our quest for safety - we’ve made safe auto-correct the default behavior of RuboCop! The change essentially makes --auto-correct and --safe-auto-correct the same.1

    $ rubocop -a
    # or
    $ rubocop --auto-correct
    # or
    $ rubocop --safe-auto-correct # deprecated
    

    I guess you might be wondering can you still have RuboCop run every possible auto-correction. Yes, you can!

    $ rubocop -A
    # or
    $ rubocop --auto-correct-all
    

    I think that’s pretty simple - a bigger A means more epic (and more dangerous) auto-corrections. :-) RuboCop 0.87 was released yesterday with these changes. I hope you’ll enjoy them!

    I hope the rationale behind this is clear - a lot of people would run auto-correct in bulk and would get surprised that RuboCop made (some) code changes that are not 100% compatible with the original code. We obviously want to limit such unpleasant surprises. Originally I was optimistic that most people would be fine with the occasional small breakage here and there, given the overall savings they’d get from the auto-corrections, but the feedback me and our team got over the years made me reconsider my stance of the default behavior.2

    In general I’ve always felt it’s a bad idea to run all the auto-corrections possible, especially on a big codebase. Likely you’ll immediately notice some problems, but you might have trouble finding where did they come from and which correction caused them. That’s why my recipe for (unsafe) auto-corrections has always been less ambitious:

    # Run auto-correction for a cop at a time
    $ rubocop --auto-correct --only Some/Cop
    # Review carefully the diff
    # Run the specs/unit tests
    $ rspec
    # All is green. Let's ship it!
    # Moving forward with the next cop...
    

    I encourage more people to operate in this incremental manner, as it makes it much easier to deal with unsafe corrections, which despite their name work pretty well most of the time, but still require a bit of supervision from a human.

    That’s all I have for you today. Keep hacking!

    1. safe-auto-correct is now deprecated and will eventually be removed. 

    2. I was also optimistic that most projects would have solid test coverage that makes it easy to find such breakages. 

  • nREPL 0.8: Evolving the Protocol

    A few days ago I released the first alpha version of what is eventually going to become nREPL 0.8. The upcoming release is quite special, as it adds a couple of additional ops to the core nREPL protocol itself - namely completions and lookup. Apart from this it extends the sideloader functionality, introduced in nREPL 0.7, to allow for dynamic loading of middleware.

    One of the great things about nREPL has always been its simplicity. Implementing both nREPL servers and clients is pretty easy (as they the protocol is simple) and eventually nREPL gained traction even outside of the Clojure community, which gave birth to it.1 While the default set of nREPL ops cover all basic use-cases2, there are many other common use-cases - e.g. code completion, looking up definition and documentation, finding usages, etc. The Language Server Protocol gained a lot of traction in recent years, partly because of its broader API that can power out-of-the-box most programming editors.

    In the world of Clojure this void has historically been filled by third-party nREPL middleware (e.g. cider-nrepl and refactor-nrepl), but there are always merits to having a more powerful out-of-the-box experience. That’s why after some (a lot of) consideration I’ve decided to extend the protocol with an op for code completion (completions) and another that resolves a symbol and looks up as much data for it as possible (lookup). Both those ops make sense for pretty much every programming language and therefore they fit with nREPL’s philosophy.

    Let’s examine the new ops.

    1. You might want to read about the nREPL protocol vs the Clojure nREPL server here

    2. In the words of the Beatles - “All you need is eval…”. 

    Read More
  • The RuboCop Name Drama Redux

    By now, if you’re part of the Ruby programming community, you’ve probably heard of the proposal to rename RuboCop and the unpleasant events that followed it. I really struggled with coming up with a name for this article, as there are many topics I wanted to touch upon (e.g. the burden of OSS stewardship, being respectful and adhering to the norms that once defined the Ruby community, creating a safe collaborative environment, the final decision about the name, etc), but I’m quite limited in time right now. This generic title is the best that I could, but I hope you will forgive me and focus on the content instead. Once again I got reminded that naming is (very) hard.

    I don’t really want to go repeat/retell everything that has transpired. The GitHub issue and the surrounding Twitter conversations should give you a good idea. There’s also a nice summary, written by Tim Riley that you can peruse. The purpose of the article you’re reading now is to provide some closure for that topic and a path for us to move forward.

    So, here we go…

    Read More
  • RuboCop Defaults Survey Results

    A while ago we ran a survey with RuboCop’s users with a the goal to decide if some defaults needed to be updated before cutting RuboCop 1.0. 722 people took part of the survey, and while this number was somewhat disappointing, given the size of RuboCop’s user base, I hope the results are representative of the general sentiment of our users.

    In this article I’ll briefly summarize the results from the survey and provide some personal thoughts on them. I’m too lazy to include some nice charts here (and it’s hard to share directly those generated by Google Forms), but I hope you’ll forgive me.

    Read More

Subscribe via RSS | View Older Posts