Add basic :edit support
authorSimon Marlow <simonmar@microsoft.com>
Thu, 28 Sep 2006 13:51:56 +0000 (13:51 +0000)
committerSimon Marlow <simonmar@microsoft.com>
Thu, 28 Sep 2006 13:51:56 +0000 (13:51 +0000)
Without jumping to line numbers or %-expansion, we could add that later.

compiler/ghci/InteractiveUI.hs
docs/users_guide/ghci.xml

index 485e329..c38b376 100644 (file)
@@ -123,6 +123,9 @@ builtin_commands = [
   ("browse",    keepGoing browseCmd,           False, completeModule),
   ("cd",       keepGoing changeDirectory,      False, completeFilename),
   ("def",      keepGoing defineMacro,          False, completeIdentifier),
+  ("e",        keepGoing editFile,             False, completeFilename),
+       -- Hugs users are accustomed to :e, so make sure it doesn't overlap
+  ("edit",     keepGoing editFile,             False, completeFilename),
   ("help",     keepGoing help,                 False, completeNone),
   ("?",                keepGoing help,                 False, completeNone),
   ("info",      keepGoing info,                        False, completeIdentifier),
@@ -159,6 +162,8 @@ helpText =
  "   :browse [*]<module>         display the names defined by <module>\n" ++
  "   :cd <dir>                   change directory to <dir>\n" ++
  "   :def <cmd> <expr>           define a command :<cmd>\n" ++
+ "   :edit <file>                edit file\n" ++
+ "   :edit                       edit last module\n" ++
  "   :help, :?                   display this list of commands\n" ++
  "   :info [<name> ...]          display information about the given names\n" ++
  "   :load <filename> ...        load module(s) and their dependents\n" ++
@@ -170,6 +175,7 @@ helpText =
  "   :set args <arg> ...         set the arguments returned by System.getArgs\n" ++
  "   :set prog <progname>        set the value returned by System.getProgName\n" ++
  "   :set prompt <prompt>        set the prompt used in GHCi\n" ++
+ "   :set editor <cmd>           set the comand used for :edit\n" ++
  "\n" ++
  "   :show modules               show the currently loaded modules\n" ++
  "   :show bindings              show the current bindings made at the prompt\n" ++
@@ -242,11 +248,13 @@ jumpFunction session@(Session ref) (I# idsPtr) hValues location b
          writeIORef ref (hsc_env { hsc_IC = new_ic })
          is_tty <- hIsTerminalDevice stdin
          prel_mod <- GHC.findModule session prel_name Nothing
+        default_editor <- findEditor
          withExtendedLinkEnv (zip names hValues) $
            startGHCi (interactiveLoop is_tty True)
                      GHCiState{ progname = "<interactive>",
                                 args = [],
                                 prompt = location++"> ",
+                               editor = default_editor,
                                 session = session,
                                 options = [],
                                 prelude =  prel_mod }
@@ -255,6 +263,15 @@ jumpFunction session@(Session ref) (I# idsPtr) hValues location b
          return b
 #endif
 
+findEditor = do
+  getEnv "EDITOR" 
+    `IO.catch` \_ -> do
+#ifdef mingw32_HOST_OS
+       GetWindowsDirectory ++ "\\notepad.exe", or something
+#else
+       return ""
+#endif
+
 interactiveUI :: Session -> [(FilePath, Maybe Phase)] -> Maybe String -> IO ()
 interactiveUI session srcs maybe_expr = do
 #if defined(GHCI) && defined(BREAKPOINT)
@@ -306,10 +323,13 @@ interactiveUI session srcs maybe_expr = do
    Readline.setCompleterWordBreakCharacters word_break_chars
 #endif
 
+   default_editor <- findEditor
+
    startGHCi (runGHCi srcs maybe_expr)
        GHCiState{ progname = "<interactive>",
                   args = [],
                    prompt = "%s> ",
+                  editor = default_editor,
                   session = session,
                   options = [],
                    prelude = prel_mod }
@@ -739,6 +759,27 @@ changeDirectory dir = do
   dir <- expandPath dir
   io (setCurrentDirectory dir)
 
+editFile :: String -> GHCi ()
+editFile str
+  | null str  = do
+       -- find the name of the "topmost" file loaded
+     session <- getSession
+     graph0 <- io (GHC.getModuleGraph session)
+     graph1 <- filterM (io . GHC.isLoaded session . GHC.ms_mod_name) graph0
+     let graph2 = flattenSCCs (GHC.topSortModuleGraph True graph1 Nothing)
+     case GHC.ml_hs_file (GHC.ms_location (last graph2)) of
+       Just file -> do_edit file
+       Nothing   -> throwDyn (CmdLineError "unknown file name")
+  | otherwise = do_edit str
+  where
+       do_edit file = do
+          st <- getGHCiState
+          let cmd = editor st
+          when (null cmd) $ 
+               throwDyn (CmdLineError "editor not set, use :set editor")
+          io $ system (cmd ++ ' ':file)
+           return ()
+
 defineMacro :: String -> GHCi ()
 defineMacro s = do
   let (macro_name, definition) = break isSpace s
@@ -1169,11 +1210,13 @@ setCmd ""
                   else hsep (map (\o -> char '+' <> text (optToStr o)) opts)
           ))
 setCmd str
-  = case words str of
+  = case toArgs str of
        ("args":args) -> setArgs args
        ("prog":prog) -> setProg prog
-        ("prompt":prompt) -> setPrompt (dropWhile isSpace $ drop 6 $ dropWhile isSpace str)
+        ("prompt":prompt) -> setPrompt (after 6)
+        ("editor":cmd) -> setEditor (after 6)
        wds -> setOptions wds
+   where after n = dropWhile isSpace $ drop n $ dropWhile isSpace str
 
 setArgs args = do
   st <- getGHCiState
@@ -1185,6 +1228,10 @@ setProg [prog] = do
 setProg _ = do
   io (hPutStrLn stderr "syntax: :set prog <progname>")
 
+setEditor cmd = do
+  st <- getGHCiState
+  setGHCiState st{ editor = cmd }
+
 setPrompt value = do
   st <- getGHCiState
   if null value
@@ -1431,6 +1478,7 @@ data GHCiState = GHCiState
        progname       :: String,
        args           :: [String],
         prompt         :: String,
+       editor         :: String,
        session        :: GHC.Session,
        options        :: [GHCiOption],
         prelude        :: Module
index ae55203..76d4972 100644 (file)
@@ -1013,6 +1013,21 @@ Prelude> :. cmds.ghci
 
       <varlistentry>
        <term>
+          <literal>:edit <optional><replaceable>file</replaceable></optional></literal>
+          <indexterm><primary><literal>:edit</literal></primary></indexterm>
+        </term>
+       <listitem>
+         <para>Opens an editor to edit the file
+         <replaceable>file</replaceable>, or the most recently loaded
+         module if <replaceable>file</replaceable> is omitted.  The
+         editor to invoke is taken from the <literal>EDITOR</literal>
+         environment variable, or a default editor on your system if
+         <literal>EDITOR</literal> is not set.</para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>
           <literal>:help</literal>
           <indexterm><primary><literal>:help</literal></primary></indexterm>
         </term>