diff --git a/pyreadline/lineeditor/history.py b/pyreadline/lineeditor/history.py index 3eae6dd..640e358 100644 --- a/pyreadline/lineeditor/history.py +++ b/pyreadline/lineeditor/history.py @@ -22,7 +22,6 @@ class EscapeHistory(exceptions.Exception): from pyreadline.logger import log -_ignore_leading_spaces = False class LineHistory(object): def __init__(self): @@ -32,6 +31,7 @@ class LineHistory(object): self.history_filename = os.path.expanduser('~/.history') #Cannot expand unicode strings correctly on python2.4 self.lastcommand = None self.query = u"" + self.last_search_for = u"" def get_current_history_length(self): u'''Return the number of lines currently in the history. @@ -140,34 +140,58 @@ class LineHistory(object): def reverse_search_history(self, searchfor, startpos=None): if startpos is None: startpos = self.history_cursor - if _ignore_leading_spaces: - res = [(idx, line.lstrip()) - for idx, line in enumerate(self.history[startpos:0:-1]) - if line.lstrip().startswith(searchfor.lstrip())] - else: - res = [(idx, line) - for idx, line in enumerate(self.history[startpos:0:-1]) - if line.startswith(searchfor)] - if res: - self.history_cursor -= res[0][0] - return res[0][1].get_line_text() - return u"" + + result = lineobj.ReadLineTextBuffer("") + + for idx, line in list(enumerate(self.history))[startpos:0:-1]: + if searchfor in line: + startpos = idx + break + + #If we get a new search without change in search term it means + #someone pushed ctrl-r and we should find the next match + if self.last_search_for == searchfor: + startpos -= 1 + for idx, line in list(enumerate(self.history))[startpos:0:-1]: + if searchfor in line: + startpos = idx + break + + result = self.history[startpos] + self.history_cursor = startpos + self.last_search_for = searchfor + return result.get_line_text() def forward_search_history(self, searchfor, startpos=None): if startpos is None: startpos = self.history_cursor - if _ignore_leading_spaces: - res = [(idx, line.lstrip()) - for idx, line in enumerate(self.history[startpos:]) - if line.lstrip().startswith(searchfor.lstrip())] + origpos = startpos + + result = lineobj.ReadLineTextBuffer("") + + for idx, line in list(enumerate(self.history))[startpos:]: + if searchfor in line: + startpos = idx + break + + #If we get a new search without change in search term it means + #someone pushed ctrl-r and we should find the next match + if self.last_search_for == searchfor: + startpos += 1 + for idx, line in list(enumerate(self.history))[startpos:]: + if searchfor in line: + startpos = idx + break + if len(self.history) == startpos: + if origpos == len(self.history): + return u"" + else: + return self.history[origpos] else: - res = [(idx, line) - for idx, line in enumerate(self.history[startpos:]) - if line.startswith(searchfor)] - if res: - self.history_cursor += res[0][0] - return res[0][1].get_line_text() - return u"" + result = self.history[startpos] + self.history_cursor = startpos + self.last_search_for = searchfor + return result.get_line_text() def _search(self, direction, partial): try: diff --git a/pyreadline/modes/basemode.py b/pyreadline/modes/basemode.py index df1147d..bf458d3 100644 --- a/pyreadline/modes/basemode.py +++ b/pyreadline/modes/basemode.py @@ -133,7 +133,7 @@ class BaseMode(object): u"""Every bindable command should call this function for cleanup. Except those that want to set argument to a non-zero value. """ - self.argument=0 + self.argument = 0 def add_history(self, text): diff --git a/pyreadline/modes/emacs.py b/pyreadline/modes/emacs.py index a388b91..234c96d 100644 --- a/pyreadline/modes/emacs.py +++ b/pyreadline/modes/emacs.py @@ -75,8 +75,8 @@ class IncrementalSearchPromptMode(object): self.subsearch_oldprompt = self.prompt - if (self.previous_func != self.history_search_forward and - self.previous_func != self.history_search_backward): + if (self.previous_func != self.reverse_search_history and + self.previous_func != self.forward_search_history): self.subsearch_query = self.l_buffer[0:Point].get_line_text() if self.subsearch_direction < 0: diff --git a/pyreadline/test/test_history.py b/pyreadline/test/test_history.py index d2c97f0..cdf9624 100644 --- a/pyreadline/test/test_history.py +++ b/pyreadline/test/test_history.py @@ -18,37 +18,34 @@ from pyreadline.logger import log #---------------------------------------------------------------------- RL=lineobj.ReadLineTextBuffer -class Test_linepos (unittest.TestCase): - t=u"test text" +class Test_prev_next_history(unittest.TestCase): + t = u"test text" - def init_test(self): - history._ignore_leading_spaces=False - self.q=q=LineHistory() - for x in [u"aaaa",u"aaba",u"aaca",u"akca",u"bbb",u"ako"]: + def setUp(self): + self.q = q = LineHistory() + for x in [u"aaaa", u"aaba", u"aaca", u"akca", u"bbb", u"ako"]: q.add_history(RL(x)) def test_previous_history (self): - self.init_test() - hist=self.q - assert hist.history_cursor==6 - l=RL(u"") + hist = self.q + assert hist.history_cursor == 6 + l = RL(u"") hist.previous_history(l) - assert l.get_line_text()==u"ako" + assert l.get_line_text() == u"ako" hist.previous_history(l) - assert l.get_line_text()==u"bbb" + assert l.get_line_text() == u"bbb" hist.previous_history(l) - assert l.get_line_text()==u"akca" + assert l.get_line_text() == u"akca" hist.previous_history(l) - assert l.get_line_text()==u"aaca" + assert l.get_line_text() == u"aaca" hist.previous_history(l) - assert l.get_line_text()==u"aaba" + assert l.get_line_text() == u"aaba" hist.previous_history(l) - assert l.get_line_text()==u"aaaa" + assert l.get_line_text() == u"aaaa" hist.previous_history(l) - assert l.get_line_text()==u"aaaa" + assert l.get_line_text() == u"aaaa" def test_next_history (self): - self.init_test() hist=self.q hist.beginning_of_history() assert hist.history_cursor==0 @@ -66,14 +63,16 @@ class Test_linepos (unittest.TestCase): hist.next_history(l) assert l.get_line_text()==u"ako" - def init_test2(self): - self.q=q=LineHistory() +class Test_prev_next_history(unittest.TestCase): + t = u"test text" + + def setUp(self): + self.q = q = LineHistory() for x in [u"aaaa",u"aaba",u"aaca",u"akca",u"bbb",u"ako"]: q.add_history(RL(x)) def test_history_search_backward (self): - history._ignore_leading_spaces=False - q=LineHistory() + q = LineHistory() for x in [u"aaaa",u"aaba",u"aaca",u" aacax",u"akca",u"bbb",u"ako"]: q.add_history(RL(x)) a=RL(u"aa",point=2) @@ -82,8 +81,7 @@ class Test_linepos (unittest.TestCase): assert res.get_line_text()==x def test_history_search_forward (self): - history._ignore_leading_spaces=False - q=LineHistory() + q = LineHistory() for x in [u"aaaa",u"aaba",u"aaca",u" aacax",u"akca",u"bbb",u"ako"]: q.add_history(RL(x)) q.beginning_of_history() @@ -92,6 +90,40 @@ class Test_linepos (unittest.TestCase): res=q.history_search_forward(a) assert res.get_line_text()==x +class Test_history_search_incr_fwd_backwd(unittest.TestCase): + def setUp(self): + self.q = q = LineHistory() + for x in [u"aaaa",u"aaba",u"aaca",u"akca",u"bbb",u"ako"]: + q.add_history(RL(x)) + + def test_backward_1(self): + q = self.q + self.assertEqual(q.reverse_search_history(u"b"), u"bbb") + self.assertEqual(q.reverse_search_history(u"b"), u"aaba") + self.assertEqual(q.reverse_search_history(u"bb"), u"aaba") + + def test_backward_2(self): + q = self.q + self.assertEqual(q.reverse_search_history(u"a"), u"ako") + self.assertEqual(q.reverse_search_history(u"aa"), u"aaca") + self.assertEqual(q.reverse_search_history(u"a"), u"aaca") + self.assertEqual(q.reverse_search_history(u"ab"), u"aaba") + + + def test_forward_1(self): + q = self.q + self.assertEqual(q.forward_search_history(u"a"), u"") + + def test_forward_2(self): + q = self.q + q.history_cursor = 0 + self.assertEqual(q.forward_search_history(u"a"), u"aaaa") + self.assertEqual(q.forward_search_history(u"a"), u"aaba") + self.assertEqual(q.forward_search_history(u"ak"), u"akca") + self.assertEqual(q.forward_search_history(u"akl"), u"akca") + self.assertEqual(q.forward_search_history(u"ak"), u"akca") + self.assertEqual(q.forward_search_history(u"ako"), u"ako") + #---------------------------------------------------------------------- # utility functions