Coverage for liitos/description_lists.py: 100.00%

36 statements  

« prev     ^ index     » next       coverage.py v7.10.6, created at 2025-08-31 13:07: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') 

13ModusOption = tuple[Modus, str] 

14 

15NO_OPTION: str = '' 

16 

17OPTION_START_TRIGGER_STARTSWITH = r'\option[' 

18DESCRIPTION_START_TRIGGER_STARTSWITH = r'\begin{description}' 

19 

20 

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

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

23 

24 Examples: 

25 

26 >>> filtered = [] 

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

28 >>> assert not filtered 

29 >>> assert m == Modus.OPTION 

30 >>> assert opt == '[]' 

31 

32 >>> filtered = [] 

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

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

35 >>> assert m == Modus.COPY 

36 >>> assert opt == NO_OPTION 

37 

38 >>> filtered = [] 

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

40 >>> assert not filtered 

41 >>> assert m == Modus.OPTION 

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

43 """ 

44 if line.startswith(OPTION_START_TRIGGER_STARTSWITH): 

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

46 opt = '[' + line.split(OPTION_START_TRIGGER_STARTSWITH, 1)[1].strip() 

47 modus = Modus.OPTION 

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

49 else: 

50 outgoing.append(line) 

51 

52 return modus, opt 

53 

54 

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

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

57 

58 Examples: 

59 

60 >>> filtered = [] 

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

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

63 >>> assert m == Modus.OPTION 

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

65 

66 >>> filtered = [] 

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

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

69 >>> assert m == Modus.COPY 

70 >>> assert opt == NO_OPTION 

71 

72 >>> filtered = [] 

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

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

75 >>> assert m == Modus.COPY 

76 >>> assert opt == NO_OPTION 

77 """ 

78 if line.startswith(DESCRIPTION_START_TRIGGER_STARTSWITH): 

79 if opt != NO_OPTION: 

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

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

82 else: 

83 outgoing.append(line) 

84 modus = Modus.COPY 

85 opt = NO_OPTION 

86 else: 

87 outgoing.append(line) 

88 

89 return modus, opt 

90 

91 

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

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

94 

95 Examples: 

96 

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

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

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

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

101 >>> lines_in 

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

103 >>> processed = options(lines_in) 

104 >>> processed 

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

106 """ 

107 outgoing: list[str] = [] 

108 modus = Modus.COPY 

109 opt = NO_OPTION 

110 for slot, line in enumerate(incoming): 

111 if modus == Modus.COPY: 

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

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

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

115 

116 return outgoing