All files / src/draw/tree/theme data.ts

100% Statements 65/65
100% Branches 27/27
100% Functions 14/14
100% Lines 65/65

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 2341x 1x 1x 1x                                         1x       1x 1x 1x 1x 1x 1x 25x 1x 1x                           1x     1x 1x 1x 1x                 1x                 1x                 1x                 1x 1x                 1x                 1x                 1x 2x                   1x 1x 2x 2x 2x 2x 2x                 1x 2x                   1x 1x 2x 2x 2x 2x 2x                   1x 1x               1x 2x 2x               1x 2x 2x                                               1x 3x 3x   279x 279x 279x 17x 17x 17x 279x   405x 405x 405x  
import {Array, Number, Record, pipe} from '#util'
import {dual, identity, type EndoOf} from '#Function'
import {text, type Part} from '../../part.js'
import {type GlyphRole} from './glyph.js'
import type {Theme} from './themes.js'
 
/**
 * Get a glyph by role from a tree theme.
 *
 * At the key `flipped` you will find a version that takes the glyph role as
 * first and only argument of its first argument list, and the theme in
 * the second argument list.
 *
 * @param role Glyph role to get.
 * @param theme Theme to query.
 * @example
 * import {Draw} from 'effect-tree'
 *
 * const glyph = Draw.getGlyph('elbow', Draw.getTheme('thin'))
 *
 * expect(glyph).toBe('└')
 * @category drawing
 * @function
 */
export const getGlyph: {
  (role: GlyphRole, theme: Theme): string
  (theme: Theme): (role: GlyphRole) => string
  flipped: (role: GlyphRole) => (theme: Theme) => string
} = Object.assign(
  dual(2, (role: GlyphRole, {glyphs}: Theme): string => glyphs[role]),
  {
    flipped:
      (role: GlyphRole) =>
      ({glyphs}: Theme): string =>
        glyphs[role],
  },
)
 
/**
 * Get a glyph by role from a tree theme.
 * import {Draw} from 'effect-tree'
 *
 * const part = Draw.getGlyphPart('elbow', Draw.getTheme('thin'))
 *
 * expect(Draw.drawPart(part)).toBe('└')
 * @param theme Theme to query.
 * @returns Theme indents.
 * @category drawing
 * @function
 */
export const getGlyphPart: {
  (role: GlyphRole, theme: Theme): Part
  (theme: Theme): (role: GlyphRole) => Part
} = dual(
  2,
  (role: GlyphRole, theme: Theme): Part => text(getGlyph(role, theme)),
)
 
/**
 * Get the indent count of a tree theme.
 * @param theme Theme to query.
 * @returns Theme indents.
 * @category drawing
 * @function
 */
export const getIndents = ({indents}: Theme): number => indents
 
/**
 * Get the vertical spacing of a tree theme.
 * @param theme Theme to query.
 * @returns Theme vertical spacing.
 * @category drawing
 * @function
 */
export const getSpacing = ({spacing}: Theme): number => spacing
 
/**
 * Get the string formatter of the tree theme.
 * @param theme Theme to query.
 * @returns Theme formatter.
 * @category drawing
 * @function
 */
export const getFormatter = ({formatter}: Theme): EndoOf<string> => formatter
 
/**
 * Set the `formatter` of a theme.
 * @param formatter New formatter.
 * @returns Theme with updated formatter.
 * @category drawing
 * @function
 */
export const setFormatter: (formatter: EndoOf<string>) => EndoOf<Theme> =
  set('formatter')
 
/**
 * Set the indent count of a tree theme.
 * @param indents Number of indents from parent to child.
 * @returns Theme with updated indent count.
 * @category drawing
 * @function
 */
export const setIndents: (indents: number) => EndoOf<Theme> = set('indents')
 
/**
 * Set the vertical spacing of a tree theme.
 * @param spacing Number of lines of vertical space between tree nodes.
 * @returns Theme with updated vertical spacing.
 * @category drawing
 * @function
 */
