Coverage for history.py: 31%

66 statements  

« prev     ^ index     » next       coverage.py v7.2.5, created at 2023-05-11 13:22 -0700

1"Implement Idle Shell history mechanism with History class" 

2 

3from idlelib.config import idleConf 

4 

5 

6class History: 

7 ''' Implement Idle Shell history mechanism. 

8 

9 store - Store source statement (called from pyshell.resetoutput). 

10 fetch - Fetch stored statement matching prefix already entered. 

11 history_next - Bound to <<history-next>> event (default Alt-N). 

12 history_prev - Bound to <<history-prev>> event (default Alt-P). 

13 ''' 

14 def __init__(self, text): 

15 '''Initialize data attributes and bind event methods. 

16 

17 .text - Idle wrapper of tk Text widget, with .bell(). 

18 .history - source statements, possibly with multiple lines. 

19 .prefix - source already entered at prompt; filters history list. 

20 .pointer - index into history. 

21 .cyclic - wrap around history list (or not). 

22 ''' 

23 self.text = text 

24 self.history = [] 

25 self.prefix = None 

26 self.pointer = None 

27 self.cyclic = idleConf.GetOption("main", "History", "cyclic", 1, "bool") 

28 text.bind("<<history-previous>>", self.history_prev) 

29 text.bind("<<history-next>>", self.history_next) 

30 

31 def history_next(self, event): 

32 "Fetch later statement; start with earliest if cyclic." 

33 self.fetch(reverse=False) 

34 return "break" 

35 

36 def history_prev(self, event): 

37 "Fetch earlier statement; start with most recent." 

38 self.fetch(reverse=True) 

39 return "break" 

40 

41 def fetch(self, reverse): 

42 '''Fetch statement and replace current line in text widget. 

43 

44 Set prefix and pointer as needed for successive fetches. 

45 Reset them to None, None when returning to the start line. 

46 Sound bell when return to start line or cannot leave a line 

47 because cyclic is False. 

48 ''' 

49 nhist = len(self.history) 

50 pointer = self.pointer 

51 prefix = self.prefix 

52 if pointer is not None and prefix is not None: 

53 if self.text.compare("insert", "!=", "end-1c") or \ 

54 self.text.get("iomark", "end-1c") != self.history[pointer]: 

55 pointer = prefix = None 

56 self.text.mark_set("insert", "end-1c") # != after cursor move 

57 if pointer is None or prefix is None: 

58 prefix = self.text.get("iomark", "end-1c") 

59 if reverse: 

60 pointer = nhist # will be decremented 

61 else: 

62 if self.cyclic: 

63 pointer = -1 # will be incremented 

64 else: # abort history_next 

65 self.text.bell() 

66 return 

67 nprefix = len(prefix) 

68 while True: 

69 pointer += -1 if reverse else 1 

70 if pointer < 0 or pointer >= nhist: 

71 self.text.bell() 

72 if not self.cyclic and pointer < 0: # abort history_prev 

73 return 

74 else: 

75 if self.text.get("iomark", "end-1c") != prefix: 

76 self.text.delete("iomark", "end-1c") 

77 self.text.insert("iomark", prefix, "stdin") 

78 pointer = prefix = None 

79 break 

80 item = self.history[pointer] 

81 if item[:nprefix] == prefix and len(item) > nprefix: 

82 self.text.delete("iomark", "end-1c") 

83 self.text.insert("iomark", item, "stdin") 

84 break 

85 self.text.see("insert") 

86 self.text.tag_remove("sel", "1.0", "end") 

87 self.pointer = pointer 

88 self.prefix = prefix 

89 

90 def store(self, source): 

91 "Store Shell input statement into history list." 

92 source = source.strip() 1bcd

93 if len(source) > 2: 1bcd

94 # avoid duplicates 

95 try: 1bc

96 self.history.remove(source) 1bc

97 except ValueError: 1bc

98 pass 1bc

99 self.history.append(source) 1bc

100 self.pointer = None 1bcd

101 self.prefix = None 1bcd

102 

103 

104if __name__ == "__main__": 104 ↛ 105line 104 didn't jump to line 105, because the condition on line 104 was never true

105 from unittest import main 

106 main('idlelib.idle_test.test_history', verbosity=2, exit=False)