Improve editor support
Documentation for editFileAtPositionCommand is made more precise. This commit makes it possible for hledger-ui to open a file at the last line on emacs and kakoune. It also prevents hledger-ui from opening an editor with imprecise arguments. Passing imprecise arguments to editors can cause undefined behaviors in editors. I tested it with emacs(client), nano, vscode, kakoune, nvim, and vis.
This commit is contained in:
parent
ba94582945
commit
5725fb8ab7
@ -11,6 +11,7 @@ where
|
|||||||
import Control.Applicative ((<|>))
|
import Control.Applicative ((<|>))
|
||||||
import Data.List (intercalate)
|
import Data.List (intercalate)
|
||||||
import Data.Maybe (catMaybes)
|
import Data.Maybe (catMaybes)
|
||||||
|
import Data.Bifunctor (bimap)
|
||||||
import Safe
|
import Safe
|
||||||
import System.Environment
|
import System.Environment
|
||||||
import System.Exit
|
import System.Exit
|
||||||
@ -25,7 +26,7 @@ type TextPosition = (Int, Maybe Int)
|
|||||||
|
|
||||||
-- | The text position meaning "last line, first column".
|
-- | The text position meaning "last line, first column".
|
||||||
endPosition :: Maybe TextPosition
|
endPosition :: Maybe TextPosition
|
||||||
endPosition = Just (-1,Nothing)
|
endPosition = Just (-1, Nothing)
|
||||||
|
|
||||||
-- | Run the hledger-iadd executable on the given file, blocking until it exits,
|
-- | Run the hledger-iadd executable on the given file, blocking until it exits,
|
||||||
-- and return the exit code; or raise an error.
|
-- and return the exit code; or raise an error.
|
||||||
@ -43,65 +44,82 @@ runEditor mpos f = editFileAtPositionCommand mpos f >>= runCommand >>= waitForPr
|
|||||||
-- | Get a shell command line to open the user's preferred text editor
|
-- | Get a shell command line to open the user's preferred text editor
|
||||||
-- (or a default editor) on the given file, and to focus it at the
|
-- (or a default editor) on the given file, and to focus it at the
|
||||||
-- given text position if one is provided and if we know how.
|
-- given text position if one is provided and if we know how.
|
||||||
-- We know how to focus on position for: emacs, vim, nano, VS code, kakoune.
|
--
|
||||||
-- We know how to focus on last line for: vi.
|
-- Just ('-' : _, _) is any text position with a negative line number.
|
||||||
|
-- A text position with a negative line number means the last line.
|
||||||
--
|
--
|
||||||
-- Some tests:
|
-- Some tests:
|
||||||
-- @
|
-- @
|
||||||
-- EDITOR program is: LINE/COL specified ? Command should be:
|
-- EDITOR program: Maybe TextPosition Command should be:
|
||||||
-- ------------------ -------------------- -----------------------------------
|
-- --------------- --------------------- ------------------------------------
|
||||||
-- emacs, LINE COL EDITOR +LINE:COL FILE
|
-- emacs Just (line, Just col) emacs +LINE:COL FILE
|
||||||
-- emacsclient, LINE EDITOR +LINE FILE
|
-- Just (line, Nothing) emacs +LINE FILE
|
||||||
-- kak EDITOR FILE
|
-- Just ('-' : _, _) emacs FILE -f end-of-buffer
|
||||||
|
-- Nothing emacs FILE
|
||||||
--
|
--
|
||||||
-- nano LINE COL EDITOR +LINE,COL FILE
|
-- emacsclient Just (line, Just col) emacsclient +LINE:COL FILE
|
||||||
-- LINE EDITOR +LINE FILE
|
-- Just (line, Nothing) emacsclient +LINE FILE
|
||||||
-- EDITOR FILE
|
-- Just ('-' : _, _) emacsclient FILE
|
||||||
|
-- Nothing emacsclient FILE
|
||||||
--
|
--
|
||||||
-- code LINE COL EDITOR --goto FILE:LINE:COL
|
-- nano Just (line, Just col) nano +LINE:COL FILE
|
||||||
-- LINE EDITOR --goto FILE:LINE
|
-- Just (line, Nothing) nano +LINE FILE
|
||||||
-- EDITOR FILE
|
-- Just ('-' : _, _) nano FILE
|
||||||
|
-- Nothing nano FILE
|
||||||
--
|
--
|
||||||
-- vi/vim variants LINE [COL] EDITOR +LINE FILE
|
-- vscode Just (line, Just col) vscode --goto FILE:LINE:COL
|
||||||
-- LINE (negative) EDITOR + FILE
|
-- Just (line, Nothing) vscode --goto FILE:LINE
|
||||||
-- EDITOR FILE
|
-- Just ('-' : _, _) vscode FILE
|
||||||
|
-- Nothing vscode FILE
|
||||||
--
|
--
|
||||||
-- other [LINE [COL]] EDITOR FILE
|
-- kak Just (line, Just col) kak +LINE:COL FILE
|
||||||
|
-- Just (line, Nothing) kak +LINE FILE
|
||||||
|
-- Just ('-' : _, _) kak +: FILE
|
||||||
|
-- Nothing kak FILE
|
||||||
--
|
--
|
||||||
-- not set LINE COL emacsclient -a '' -nw +LINE:COL FILE
|
-- vi & variants Just (line, _) vi +LINE FILE
|
||||||
-- LINE emacsclient -a '' -nw +LINE FILE
|
-- Just ('-' : _, _) vi + FILE
|
||||||
-- emacsclient -a '' -nw FILE
|
-- Nothing vi FILE
|
||||||
-- @
|
|
||||||
--
|
--
|
||||||
-- Notes on opening editors at the last line of a file:
|
-- (other PROG) _ PROG FILE
|
||||||
-- @
|
--
|
||||||
-- emacs: emacs FILE -f end-of-buffer # (-f must appear after FILE, +LINE:COL must appear before)
|
-- (not set) Just (line, Just col) emacsclient -a '' -nw +LINE:COL FILE
|
||||||
-- emacsclient: can't
|
-- Just (line, Nothing) emacsclient -a '' -nw +LINE FILE
|
||||||
-- vi: vi + FILE
|
-- Just ('-' : _, _) emacsclient -a '' -nw FILE
|
||||||
-- kakoune: kak +: FILE
|
-- Nothing emacsclient -a '' -nw FILE
|
||||||
-- @
|
-- @
|
||||||
--
|
--
|
||||||
editFileAtPositionCommand :: Maybe TextPosition -> FilePath -> IO String
|
editFileAtPositionCommand :: Maybe TextPosition -> FilePath -> IO String
|
||||||
editFileAtPositionCommand mpos f = do
|
editFileAtPositionCommand mpos f = do
|
||||||
cmd <- getEditCommand
|
cmd <- getEditCommand
|
||||||
let
|
let editor = lowercase $ takeBaseName $ headDef "" $ words' cmd
|
||||||
editor = lowercase $ takeBaseName $ headDef "" $ words' cmd
|
f' = singleQuoteIfNeeded f
|
||||||
f' = singleQuoteIfNeeded f
|
mpos' = Just . bimap show (fmap show) =<< mpos
|
||||||
ml = show.fst <$> mpos
|
join sep = intercalate sep . catMaybes
|
||||||
mc = maybe Nothing (fmap show.snd) mpos
|
args = case editor of
|
||||||
args = case editor of
|
"emacs" -> case mpos' of
|
||||||
e | e `elem` ["emacs", "emacsclient",
|
Nothing -> [f']
|
||||||
"kak"] -> ['+' : join ":" [ml,mc], f']
|
Just ('-' : _, _) -> [f', "-f", "end-of-buffer"]
|
||||||
e | e `elem` ["nano"] -> ['+' : join "," [ml,mc], f']
|
Just (l, mc) -> ['+' : join ":" [Just l, mc], f']
|
||||||
e | e `elem` ["code"] -> ["--goto " ++ join ":" [Just f',ml,mc]]
|
e | e `elem` ["emacsclient", "nano"] -> case mpos' of
|
||||||
e | e `elem` ["vi", "vim", "view", "nvim", "evim", "eview",
|
Nothing -> [f']
|
||||||
"gvim", "gview", "rvim", "rview", "rgvim", "rgview",
|
Just ('-' : _, _) -> [f']
|
||||||
"ex"] -> [maybe "" plusMaybeLine ml, f']
|
Just (l, mc) -> ['+' : join ":" [Just l, mc], f']
|
||||||
_ -> [f']
|
"vscode" -> case mpos' of
|
||||||
where
|
Nothing -> [f']
|
||||||
join sep = intercalate sep . catMaybes
|
Just ('-' : _, _) -> [f']
|
||||||
plusMaybeLine l = "+" ++ if take 1 l == "-" then "" else l
|
Just (l, mc) -> ["--goto", join ":" [Just f', Just l, mc]]
|
||||||
|
"kak" -> case mpos' of
|
||||||
|
Nothing -> [f']
|
||||||
|
Just ('-' : _, _) -> ["+:", f']
|
||||||
|
Just (l, mc) -> ['+' : join ":" [Just l, mc], f']
|
||||||
|
e | e `elem` ["vi", "vim", "view", "nvim", "evim", "eview",
|
||||||
|
"gvim", "gview", "rvim", "rview",
|
||||||
|
"rgvim", "rgview", "ex"] -> case mpos' of
|
||||||
|
Nothing -> [f']
|
||||||
|
Just ('-' : _, _) -> ["+", f']
|
||||||
|
Just (l, _) -> ['+' : l, f']
|
||||||
|
_ -> [f']
|
||||||
return $ unwords $ cmd:args
|
return $ unwords $ cmd:args
|
||||||
|
|
||||||
-- | Get the user's preferred edit command. This is the value of the
|
-- | Get the user's preferred edit command. This is the value of the
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user