Installing/hacking Yi
Installing Yi
Yi requires GHC 7.8 at minimum and is mainly developed with GHC 7.10.1.
With the Haskell Platform installed, yi can be installed with cabal-install:
$ cabal update
$ cabal install yi
On Linux systems, you will need ncurses development headers for the Vty frontend. For example, Ubuntu requires libncurses5-dev
for ncurses support and libicu-dev
for unicode support.
The frontends for yi can be manually specified as follows:
$ cabal install yi -fvty -fpango
Options are -fvty
and -fpango
.
Note: If you get a frontend not found
warning, install yi with the frontend manually specified by -fvty or -fpango while doing cabal install.
Note: If you’re having weird problems such as your changes not seeming to take effect, you might have some stale stuff in the cache directory which you should empty: on my system it’s ~/.cache/yi
.
Note: If you use Stack, you can perform the equivalent of the above cabal
commands with:
$ stack install yi
or
$ stack install yi --flag yi:vty --flag yi:pango
then start Yi using:
$ stack exec yi --package yi
Installing Yi from source
The Yi source tree is split into multiple packages such as yi-core, yi-language. These are all described in hpack files, from which there will be derived appropriate Cabal files during building.
Generally, Yi can be built using cabal or stack. For the stack users: Please note that stackage nightlies have recently been switched to GHC 8. Since older versions of stack might fail to build Yi using GHC 8 or do not automatically generate Cabal files, consider upgrading to a recent version of stack in case you encounter build problems.
Using Cabal-install
You can build and install Yi from source using Cabal-install as follows:
$ git clone https://github.com/yi-editor/yi
$ cd yi
$ cabal update
$ cabal install
If you are looking to get absolutely latest sources, make sure to install the supporting packages before installing Yi. Usually these packages are rarely updated and the version on Hackage is up-to-date. You can set up the supporting repositories from the GitHub project page.
Using Stack
Building and installing using stack should be similarly straightforward. After having cloned the repository and changed into its directory use the following commands:
$ stack build
$ stack install
Using a Cabal sandbox
Cabal-install 1.18 and higher support sandboxing which isolates the Yi installation from the rest of the cabal environment. This is especially important if you plan on hacking on Yi itself, or on libraries for Yi.
If your cabal version is lower than 1.20, you can install a newer version of cabal-install using cabal itself. This is recommended since newer versions of Cabal have several useful utilities for sandboxed environments.
$ cabal update
$ cabal install cabal-install
$ cabal --version
As Yi compiles your config file once you start it, the config needs to know where to look for any of its dependencies, such as Yi itself! If these are inside of the sandbox, it doesn’t know where to look and you’ll get config compilation errors due to missing modules.
To sandbox, navigate to your source yi directory. For me it’s ~/programming/yi/
.
We then setup a cabal sandbox:
$ cd ~/programming/yi
$ cabal sandbox init
$ cabal install --only-dependencies
$ cabal install
From cabal-install 1.20, Yi can be launched in an environment using the sandbox’s package DB using cabal exec yi
. It may be useful to create an alias or small script for this, along the lines of:
#!/usr/bin/env bash
YI_DIR=$HOME/programming/yi/yi
env CABAL_SANDBOX_CONFIG=$YI_DIR/cabal.sandbox.config cabal exec $YI_DIR/dist/build/yi/yi -- "$@"
The "$@"
part means that all the arguments we pass to this script are passed on to the Yi binary which means we can still use all the regular flags, such as yi --as=emacs
. Of course, you’ll need to adjust the paths used to match your sandbox and package directories.
There’s one more thing to mention in this section, and that is config dependencies. One of the great things about Yi is that we have access to the wealth of existing Haskell libraries and we can take advantage of this in our config file. There are two scenarios:
If the package your config depends on is on Hackage and you want to use that, just use
cabal install
in the sandboxed Yi directory. So if your config depends onsemigroups
, you’d runcabal install semigroups
. After doing this,semigroups
should now be visible when your config is getting compiled.If the package your config depends on is local, for example when you’re developing the library that you want to use or if you need a patched version, you’ll have to use
cabal sandbox add-source
command. As an example, I’m developing ayi-haskell-utils
package and my config depends on it. To accommodate for this, I rancabal sandbox add-source ~/programming/yi-haskell-utils
. You can thencabal install yi-haskell-utils
to add the package to the sandbox. You should callcabal build
in the sandbox directory after you modify a local package so that the sandbox has an up-to-date version of the package.
I suspect that it’d be perfectly possible to make your config file into a cabal project and manage the dependencies that way but I have not yet investigated this approach.
Still doesn’t work? Try the older instructions such as those removed here or here. Make sure to let us know!
Installing Yi with Nix
If you’re interested in Hacking Yi with Nix, see the section on Hacking Yi with Nix. For simpler setups, you can use the Yi wrapper provided. You will need to edit your nix config file (usually ~/.nixpkgs/config.nix
) to reflect the following:
{ pkgs }:
with pkgs;
let
ghcCompiler = pkgs.haskell.packages.ghc7101;
in
{
packageOverrides = super: let self = super.pkgs; in
{
yi-custom = pkgs.yi.override {
haskellPackages = ghcCompiler;
extraPackages = p: with p; [ lens ];
};
};
}
where extraPackages
is a list of haskell packages you wish to use in your Yi config, and haskellPackages
is your haskell package set.
If you want to compile and install local version of certain haskell packages, within packageOverrides
you could define a function as follows:
yi_packages = p: p.override {
overrides = se : su : {
yi = self.haskellPackages.callPackage /home/siddhu/Documents/code/yi {};
yi-language = self.haskellPackages.callPackage /home/siddhu/Documents/code/yi-language {};
};
};
}
and replace haskellPackages = ghcCompiler
with haskellPackages = yi_packages ghcCompiler
.
If the above doesn’t work, or if you only need to test Yi once, you could manually set $NIX_GHC
to point to your existing haskellpackages by running the following in the terminal:
$ nix-shell -p yi
$ export NIX_GHC=$(which ghc)
$ yi
Hacking Yi with nix
Hacking Yi with nix is pretty easy. Forget about cabal sanboxes and other nastiness. You will first need a way to override the Haskell package set. I will not go into this here, see NixOS wiki and other associated resources for help.
Once you have done this, you’ll need project files for each repository Yi depends on, as seen in its cabal file and from the repos here.
I’ll first discuss setting up the supporting repos and then move onto the main repository. The supporting repos don’t need anything unusual, make project files and stick them in your overrides as always.
Setting up supporting repositiories
For each repository, you’ll need default.nix
and most likely shell.nix
too if you want to do hacking in it. Consider yi-rope
repository as an example.
Check out yi-rope
from git into somewhere. Generate default.nix
with cabal2nix
and stash it somewhere accessible. Add shell.nix
in the same directory, with the trivial contents:
let pkgs = import <nixpkgs> {};
myHaskellPackages = pkgs.myHaskellPackages;
haskellPackages = myHaskellPackages.override {
extension = self: super: {
yiRope = myHaskellPackages.callPackage ./. {};
};
};
in pkgs.lib.overrideDerivation haskellPackages.yiRope (attrs: {
noHaddock = true;
buildInputs = [ ] ++ attrs.buildInputs;
})
myHaskellPackages
is my overriden package set. noHaddock = true
so that hacking is not slow inside nix-shell. You can add extra tools you like to use to the buildInputs
list.
Symlink your default.nix
and shell.nix
from within your yi-rope
checkout and ignore them in git (locally, not in .gitignore)
[shana@lenalee:~/programming/yi]$ l ~/programming/yi-rope
total 168K
drwxr-xr-x 6 shana shana 4.0K Sep 29 06:59 .
drwxr-xr-x 199 shana shana 4.0K Oct 5 18:36 ..
lrwxrwxrwx 1 shana shana 64 Sep 6 06:59 default.nix -> /home/shana/programming/nix-project-defaults/yi-rope/default.nix
lrwxrwxrwx 1 shana shana 62 Sep 6 06:58 shell.nix -> /home/shana/programming/nix-project-defaults/yi-rope/shell.nix
…
Then stick it into your Haskell overrides. Currently my overrides for Yi repos look as follows:
wordTrie = normalPackageS se "word-trie";
ooPrototypes = normalPackageS se "oo-prototypes";
yiLanguage = normalPackageS se "yi-language";
yiRope = normalPackageS se "yi-rope";
dynamicState = normalPackageS se "dynamic-state";
where normalPackageS
is just a handy alias for callPackage
. See my setup for details. Hack on these as you would with any normal project.
Setting up main repository
Now for the main repository. The tricky bit is that Yi needs to be able to find its packages and its dependencies at runtime so that running the GHCi evaluator (M-x
) works. First generate default.nix
and use a stock shell.nix
as usual. You might want to manually add vty
to dependency list and set
configureFlags = [ "-fpango" "-fvty" ];
# https://ghc.haskell.org/trac/ghc/ticket/9170
noHaddock = self.ghc.version == "7.6.3";
Symlink the project files as usual. The generated default.nix
should have all the supporting repositories listed so nix-shell --pure
should just work: remember to add the repos to your Haskell package set.
Once you’re in the shell, the first thing you want to do is eval "$configurePhase" && eval "$buildPhase"
: this will fully build Yi once which has the side effect of generating all the CPP macros and cabal-generated files. It also has the effect of setting GHC_PACKAGE_PATH
which is useful later.
You should now be able to simply use ghci
on any file in the repository: .ghci
is already set up to pick everything up.
Once you’re done making changes, you probably want to run Yi and try them out. Run eval "$buildPhase"
to build Yi. You can run the tests with eval "$checkPhase"
.
Now to run it, use something like
[nix-shell:~/programming/yi/yi]$ GHC_PACKAGE_PATH="$(pwd)/dist/package.conf.inplace:$GHC_PACKAGE_PATH" TERM=xterm yi_datadir=. dist/build/yi/yi -y /tmp -fpango --as=emacs /tmp/T.hs
Here’s what happens: we set GHC_PACKAGE_PATH
to the generated Yi package config inside dist (this allows us to find Yi modules) and the rest of packages (this allows to find all the dependencies). The reason for $(pwd)/…
rather than a relative path is to keep this working after we M-x cd
inside the editor. We set TERM
which you might need depending on your terminal due to nix-shell
messing with some env vars to ensure purity. We also set yi_datadir
to the current directory so that Yi can finds its icon and such.
Once all those are set, we run the compiled Yi itself (dist/build/yi/yi
), point it somewhere away from the user config directory (-y /tmp
), choose the frontend (-fpango
), choose the keymap (--as=emacs
) and choose the file to open (/tmp/T.hs
). This should succesfully start up Yi and ensure M-x
and all such dynamic commands work. All in all, this gives you an environment allowing you to hack Yi with nix-shell
as always even though it’s spread across multiple repositories and has an unusual setup.
Actually installing Yi with nix along with user config is a bit out of scope of this section but it works similarly, wrapping the binary into a script which sets GHC_PACKAGE_PATH
.
Automatic version tracking
Here’s an extra bit: manually tracking changing versions of the subrepositories can get pretty boring, pretty quickly. You can however use nix to automatically read the version from the cabal file and create the derivation based on the result. It ends up a bit ugly but it works. For yi-rope
my default.nix
is therefore
{ cabal, binary, deepseq, fingertree, hspec, QuickCheck
, quickcheckInstances, text, criterion
}:
let pkgs = import <nixpkgs> {};
lib = pkgs.lib;
sr = "/home/shana/programming/yi-rope";
file = builtins.readFile (sr + "/yi-rope.cabal");
strs = lib.strings.splitString "\n" file;
vstr = builtins.head (builtins.filter (s: lib.strings.hasPrefix "version:" s) strs);
vrsn = lib.strings.removePrefix "version:" (lib.strings.replaceChars [" "] [""] vstr);
in
cabal.mkDerivation (self: {
pname = "yi-rope";
version = vrsn;
src = sr;
buildDepends = [ binary deepseq fingertree text criterion ];
testDepends = [ hspec QuickCheck quickcheckInstances text ];
meta = {
description = "A rope data structure used by Yi";
license = self.stdenv.lib.licenses.gpl3;
platforms = self.ghc.meta.platforms;
};
})
The downside is if you ever run cabal2nix
over it, this scheme will get clobbered so it’s mostly only useful for the repositories which will most likely not get many extra dependencies but are likely to change versions often.
Installing Yi on macOS
ICU dependency
If you’re seeing an error like the following:
cabal: Error: some packages failed to install:
text-icu-0.7.0.1 failed during the configure step. The exception was:
ExitFailure 1
First make sure that the icu library is installed on the system:
$ brew install icu4c
Yi might not be able to find the icu library on MacOS unless the lib and include paths are explicitly passed as arguments to cabal.
$ cabal install text-icu --extra-lib-dirs=/usr/local/opt/icu4c/lib --extra-include-dirs=/usr/local/opt/icu4c/include
$ cabal install yi --extra-lib-dirs=/usr/local/opt/icu4c/lib --extra-include-dirs=/usr/local/opt/icu4c/include
Or if you prefer stack instead:
$ stack install text-icu --extra-lib-dirs=/usr/local/opt/icu4c/lib --extra-include-dirs=/usr/local/opt/icu4c/include
$ stack install yi
Pango dependency
Make sure the C library is available:
brew install pango
Pass have-quartz-gtk
flag to cabal/stack when building (possibly transitively) the gtk package:
cabal install gtk -fhave-quartz-gtk
# or
stack install --flag gtk:have-quartz-gtk
Not passing that flag would result in a compilation failure like this:
Couldn't match expected type ‘Ptr ()’
with actual type ‘Maybe DrawWindow’