diff --git a/method.py b/method.py index dee18fc..59564c4 100644 --- a/method.py +++ b/method.py @@ -141,6 +141,7 @@ class Search(Method): self.old_cursor = w.logical_cursor() self.old_window = w self.direction = 'next' + self.is_literal = True w.application.open_mini_buffer('I-Search: ', lambda x: None, self, None, 'search') class ReverseSearch(Method): '''Interactive search; finds previous occurance of text in buffer''' @@ -148,6 +149,7 @@ class ReverseSearch(Method): self.old_cursor = w.logical_cursor() self.old_window = w self.direction = 'previous' + self.is_literal = True w.application.open_mini_buffer('I-Search: ', lambda x: None, self, None, 'search') class Replace(Method): '''Replace occurances of string X with string Y''' diff --git a/mode_search.py b/mode_search.py index 775a7db..0d63315 100644 --- a/mode_search.py +++ b/mode_search.py @@ -1,4 +1,4 @@ -import sets, string +import re, sets, string import color, method, minibuffer, mode2, search from point2 import Point @@ -32,15 +32,23 @@ class Search(mode2.Fundamental): def name(self): return "Search" +def _make_regex(w, s): + if w.buffer.method.is_literal: + s = search.escape_literal(s) + return re.compile(s) + class SearchNext(method.Method): def execute(self, w, **vargs): w.buffer.method.direction = 'next' - if not w.buffer.make_string(): - w.buffer.set_data(w.application.last_search) + s = w.buffer.make_string() + r = _make_regex(w, s) + if s: + try: + search.find_next(s, w.buffer.method.old_window, move=True) + except search.IllegalPatternError: + w.application.clear_highlighted_ranges() else: - s = w.buffer.make_string() - w2 = w.buffer.method.old_window - search.find_next(s, w2, move=True) + w.buffer.set_data(w.application.last_search) class SearchPrevious(method.Method): def execute(self, w, **vargs): @@ -49,8 +57,12 @@ class SearchPrevious(method.Method): return else: s = w.buffer.make_string() + r = _make_regex(w, s) w2 = w.buffer.method.old_window - search.find_previous(s, w2, move=True) + try: + search.find_previous(s, w2, move=True) + except search.IllegalPatternError: + w.application.clear_highlighted_ranges() class EndSearch(method.Method): def execute(self, w, **vargs): @@ -82,11 +94,15 @@ def _post_delete(w): w.application.clear_highlighted_ranges() return s = w.buffer.make_string() + r = _make_regex(w, s) w2 = w.buffer.method.old_window - if w.buffer.method.direction == 'next': - search.find_next(s, w2, move=False) - else: - search.find_previous(s, w2, move=False) + try: + if w.buffer.method.direction == 'next': + search.find_next(s, w2, move=False) + else: + search.find_previous(s, w2, move=False) + except search.IllegalPatternError: + w.application.clear_highlighted_ranges() class InsertSearchString(method.Method): def __init__(self, s): @@ -101,11 +117,15 @@ class InsertSearchString(method.Method): return else: s = w.buffer.make_string() + r = _make_regex(w, s) w2 = w.buffer.method.old_window - if w.buffer.method.direction == 'next': - search.find_next(s, w2, move=False) - else: - search.find_previous(s, w2, move=False) + try: + if w.buffer.method.direction == 'next': + search.find_next(s, w2, move=False) + else: + search.find_previous(s, w2, move=False) + except search.IllegalPatternError: + w.application.clear_highlighted_ranges() def _end(w): w.application.close_mini_buffer() diff --git a/regex.py b/regex.py index 97eb12b..a923726 100644 --- a/regex.py +++ b/regex.py @@ -1,5 +1,8 @@ import re +# meta regexes +meta_chars = re.compile(r'([\.\^\$\*\+\?\{\}\(\)\[\]\|\\])') + # whitespace regexes leading_whitespace = re.compile('^ *') trailing_whitespace = re.compile(' *$') @@ -11,11 +14,11 @@ word_char = re.compile('^[A-Za-z0-9_]$') # perl regexes perl_base = re.compile("^sub ") -perl_hash_cleanup = re.compile("^( *)([^ ]+|'(?:\\.|[^'\\'])*'|\"(?:\\.|[^\\\"]*)\")( *)(=>)( *)([^ ].*)$") -perl_assign_cleanup = re.compile("^( *)((?:my |our )?[^ ]+)( *)(=(?!>))( *)([^ ].*)$") -perl_function = re.compile("^ *sub ([A-Za-z_][A-Za-z0-9_]*)") +perl_hash_cleanup = re.compile(r"^( *)([^ ]+|'(?:\.|[^'\'])*'|\"(?:\.|[^\\\"]*)\")( *)(=>)( *)([^ ].*)$") +perl_assign_cleanup = re.compile(r"^( *)((?:my |our )?[^ ]+)( *)(=(?!>))( *)([^ ].*)$") +perl_function = re.compile(r"^ *sub ([A-Za-z_][A-Za-z0-9_]*)") # python regexes -python_base = re.compile("^[^ ]") -python_dict_cleanup = re.compile("^( *)((?:[^'\":]|'(?:\\.|[^\\'])*'|\"(?:\\.|[^\\'])*)+?)( *)(:)( *)([^ ].*)$") -python_assign_cleanup = re.compile("^( *)([^ ]+)( *)(=)( *)([^ ].*)$") +python_base = re.compile(r"^[^ ]") +python_dict_cleanup = re.compile(r"^( *)((?:[^'\":]|'(?:\.|[^\'])*'|\"(?:\.|[^\'])*)+?)( *)(:)( *)([^ ].*)$") +python_assign_cleanup = re.compile(r"^( *)([^ ]+)( *)(=)( *)([^ ].*)$") diff --git a/search.py b/search.py index 8cd9ba5..b29bca2 100644 --- a/search.py +++ b/search.py @@ -1,9 +1,17 @@ +import re +import regex from point2 import Point bg_color = 'black' selected_color = 'magenta' unselected_color = 'yellow' +class IllegalPatternError(Exception): + pass + +def escape_literal(s): + return regex.meta_chars.sub(r'\\\1', s) + def find_ranges(s, w, start=None, end=None): if not w.buffer.lines: return [] @@ -16,26 +24,22 @@ def find_ranges(s, w, start=None, end=None): (x2, y2) = (len(w.buffer.lines[-1]) - 1, len(w.buffer.lines) - 1) else: (x2, y2) = end.xy() - #if x2 == 0: - # y2 -= 1 - # x2 = len(w.buffer.lines[-1]) - 1 - #else: - # x2 -= 1 + + #s2 = escape_literal(s) + #r = re.compile(s2, re.IGNORECASE) + + r = re.compile(s, re.IGNORECASE) ranges = [] - #while y <= y2: while y <= y2: if y == y2: limit = x2 else: limit = len(w.buffer.lines[y]) - 1 - while x <= limit: - try: - i = w.buffer.lines[y].index(s, x) - except ValueError: - break - x = i + len(s) - ranges.append([Point(i, y), Point(x, y), bg_color, unselected_color]) + for m in r.finditer(w.buffer.lines[y], x, limit): + if len(m.group(0)) == 0: + raise IllegalPatternError, "zero-width match found for: %r" % s + ranges.append([Point(m.start(), y), Point(m.end(), y), bg_color, unselected_color]) x = 0 y += 1 return ranges