ElectronicBabylonianLiterature/ebl-frontend

View on GitHub
src/transliteration/domain/columns.ts

Summary

Maintainability
A
1 hr
Test Coverage
A
100%
import _ from 'lodash'
import { Token } from './token'
import { isAkkadianWord, isColumn } from './type-guards'
import { LineAccumulator } from 'transliteration/ui/LineAccumulator'
import { PhoneticProps } from 'akkadian/application/phonetics/segments'

export interface TextLineColumn {
  span: number | null
  content: Token[]
}

function updatePhoneticPropsContext(
  content: Token[],
  index: number,
  phoneticProps?: PhoneticProps
): PhoneticProps {
  const previousWord = _.find(content.slice(0, index).reverse(), (token) =>
    isAkkadianWord(token)
  )
  const nextWord = _.find(content.slice(index + 1), (token) =>
    isAkkadianWord(token)
  )
  return {
    ...phoneticProps,
    wordContext: {
      ...(previousWord &&
        isAkkadianWord(previousWord) && {
          previousWord,
        }),
      ...(nextWord &&
        isAkkadianWord(nextWord) && {
          nextWord,
        }),
    },
  }
}

export const defaultSpan = 1

export function lineAccFromColumns({
  columns,
  isInLineGroup = false,
  showMeter = false,
  showIpa = false,
  phoneticProps,
  highlightLemmas = [],
}: {
  columns: readonly TextLineColumn[]
  isInLineGroup?: boolean
  showMeter?: boolean
  showIpa?: boolean
  phoneticProps?: PhoneticProps
  highlightLemmas: readonly string[]
}): LineAccumulator {
  return columns.reduce((acc: LineAccumulator, column) => {
    acc.addColumn(column.span)
    column.content.reduce(
      (acc: LineAccumulator, token: Token, index: number) => {
        acc.addColumnToken(
          token,
          index,
          isInLineGroup,
          showMeter,
          showIpa,
          updatePhoneticPropsContext(column.content, index, phoneticProps),
          _.isEmpty(_.intersection(token.uniqueLemma, highlightLemmas))
            ? []
            : ['highlight']
        )
        return acc
      },
      acc
    )
    return acc
  }, new LineAccumulator())
}

export function numberOfColumns(columns: readonly TextLineColumn[]): number {
  return _(columns)
    .map((column) => column.span ?? defaultSpan)
    .sum()
}

export function maxColumns(lines: readonly TextLineColumn[][]): number {
  return _(lines).map(numberOfColumns).max() ?? 1
}

export function containsColumns(tokens: readonly Token[]): boolean {
  return tokens.some(isColumn)
}

export function createColumns(tokens: readonly Token[]): TextLineColumn[] {
  const hasColumns = containsColumns(tokens)
  return _.reduce<Token, TextLineColumn[]>(
    tokens,
    (columns, current) => {
      if (isColumn(current)) {
        if (_.isEmpty(columns) && current.number === null) {
          columns.push({
            span: defaultSpan,
            content: [],
          })
        }
        columns.push({
          span: current.number ?? defaultSpan,
          content: [],
        })
      } else if (_.isEmpty(columns)) {
        columns.push({
          span: hasColumns ? 1 : null,
          content: [current],
        })
      } else {
        _.last(columns)?.content.push(current)
      }
      return columns
    },
    []
  )
}