shh 0.2.0.2 → 0.2.0.3
raw patch · 4 files changed
+132/−4 lines, 4 filesdep +hashabledep +temporarydep ~asyncdep ~basedep ~directorynew-component:exe:shhPVP: minor bump suggested
API additions: PVP suggests at least a minor version bump
Dependencies added: hashable, temporary
Dependency ranges changed: async, base, directory, split
API changes (from Hackage documentation)
+ Shh: class ExecArgs a
+ Shh: class Unit a
Files
- README.md +41/−1
- app/shh-app.hs +70/−0
- shh.cabal +19/−3
- src/Shh.hs +2/−0
README.md view
@@ -6,6 +6,8 @@ Shh is a library to enable convinient shell-like programming in Haskell. It works well in scripts, and from GHCi, allowing you to use GHCi as a shell. +It is also a wrapper tool around launching GHCi as a shell.+ It supports * Automatically defining a function for each executable on your `$PATH`@@ -99,6 +101,20 @@ ls &!> StdOut Redirect stderr of `ls` to wherever stdout is going. StdOut <!& ls Same as above +## Globbing++Currently Shh does not have any built in globbing support. Rather, it is+currently suggested to use another library to do globbing. For example,+using the [Glob](http://hackage.haskell.org/package/Glob) package, it is+possible to do something like++ wc =<< glob "*.md"++Certainly more verbose than the Bash equivalent, however, also more explicit,+which is probably a good thing. If this turns out to be too cumbersome, we+might introduce a more succinct globbing feature, though it will always be+explicit, and thus always more verbose than most other shells.+ ## Usage Enable Temlpate Haskell and load the environment@@ -120,7 +136,31 @@ `initInteractive` function. This sets the line buffering appropriately and ensures the terminal is in canonical mode. +### Shh as a Shell++There is a tool called `shh` which is a fairly small wrapper around launching+GHCi which automatically loads your environment and allows you to have custom+config when using GHCi as a shell.++The `shh` binary will look in your `$SHH_DIR` (defaults to `$HOME/.shh`) for+a `Shell.hs`, `init.ghci` and `wrapper` files. If these don't exist default+ones will be created.++The `Shell.hs` file should contain any top level definitions that you would+like to be available in your Shell. By default it loads your environment.++The `init.ghci` file is loaded by GHCi after your `.ghci` files. This lets+you specify settings that you want to take effect when using GHCi as a shell.+By default it sets a shell-like prompt.++The `wrapper` file is an executable that is called with the command that is+to be executed. By default it just calls `exec` with the arguments passed to+it. The use-case for this is to be able to set up the environment for `shh`.+You might, for example, wrap the execution in a `nix-shell`. Either way,+it is up to you to make sure that the compiler, and packages you require are+available, either globally, or provided by the `wrapper` script.+ ### Script Usage -TODO: Fill this in once on Hackage.+TODO: Fill this in once the user experience is better.
+ app/shh-app.hs view
@@ -0,0 +1,70 @@+{-# LANGUAGE LambdaCase #-}+module Main where++import Shh+import System.IO+import System.Environment+import System.IO.Temp+import System.Directory+import Data.Hashable (hash)+import Data.List.Split (splitOn)++defaultShell = "\+\{-# LANGUAGE TemplateHaskell #-}\n\+\module Shell where\n\+\import Shh\n\+\$(loadEnv SearchPath)\n\+\ "++defaultInitGhci = "\+\:seti -XNoOverloadedLists\n\+\import Shh\n\+\import Shh.Prompt\n\+\:set prompt-function promptFormat \"\\n\\ESC[1;32m[%u@%h:%w]λ \\ESC[0m\"\n\+\:set prompt-cont \"| \"\n\+\ "+++defaultWrapper = "\+\#! /usr/bin/env sh\n\+\exec \"$@\"\n\+\ "++debug = putStrLn++writeIfMissing :: FilePath -> String -> IO ()+writeIfMissing fp s = do+ doesFileExist fp >>= \case+ True -> pure ()+ False -> writeFile fp s++main :: IO ()+main = do+ a <- getArgs+ shhDir <- lookupEnv "SHH_DIR" >>= \case+ Nothing -> lookupEnv "HOME" >>= \case+ Nothing -> error "Please specify HOME or SHH_DIR environment variables"+ Just h -> pure $ h <> "/.shh"+ Just s -> pure s++ let+ wrapped :: (Unit a, ExecArgs a) => a+ wrapped = exe (shhDir <> "/wrapper")+++ debug $ "Shh home is: " <> shhDir++ createDirectoryIfMissing False shhDir++ withCurrentDirectory shhDir $ do+ writeIfMissing "init.ghci" defaultInitGhci+ writeIfMissing "wrapper" defaultWrapper+ setPermissions "wrapper" $+ setOwnerExecutable True $+ setOwnerReadable True $+ setOwnerWritable True $+ emptyPermissions+ writeIfMissing "Shell.hs" defaultShell++ wrapped "ghci" "-ghci-script" (shhDir <> "/init.ghci") (shhDir <> "/Shell.hs")+
shh.cabal view
@@ -1,5 +1,5 @@ name: shh-version: 0.2.0.2+version: 0.2.0.3 synopsis: Simple shell scripting from Haskell description: Provides a shell scripting environment for Haskell. It helps you all external binaries, and allows you to@@ -9,7 +9,7 @@ license-file: LICENSE author: Luke Clifton maintainer: lukec@themk.net-copyright: (c) 2018 Luke Clifton+copyright: (c) 2018, 2019 Luke Clifton category: System build-type: Simple extra-source-files: ChangeLog.md, README.md@@ -37,6 +37,23 @@ default-language: Haskell2010 ghc-options: -Wall +executable shh+ ghc-options: -threaded+ default-language: Haskell2010+ build-depends:+ base >=4.9,+ async,+ hashable,+ hashable,+ temporary,+ directory,+ shh,+ split+ hs-source-dirs: app+ main-is: shh-app.hs+ build-tools:+ ghc+ executable shh-example ghc-options: -threaded -with-rtsopts=-N default-language: Haskell2010@@ -48,7 +65,6 @@ main-is: Example.hs build-tools: coreutils,- vault, vim
src/Shh.hs view
@@ -48,6 +48,8 @@ , catchCode -- | == Constructing Arguments , ExecArg(..)+ , ExecArgs()+ , Unit() -- | == Template Haskell helpers , ExecReference(..) , load