Skip to content

Commit

Permalink
Rewrite fzf in Go
Browse files Browse the repository at this point in the history
  • Loading branch information
junegunn committed Jan 3, 2015
1 parent 7ba93d9 commit f317730
Show file tree
Hide file tree
Showing 32 changed files with 3,464 additions and 47 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
bin
src/fzf/fzf_*
pkg
Gemfile.lock
.DS_Store
122 changes: 75 additions & 47 deletions install
Original file line number Diff line number Diff line change
Expand Up @@ -3,60 +3,81 @@
cd `dirname $BASH_SOURCE`
fzf_base=`pwd`

# ruby executable
echo -n "Checking Ruby executable ... "
ruby=`which ruby`
if [ $? -ne 0 ]; then
echo "ruby executable not found!"
exit 1
fi

# System ruby is preferred
system_ruby=/usr/bin/ruby
if [ -x $system_ruby -a $system_ruby != "$ruby" ]; then
$system_ruby --disable-gems -rcurses -e0 2> /dev/null
[ $? -eq 0 ] && ruby=$system_ruby
fi

echo "OK ($ruby)"
ARCHI=$(uname -sm)

# Curses-support
echo -n "Checking Curses support ... "
"$ruby" -rcurses -e0 2> /dev/null
if [ $? -eq 0 ]; then
echo "OK"
else
echo "Not found"
echo "Installing 'curses' gem ... "
if (( EUID )); then
/usr/bin/env gem install curses --user-install
download() {
echo "Downloading fzf executable ($1) ..."
if curl -fLo "$fzf_base"/bin/fzf https://github.com/junegunn/fzf-bin/releases/download/snapshot/$1; then
chmod +x "$fzf_base"/bin/fzf
else
/usr/bin/env gem install curses
echo "Failed to download $1"
exit 1
fi
}

mkdir -p "$fzf_base"/bin
if [ "$ARCHI" = "Darwin x86_64" ]; then
download fzf_darwin_amd64
elif [ "$ARCHI" = "Linux x86_64" ]; then
download fzf_linux_amd64
else # No prebuilt executable
echo "No prebuilt binary for $ARCHI ... Installing legacy Ruby version ..."

# ruby executable
echo -n "Checking Ruby executable ... "
ruby=`which ruby`
if [ $? -ne 0 ]; then
echo
echo "Failed to install 'curses' gem."
if [[ $(uname -r) =~ 'ARCH' ]]; then
echo "Make sure that base-devel package group is installed."
fi
echo "ruby executable not found!"
exit 1
fi
fi

# Ruby version
echo -n "Checking Ruby version ... "
"$ruby" -e 'exit RUBY_VERSION >= "1.9"'
if [ $? -eq 0 ]; then
echo ">= 1.9"
"$ruby" --disable-gems -rcurses -e0 2> /dev/null
# System ruby is preferred
system_ruby=/usr/bin/ruby
if [ -x $system_ruby -a $system_ruby != "$ruby" ]; then
$system_ruby --disable-gems -rcurses -e0 2> /dev/null
[ $? -eq 0 ] && ruby=$system_ruby
fi

echo "OK ($ruby)"

# Curses-support
echo -n "Checking Curses support ... "
"$ruby" -rcurses -e0 2> /dev/null
if [ $? -eq 0 ]; then
echo "OK"
else
echo "Not found"
echo "Installing 'curses' gem ... "
if (( EUID )); then
/usr/bin/env gem install curses --user-install
else
/usr/bin/env gem install curses
fi
if [ $? -ne 0 ]; then
echo
echo "Failed to install 'curses' gem."
if [[ $(uname -r) =~ 'ARCH' ]]; then
echo "Make sure that base-devel package group is installed."
fi
exit 1
fi
fi

# Ruby version
echo -n "Checking Ruby version ... "
"$ruby" -e 'exit RUBY_VERSION >= "1.9"'
if [ $? -eq 0 ]; then
fzf_cmd="$ruby --disable-gems $fzf_base/fzf"
echo ">= 1.9"
"$ruby" --disable-gems -rcurses -e0 2> /dev/null
if [ $? -eq 0 ]; then
fzf_cmd="$ruby --disable-gems $fzf_base/fzf"
else
fzf_cmd="$ruby $fzf_base/fzf"
fi
else
echo "< 1.9"
fzf_cmd="$ruby $fzf_base/fzf"
fi
else
echo "< 1.9"
fzf_cmd="$ruby $fzf_base/fzf"
fi

# Auto-completion
Expand Down Expand Up @@ -85,10 +106,17 @@ for shell in bash zsh; do
# Setup fzf function
# ------------------
unalias fzf 2> /dev/null
fzf() {
$fzf_cmd "\$@"
}
export -f fzf > /dev/null
unset fzf 2> /dev/null
if [ -x "$fzf_base/bin/fzf" ]; then
if [[ ! "\$PATH" =~ "$fzf_base/bin" ]]; then
export PATH="$fzf_base/bin:\$PATH"
fi
else
fzf() {
$fzf_cmd "\$@"
}
export -f fzf > /dev/null
fi
# Auto-completion
# ---------------
Expand Down
33 changes: 33 additions & 0 deletions src/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
FROM ubuntu:14.04
MAINTAINER Junegunn Choi <junegunn.c@gmail.com>

