diff options
author | Hugo Hörnquist <hugo@lysator.liu.se> | 2022-10-29 13:11:06 +0200 |
---|---|---|
committer | Hugo Hörnquist <hugo@lysator.liu.se> | 2022-10-29 13:11:06 +0200 |
commit | 3da17902b31192d307c4adbfdb1f2e9c693713bc (patch) | |
tree | 6107f28a387146b9263a8ac5658f546a9e5046ea | |
parent | Setup basic configuration. (diff) | |
download | vimwiki-scripts-3da17902b31192d307c4adbfdb1f2e9c693713bc.tar.gz vimwiki-scripts-3da17902b31192d307c4adbfdb1f2e9c693713bc.tar.xz |
Major cleanup.
-rw-r--r-- | hs/Config.hs | 52 | ||||
-rw-r--r-- | hs/Data/Ini/Config.hs | 529 | ||||
-rw-r--r-- | hs/Data/Ini/Config/KeyValue.hs | 15 | ||||
-rw-r--r-- | hs/Data/Ini/Config/URIDict.hs | 27 | ||||
-rw-r--r-- | hs/Files.hs | 13 | ||||
-rw-r--r-- | hs/Handlingar.hs | 3 | ||||
-rw-r--r-- | hs/Html.hs | 268 | ||||
-rw-r--r-- | hs/System/Home.hs | 27 | ||||
-rw-r--r-- | hs/Util.hs | 3 | ||||
-rw-r--r-- | hs/Vimwiki/Man.hs | 42 | ||||
-rw-r--r-- | hs/config.hs | 5 | ||||
-rw-r--r-- | hs/main.hs | 10 | ||||
-rw-r--r-- | hs/private.ini | 13 | ||||
-rw-r--r-- | hs/public.ini | 8 | ||||
-rw-r--r-- | hs/vimwiki.cabal | 41 |
15 files changed, 825 insertions, 231 deletions
diff --git a/hs/Config.hs b/hs/Config.hs index 691f67f..69dedf6 100644 --- a/hs/Config.hs +++ b/hs/Config.hs @@ -1,40 +1,45 @@ {-# LANGUAGE TemplateHaskell , OverloadedStrings + , FlexibleInstances #-} module Config where import Control.Lens - -import Options.Applicative - import Data.Ini.Config +import Data.Map (Map) +import Network.URI (URI) +import Options.Applicative +import Data.Ini.Config.URIDict +import Data.String data Args = Args - { indir :: String - , outdir :: String - } + { indir :: Maybe String + , outdir :: Maybe String + , conffile :: Maybe String + , webp :: Maybe String + } deriving (Show) + +instance IsString (Maybe String) where + fromString a = Just a argp :: Parser Args argp = Args - <$> strOption (long "indir" <> short 'i' <> help "Input Directory" <> metavar "DIR") - <*> strOption (long "outdir" <> short 'o' <> help "Output Directory" <> metavar "DIR") + <$> strOption (long "indir" <> short 'i' <> help "Input Directory" <> metavar "DIR" <> value Nothing) + <*> strOption (long "outdir" <> short 'o' <> help "Output Directory" <> metavar "DIR" <> value Nothing) + <*> strOption (long "conf" <> short 'c' <> help "Alternative Configuration" <> metavar "CONF" <> value Nothing) + <*> strOption (long "webp" <> help "Prefix for web" <> metavar "PREFIX" <> value Nothing) opts = info (argp <**> helper) (fullDesc <> progDesc "Build Vimwiki" <> header "build - build vimwiki") -{- -data Wiki = Wiki - { name :: String - , url :: String } deriving (Eq, Show) --} data Configuration = Configuration { _output :: OutputConfig , _data' :: DataConfig - -- , _wikis :: [Wiki] + , _wikis :: Map String URI } deriving (Eq, Show) data DataConfig = DataConfig @@ -49,6 +54,7 @@ data OutputConfig = OutputConfig , _xapianOmega :: Maybe String -- Are we in a subdirectory from the web root? , _webPath :: String + , _manProvider :: Maybe String } deriving (Eq, Show) makeLenses ''Configuration @@ -58,18 +64,20 @@ makeLenses ''OutputConfig confParser :: IniParser Configuration confParser = do out <- section "Output" $ do - mu4eURL <- fieldMbOf "mu4eURL" string + mu4eURL <- fieldMbOf "mu4eURL" string xapianOmega <- fieldMbOf "xapianOmega" string - webPath <- fieldDefOf "webpath" string "" + webPath <- fieldDefOf "webpath" string "" + manProvider <- fieldMbOf "manProvider" string return OutputConfig { _mu4eURL = mu4eURL , _xapianOmega = xapianOmega + , _manProvider = manProvider , _webPath = webPath } data' <- sectionDef "Data" (_data' defaultConf) $ do - inputDir <- fieldOf "inputDir" string + inputDir <- fieldOf "inputDir" string outputDir <- fieldOf "outputDir" string return DataConfig { _inputDir = inputDir, _outputDir = outputDir } - -- wikis <- section "wikis" - return $ Configuration out data' + wikis <- section "Wikis" uriDictParser + return $ Configuration out data' wikis runParser = flip parseIniFile $ confParser @@ -80,9 +88,11 @@ defaultConf = Configuration { _mu4eURL = Nothing , _xapianOmega = Nothing , _webPath = "" + , _manProvider = Nothing } , _data' = DataConfig - { _inputDir = "/home/user/wiki" - , _outputDir = "/tmp/wiki-output" + { _inputDir = "" + , _outputDir = "" } + , _wikis = mempty } diff --git a/hs/Data/Ini/Config.hs b/hs/Data/Ini/Config.hs new file mode 100644 index 0000000..00a3819 --- /dev/null +++ b/hs/Data/Ini/Config.hs @@ -0,0 +1,529 @@ +{-# LANGUAGE GeneralizedNewtypeDeriving #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE ScopedTypeVariables #-} + +-- | +-- Module : Data.Ini.Config +-- Copyright : (c) Getty Ritter, 2017 +-- License : BSD +-- Maintainer : Getty Ritter <config-ini@infinitenegativeutility.com> +-- Stability : experimental +-- +-- The 'config-ini' library exports some simple monadic functions to +-- make parsing INI-like configuration easier. INI files have a +-- two-level structure: the top-level named chunks of configuration, +-- and the individual key-value pairs contained within those chunks. +-- For example, the following INI file has two sections, @NETWORK@ +-- and @LOCAL@, and each contains its own key-value pairs. Comments, +-- which begin with @#@ or @;@, are ignored: +-- +-- > [NETWORK] +-- > host = example.com +-- > port = 7878 +-- > +-- > # here is a comment +-- > [LOCAL] +-- > user = terry +-- +-- The combinators provided here are designed to write quick and +-- idiomatic parsers for files of this form. Sections are parsed by +-- 'IniParser' computations, like 'section' and its variations, +-- while the fields within sections are parsed by 'SectionParser' +-- computations, like 'field' and its variations. If we want to +-- parse an INI file like the one above, treating the entire +-- @LOCAL@ section as optional, we can write it like this: +-- +-- > data Config = Config +-- > { cfNetwork :: NetworkConfig, cfLocal :: Maybe LocalConfig } +-- > deriving (Eq, Show) +-- > +-- > data NetworkConfig = NetworkConfig +-- > { netHost :: String, netPort :: Int } +-- > deriving (Eq, Show) +-- > +-- > data LocalConfig = LocalConfig +-- > { localUser :: Text } +-- > deriving (Eq, Show) +-- > +-- > configParser :: IniParser Config +-- > configParser = do +-- > netCf <- section "NETWORK" $ do +-- > host <- fieldOf "host" string +-- > port <- fieldOf "port" number +-- > return NetworkConfig { netHost = host, netPort = port } +-- > locCf <- sectionMb "LOCAL" $ +-- > LocalConfig <$> field "user" +-- > return Config { cfNetwork = netCf, cfLocal = locCf } +-- +-- +-- We can run our computation with 'parseIniFile', which, +-- when run on our example file above, would produce the +-- following: +-- +-- >>> parseIniFile example configParser +-- Right (Config {cfNetwork = NetworkConfig {netHost = "example.com", netPort = 7878}, cfLocal = Just (LocalConfig {localUser = "terry"})}) +module Data.Ini.Config + ( -- * Parsing Files + parseIniFile, + + -- * Parser Types + IniParser, + SectionParser(SectionParser), + + -- * Section-Level Parsing + section, + sections, + sectionOf, + sectionsOf, + sectionMb, + sectionDef, + + -- * Field-Level Parsing + field, + fieldOf, + fieldMb, + fieldMbOf, + fieldDef, + fieldDefOf, + fieldFlag, + fieldFlagDef, + + -- * Reader Functions + readable, + number, + string, + flag, + listWithSeparator, + ) +where + +import Control.Applicative (Alternative (..)) +import Control.Monad.Trans.Except +import Data.Ini.Config.Raw +import Data.Sequence (Seq) +import qualified Data.Sequence as Seq +import Data.String (IsString (..)) +import Data.Text (Text) +import qualified Data.Text as T +import Data.Typeable (Proxy (..), Typeable, typeRep) +import GHC.Exts (IsList (..)) +import Text.Read (readMaybe) + +lkp :: NormalizedText -> Seq (NormalizedText, a) -> Maybe a +lkp t = go . Seq.viewl + where + go ((t', x) Seq.:< rs) + | t == t' = Just x + | otherwise = go (Seq.viewl rs) + go Seq.EmptyL = Nothing + +addLineInformation :: Int -> Text -> StParser s a -> StParser s a +addLineInformation lineNo sec = withExceptT go + where + go e = + "Line " ++ show lineNo + ++ ", in section " + ++ show sec + ++ ": " + ++ e + +type StParser s a = ExceptT String ((->) s) a + +-- | An 'IniParser' value represents a computation for parsing entire +-- INI-format files. +newtype IniParser a = IniParser (StParser RawIni a) + deriving (Functor, Applicative, Alternative, Monad) + +-- | A 'SectionParser' value represents a computation for parsing a single +-- section of an INI-format file. +newtype SectionParser a = SectionParser (StParser IniSection a) + deriving (Functor, Applicative, Alternative, Monad) + +-- | Parse a 'Text' value as an INI file and run an 'IniParser' over it +parseIniFile :: Text -> IniParser a -> Either String a +parseIniFile text (IniParser mote) = do + ini <- parseRawIni text + runExceptT mote ini + +-- | Find a named section in the INI file and parse it with the provided +-- section parser, failing if the section does not exist. In order to +-- support classic INI files with capitalized section names, section +-- lookup is __case-insensitive__. +-- +-- >>> parseIniFile "[ONE]\nx = hello\n" $ section "ONE" (field "x") +-- Right "hello" +-- >>> parseIniFile "[ONE]\nx = hello\n" $ section "TWO" (field "x") +-- Left "No top-level section named \"TWO\"" +section :: Text -> SectionParser a -> IniParser a +section name (SectionParser thunk) = IniParser $ + ExceptT $ \(RawIni ini) -> + case lkp (normalize name) ini of + Nothing -> Left ("No top-level section named " ++ show name) + Just sec -> runExceptT thunk sec + +-- | Find multiple named sections in the INI file and parse them all +-- with the provided section parser. In order to support classic INI +-- files with capitalized section names, section lookup is +-- __case-insensitive__. +-- +-- >>> parseIniFile "[ONE]\nx = hello\n[ONE]\nx = goodbye\n" $ sections "ONE" (field "x") +-- Right (fromList ["hello","goodbye"]) +-- >>> parseIniFile "[ONE]\nx = hello\n" $ sections "TWO" (field "x") +-- Right (fromList []) +sections :: Text -> SectionParser a -> IniParser (Seq a) +sections name (SectionParser thunk) = IniParser $ + ExceptT $ \(RawIni ini) -> + let name' = normalize name + in mapM + (runExceptT thunk . snd) + (Seq.filter (\(t, _) -> t == name') ini) + +-- | A call to @sectionOf f@ will apply @f@ to each section name and, +-- if @f@ produces a "Just" value, pass the extracted value in order +-- to get the "SectionParser" to use for that section. This will +-- find at most one section, and will produce an error if no section +-- exists. +-- +-- >>> parseIniFile "[FOO]\nx = hello\n" $ sectionOf (T.stripSuffix "OO") (\ l -> fmap ((,) l) (field "x")) +-- Right ("F","hello") +-- >>> parseIniFile "[BAR]\nx = hello\n" $ sectionOf (T.stripSuffix "OO") (\ l -> fmap ((,) l) (field "x")) +-- Left "No matching top-level section" +sectionOf :: (Text -> Maybe b) -> (b -> SectionParser a) -> IniParser a +sectionOf fn sectionParser = IniParser $ + ExceptT $ \(RawIni ini) -> + let go Seq.EmptyL = Left "No matching top-level section" + go ((t, sec) Seq.:< rs) + | Just v <- fn (actualText t) = + let SectionParser thunk = sectionParser v + in runExceptT thunk sec + | otherwise = go (Seq.viewl rs) + in go (Seq.viewl ini) + +-- | A call to @sectionsOf f@ will apply @f@ to each section name and, +-- if @f@ produces a @Just@ value, pass the extracted value in order +-- to get the "SectionParser" to use for that section. This will +-- return every section for which the call to @f@ produces a "Just" +-- value. +-- +-- >>> parseIniFile "[FOO]\nx = hello\n[BOO]\nx = goodbye\n" $ sectionsOf (T.stripSuffix "OO") (\ l -> fmap ((,) l) (field "x")) +-- Right (fromList [("F","hello"),("B","goodbye")]) +-- >>> parseIniFile "[BAR]\nx = hello\n" $ sectionsOf (T.stripSuffix "OO") (\ l -> fmap ((,) l) (field "x")) +-- Right (fromList []) +sectionsOf :: (Text -> Maybe b) -> (b -> SectionParser a) -> IniParser (Seq a) +sectionsOf fn sectionParser = IniParser $ + ExceptT $ \(RawIni ini) -> + let go Seq.EmptyL = return Seq.empty + go ((t, sec) Seq.:< rs) + | Just v <- fn (actualText t) = + let SectionParser thunk = sectionParser v + in do + x <- runExceptT thunk sec + xs <- go (Seq.viewl rs) + return (x Seq.<| xs) + | otherwise = go (Seq.viewl rs) + in go (Seq.viewl ini) + +-- | Find a named section in the INI file and parse it with the provided +-- section parser, returning 'Nothing' if the section does not exist. +-- In order to +-- support classic INI files with capitalized section names, section +-- lookup is __case-insensitive__. +-- +-- >>> parseIniFile "[ONE]\nx = hello\n" $ sectionMb "ONE" (field "x") +-- Right (Just "hello") +-- >>> parseIniFile "[ONE]\nx = hello\n" $ sectionMb "TWO" (field "x") +-- Right Nothing +sectionMb :: Text -> SectionParser a -> IniParser (Maybe a) +sectionMb name (SectionParser thunk) = IniParser $ + ExceptT $ \(RawIni ini) -> + case lkp (normalize name) ini of + Nothing -> return Nothing + Just sec -> Just `fmap` runExceptT thunk sec + +-- | Find a named section in the INI file and parse it with the provided +-- section parser, returning a default value if the section does not exist. +-- In order to +-- support classic INI files with capitalized section names, section +-- lookup is __case-insensitive__. +-- +-- >>> parseIniFile "[ONE]\nx = hello\n" $ sectionDef "ONE" "def" (field "x") +-- Right "hello" +-- >>> parseIniFile "[ONE]\nx = hello\n" $ sectionDef "TWO" "def" (field "x") +-- Right "def" +sectionDef :: Text -> a -> SectionParser a -> IniParser a +sectionDef name def (SectionParser thunk) = IniParser $ + ExceptT $ \(RawIni ini) -> + case lkp (normalize name) ini of + Nothing -> return def + Just sec -> runExceptT thunk sec + +--- + +throw :: String -> StParser s a +throw msg = ExceptT (\_ -> Left msg) + +getSectionName :: StParser IniSection Text +getSectionName = ExceptT (return . isName) + +rawFieldMb :: Text -> StParser IniSection (Maybe IniValue) +rawFieldMb name = ExceptT $ \m -> + return (lkp (normalize name) (isVals m)) + +rawField :: Text -> StParser IniSection IniValue +rawField name = do + sec <- getSectionName + valMb <- rawFieldMb name + case valMb of + Nothing -> + throw + ( "Missing field " ++ show name + ++ " in section " + ++ show sec + ) + Just x -> return x + +getVal :: IniValue -> Text +getVal = T.strip . vValue + +-- | Retrieve a field, failing if it doesn't exist, and return its raw value. +-- +-- >>> parseIniFile "[MAIN]\nx = hello\n" $ section "MAIN" (field "x") +-- Right "hello" +-- >>> parseIniFile "[MAIN]\nx = hello\n" $ section "MAIN" (field "y") +-- Left "Missing field \"y\" in section \"MAIN\"" +field :: Text -> SectionParser Text +field name = SectionParser $ getVal `fmap` rawField name + +-- | Retrieve a field and use the supplied parser to parse it as a value, +-- failing if the field does not exist, or if the parser fails to +-- produce a value. +-- +-- >>> parseIniFile "[MAIN]\nx = 72\n" $ section "MAIN" (fieldOf "x" number) +-- Right 72 +-- >>> parseIniFile "[MAIN]\nx = hello\n" $ section "MAIN" (fieldOf "x" number) +-- Left "Line 2, in section \"MAIN\": Unable to parse \"hello\" as a value of type Integer" +-- >>> parseIniFile "[MAIN]\nx = 72\n" $ section "MAIN" (fieldOf "y" number) +-- Left "Missing field \"y\" in section \"MAIN\"" +fieldOf :: Text -> (Text -> Either String a) -> SectionParser a +fieldOf name parse = SectionParser $ do + sec <- getSectionName + val <- rawField name + case parse (getVal val) of + Left err -> addLineInformation (vLineNo val) sec (throw err) + Right x -> return x + +-- | Retrieve a field, returning a @Nothing@ value if it does not exist. +-- +-- >>> parseIniFile "[MAIN]\nx = hello\n" $ section "MAIN" (fieldMb "x") +-- Right (Just "hello") +-- >>> parseIniFile "[MAIN]\nx = hello\n" $ section "MAIN" (fieldMb "y") +-- Right Nothing +fieldMb :: Text -> SectionParser (Maybe Text) +fieldMb name = SectionParser $ fmap getVal `fmap` rawFieldMb name + +-- | Retrieve a field and parse it according to the given parser, returning +-- @Nothing@ if it does not exist. If the parser fails, then this will +-- fail. +-- +-- >>> parseIniFile "[MAIN]\nx = 72\n" $ section "MAIN" (fieldMbOf "x" number) +-- Right (Just 72) +-- >>> parseIniFile "[MAIN]\nx = hello\n" $ section "MAIN" (fieldMbOf "x" number) +-- Left "Line 2, in section \"MAIN\": Unable to parse \"hello\" as a value of type Integer" +-- >>> parseIniFile "[MAIN]\nx = 72\n" $ section "MAIN" (fieldMbOf "y" number) +-- Right Nothing +fieldMbOf :: Text -> (Text -> Either String a) -> SectionParser (Maybe a) +fieldMbOf name parse = SectionParser $ do + sec <- getSectionName + mb <- rawFieldMb name + case mb of + Nothing -> return Nothing + Just v -> case parse (getVal v) of + Left err -> addLineInformation (vLineNo v) sec (throw err) + Right x -> return (Just x) + +-- | Retrieve a field and supply a default value for if it doesn't exist. +-- +-- >>> parseIniFile "[MAIN]\nx = hello\n" $ section "MAIN" (fieldDef "x" "def") +-- Right "hello" +-- >>> parseIniFile "[MAIN]\nx = hello\n" $ section "MAIN" (fieldDef "y" "def") +-- Right "def" +fieldDef :: Text -> Text -> SectionParser Text +fieldDef name def = SectionParser $ + ExceptT $ \m -> + case lkp (normalize name) (isVals m) of + Nothing -> return def + Just x -> return (getVal x) + +-- | Retrieve a field, parsing it according to the given parser, and returning +-- a default value if it does not exist. If the parser fails, then this will +-- fail. +-- +-- >>> parseIniFile "[MAIN]\nx = 72\n" $ section "MAIN" (fieldDefOf "x" number 99) +-- Right 72 +-- >>> parseIniFile "[MAIN]\nx = hello\n" $ section "MAIN" (fieldDefOf "x" number 99) +-- Left "Line 2, in section \"MAIN\": Unable to parse \"hello\" as a value of type Integer" +-- >>> parseIniFile "[MAIN]\nx = 72\n" $ section "MAIN" (fieldDefOf "y" number 99) +-- Right 99 +fieldDefOf :: Text -> (Text -> Either String a) -> a -> SectionParser a +fieldDefOf name parse def = SectionParser $ do + sec <- getSectionName + mb <- rawFieldMb name + case mb of + Nothing -> return def + Just v -> case parse (getVal v) of + Left err -> addLineInformation (vLineNo v) sec (throw err) + Right x -> return x + +-- | Retrieve a field and treat it as a boolean, failing if it +-- does not exist. +-- +-- >>> parseIniFile "[MAIN]\nx = yes\n" $ section "MAIN" (fieldFlag "x") +-- Right True +-- >>> parseIniFile "[MAIN]\nx = yes\n" $ section "MAIN" (fieldFlag "y") +-- Left "Missing field \"y\" in section \"MAIN\"" +fieldFlag :: Text -> SectionParser Bool +fieldFlag name = fieldOf name flag + +-- | Retrieve a field and treat it as a boolean, subsituting +-- a default value if it doesn't exist. +-- +-- >>> parseIniFile "[MAIN]\nx = yes\n" $ section "MAIN" (fieldFlagDef "x" False) +-- Right True +-- >>> parseIniFile "[MAIN]\nx = hello\n" $ section "MAIN" (fieldFlagDef "x" False) +-- Left "Line 2, in section \"MAIN\": Unable to parse \"hello\" as a boolean" +-- >>> parseIniFile "[MAIN]\nx = yes\n" $ section "MAIN" (fieldFlagDef "y" False) +-- Right False +fieldFlagDef :: Text -> Bool -> SectionParser Bool +fieldFlagDef name = fieldDefOf name flag + +--- + +-- | Try to use the "Read" instance for a type to parse a value, failing +-- with a human-readable error message if reading fails. +-- +-- >>> readable "(5, 7)" :: Either String (Int, Int) +-- Right (5,7) +-- >>> readable "hello" :: Either String (Int, Int) +-- Left "Unable to parse \"hello\" as a value of type (Int,Int)" +readable :: forall a. (Read a, Typeable a) => Text -> Either String a +readable t = case readMaybe str of + Just v -> Right v + Nothing -> + Left + ( "Unable to parse " ++ show str + ++ " as a value of type " + ++ show typ + ) + where + str = T.unpack t + typ = typeRep prx + prx :: Proxy a + prx = Proxy + +-- | Try to use the "Read" instance for a numeric type to parse a value, +-- failing with a human-readable error message if reading fails. +-- +-- >>> number "5" :: Either String Int +-- Right 5 +-- >>> number "hello" :: Either String Int +-- Left "Unable to parse \"hello\" as a value of type Int" +number :: (Num a, Read a, Typeable a) => Text -> Either String a +number = readable + +-- | Convert a textual value to the appropriate string type. This will +-- never fail. +-- +-- >>> string "foo" :: Either String String +-- Right "foo" +string :: (IsString a) => Text -> Either String a +string = return . fromString . T.unpack + +-- | Convert a string that represents a boolean to a proper boolean. This +-- is case-insensitive, and matches the words @true@, @false@, @yes@, +-- @no@, as well as single-letter abbreviations for all of the above. +-- If the input does not match, then this will fail with a human-readable +-- error message. +-- +-- >>> flag "TRUE" +-- Right True +-- >>> flag "y" +-- Right True +-- >>> flag "no" +-- Right False +-- >>> flag "F" +-- Right False +-- >>> flag "That's a secret!" +-- Left "Unable to parse \"That's a secret!\" as a boolean" +flag :: Text -> Either String Bool +flag s = case T.toLower s of + "true" -> Right True + "yes" -> Right True + "t" -> Right True + "y" -> Right True + "false" -> Right False + "no" -> Right False + "f" -> Right False + "n" -> Right False + _ -> Left ("Unable to parse " ++ show s ++ " as a boolean") + +-- | Convert a reader for a value into a reader for a list of those +-- values, separated by a chosen separator. This will split apart +-- the string on that separator, get rid of leading and trailing +-- whitespace on the individual chunks, and then attempt to parse +-- each of them according to the function provided, turning the +-- result into a list. +-- +-- This is overloaded with the "IsList" typeclass, so it can be +-- used transparently to parse other list-like types. +-- +-- >>> listWithSeparator "," number "2, 3, 4" :: Either String [Int] +-- Right [2,3,4] +-- >>> listWithSeparator " " number "7 8 9" :: Either String [Int] +-- Right [7,8,9] +-- >>> listWithSeparator ":" string "/bin:/usr/bin" :: Either String [FilePath] +-- Right ["/bin","/usr/bin"] +-- >>> listWithSeparator "," number "7 8 9" :: Either String [Int] +-- Left "Unable to parse \"7 8 9\" as a value of type Int" +listWithSeparator :: + (IsList l) => + Text -> + (Text -> Either String (Item l)) -> + Text -> + Either String l +listWithSeparator sep rd = + fmap fromList . mapM (rd . T.strip) . T.splitOn sep + +-- $setup +-- +-- >>> :{ +-- data NetworkConfig = NetworkConfig +-- { netHost :: String, netPort :: Int } +-- deriving (Eq, Show) +-- >>> :} +-- +-- >>> :{ +-- data LocalConfig = LocalConfig +-- { localUser :: Text } +-- deriving (Eq, Show) +-- >>> :} +-- +-- >>> :{ +-- data Config = Config +-- { cfNetwork :: NetworkConfig, cfLocal :: Maybe LocalConfig } +-- deriving (Eq, Show) +-- >>> :} +-- +-- >>> :{ +-- let configParser = do +-- netCf <- section "NETWORK" $ do +-- host <- fieldOf "host" string +-- port <- fieldOf "port" number +-- return NetworkConfig { netHost = host, netPort = port } +-- locCf <- sectionMb "LOCAL" $ +-- LocalConfig <$> field "user" +-- return Config { cfNetwork = netCf, cfLocal = locCf } +-- >>> :} +-- +-- >>> :{ +-- let example = "[NETWORK]\nhost = example.com\nport = 7878\n\n# here is a comment\n[LOCAL]\nuser = terry\n" +-- >>> :} diff --git a/hs/Data/Ini/Config/KeyValue.hs b/hs/Data/Ini/Config/KeyValue.hs new file mode 100644 index 0000000..d44dfc6 --- /dev/null +++ b/hs/Data/Ini/Config/KeyValue.hs @@ -0,0 +1,15 @@ +module Data.Ini.Config.KeyValue + ( kvParser + ) where + + +import Data.Ini.Config +import Data.Ini.Config.Raw +import Data.Text +import Control.Monad.Trans.Except +import Data.Sequence (Seq) + +kvParser :: SectionParser (Seq (Text,Text)) +kvParser = SectionParser $ + ExceptT $ \m -> + return $ (\(_, v) -> (vName v, vValue v)) <$> (isVals m) diff --git a/hs/Data/Ini/Config/URIDict.hs b/hs/Data/Ini/Config/URIDict.hs new file mode 100644 index 0000000..9f35b0e --- /dev/null +++ b/hs/Data/Ini/Config/URIDict.hs @@ -0,0 +1,27 @@ +{-# LANGUAGE ImportQualifiedPost #-} + +module Data.Ini.Config.URIDict + ( uriDictParser + ) where + +import Control.Lens +import Data.Foldable (toList) +import Data.Ini.Config +import Data.Ini.Config.KeyValue +import Data.Map (Map) +import Data.Map qualified as Map +import Data.Maybe (isJust, fromJust) +import Data.Text hiding (filter) +import Network.URI (URI, parseURI) + +uriDictParser :: SectionParser (Map String URI) +-- uriDictParser = fromList . filter (isJust . snd) . fmap (_2 %~ parseURI) $ kvParser +-- uriDictParser = fmap kvParser +uriDictParser = fmap (Map.fromList) + $ fmap (fmap (_2 %~ fromJust)) + $ fmap (filter (isJust . snd)) + $ fmap toList + $ fmap (fmap (_1 %~ unpack . strip)) + $ fmap (fmap (_2 %~ parseURI . unpack . strip)) + -- $ fmap (fmap (_2 .~ Just nullURI)) + $ kvParser diff --git a/hs/Files.hs b/hs/Files.hs index 1f85297..20554c1 100644 --- a/hs/Files.hs +++ b/hs/Files.hs @@ -2,6 +2,7 @@ module Files ( mkdirP , fileTree , isFiletype +, copyFiles ) where import System.Directory hiding (isSymbolicLink) @@ -19,10 +20,9 @@ import System.Posix.Files import System.FilePath ( joinPath , takeExtension + , (</>) ) -import Data.List.Extra (snoc) - mkdirP = createDirectoryIfMissing True fmt :: FileStatus -> String @@ -43,7 +43,7 @@ fileTree base = do items <- listDirectory (joinPath base) concat <$> mapM (\entry -> do - let here = snoc base entry + let here = base <> [entry] let path = joinPath here st <- getFileStatus path let d = (st, here) @@ -56,3 +56,10 @@ isFiletype :: String -> FileStatus -> [FilePath] -> Bool isFiletype extension st path = isRegularFile st && (takeExtension . last $ path) == ('.' : extension) +copyFile' :: FilePath -> FileStatus -> [FilePath] -> IO () +copyFile' dest st path + | isDirectory st = createDirectoryIfMissing True (dest </> (joinPath $ tail path)) + | otherwise = copyFile (joinPath path) $ dest </> (joinPath $ tail path) + +copyFiles :: FilePath -> [(FileStatus, [FilePath])] -> IO () +copyFiles dest = mapM_ (uncurry $ copyFile' dest) diff --git a/hs/Handlingar.hs b/hs/Handlingar.hs index 36d1a35..281223f 100644 --- a/hs/Handlingar.hs +++ b/hs/Handlingar.hs @@ -10,7 +10,6 @@ import Text.Pandoc ) import Text.Pandoc.Builder import Data.Text (Text, pack) -import Text.Show.Pretty (pPrintList) import System.Environment (getArgs) import Data.Default (def) @@ -63,7 +62,7 @@ main = do pandoc <- runIOorExplode $ readVimwiki def text let Pandoc _ blocks = pandoc -- putStr . valToStr $ blocks - pPrintList $ getHeadingData (pack heading) blocks + mapM_ print $ getHeadingData (pack heading) blocks -- let htmlString = toStrict . renderHtml $ html -- liftIO $ T.writeFile outTarget htmlString _ -> error "Invalid command line" @@ -7,9 +7,6 @@ module Html ( main ) where -import Control.Lens.Operators hiding ((<.>)) - -import System.Environment (lookupEnv) import System.FilePath ( joinPath , takeDirectory @@ -20,150 +17,91 @@ import System.FilePath , dropExtension , takeBaseName ) -import System.Directory (copyFile, createDirectoryIfMissing) - -import Text.Pandoc - ( PandocMonad - , PandocIO - -- , ReaderOptions( readerStandalone - -- ) - -- , WriterOptions( writerTemplate - -- , writerTableOfContents - -- , writerVariables - -- ) - , runIOorExplode - -- , compileDefaultTemplate - -- , compileTemplate - -- , getTemplate - , writeHtml5 - , readVimwiki - , toLang - , setTranslations - ) - --- import Text.Pandoc.Class --- ( getsCommonState --- , CommonState(stLog) --- ) - -import Text.Pandoc.Shared (stringify) +import Control.Lens.Operators hiding ((<.>)) +import Control.Lens.Setter (ASetter) -import Text.Pandoc.Writers.Shared - ( toTableOfContents - , lookupMetaString - ) +import Control.Monad.IO.Class (liftIO) +import Data.Default (def) +import Data.List (partition) +import Data.Map qualified as M +import Data.Map (Map) +import Data.Maybe (listToMaybe, fromMaybe, fromJust) +import Data.Set (Set) +import Data.Set qualified as S import Data.Text (Text, unpack, pack) import Data.Text.IO qualified as T import Data.Text.Lazy (toStrict) ---import Network.URI --(parseURI, uriPath, uriScheme, uriFragment, uriQuery, uriAuthority, URI, URIAuth) + import Network.URI hiding (query) import Network.URI.Lens -import Data.Maybe (listToMaybe, fromMaybe, fromJust) -import Data.List (partition) - -import Control.Monad.IO.Class (liftIO) -import Data.Default (def) - --- import Text.Blaze -import Text.Blaze.Html () -import Text.Blaze.Html.Renderer.Text (renderHtml) -import Text.Pandoc.Walk - ( Walkable (..) - , walk ) +import System.Directory (copyFile, createDirectoryIfMissing) +import System.Environment.XDG.BaseDir (getUserConfigDir) -import Text.Blaze.Html5 hiding (main, html, summary, header, string, section, output) +import Text.Blaze.Html5 (Html, toHtml, (!), textValue, docTypeHtml) import Text.Blaze.Html5 qualified as H -import Text.Blaze.Html5.Attributes hiding (summary) +import Text.Blaze.Html5.Attributes (href, role, rel, content, name, charset) import Text.Blaze.Html5.Attributes qualified as A -import Text.Pandoc.Definition - ( Pandoc (Pandoc) +import Text.Blaze.Html.Renderer.Text (renderHtml) + +import Text.Pandoc + ( PandocMonad + , PandocIO + , runIOorExplode + , writeHtml5 + , readVimwiki + , toLang + , setTranslations + , Pandoc (Pandoc) , Inline (..) , Block (..) , Format(Format) ) --- import Text.Pandoc.Builder + +import Text.Pandoc.Shared (stringify) +import Text.Pandoc.Walk (Walkable (..), walk) +import Text.Pandoc.Writers.Shared (toTableOfContents, lookupMetaString) + import Files import Links import Util import Config - --- import Text.DocTemplates (Context(..), ToContext(toVal)) -import Data.Map qualified as M -import Data.Set (Set) -import Data.Set qualified as S -import Data.String (IsString) +-- import System.Home +import Vimwiki.Man isWikiFile = isFiletype "wiki" -- TODO -- Colorful headers? -- All headers should link to themselves +-- remove all fromJust, instead omit those elements -{- -vimwikiToHTML :: Text -> IO Text --- vimwikiToHTML txt = runIOorExplode $ do --- pdoc <- readVimwiki def txt --- html <- writeHtml5 def pdoc --- return $ toStrict . renderHtml $ html -vimwikiToHTML txt = do - pdoc <- runIOorExplode $ readVimwiki def txt - print pdoc - html <- runIOorExplode $ writeHtml5 def pdoc - return $ toStrict . renderHtml $ html --} - -type UrlRewriter = [Inline] -> URI -> ([Inline], Text) - -mailRewriter :: String -> UrlRewriter + +mailRewriter :: String -> [Inline] -> URI -> ([Inline], Text) mailRewriter mu4eURL body uri = (body, pack $ mu4eURL <> "?id=" <> uriPath uri) --- Comparison of online man pages: --- https://gist.github.com/rixx/6cb5fa38f694009ad0bd50c275bb61f2 - -archMan :: (Semigroup a, IsString a) => a -> Maybe a -> Maybe a -> a -archMan page section' language' - = "https://man.archlinux.org/man/" <> page <> f section' <> f language' - where f = fromMaybe "" . fmap ("." <>) - -mannedMan :: (Semigroup a, IsString a) => a -> Maybe a -> Maybe a -> a -mannedMan page section' language' - = "https://manned.org/man" <> f language' <> "/" <> page <> f section' - where f = fromMaybe "" . fmap ("." <>) - -manRewriter :: UrlRewriter -manRewriter _ uri - = ([Str $ path <> "(" <> (fromMaybe "?" section') <> ")"] - , mannedMan path section' language') - where path :: Text - path = pack $ uriPath uri - section' = pack . tail <$> nullToMaybe (uriFragment uri) - language' = pack . tail <$> nullToMaybe (uriQuery uri) - -urlRewrites :: Configuration -> M.Map String UrlRewriter +urlRewrites :: Configuration -> Map String ([Inline] -> URI -> ([Inline], Text)) urlRewrites conf = M.fromList [ ("mail", mailRewriter $ (fromJust $ conf ^. output . mu4eURL)) - , ("man", manRewriter) + -- , ("man", manRewriter $ (rewriters ! (fromJust $ conf ^. output . manProvider))) + -- TODO choose man implementation from + -- configuration item + , ("man", manRewriter $ mannedMan) ] --- -- TODO don't do this for inter-page links --- | otherwise = Link attributes body (url <> ".html", title) - fixExtension url | hasExtension url = url | otherwise = url <.> "html" --- TODO make this part of the configuration -resolveInterwiki :: String -> URI -> String -resolveInterwiki wikiname uri = - case wikiname of - "public" -> show $ (fromJust $ parseURI "http://wiki.gandalf.adrift.space/") & uriPathLens %~ (<> uriPath uri <> ".html") - "private" -> show $ (fromJust $ parseURI "http://wiki.gandalf.adrift.space/private/") & uriPathLens %~ (<> uriPath uri <> ".html") - _ -> error $ "Unknown Wiki name: '" <> wikiname <> "'" +resolveInterwiki :: Map String URI -> String -> URI -> String +resolveInterwiki wikilinks wikiname uri = + case M.lookup wikiname wikilinks of + Just uri' -> show $ uri' & uriPathLens %~ (<> fixExtension (uriPath uri)) + Nothing -> error $ "Unknown Wiki name: " <> wikiname <> "'" @@ -175,10 +113,10 @@ rebuildLinks conf l@(Link attributes body (url, title@"wikilink")) in Link attributes [ Str . pack . uriPath $ uri , Superscript [Str $ pack wikiname]] - (pack $ resolveInterwiki wikiname uri, title) + (pack $ resolveInterwiki (conf ^. wikis) wikiname uri, title) _ -> case M.lookup (init . uriScheme $ uri) (urlRewrites conf) of Just proc -> (proc body uri) & (\(body, url) -> Link attributes body (url, title)) - Nothing -> l -- Link attributes body (url <> ".html", title) + Nothing -> l Nothing -> case break (== '#') (unpack url) of ("", '#':_) -> Link attributes body (url, title) (page, "") -> Link attributes body (pack $ fixExtension page, title) @@ -186,10 +124,6 @@ rebuildLinks conf l@(Link attributes body (url, title@"wikilink")) _ -> l rebuildLinks _ l = l --- Link ("",[],[]) [Str "#M\246te 7"] ("#M\246te 7","wikilink") --- Link ("",[],[]) [Str "https://styrdokument.liu.se/Regelsamling/Fil/1513619"] ("https://styrdokument.liu.se/Regelsamling/Fil/1513619","") --- Link ("",[],[]) [Str "Beslut",Space,Str "LIU-2020-02033"] ("file:LinTek/2021/Beslut om riktlinjer och rutiner f\246r genomf\246rande av skriftliga salsskrivningar inklusive digitala tent.pdf","wikilink") - checkbox extra = RawInline (Format "html") $ "<input type='checkbox' disabled='true' " <> extra <> "/>" @@ -214,39 +148,7 @@ applyFilters conf pandoc = pandoc & walk buildCheckboxes' handleSourceText :: PandocMonad m => Configuration -> Text -> m Pandoc -handleSourceText conf text = do - pandoc <- readVimwiki def text - - --runIO $ print . show $ pandoc - -- Pandoc (Meta {unMeta = fromList [ ("date",MetaInlines [Str "2000-01-01"]) - -- , ("title",MetaInlines [Str "Custom",Space,Str "Title"])]}) - -- [ Header 1 ("Test Wiki",[],[]) [Str "Test",Space,Str "Wiki"] - -- , Header 1 ("Generated Tags",[],[]) [Str "Generated",Space,Str "Tags"]] - - let Pandoc meta blocks = pandoc - -- let pandoc = Pandoc (meta & setMeta "lang" ("sv-SE"::Text)) blocks - let pandoc = Pandoc meta blocks - - let pandoc' = pandoc - & (applyFilters conf) - -- & setTitle (str "Some form of title") - -- & setAuthors [ str "Hugo Hörnquist" ] - -- & setDate (str "2022-01-26") - - -- template <- compileDefaultTemplate "html5" - {- - templateData <- getTemplate "template.html5" - template' <- compileTemplate "template.html5" templateData - template <- case template' of - Left err -> error err - Right template -> return template - -} - - return pandoc' - --- Becomes wikilink when the URI scheme doesn't match one of --- Text.Pandoc.Shared (schemes) --- Link (_,_,_) _ (_, "wikilink") +handleSourceText conf text = (& applyFilters conf) <$> readVimwiki def text breadcrumbLinks :: Configuration -> [FilePath] -> [String] breadcrumbLinks conf parts = f <$> (accumulate $ (conf ^. output . webPath) : parts) @@ -265,8 +167,8 @@ htmlWrap conf title parts backlinks toc main = docTypeHtml $ do H.meta ! charset "utf-8" H.meta ! name "generator" ! content "pandoc" H.meta ! name "viewport" ! content "width=device-width, initial-scale=1.0, user-scalable=yes" - H.link ! rel "stylesheet" ! (href . textValue $ (pack $ conf ^. output . webPath) <> "/style.css") - H.script ! (A.src . textValue $ ((pack $ conf ^. output . webPath) <> "/script.js")) $ mempty + H.link ! rel "stylesheet" ! (href . textValue $ (pack $ conf ^. output . webPath </> "style.css")) + H.script ! (A.src . textValue $ (pack $ conf ^. output . webPath </> "script.js")) $ mempty H.title $ H.string $ title <> " — Vimwiki" H.body $ do H.header $ do @@ -308,7 +210,7 @@ headerContent _ = Nothing -handlePart :: Configuration -> FilePath -> M.Map Text (Set FilePath) -> FilePath -> [FilePath] -> PandocIO () +handlePart :: Configuration -> FilePath -> Map Text (Set FilePath) -> FilePath -> [FilePath] -> PandocIO () handlePart conf outdir backlinks wiki_root parts = do let item_path = joinPath parts let inTarget = wiki_root </> item_path @@ -325,8 +227,8 @@ handlePart conf outdir backlinks wiki_root parts = do pandoc <- handleSourceText conf text let Pandoc meta blocks = pandoc toc <- writeHtml5 def $ Pandoc meta $ [toTableOfContents def blocks] - -- writeHtml5 htmlAttr pandoc html <- writeHtml5 def pandoc + -- Since we (explicitly) don't reset the state between invocations -- the list of log entries continues to grow. We could check if -- before and after, and check if it is langer @@ -341,38 +243,62 @@ handlePart conf outdir backlinks wiki_root parts = do let htmlString = toStrict . renderHtml . htmlWrap conf (unpack title) parts bl toc $ html liftIO $ T.writeFile outTarget htmlString --- TODO copy remaining files verbatim + +-- Like '.~', but the RHS is only possibly a value. Set the value in +-- the structure when there is one, and do nothing when there isn't +-- one. +(.~?) :: ASetter s s a b -> Maybe b -> s -> s +_ .~? Nothing = id +lens .~? (Just x) = lens .~ x +infixr 4 .~? + main = do args <- parseCmdline - let indir' = indir args - let outdir' = outdir args - (wiki_files, other_files) <- partition (uncurry isWikiFile) <$> fileTree [ indir' ] - wp <- fromMaybe "" <$> lookupEnv "WEB_PATH" + confDir <- getUserConfigDir "vimwiki" - confText <- T.readFile "config.ini" + confText <- T.readFile $ fromMaybe (confDir </> "config.ini") (conffile args) conf' <- case runParser confText of Left err -> error err Right conf -> return conf - -- conf' <- (read :: String -> Configuration) <$> readFile' "/home/hugo/code/vimwiki-scripts/hs/config.hs" - -- TODO Merge defaultConfig - let conf = (conf' & data' . inputDir .~ indir' - & data' . outputDir .~ outdir' - & output . webPath .~ wp - ) + let conf = (conf' & data' . inputDir .~? indir args + & data' . outputDir .~? outdir args + & output . webPath .~? webp args + ) + + -- indir' <- expandTilde `mapM` indir args + -- outdir' <- expandTilde `mapM` outdir args + + -- print conf + if null (conf ^. data' . inputDir) + then error "No input directory given" + else return () + + if null (conf ^. data' . outputDir) + then error "No output directory given" + else return () + + print $ "input = " <> (conf ^. data' . inputDir) + print $ "output = " <> (conf ^. data' . outputDir) + + (wikiFiles, otherFiles) <- partition (uncurry isWikiFile) <$> fileTree [ conf ^. data' . inputDir ] + + -- mapM_ print otherFiles + -- TODO ignore hidden files + copyFiles (conf ^. (data' . outputDir)) otherFiles createDirectoryIfMissing True $ conf ^. data' . outputDir - copyFile "style2.css" (conf ^. (data' . outputDir) <> "/style.css") - copyFile "script.js" (conf ^. (data' . outputDir) <> "/script.js") + copyFile "style2.css" $ (conf ^. (data' . outputDir)) <> "/style.css" + copyFile "script.js" $ (conf ^. (data' . outputDir)) <> "/script.js" -- What each page links to -- forwardLinks :: [(FilePath, [String])] - let relative_paths = tail . snd <$> wiki_files - forwardLinks <- runIOorExplode $ mapM (findLinks indir') relative_paths - -- TODO backlinks should look at basename of filename, for both - -- link and target. + let relative_paths = tail . snd <$> wikiFiles + forwardLinks <- runIOorExplode $ mapM (findLinks $ conf ^. data' . inputDir) relative_paths + + -- TODO backlinks should look at basename of filename, for both link and target. -- http://wiki.gandalf.adrift.space/Lysator/Styrelse/wi_1233359070.html let backlinks = M.unions $ uncurry buildBacklinkSet <$> forwardLinks @@ -384,5 +310,5 @@ main = do Nothing -> return () Just l -> setTranslations l - mapM_ (handlePart conf outdir' backlinks indir') relative_paths - -- mapM_ (putStrLn . show) wiki_files + mapM_ (handlePart conf (conf^.data'.outputDir) backlinks $ (conf ^. data' . inputDir)) relative_paths + -- mapM_ (putStrLn . show) wikiFiles diff --git a/hs/System/Home.hs b/hs/System/Home.hs new file mode 100644 index 0000000..30a08f1 --- /dev/null +++ b/hs/System/Home.hs @@ -0,0 +1,27 @@ +module System.Home where + +import System.IO.Error +import System.Posix.User +import System.FilePath + +-- Return home directory for given user, or currently logged in user. +getUserHome :: Maybe String -> IO (Either IOError String) +getUserHome Nothing = (Just <$> getEffectiveUserName) >>= getUserHome +getUserHome (Just name) = tryIOError $ homeDirectory <$> getUserEntryForName name + +-- If the first component of path is either a tilde `~', or a tilde +-- joined with a user name `~hugo', expand that to the revelant home +-- directory, and return the modified path. +expandTilde :: FilePath -> IO FilePath +expandTilde path = do + let (fst' : rest) = splitDirectories path + mfst <- case fst' of + "~" -> getUserHome Nothing + ('~':name) -> getUserHome (Just name) + _ -> return . Left . userError $ "Nothing" + return . joinPath $ case mfst of + Right fst -> fst : rest + Left _ -> rest + + + @@ -2,7 +2,6 @@ module Util ( accumulate , intersperse , joinBy --- , (&) , nullToMaybe , firstJust ) where @@ -22,8 +21,6 @@ intersperse y (x:xs) = x : y : intersperse y xs joinBy :: Monoid a => a -> [a] -> a joinBy delim lst = mconcat $ intersperse delim lst -(&) = flip ($) - nullToMaybe :: (Eq a, Monoid a) => a -> Maybe a nullToMaybe m diff --git a/hs/Vimwiki/Man.hs b/hs/Vimwiki/Man.hs new file mode 100644 index 0000000..dde9028 --- /dev/null +++ b/hs/Vimwiki/Man.hs @@ -0,0 +1,42 @@ +{-# LANGUAGE OverloadedStrings + , ImportQualifiedPost + #-} + +module Vimwiki.Man where + +import Data.Map (Map) +import Data.Map qualified as M +import Data.Maybe (fromMaybe) +import Data.String (IsString) +import Data.Text hiding (tail) +import Network.URI +import Text.Pandoc (Inline (Str)) +import Util (nullToMaybe) + +manRewriter :: (Text -> Maybe Text -> Maybe Text -> Text) -> [Inline] -> URI -> ([Inline], Text) +manRewriter manImpl _ uri + = ([Str $ path <> "(" <> (fromMaybe "?" section') <> ")"] + , manImpl path section' language') + where path :: Text + path = pack $ uriPath uri + section' = pack . tail <$> nullToMaybe (uriFragment uri) + language' = pack . tail <$> nullToMaybe (uriQuery uri) + +archMan :: (Semigroup a, IsString a) => a -> Maybe a -> Maybe a -> a +archMan page section' language' + = "https://man.archlinux.org/man/" <> page <> f section' <> f language' + where f = fromMaybe "" . fmap ("." <>) + +mannedMan :: (Semigroup a, IsString a) => a -> Maybe a -> Maybe a -> a +mannedMan page section' language' + = "https://manned.org/man" <> f language' <> "/" <> page <> f section' + where f = fromMaybe "" . fmap ("." <>) + + +-- Comparison of online man pages: +-- https://gist.github.com/rixx/6cb5fa38f694009ad0bd50c275bb61f2 +rewriters :: (Semigroup a, IsString a) => Map String (a -> Maybe a -> Maybe a -> a) +rewriters = M.fromList [ ("arch", archMan) + , ("manned", mannedMan) + ] + diff --git a/hs/config.hs b/hs/config.hs deleted file mode 100644 index eee62e5..0000000 --- a/hs/config.hs +++ /dev/null @@ -1,5 +0,0 @@ -Configuration -{ mu4eURL = "http://localhost:8090" -, xapianOmega = "http://wiki.gandalf.adrift.space/search" -} - @@ -8,15 +8,9 @@ import qualified Html main = Html.main - -- TODO --- at top, trace to go back --- in footer, --- backlinks --- --- Special url types --- - wn.public: --- - mail: +-- Fix backlinks +-- Link to git repo in footer -- -- -- För handlingar: diff --git a/hs/private.ini b/hs/private.ini new file mode 100644 index 0000000..c62c2a1 --- /dev/null +++ b/hs/private.ini @@ -0,0 +1,13 @@ +[Output] +mu4eURL = http://localhost:8090 +xapianOmega = http://wiki.gandalf.adrift.space/search +manProvider = manned +webPath = /private + +[Data] +inputDir = ~/wiki/private +outputDir = /tmp/wiki/private + +[Wikis] +public = http://wiki.gandalf.adrift.space/ +private = http://wiki.gandalf.adrift.space/private/ diff --git a/hs/public.ini b/hs/public.ini new file mode 100644 index 0000000..0b53a55 --- /dev/null +++ b/hs/public.ini @@ -0,0 +1,8 @@ +[Output] +mu4eURL = http://localhost:8090 +xapianOmega = http://wiki.gandalf.adrift.space/search +webPath = / + +[Wikis] +public = http://wiki.gandalf.adrift.space/ +private = http://wiki.gandalf.adrift.space/private/ diff --git a/hs/vimwiki.cabal b/hs/vimwiki.cabal index 74d497b..04d37c5 100644 --- a/hs/vimwiki.cabal +++ b/hs/vimwiki.cabal @@ -32,24 +32,29 @@ executable Main Links, Html, Handlingar, + Data.Ini.Config, + Data.Ini.Config.KeyValue, + Data.Ini.Config.URIDict, + Vimwiki.Man, + System.Home, Util build-depends: - base >=4.9, - data-default, - directory, - filepath, - unix, - pandoc, - pandoc-types, - text, - containers, - config-ini, - optparse-applicative, - blaze-markup, - blaze-html, - doctemplates, - lens, - network-uri, - pretty-show, - extra + blaze-html >= 0.9, + data-default >= 0.7, + directory >= 1.3.6, + filepath >= 1.4.2, + lens >= 5.1, + network-uri >= 2.6.4, + optparse-applicative >= 0.17, + pandoc >= 2.19, + pandoc-types >= 1.22, + unix >= 2.7.2, + xdg-basedir >= 0.2, + -- These are locked by config-ini, since Data.Ini.Config + -- is a vendored (and barely changed) file + config-ini == 0.2.5.0, + base (>=4.8 && <5), + containers (>=0.5 && <0.7), + text (>=1.2.2 && <2.1), + transformers (>=0.4.1 && <0.6) default-language: Haskell2010 |