Coverage for liitos/description_lists.py: 100.00%

35 statements  

« prev     ^ index     » next       coverage.py v7.6.10, created at 2025-01-05 17:22:35 +00:00

1"""Apply any option command to subsequent description environment. 

2 

3Implementation Note: The empty string marker is used to indicate absence of option command. 

4""" 

5 

6from collections.abc import Iterable 

7from enum import Enum 

8from typing import Union 

9 

10from liitos import log 

11 

12Modus = Enum('Modus', 'COPY OPTION') 

13NO_OPTION: str = '' 

14 

15OPTION_STARTT_TRIGGER_STARTSWITH = r'\option[' 

16DESCRIPTION_START_TRIGGER_STARTSWITH = r'\begin{description}' 

17 

18 

19def filter_seek_option(line: str, slot: int, modus: Modus, opt: str, outgoing: list[str]) -> tuple[Modus, str]: 

20 r"""Filter line, seek for an option command, and return updated mnodus, opt pair. 

21 

22 Examples: 

23 

24 >>> filtered = [] 

25 >>> m, opt = filter_seek_option(r'\option[]', 0, Modus.COPY, NO_OPTION, filtered) 

26 >>> assert not filtered 

27 >>> assert m == Modus.OPTION 

28 >>> assert opt == '[]' 

29 

30 >>> filtered = [] 

31 >>> m, opt = filter_seek_option('foo', 0, Modus.COPY, NO_OPTION, filtered) 

32 >>> assert filtered == ['foo'] 

33 >>> assert m == Modus.COPY 

34 >>> assert opt == NO_OPTION 

35 

36 >>> filtered = [] 

37 >>> m, opt = filter_seek_option(r'\option[foo=bar]', 0, Modus.COPY, NO_OPTION, filtered) 

38 >>> assert not filtered 

39 >>> assert m == Modus.OPTION 

40 >>> assert opt == '[foo=bar]' 

41 """ 

42 if line.startswith(OPTION_STARTT_TRIGGER_STARTSWITH): 

43 log.info(f'trigger an option mod for the next description environment at line #{slot + 1}|{line}') 

44 opt = '[' + line.split(OPTION_STARTT_TRIGGER_STARTSWITH, 1)[1].strip() 

45 modus = Modus.OPTION 

46 log.info(f' -> parsed option as ({opt})') 

47 else: 

48 outgoing.append(line) 

49 

50 return modus, opt 

51 

52 

53def filter_seek_description(line: str, slot: int, modus: Modus, opt: str, outgoing: list[str]) -> tuple[Modus, str]: 

54 r"""Filter line, seek for a description, add options if applicable, and return updated mnodus, option pair. 

55 

56 Examples: 

57 

58 >>> filtered = [] 

59 >>> m, opt = filter_seek_description('quux', 0, Modus.OPTION, '[foo=bar]', filtered) 

60 >>> assert filtered == ['quux'] 

61 >>> assert m == Modus.OPTION 

62 >>> assert opt == '[foo=bar]' 

63 

64 >>> filtered = [] 

65 >>> m, opt = filter_seek_description(r'\begin{description}', 0, Modus.OPTION, NO_OPTION, filtered) 

66 >>> assert filtered == [r'\begin{description}'] 

67 >>> assert m == Modus.COPY 

68 >>> assert opt == NO_OPTION 

69 

70 >>> filtered = [] 

71 >>> m, opt = filter_seek_description(r'\begin{description}', 0, Modus.OPTION, '[foo=bar]', filtered) 

72 >>> assert filtered == [r'\begin{description}[foo=bar]'] 

73 >>> assert m == Modus.COPY 

74 >>> assert opt == NO_OPTION 

75 """ 

76 if line.startswith(DESCRIPTION_START_TRIGGER_STARTSWITH): 

77 if opt != NO_OPTION: 

78 log.info(f'- found the option target start at line #{slot + 1}|{line}') 

79 outgoing.append(f'{DESCRIPTION_START_TRIGGER_STARTSWITH}{opt}') 

80 else: 

81 outgoing.append(line) 

82 modus = Modus.COPY 

83 opt = NO_OPTION 

84 else: 

85 outgoing.append(line) 

86 

87 return modus, opt 

88 

89 

90def options(incoming: Iterable[str], lookup: Union[dict[str, str], None] = None) -> list[str]: 

91 r"""Later alligator. \option[style=multiline,leftmargin=6em] 

92 

93 Examples: 

94 

95 >>> in_opts = '[style=multiline,leftmargin=6em]' 

96 >>> opt_line = f'\\option{in_opts}' 

97 >>> beg_desc = '\\begin{description}' 

98 >>> lines_in = ['a', '', opt_line, '', beg_desc, 'whatever'] 

99 >>> lines_in 

100 ['a', '', '\\option[style=multiline,leftmargin=6em]', '', '\\begin{description}', 'whatever'] 

101 >>> processed = options(lines_in) 

102 >>> processed 

103 ['a', '', '', '\\begin{description}[style=multiline,leftmargin=6em]', 'whatever'] 

104 """ 

105 outgoing: list[str] = [] 

106 modus = Modus.COPY 

107 opt = NO_OPTION 

108 for slot, line in enumerate(incoming): 

109 if modus == Modus.COPY: 

110 modus, opt = filter_seek_option(line, slot, modus, opt, outgoing) 

111 else: # if modus == Modus.OPTION: 

112 modus, opt = filter_seek_description(line, slot, modus, opt, outgoing) 

113 

114 return outgoing