# apt-get
RUN apt-get update && apt-get -y upgrade
RUN apt-get install -y --force-yes git vim-nox curl procps sudo \
build-essential libncurses-dev

# Setup jg user with sudo privilege
RUN useradd -s /bin/bash -m jg && echo 'jg:jg' | chpasswd && \
echo 'jg ALL=(ALL) NOPASSWD: ALL' > /etc/sudoers.d/jg

# Setup dotfiles
USER jg
RUN cd ~ && git clone https://github.com/junegunn/dotfiles.git && \
dotfiles/install > /dev/null

# Install Go 1.4
RUN cd ~ && curl https://storage.googleapis.com/golang/go1.4.linux-amd64.tar.gz | tar -xz && \
mv go go1.4 && \
echo 'export GOROOT=~/go1.4' >> ~/dotfiles/bashrc-extra && \
echo 'export PATH=~/go1.4/bin:$PATH' >> ~/dotfiles/bashrc-extra

# Symlink fzf directory
RUN mkdir -p ~jg/go/src/github.com/junegunn && \
ln -s /fzf ~jg/go/src/github.com/junegunn/fzf

# Volume
VOLUME /fzf

# Default CMD
CMD cd ~jg/go/src/github.com/junegunn/fzf/src && /bin/bash -l

21 changes: 21 additions & 0 deletions src/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
The MIT License (MIT)

Copyright (c) 2015 Junegunn Choi

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
49 changes: 49 additions & 0 deletions src/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
BINARY := fzf/fzf

UNAME_S := $(shell uname -s)
ifeq ($(UNAME_S),Darwin)
BINARY := $(BINARY)_darwin
else ifeq ($(UNAME_S),Linux)
BINARY := $(BINARY)_linux
endif

UNAME_M := $(shell uname -m)
ifneq ($(filter i386 i686,$(UNAME_M)),)
$(error "filtered is not supported, yet.")
endif

ifeq ($(UNAME_M),x86_64)
BINARY := $(BINARY)_amd64
else ifneq ($(filter i386 i686,$(UNAME_M)),)
BINARY := $(BINARY)_386
else # TODO
$(error "$(UNAME_M) is not supported, yet.")
endif

BINDIR = ../bin
SOURCES = $(wildcard *.go fzf/*.go)

all: build

build: $(BINARY)

$(BINARY): $(SOURCES)
go get
go test -v
cd fzf && go build -o $(notdir $(BINARY))

install: $(BINARY)
mkdir -p $(BINDIR)
cp -f $(BINARY) $(BINDIR)/fzf

clean:
rm -f $(BINARY)

docker:
docker build -t junegunn/ubuntu-sandbox .

linux64:
docker run -i -t -u jg -v $(shell cd ..; pwd):/fzf junegunn/ubuntu-sandbox \
/bin/bash -ci 'cd ~jg/go/src/github.com/junegunn/fzf/src; make build'

.PHONY: build install linux64 clean docker run
59 changes: 59 additions & 0 deletions src/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
fzf in Go
=========

This directory contains the source code for the new fzf implementation in Go.
This new version has the following benefits over the previous Ruby version.

- Immensely faster
- No GIL. Performance is linearly proportional to the number of cores.
- It's so fast that I even decided to remove the sort limit (`--sort=N`)
- Does not require Ruby and distributed as an executable binary
- Ruby dependency is especially painful on Ruby 2.1 or above which
ships without curses gem

Build
-----

```sh
# Build fzf executable
make

# Install the executable to ../bin directory
make install

# Build executable for Linux x86_64 using Docker
make linux64
```


Prebuilt binaries
-----------------

- Darwin x86_64
- Linux x86_64

Third-party libraries used
--------------------------

- [ncurses](https://www.gnu.org/software/ncurses/)
- [mattn/go-runewidth](https://github.com/mattn/go-runewidth)
- Licensed under [MIT](http://mattn.mit-license.org/2013)
- [mattn/go-shellwords](https://github.com/mattn/go-shellwords)
- Licensed under [MIT](http://mattn.mit-license.org/2014)

Contribution
------------

For the moment, I will not add or accept any new features until we can be sure
that the implementation is stable and we have a sufficient number of test
cases. However, fixes for obvious bugs and new test cases are welcome.

I also care much about the performance of the implementation (that's the
reason I rewrote the whole thing in Go, right?), so please make sure that your
change does not result in performance regression. Please be minded that we
still don't have a quantitative measure of the performance.

License
-------

- [MIT](LICENSE)
Loading

0 comments on commit f317730

Please sign in to comment.