astynax / hemmet

Emmet-like text expansion tool capable to produce HTML, CSS, file trees, that stuff
Other
20 stars 6 forks source link

Generalize the output customization. #15

Open astynax opened 3 years ago

astynax commented 3 years ago

For now there is only a couple of preprocessors for some genenerators. But it will be nice to have a way to customize the output: pretty-printing, size of indentation. It can be a mini-language like "key=val,key=val|" that will prepend the template itself.

uhbif19 commented 2 years ago

I would do it by writing generic prefix-parsing function, like this (not sure about names now):

data FilesystemKind = Python | Haskell
class RendererKindRead FilesystemKind where
     prefix Python  = "py"
     prefix Haskell = "hs"

>>> parsePrefix @FilesystemKind "hs,indent=4,other_param=something|sometemplate"
Config {
     rendererKind = Haskell
     indent = 4
     other_param = Something
 }
astynax commented 2 years ago

I don't thing that any classes are strictly necessary here. Will records of functions be just enough? I can see something like this:

type Args = [(String, String)]

data Preprocessor a where
  Preprocessor :: forall args =>
    { pParseArgs :: Args -> Maybe args
    , pGetTransformation :: args -> Transformation a
    } -> Preprocessor a

fooPreprocessors :: [(String, Preprocessor Foo)]
fooPreprocessors =
  [ ("hs", haskellify)
  ...]

data HaskellifyArgs = HaskellyfyArgs
  { haIndent :: Int
  , ... }

haskellify = Preprocessor {..}
  where
    pParseArgs args = do
      indent <- lookup "indent" args >>= readMaybe
      ...
    pMakeTransformation HaskellifyArgs{..} = ...
uhbif19 commented 2 years ago

@astynax Your solution presumes that each prefix/preprocessor has its own config, while mine uses the same config for every prefix. So I think it is worth to discuss which customization options you wanna see and check if they do differ for different prefixes.

Also I do not understand, why using classes are bad. My class uses just the same idea as Read.

astynax commented 2 years ago

Each preprocessor should have its own config. This means that your interface-like class will grow.

Also, I have thought about the external preprocessors like pandoc filters. My variant can be easily extended with external filter support: one can just add a new value of Preprocessor type. Data kind based solution will need a special ("Other") constructor for that, and IMHO such "backdoors" don't look nice.

There is a case that can make class-bases solution viable: we may decide to link proprocessors not only to the inputs but also for the outputs. MPTC here will work as multiple dispatching:

class Preprocess (name :: KnownSymbol) tree out where
   ...

instance Preprocess "hs" FileTree Tree where
  ...

But even it that case it may be simpler to have just a Map of Preprocessors.