Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Factored out searching for app files to Yi.Paths.

Now Yi uses ~/.yi/ directory to store:
 * 'yi.hs' config script
 * 'articles.db' for Yi.IReader
 * 'history' blob for Yi.PersistentState
 * 'local/Env.hs' for GHCi command evaluator environment

This patch refactors searching for four above files to Yi.Paths,
so that they can be consistently updated.
I did it in preparation for prioritizing XDG-style directories
as implemented by xdg-basedir - if they exist, or there is no Yi config
present.
If the XDG-style directories ~/.config/yi and ~/.local/share/yi
do not exist, but ~/.yi exists, then future patch will fall back
to current behaviour.

NOTE: Indirectly Yi also uses ~/.cache/yi/ for storing binaries with
configuration compiled in. This is however handled by dyre library.
And already consistent with XDG directory standard by xdg-basedir
package.
  • Loading branch information...
commit 8695beaa6f95a7486d98e6f83301cac466a28c4b 1 parent 3d33b52
mgajda mgajda authored
3  yi/src/library/Yi/Boot.hs
View
@@ -17,6 +17,7 @@ import Yi.Editor
import Yi.Keymap
import Yi.Main
import qualified Yi.UI.Common as UI
+import qualified Yi.Paths(getConfigDir)
-- | once the custom yi is compiled this restores the editor state (if requested) then proceeds to
-- run the editor.
@@ -52,7 +53,7 @@ yiDriver cfg = do
{ Dyre.projectName = "yi"
, Dyre.realMain = realMain
, Dyre.showError = showErrorsInConf
- , Dyre.configDir = Just . getAppUserDataDirectory $ "yi"
+ , Dyre.configDir = Just Yi.Paths.getConfigDir
, Dyre.hidePackages = ["mtl"]
, Dyre.ghcOpts = (["-threaded", "-O2"] ++
#ifdef PROFILING
8 yi/src/library/Yi/Config/Default.hs
View
@@ -16,6 +16,7 @@ import Yi.Command (cabalBuildE, cabalConfigureE, grepFind, makeBuild, reloadProj
import {-# source #-} Yi.Boot
import Yi.Config
import Yi.Config.Misc
+import Yi.Paths(getConfigFilename)
import Yi.Core
import Yi.Eval(publishedActions)
import Yi.File
@@ -233,14 +234,13 @@ nilKeymap = choice [
where configHelp = newBufferE (Left "configuration help") $ R.fromString $ unlines $
["This instance of Yi is not configured.",
"To get a standard reasonable keymap, you can run yi with either --as=cua, --as=vim or --as=emacs.",
- "You should however create your own ~/.yi/yi.hs file: ",
+ "You should however create your own ~/.config/yi/yi.hs file: ",
"You can type 'c', 'e' or 'v' now to create and edit it using a temporary cua, emacs or vim keymap."]
openCfg km kmName = write $ do
dataDir <- io getDataDir
let exampleCfg = dataDir </> "example-configs" </> kmName
- homeDir <- io getHomeDirectory
- cfgDir <- io $ getAppUserDataDirectory "yi"
- let cfgFile = cfgDir </> "yi.hs"
+ cfgFile <- getConfigFilename
+ let cfgDir = takeDirectory cfgFile
cfgExists <- io $ doesFileExist cfgDir
io $ createDirectoryIfMissing True cfgDir -- so that the file can be saved
discard $ editFile cfgFile -- load config file
7 yi/src/library/Yi/Eval.hs
View
@@ -23,8 +23,7 @@ import Data.List
import Data.Monoid
import Prelude hiding (error, (.))
import qualified Language.Haskell.Interpreter as LHI
-import System.FilePath
-import System.Directory
+import System.Directory(doesFileExist)
import qualified Data.HashMap.Strict as M
import Yi.Config.Simple.Types
@@ -32,6 +31,7 @@ import Yi.Core hiding (concatMap)
import Yi.File
import Yi.Hooks
import Yi.Regex
+import qualified Yi.Paths(getEvaluatorContextFilename)
-- | Runs the action, as written by the user.
--
@@ -85,8 +85,7 @@ ghciEvaluator :: Evaluator
ghciEvaluator = Evaluator{..} where
execEditorActionImpl :: String -> YiM ()
execEditorActionImpl s = do
- contextPath <- (</> ".yi" </> "local") <$> io getHomeDirectory
- let contextFile = contextPath </> "Env.hs"
+ contextFile <- Yi.Paths.getEvaluatorContextFilename
haveUserContext <- io $ doesFileExist contextFile
res <- io $ LHI.runInterpreter $ do
LHI.set [LHI.searchPath LHI.:= []]
11 yi/src/library/Yi/IReader.hs
View
@@ -23,6 +23,7 @@ import Yi.Buffer.Region (readRegionB)
import Yi.Dynamic
import Yi.Keymap (withBuffer, YiM)
import Yi.Prelude (getA, putA, io, discard, Initializable(..))
+import Yi.Paths(getArticleDbFilename)
type Article = B.ByteString
newtype ArticleDB = ADB { unADB :: Seq Article }
@@ -64,21 +65,17 @@ insertArticle (ADB adb) new = ADB (new <| adb)
-- | Serialize given 'ArticleDB' out.
writeDB :: ArticleDB -> YiM ()
-writeDB adb = discard $ io . join . fmap (flip encodeFile adb) $ dbLocation
+writeDB adb = discard $ io . join . fmap (flip encodeFile adb) $ getArticleDbFilename
--- | Read in database from 'dbLocation' and then parse it into an 'ArticleDB'.
+-- | Read in database from 'getArticleDbFilename' and then parse it into an 'ArticleDB'.
readDB :: YiM ArticleDB
-readDB = io $ (dbLocation >>= r) `catch` returnDefault
+readDB = io $ (getArticleDbFilename >>= r) `catch` returnDefault
where r = fmap (decode . BL.fromChunks . return) . B.readFile
-- We read in with strict bytestrings to guarantee the file is closed,
-- and then we convert it to the lazy bytestring data.binary expects.
-- This is inefficient, but alas...
returnDefault (_ :: SomeException) = return initial
--- | The canonical location. We assume \~\/.yi has been set up already.
-dbLocation :: IO FilePath
-dbLocation = getHomeDirectory >>= \home -> return (home ++ "/.yi/articles.db")
-
-- | Returns the database as it exists on the disk, and the current Yi buffer contents.
-- Note that the Initializable typeclass gives us an empty Seq. So first we try the buffer
-- state in the hope we can avoid a very expensive read from disk, and if we find nothing
62 yi/src/library/Yi/Paths.hs
View
@@ -0,0 +1,62 @@
+{-# LANGUAGE NoMonomorphismRestriction #-}
+module Yi.Paths(
+ getEvaluatorContextFilename
+ ,getConfigFilename
+ ,getArticleDbFilename
+ ,getPersistentStateFilename
+ ,getConfigDir
+
+ ,getConfigPath
+ ,getDataPath
+) where
+
+import System.Directory(getHomeDirectory, getAppUserDataDirectory, -- TODO: phase out in favour of xdg-dir
+ doesFileExist, createDirectoryIfMissing)
+import System.FilePath((</>), takeDirectory)
+import Control.Applicative((<$>))
+import Control.Monad(filterM, Monad)
+import Control.Monad.Trans(liftIO, MonadIO)
+-- import qualified System.Environment.XDG.BaseDir as XDG
+
+-- TODO: These would be replaced by xdg-basedir equivalents
+getConfigDir = liftIO $ getAppUserDataDirectory "yi"
+getDataDir = liftIO $ getAppUserDataDirectory "yi"
+
+-- | This is internal function that factors out common code from
+-- getConfigPath and getDataPath
+getPathHelper :: (MonadIO m) => (String -> IO FilePath) -> FilePath -> m FilePath
+getPathHelper basePathGenerator fname = do
+ baseDir <- liftIO $ basePathGenerator "yi"
+ case fname of
+ "" -> return baseDir -- calling for directory path
+ _ -> return $ baseDir </> fname
+
+-- | Given a path relative to application data directory,
+-- this function finds a path to a given data file.
+getDataPath :: (MonadIO m) => FilePath -> m FilePath
+getDataPath = getPathHelper getAppUserDataDirectory
+
+-- | Given a path relative to application configuration directory,
+-- this function finds a path to a given configuration file.
+getConfigPath :: (MonadIO m) => FilePath -> m FilePath
+getConfigPath = getPathHelper getAppUserDataDirectory
+
+-- Note: Dyre also uses XDG cache dir - that would be:
+--getCachePath = getPathHelper XDG.getUserCacheDirectory
+
+-- Below are all points that are used in Yi code (to keep it clean.)
+getEvaluatorContextFilename, getConfigFilename, getArticleDbFilename, getPersistentStateFilename :: (MonadIO m) => m FilePath
+
+-- | Get Yi master configuration script.
+getConfigFilename = getConfigPath "yi.hs"
+
+-- | Get articles.db database of locations to visit (for Yi.IReader.)
+getArticleDbFilename = getConfigPath "articles.db"
+
+-- | Get path to Yi history that stores state between runs.
+getPersistentStateFilename = getDataPath "history"
+
+-- | Get path to environment file that defines namespace used by Yi
+-- command evaluator.
+getEvaluatorContextFilename = getConfigPath $ "local" </> "Env.hs"
+
9 yi/src/library/Yi/PersistentState.hs
View
@@ -15,8 +15,7 @@ import Prelude hiding ((.))
import Data.Binary
import Data.DeriveTH
import Data.Accessor.Template(nameDeriveAccessors)
-import System.FilePath((</>))
-import System.Directory(getAppUserDataDirectory, doesFileExist)
+import System.Directory(doesFileExist)
import qualified Data.Map as M
import Control.Exc(ignoringException)
@@ -32,6 +31,7 @@ import Yi.Keymap.Vim.TagStack(VimTagStack(..), getTagStack, setTagStack)
import Yi.KillRing(Killring(..))
import Yi.Search(getRegexE, setRegexE)
import Yi.Regex(SearchExp(..))
+import Yi.Paths(getPersistentStateFilename)
data PersistentState = PersistentState { histories :: !Histories
@@ -55,11 +55,6 @@ $(nameDeriveAccessors ''MaxHistoryEntries (\n -> Just (n ++ "A")))
maxHistoryEntries :: Field Int
maxHistoryEntries = unMaxHistoryEntriesA . customVariable
--- | Finds a path of history file.
-getPersistentStateFilename :: YiM String
-getPersistentStateFilename = do cfgDir <- io $ getAppUserDataDirectory "yi"
- return $ cfgDir </> "history"
-
-- | Trims per-command histories to contain at most N completions each.
trimHistories :: Int -> Histories -> Histories
trimHistories maxHistory = M.map trimH
1  yi/yi.cabal
View
@@ -167,6 +167,7 @@ library
Yi.Mode.Latex
Yi.Modes
Yi.Monad
+ Yi.Paths
Yi.PersistentState
Yi.Prelude
Yi.Process
Please sign in to comment.
Something went wrong with that request. Please try again.