fmidue / output-blocks

0 stars 0 forks source link

Space handling at `code` calls #8

Open jvoigtlaender opened 7 months ago

jvoigtlaender commented 7 months ago

Current use sites of code do seem to make different assumptions about whether or not spaces are added by the primitive itself.

For example, there is this: https://github.com/fmidue/modelling-tasks/blob/88182aa38434bcfbfa457c22120bcb8bc3a5c737/src/Modelling/ActivityDiagram/EnterAS.hs#L193-L197 which explicitly adds a space before code but not after it.

And this: https://github.com/fmidue/modelling-tasks/blob/88182aa38434bcfbfa457c22120bcb8bc3a5c737/src/Modelling/PetriNet/Concurrency.hs#L177-L187 which seems to go through a lot of pain (the #{" "} syntax) to really make sure that there is definitely a space after code.

But also this: https://github.com/fmidue/modelling-tasks/blob/88182aa38434bcfbfa457c22120bcb8bc3a5c737/src/Modelling/CdOd/SelectValidCd.hs#L224-L230 which doesn't seem to feel a need to do anything special about spaces surrounding code at all.

I don't know whether this has some historical reason, or maybe has to do with different rendering routes (html vs. latex) that haven't yet been tried for all task types.

In any case, wouldn't it be better to have the code primitive itself do an "always sane thing", and relieve the users from thinking about spaces?

Specifically, when rewriting existing code in terms of code, say here: https://github.com/fmidue/term-tasks/blob/c5410222ff7f84049bf0cc823c0508c0d3fd9c7a/src/TermTasks/Messages.hs#L30-L33 it would be nice to be able to not have to think about spaces and instead do the most simple rewriting at the use site.

marcellussiegburg commented 1 month ago

This is something that, I think, cannot be trivially handled by this library itself at the moment, but rather depends on the renderer (i.e. the OutputMonad instance). In this repository we have a definition which looks like this and adds spaces: https://github.com/fmidue/output-monad/blob/1cfddc487ebe6333de77f5adf9e6f06c15c8f79e/output-monad/src/Control/Monad/Output.hs#L293 However this has a disadvantage. If we use it like this:

text "foo" *> code "bar"

The output is (sane):

foo <bar> 

But if we use it like this:

paragraph (text "foo") *> paragraph (code "bar")

The output is:

foo
 <bar> 

So the question that is arising is: How would the library know what to do? We could provide different primitives: codeBegin (no space at before code), codeEnd (no space after code), codeAlone (no space before and after code) additionally to code (space before and after code). But this would not resolve the issue as it delegates the problem to the user (again) [it would also require equivalent versions for translatedCode]. I think it would be required to either fix this during rendering (probably very hacky and error-prone — not as part of this library) or to introduce some sort of state that can be used to choose an appropriate handling depending on the previous function (it might be required to change the GenericLangM data type and the GenericReportT data type). This might require thinking about various cases, e.g. for text: text "a" *> text "b" renders to ab we would not like it to be rendered to a b; and the same probably holds for code "a" *> code "b", which then could be rendered as <ab> instead of <a> <b> (as with the current String renderer).

A workaround could also be to provide the ability to explicitly insert spaces by adding a space function. It would still require the user to add spaces, but make oddities like in the original post mentioned less obscure. (Maybe even a second version for nonBreakingSpaces.); e.g.:

    translate $ do
      english [i|Stating|]
      german [i|Die Angabe von|]
    space
    let ts = transitionPairShow findInitial
    code $ show ts
    space
    translate $ do
      let (t1, t2) = bimap show show ts
      english [iii|
        as answer would indicate that transitions #{t1} and #{t2}
        are concurrently activated under the initial marking.
        |]
      [...]
    space
marcellussiegburg commented 1 month ago

Another option would be to use the ADT GenericOutput in order to sanitise the output.

For example glueing code blocks together could be done like this (of course this should rather be some fold function instead of the pattern matching)

ghci> let run y = runLangMReport (pure ()) (>>) y >>= \(_, x) -> (x Control.OutputCapable.Blocks.English :: IO ())
ghci> let joinEntries = M.mergeWithKey (\_ x y-> Just $ x ++ y) id id 
ghci> let sanitise [Code x, Code y] = [Code $ joinEntries x y]
ghci> join $ (run . Control.OutputCapable.Blocks.Generic.Type.toOutputCapable (\() -> pure ())) . sanitise <$> Control.OutputCapable.Blocks.Type.getSpecialOutputSequence (do {code "a" *> code "b"})
 <ab> 

This would also not be part of the library, but the library could provide the sanitise function which should be used before using runLangMReport or any equivalent function.

The most important question is: What is the

"always sane thing"

?

There is no exception?! Anything else?