yi-0.3: Yi/Main.hs
-- Copyright (c) Tuomo Valkonen 2004.
-- Copyright (c) Don Stewart 2004-5.
-- Copyright (c) Jean-Philippe Bernardy 2006,2007.
--
-- This program is free software; you can redistribute it and/or modify
-- it under the terms of the GNU General Public License as published by
-- the Free Software Foundation; either version 2 of the License, or
-- (at your option) any later version.
--
--
-- | This is the real main module of Yi, and is shared between
-- Main.hs (the static binary), and dynamically loaded by Boot.hs.
-- We take any config arguments from the boot loader (if that is how we
-- are being invoked) parse command line args, initialise the ui, before
-- jumping into an event loop.
--
module Yi.Main (main, Kernel) where
import Prelude hiding (error)
import qualified Yi.Core as Core
import qualified Yi.Dired as Dired
import qualified Yi.Buffer as Buffer
import qualified Yi.Keymap as Keymap
import qualified Yi.Eval as Eval
import qualified Yi.Editor as Editor
import qualified Yi.Keymap.Emacs as Emacs
import qualified Yi.Keymap.Vim as Vim
import Yi.UI.Common (UIBoot)
import Yi.Kernel
import Yi.Debug
import Yi.Interact hiding (write)
#ifdef FRONTEND_COCOA
import qualified Yi.UI.Cocoa
import Foundation (withAutoreleasePool)
#endif
#ifdef FRONTEND_GTK
import qualified Yi.UI.Gtk
#endif
#ifdef FRONTEND_VTY
import qualified Yi.UI.Vty
#endif
import Data.Char
import Data.List ( intersperse )
import System.Console.GetOpt
import System.Environment ( getArgs )
import System.Exit
#include "ghcconfig.h"
frontends :: [(String,UIBoot)]
frontends =
#ifdef FRONTEND_COCOA
("cocoa", Yi.UI.Cocoa.start) :
#endif
#ifdef FRONTEND_GTK
("gtk", Yi.UI.Gtk.start) :
#endif
#ifdef FRONTEND_VTY
("vty", Yi.UI.Vty.start) :
#endif
[]
frontendNames :: [String]
frontendNames = map fst' frontends
where fst' :: (a,UIBoot) -> a
fst' (x,_) = x
-- ---------------------------------------------------------------------
-- | Argument parsing. Pretty standard.
data Opts = Help
| Version
| OptIgnore String
| LineNo String
| EditorNm String
| File String
| Frontend String
| ConfigFile String
-- | List of editors for which we provide an emulation.
editors :: [(String,Keymap.Keymap)]
editors = [("emacs", Emacs.keymap),
("vim", Vim.keymap)]
options :: [OptDescr Opts]
options = [
Option ['f'] ["frontend"] (ReqArg Frontend "[frontend]")
("Select frontend, which can be one of:\n" ++
(concat . intersperse ", ") frontendNames),
Option ['y'] ["config-file"] (ReqArg ConfigFile "path") "Specify a configuration file",
Option ['V'] ["version"] (NoArg Version) "Show version information",
Option ['B'] ["libdir"] (ReqArg OptIgnore "libdir") "Path to runtime libraries",
Option ['b'] ["bindir"] (ReqArg OptIgnore "bindir") "Path to runtime library binaries\n(default: libdir)",
Option ['h'] ["help"] (NoArg Help) "Show this help",
Option ['l'] ["line"] (ReqArg LineNo "[num]") "Start on line number",
Option [] ["as"] (ReqArg EditorNm "[editor]")
("Start with editor keymap, where editor is one of:\n" ++
(concat . intersperse ", " . map fst) editors)
]
-- | usage string.
usage, versinfo :: IO ()
usage = putStr $ usageInfo "Usage: yi [option...] [file]" options
versinfo = putStrLn $ "yi 0.3.0"
-- TODO: pull this out of the cabal configuration
-- | deal with real options
do_opt :: Opts -> IO (Keymap.YiM ())
do_opt o = case o of
Frontend f -> case map toLower f `elem` frontendNames of
False -> do putStrLn ("Unknown frontend: " ++ show f)
exitWith (ExitFailure 1)
_ -> return (return ()) -- Processed differently
OptIgnore _ -> return (return ())
ConfigFile _ -> return (return ())
Help -> usage >> exitWith ExitSuccess
Version -> versinfo >> exitWith ExitSuccess
LineNo l -> return (Keymap.withBuffer (Buffer.gotoLn (read l)) >> return ())
File file -> return (Dired.fnewE file)
EditorNm emul -> case lookup (map toLower emul) editors of
Just km -> return $ Core.changeKeymap km
Nothing -> do putStrLn ("Unknown emulation: " ++ show emul)
exitWith (ExitFailure 1)
-- | everything that is left over
do_args :: [String] -> IO (Core.StartConfig, [Keymap.YiM ()])
do_args args =
case (getOpt (ReturnInOrder File) options args) of
(o, [], []) -> do
let frontend = head $ [f | Frontend f <- o] ++ [head frontendNames]
yiConfig = head $ [f | ConfigFile f <- o] ++ ["YiConfig"]
config = Core.StartConfig { Core.startFrontEnd = case lookup frontend frontends of
Nothing -> error "Panic: frontend not found"
Just x -> x
, Core.startConfigFile = yiConfig
}
actions <- mapM do_opt o
return (config, actions)
(_, _, errs) -> do putStrLn (concat errs)
exitWith (ExitFailure 1)
startConsole :: Keymap.YiM ()
startConsole = do
console <- Core.withEditor $ Editor.getBufferWithName "*console*"
Keymap.setBufferKeymap console (Eval.consoleKeymap <||)
openScratchBuffer :: Keymap.YiM ()
openScratchBuffer = Core.withEditor $ do -- emacs-like behaviour
Editor.newBufferE "*scratch*"
("-- This buffer is for notes you don't want to save, and for haskell evaluation\n" ++
"-- If you want to create a file, open that file,\n" ++
"-- then enter the text in that file's own buffer.\n\n")
return ()
-- ---------------------------------------------------------------------
-- | Static main. This is the front end to the statically linked
-- application, and the real front end, in a sense. 'dynamic_main' calls
-- this after setting preferences passed from the boot loader.
--
main :: Kernel -> IO ()
#ifdef FRONTEND_COCOA
main kernel = withAutoreleasePool $ do
#else
main kernel = do
#endif
(config, mopts) <- do_args =<< getArgs
Core.startEditor config kernel Nothing (startConsole : openScratchBuffer : mopts)