@@ -28,6 +28,7 @@ module Dodo
2828 , encloseWithSeparator
2929 , foldWithSeparator
3030 , foldWith
31+ , locally
3132 , print
3233 , Printer (..)
3334 , plainText
@@ -48,7 +49,7 @@ import Data.String as String
4849import Data.String.Regex as Regex
4950import Data.String.Regex.Flags (global )
5051import Data.String.Regex.Unsafe (unsafeRegex )
51- import Dodo.Internal (Doc (..), Position , bothNotEmpty , isEmpty , notEmpty )
52+ import Dodo.Internal (Doc (..), Position , LocalOptions , bothNotEmpty , isEmpty , notEmpty )
5253import Dodo.Internal (Doc , Position , bothNotEmpty , isEmpty , notEmpty ) as Exports
5354import Dodo.Internal.Buffer (Buffer )
5455import Dodo.Internal.Buffer as Buffer
@@ -193,6 +194,11 @@ foldWithSeparator separator = foldWith (\a b -> a <> (separator <> b))
193194foldWith :: forall f a . Foldable f => (Doc a -> Doc a -> Doc a ) -> f (Doc a ) -> Doc a
194195foldWith f = foldr (bothNotEmpty f) mempty
195196
197+ -- | *EXPERIMENTAL:* modifies printing state and options locally for a document.
198+ -- | This may change or be removed at any time.
199+ locally :: forall a . (LocalOptions -> LocalOptions ) -> Doc a -> Doc a
200+ locally = Local
201+
196202-- | Custom printers can be used to render richer documents than just plain
197203-- | text.
198204-- | * `emptyBuffer` - The initial buffer.
@@ -255,6 +261,7 @@ data DocCmd a
255261 | Dedent String Int
256262 | LeaveAnnotation a (List a )
257263 | LeaveFlexGroup (Doc a ) (Doc a )
264+ | LeaveLocal LocalOptions
258265
259266data FlexGroupStatus b a
260267 = NoFlexGroup
@@ -267,6 +274,7 @@ type FlexGroupState b a =
267274 , annotations :: List a
268275 , indentSpaces :: String
269276 , stack :: List (DocCmd a )
277+ , options :: PrintOptions
270278 }
271279
272280type DocState b a =
@@ -275,15 +283,39 @@ type DocState b a =
275283 , annotations :: List a
276284 , indentSpaces :: String
277285 , flexGroup :: FlexGroupStatus b a
286+ , options :: PrintOptions
278287 }
279288
280289resetState :: forall a b . FlexGroupState b a -> DocState b a
281- resetState { position, buffer, annotations, indentSpaces } =
282- { position, buffer, annotations, indentSpaces, flexGroup: NoFlexGroup }
290+ resetState { position, buffer, annotations, indentSpaces, options } =
291+ { position, buffer, annotations, indentSpaces, flexGroup: NoFlexGroup , options }
283292
284293storeState :: forall a b . List (DocCmd a ) -> DocState b a -> FlexGroupState b a
285- storeState stack { position, buffer, annotations, indentSpaces } =
286- { position, buffer, annotations, indentSpaces, stack }
294+ storeState stack { position, buffer, annotations, indentSpaces, options } =
295+ { position, buffer, annotations, indentSpaces, stack, options }
296+
297+ storeOptions :: forall a b . Int -> LocalOptions -> DocState b a -> DocState b a
298+ storeOptions prevIndent localOptions state = do
299+ let
300+ newOptions =
301+ { indentUnit: localOptions.indentUnit
302+ , indentWidth: localOptions.indentWidth
303+ , pageWidth: localOptions.pageWidth
304+ , ribbonRatio: localOptions.ribbonRatio
305+ }
306+ state
307+ { indentSpaces = localOptions.indentSpaces
308+ , options = newOptions
309+ , position
310+ { pageWidth = newOptions.pageWidth
311+ , ribbonWidth = calcRibbonWidth newOptions prevIndent
312+ , nextIndent = localOptions.indent
313+ }
314+ }
315+
316+ calcRibbonWidth :: PrintOptions -> Int -> Int
317+ calcRibbonWidth { pageWidth, ribbonRatio } n =
318+ max 0 $ Int .ceil $ mul ribbonRatio $ Int .toNumber $ pageWidth - n
287319
288320-- | Prints a documents given a printer and print options.
289321-- |
@@ -297,11 +329,8 @@ storeState stack { position, buffer, annotations, indentSpaces } =
297329print :: forall b a r . Printer b a r -> PrintOptions -> Doc a -> r
298330print (Printer printer) opts = flip go initState <<< pure <<< Doc
299331 where
300- ribbonRatio :: Number
301- ribbonRatio = max 0.0 (min 1.0 opts.ribbonRatio)
302-
303- calcRibbonWidth :: Int -> Int
304- calcRibbonWidth = max 0 <<< Int .ceil <<< mul ribbonRatio <<< Int .toNumber <<< (opts.pageWidth - _)
332+ initOptions :: PrintOptions
333+ initOptions = opts { ribbonRatio = max 0.0 (min 1.0 opts.ribbonRatio) }
305334
306335 initState :: DocState b a
307336 initState =
@@ -310,13 +339,14 @@ print (Printer printer) opts = flip go initState <<< pure <<< Doc
310339 , column: 0
311340 , indent: 0
312341 , nextIndent: 0
313- , pageWidth: opts .pageWidth
314- , ribbonWidth: calcRibbonWidth 0
342+ , pageWidth: initOptions .pageWidth
343+ , ribbonWidth: calcRibbonWidth initOptions 0
315344 }
316345 , buffer: Buffer .new printer.emptyBuffer
317346 , annotations: List.Nil
318347 , indentSpaces: " "
319348 , flexGroup: NoFlexGroup
349+ , options: initOptions
320350 }
321351
322352 go :: List (DocCmd a ) -> DocState b a -> r
@@ -356,7 +386,7 @@ print (Printer printer) opts = flip go initState <<< pure <<< Doc
356386 { line = state.position.line + 1
357387 , column = 0
358388 , indent = state.position.nextIndent
359- , ribbonWidth = calcRibbonWidth state.position.nextIndent
389+ , ribbonWidth = calcRibbonWidth state.options state. position.nextIndent
360390 }
361391 , buffer = Buffer .modify printer.writeBreak state.buffer
362392 , flexGroup = NoFlexGroup
@@ -367,7 +397,7 @@ print (Printer printer) opts = flip go initState <<< pure <<< Doc
367397 { position
368398 { indent = state.position.nextIndent + opts.indentWidth
369399 , nextIndent = state.position.nextIndent + opts.indentWidth
370- , ribbonWidth = calcRibbonWidth (state.position.nextIndent + opts.indentWidth)
400+ , ribbonWidth = calcRibbonWidth state.options (state.position.nextIndent + opts.indentWidth)
371401 }
372402 , indentSpaces = state.indentSpaces <> opts.indentUnit
373403 }
@@ -382,7 +412,7 @@ print (Printer printer) opts = flip go initState <<< pure <<< Doc
382412 { position
383413 { indent = state.position.nextIndent + width
384414 , nextIndent = state.position.nextIndent + width
385- , ribbonWidth = calcRibbonWidth (state.position.nextIndent + width)
415+ , ribbonWidth = calcRibbonWidth state.options (state.position.nextIndent + width)
386416 }
387417 , indentSpaces = state.indentSpaces <> power " " width
388418 }
@@ -423,6 +453,18 @@ print (Printer printer) opts = flip go initState <<< pure <<< Doc
423453 { annotations = ann : state.annotations
424454 , buffer = Buffer .modify (printer.enterAnnotation ann state.annotations) state.buffer
425455 }
456+ Local k doc1 -> do
457+ let
458+ prevOptions =
459+ { indent: state.position.indent
460+ , indentSpaces: state.indentSpaces
461+ , indentUnit: state.options.indentUnit
462+ , indentWidth: state.options.indentWidth
463+ , pageWidth: state.options.pageWidth
464+ , ribbonRatio: state.options.ribbonRatio
465+ }
466+ localOptions = k prevOptions
467+ go (Doc doc1 : LeaveLocal prevOptions: stk) $ storeOptions state.position.indent localOptions state
426468 Empty ->
427469 go stk state
428470 LeaveFlexGroup doc1 doc2 -> case state.flexGroup of
@@ -445,3 +487,5 @@ print (Printer printer) opts = flip go initState <<< pure <<< Doc
445487 { annotations = anns
446488 , buffer = Buffer .modify (printer.leaveAnnotation ann anns) state.buffer
447489 }
490+ LeaveLocal prevOptions ->
491+ go stk $ storeOptions state.position.indent prevOptions state
0 commit comments