export const setSpacing: (spacing: number) => EndoOf<Theme> = set('spacing')
 
/**
 * Increment tree theme indents by the given count, by default `1`.
 * @param increment Number of characters to add to the theme indent count.
 * @returns Theme with updated vertical spacing.
 * @category drawing
 * @function
 */
export const incrementIndents = (increment = 1): EndoOf<Theme> =>
  pipe(increment, Number.sum, modIndents)
 
/**
 * Decrement tree theme indents by the given count, by default `1`. If the
 * indent count is zero the theme is return unchanged.
 * @param decrement Number of characters to remove from the theme indent count.
 * @returns Theme with updated vertical spacing.
 * @category drawing
 * @function
 */
export const decrementIndents =
  (decrement = 1): EndoOf<Theme> =>
  theme =>
    pipe(
      theme,
      getIndents(theme) === 0 ? identity : incrementIndents(-1 * decrement),
    )
 
/**
 * Increment tree theme spacing by the given count, by default `1`.
 * @param increment Number of vertical lines to add to the theme spacing.
 * @returns Theme with updated vertical spacing.
 * @category drawing
 * @function
 */
export const incrementSpacing = (increment = 1): EndoOf<Theme> =>
  pipe(increment, Number.sum, modSpacing)
 
/**
 * Decrement tree theme spacing by the given count, by default `1`. If the
 * spacing is at zero the theme is returned unchanged.
 * @param decrement Number of vertical lines to remove from the theme spacing.
 * @returns Theme with updated vertical spacing.
 * @category drawing
 * @function
 */
export const decrementSpacing =
  (decrement = 1): EndoOf<Theme> =>
  theme =>
    pipe(
      theme,
      getSpacing(theme) === 0 ? identity : incrementSpacing(-1 * decrement),
    )
 
/**
 * Returns a list of newlines that is the size of the tree theme vertical
 * spacing setting minus one.
 * @param theme Tree theme to query for spacing.
 * @returns An array of single newlines. Newline count will be equal to theme spacing.
 * @category drawing
 * @function
 */
export const fillSpacing = ({spacing}: Theme): string[] =>
  Array.replicate(spacing - 1)('\n')
 
/**
 * Modify the indent count of a tree theme using the given function.
 * @param f Will be given the indent count and expected to return an updated value.
 * @category drawing
 * @function
 */
export function modIndents(f: EndoOf<number>): EndoOf<Theme> {
  return modTheme('indents')(f)
}
 
/**
 * Modify the vertical spacing of a tree theme using the given function.
 * @param f Will be given the vertical spacing and expected to return an updated value.
 * @category drawing
 * @function
 */
export function modSpacing(f: EndoOf<number>): EndoOf<Theme> {
  return modTheme('spacing')(f)
}
 
/**
 * Modify the formatter function of a tree theme using the given function.
 * @example
 * import {Draw, from, of} from 'effect-tree'
 * import {pipe, flow} from 'effect'
 *
 * const theme: Draw.Theme = pipe(
 *   Draw.getTheme('thin'),
 *   Draw.modFormatter(f => flow(f, label  => `«${label}»`)),
 * )
 *
 * const tree = from('a', of('b'), of('c'))
 *
 * expect(Draw.themedTree(tree, theme)).toEqual([
 *   '┬«a» ',
 *   '├─«b»',
 *   '└─«c»',
 * ])
 * @param f Will be given the formatter function and expected to return an updated value.
 * @category drawing
 * @function
 */
export function modFormatter(f: EndoOf<EndoOf<string>>): EndoOf<Theme> {
  return modTheme('formatter')(f)
}
 
function modTheme<Field extends keyof Theme>(key: Field) {
  return (f: EndoOf<Theme[Field]>): EndoOf<Theme> =>
    theme => ({
      ...theme,
      ...Record.singleton(key, f(theme[key])),
    })
}
 
function set<Field extends keyof Theme>(key: Field) {
  return (value: Theme[Field]): EndoOf<Theme> => modTheme(key)(() => value)
}