ui: editor: cleanup, vi support, positioning support
This commit is contained in:
		
							parent
							
								
									4923efefb9
								
							
						
					
					
						commit
						c79750bafd
					
				| @ -268,7 +268,7 @@ asHandle ui0@UIState{ | |||||||
|         EvKey (KChar c)   [] | c `elem` ['h','?'] -> continue $ setMode Help ui |         EvKey (KChar c)   [] | c `elem` ['h','?'] -> continue $ setMode Help ui | ||||||
|         EvKey (KChar 'g') [] -> liftIO (uiReloadJournalIfChanged copts d j ui) >>= continue |         EvKey (KChar 'g') [] -> liftIO (uiReloadJournalIfChanged copts d j ui) >>= continue | ||||||
|         EvKey (KChar 'a') [] -> suspendAndResume $ clearScreen >> setCursorPosition 0 0 >> add copts j >> uiReloadJournalIfChanged copts d j ui |         EvKey (KChar 'a') [] -> suspendAndResume $ clearScreen >> setCursorPosition 0 0 >> add copts j >> uiReloadJournalIfChanged copts d j ui | ||||||
|         EvKey (KChar 'E') [] -> suspendAndResume $ void (runEditor endPos j) >> uiReloadJournalIfChanged copts d j ui |         EvKey (KChar 'E') [] -> suspendAndResume $ void (journalRunEditor endPos j) >> uiReloadJournalIfChanged copts d j ui | ||||||
|         EvKey (KChar '0') [] -> continue $ regenerateScreens j d $ setDepth (Just 0) ui |         EvKey (KChar '0') [] -> continue $ regenerateScreens j d $ setDepth (Just 0) ui | ||||||
|         EvKey (KChar '1') [] -> continue $ regenerateScreens j d $ setDepth (Just 1) ui |         EvKey (KChar '1') [] -> continue $ regenerateScreens j d $ setDepth (Just 1) ui | ||||||
|         EvKey (KChar '2') [] -> continue $ regenerateScreens j d $ setDepth (Just 2) ui |         EvKey (KChar '2') [] -> continue $ regenerateScreens j d $ setDepth (Just 2) ui | ||||||
|  | |||||||
| @ -15,61 +15,69 @@ import System.Process | |||||||
| 
 | 
 | ||||||
| import Hledger | import Hledger | ||||||
| 
 | 
 | ||||||
| -- | A shell command line for invoking an editor, containing one placeholder ("FILE") |  | ||||||
| -- which will be replaced with a quoted file path. This exists because some desirable |  | ||||||
| -- editor commands do not fit the simple "$EDITOR FILE" pattern. |  | ||||||
| type EditorCommandTemplate = String |  | ||||||
| 
 |  | ||||||
| -- | Editors we know how to create more specific command lines for. | -- | Editors we know how to create more specific command lines for. | ||||||
| data EditorType = Emacs | Other | data EditorType = Emacs | EmacsClient | Vi | Other | ||||||
| 
 | 
 | ||||||
| -- | A position we can move to in a text editor: a line number | -- | A position we can move to in a text editor: a line and optional column number. | ||||||
| -- and optionally character number. 1 (or 0) means the first; a negative number | -- 1 (or 0) means the first and -1 means the last (and -2 means the second last, etc. | ||||||
| -- counts back from the end (so -1 means the last line, -2 the second last etc.) | -- though this may not be well supported.) | ||||||
| type TextPosition = (Int, Maybe Int) | type TextPosition = (Int, Maybe Int) | ||||||
| 
 | 
 | ||||||
| endPos :: Maybe TextPosition | endPos :: Maybe TextPosition | ||||||
| endPos = Just (1,Nothing) | endPos = Just (-1,Nothing) | ||||||
| 
 | 
 | ||||||
| -- | Construct a shell command template for starting the user's preferred text editor, | -- | Try running the user's preferred text editor, or a default edit command, | ||||||
| -- optionally at a given position. | -- on the main journal file, blocking until it exits, and returning the exit code; | ||||||
| -- XXX The position parameter is currently ignored and assumed to be end-of-file. | -- or raise an error. | ||||||
| -- | journalRunEditor :: Maybe TextPosition -> Journal -> IO ExitCode | ||||||
| -- The basic editor command will be the value of environment variable $HLEDGER_UI_EDITOR, | journalRunEditor mpos j = | ||||||
| -- or $EDITOR, or "emacs -nw". If a position is specified, and the command looks like one of |   editorOpenPositionCommand mpos (journalFilePath j) >>= runCommand >>= waitForProcess | ||||||
| -- the editors we know (currently only emacs and emacsclient), it is modified so as to jump | 
 | ||||||
| -- to that position. | -- Get the basic shell command to start the user's preferred text editor. | ||||||
| -- | -- This is the value of environment variable $HLEDGER_UI_EDITOR, or $EDITOR, or | ||||||
| -- Some examples: | -- a default (emacsclient -a '' -nw, start/connect to an emacs daemon in terminal mode). | ||||||
| -- $EDITOR=vi            -> "vi FILE" | editorCommand :: IO String | ||||||
| -- $EDITOR=emacs         -> "emacs FILE -f end-of-buffer" | editorCommand = do | ||||||
| -- $EDITOR not set       -> "emacs -nw FILE -f end-of-buffer" |  | ||||||
| -- |  | ||||||
| editorCommandTemplate :: Maybe TextPosition -> IO EditorCommandTemplate |  | ||||||
| editorCommandTemplate mpos = do |  | ||||||
|   hledger_ui_editor_env <- lookupEnv "HLEDGER_UI_EDITOR" |   hledger_ui_editor_env <- lookupEnv "HLEDGER_UI_EDITOR" | ||||||
|   editor_env            <- lookupEnv "EDITOR" |   editor_env            <- lookupEnv "EDITOR" | ||||||
|   let Just exe = hledger_ui_editor_env <|> editor_env <|> Just "emacs -nw" |   let Just cmd = | ||||||
|   return $ |         hledger_ui_editor_env | ||||||
|    case (identifyEditor exe, mpos) of |         <|> editor_env | ||||||
|     (Emacs,_) -> exe ++ " FILE -f end-of-buffer" |         <|> Just "emacsclient -a '' -nw" | ||||||
|     _         -> exe ++ " FILE" |   return cmd | ||||||
| 
 | 
 | ||||||
| -- Identify the editor type, if we know it, from the value of $HLEDGER_EDITOR_UI or $EDITOR. | -- | Get a shell command to start the user's preferred text editor, or a default, | ||||||
|  | -- and optionally jump to a given position in the file. This will be the basic | ||||||
|  | -- editor command, with the appropriate options added, if we know how. | ||||||
|  | -- Currently we know how to do this for emacs and vi. | ||||||
|  | -- Some examples: | ||||||
|  | -- $EDITOR=notepad         -> "notepad FILE" | ||||||
|  | -- $EDITOR=vi              -> "vi +LINE FILE" | ||||||
|  | -- $EDITOR=vi, line -1     -> "vi + FILE" | ||||||
|  | -- $EDITOR=emacs           -> "emacs +LINE:COL FILE" | ||||||
|  | -- $EDITOR=emacs, line -1  -> "emacs FILE -f end-of-buffer" | ||||||
|  | -- $EDITOR not set         -> "emacs -nw FILE -f end-of-buffer" | ||||||
|  | -- | ||||||
|  | editorOpenPositionCommand :: Maybe TextPosition -> FilePath -> IO String | ||||||
|  | editorOpenPositionCommand mpos f = do | ||||||
|  |   cmd <- editorCommand | ||||||
|  |   let f' = singleQuoteIfNeeded f | ||||||
|  |   return $ | ||||||
|  |    case (identifyEditor cmd, mpos) of | ||||||
|  |     (Emacs, Just (line,_))    | line < 0  -> cmd ++ " " ++ f' ++ " -f end-of-buffer" | ||||||
|  |     (Emacs, Just (line,mcol)) | line >= 0 -> cmd ++ " " ++ posopt ++ " " ++ f' | ||||||
|  |       where posopt = "+" ++ show line ++ maybe "" ((":"++).show) mcol | ||||||
|  |     (Vi, Just (line,_))                   -> cmd ++ " " ++ posopt ++ " " ++ f' | ||||||
|  |       where posopt = "+" ++ if line >= 0 then show line else "" | ||||||
|  |     _                                     -> cmd ++ " " ++ f' | ||||||
|  | 
 | ||||||
|  | -- Identify which text editor is used in the basic editor command, if possible. | ||||||
| identifyEditor :: String -> EditorType | identifyEditor :: String -> EditorType | ||||||
| identifyEditor cmd | identifyEditor cmd | ||||||
|   | "emacs" `isPrefixOf` exe = Emacs |   | "emacsclient" `isPrefixOf` exe = EmacsClient | ||||||
|   | otherwise = Other |   | "emacs" `isPrefixOf` exe       = Emacs | ||||||
|  |   | exe `elem` ["vi","vim","ex","view","gvim","gview","evim","eview","rvim","rview","rgvim","rgview"] | ||||||
|  |                                    = Vi | ||||||
|  |   | otherwise                      = Other | ||||||
|   where |   where | ||||||
|     exe = lowercase $ takeFileName $ headDef "" $ words' cmd |     exe = lowercase $ takeFileName $ headDef "" $ words' cmd | ||||||
| 
 |  | ||||||
| fillEditorCommandTemplate :: FilePath -> EditorCommandTemplate -> String |  | ||||||
| fillEditorCommandTemplate f t = regexReplace "FILE" (singleQuoteIfNeeded f) t |  | ||||||
| 
 |  | ||||||
| -- | Try running $EDITOR, or a default edit command, on the main journal file, |  | ||||||
| -- blocking until it exits, and returning the exit code; or raise an error. |  | ||||||
| runEditor :: Maybe TextPosition -> Journal -> IO ExitCode |  | ||||||
| runEditor mpos j = do |  | ||||||
|   fillEditorCommandTemplate (journalFilePath j) <$> editorCommandTemplate mpos |  | ||||||
|   >>= runCommand |  | ||||||
|   >>= waitForProcess |  | ||||||
|  | |||||||
| @ -78,7 +78,7 @@ esHandle ui@UIState{ | |||||||
|         EvKey (KChar 'q') [] -> halt ui |         EvKey (KChar 'q') [] -> halt ui | ||||||
|         EvKey KEsc        [] -> continue $ resetScreens d ui |         EvKey KEsc        [] -> continue $ resetScreens d ui | ||||||
|         EvKey (KChar c)   [] | c `elem` ['h','?'] -> continue $ setMode Help ui |         EvKey (KChar c)   [] | c `elem` ['h','?'] -> continue $ setMode Help ui | ||||||
|         EvKey (KChar 'E') [] -> suspendAndResume $ void (runEditor endPos j) >> uiReloadJournalIfChanged copts d j (popScreen ui) |         EvKey (KChar 'E') [] -> suspendAndResume $ void (journalRunEditor endPos j) >> uiReloadJournalIfChanged copts d j (popScreen ui) | ||||||
|         EvKey (KChar 'g') [] -> liftIO (uiReloadJournalIfChanged copts d j (popScreen ui)) >>= continue |         EvKey (KChar 'g') [] -> liftIO (uiReloadJournalIfChanged copts d j (popScreen ui)) >>= continue | ||||||
| --           (ej, _) <- liftIO $ journalReloadIfChanged copts d j | --           (ej, _) <- liftIO $ journalReloadIfChanged copts d j | ||||||
| --           case ej of | --           case ej of | ||||||
|  | |||||||
| @ -250,7 +250,7 @@ rsHandle ui@UIState{ | |||||||
|         EvKey (KChar c)   [] | c `elem` ['h','?'] -> continue $ setMode Help ui |         EvKey (KChar c)   [] | c `elem` ['h','?'] -> continue $ setMode Help ui | ||||||
|         EvKey (KChar 'g') [] -> liftIO (uiReloadJournalIfChanged copts d j ui) >>= continue |         EvKey (KChar 'g') [] -> liftIO (uiReloadJournalIfChanged copts d j ui) >>= continue | ||||||
|         EvKey (KChar 'a') [] -> suspendAndResume $ clearScreen >> setCursorPosition 0 0 >> add copts j >> uiReloadJournalIfChanged copts d j ui |         EvKey (KChar 'a') [] -> suspendAndResume $ clearScreen >> setCursorPosition 0 0 >> add copts j >> uiReloadJournalIfChanged copts d j ui | ||||||
|         EvKey (KChar 'E') [] -> suspendAndResume $ void (runEditor endPos j) >> uiReloadJournalIfChanged copts d j ui |         EvKey (KChar 'E') [] -> suspendAndResume $ void (journalRunEditor endPos j) >> uiReloadJournalIfChanged copts d j ui | ||||||
|         EvKey (KChar 'F') [] -> scrollTop >> (continue $ regenerateScreens j d $ toggleFlat ui) |         EvKey (KChar 'F') [] -> scrollTop >> (continue $ regenerateScreens j d $ toggleFlat ui) | ||||||
|         EvKey (KChar 'Z') [] -> scrollTop >> (continue $ regenerateScreens j d $ toggleEmpty ui) |         EvKey (KChar 'Z') [] -> scrollTop >> (continue $ regenerateScreens j d $ toggleEmpty ui) | ||||||
|         EvKey (KChar 'C') [] -> scrollTop >> (continue $ regenerateScreens j d $ toggleCleared ui) |         EvKey (KChar 'C') [] -> scrollTop >> (continue $ regenerateScreens j d $ toggleCleared ui) | ||||||
|  | |||||||
| @ -124,7 +124,7 @@ tsHandle ui@UIState{aScreen=s@TransactionScreen{tsTransaction=(i,t) | |||||||
|         EvKey (KChar 'q') [] -> halt ui |         EvKey (KChar 'q') [] -> halt ui | ||||||
|         EvKey KEsc        [] -> continue $ resetScreens d ui |         EvKey KEsc        [] -> continue $ resetScreens d ui | ||||||
|         EvKey (KChar c)   [] | c `elem` ['h','?'] -> continue $ setMode Help ui |         EvKey (KChar c)   [] | c `elem` ['h','?'] -> continue $ setMode Help ui | ||||||
|         EvKey (KChar 'E') [] -> suspendAndResume $ void (runEditor endPos j) >> uiReloadJournalIfChanged copts d j ui |         EvKey (KChar 'E') [] -> suspendAndResume $ void (journalRunEditor endPos j) >> uiReloadJournalIfChanged copts d j ui | ||||||
|         EvKey (KChar 'g') [] -> do |         EvKey (KChar 'g') [] -> do | ||||||
|           d <- liftIO getCurrentDay |           d <- liftIO getCurrentDay | ||||||
|           (ej, _) <- liftIO $ journalReloadIfChanged copts d j |           (ej, _) <- liftIO $ journalReloadIfChanged copts d j | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user