Module: Redcar::DocumentSearch::FindCommandMixin

Included in:
FindCommandBase, ReplaceCommandBase
Defined in:
plugins/document_search/lib/document_search/commands.rb

Overview

Utilities for extended search commands.

Instance Method Summary (collapse)

Instance Method Details

- (Object) is_valid(query)

Indicates if the query is valid.



8
9
10
# File 'plugins/document_search/lib/document_search/commands.rb', line 8

def is_valid(query)
  query.inspect != "//i"
end

- (Object) make_literal_query(query, options)

An instance of a search type method: Plain text search



18
19
20
# File 'plugins/document_search/lib/document_search/commands.rb', line 18

def make_literal_query(query, options)
  make_regex_query(Regexp.escape(query), options)
end

- (Object) make_regex_query(query, options)

An instance of a search type method: Regular expression



13
14
15
# File 'plugins/document_search/lib/document_search/commands.rb', line 13

def make_regex_query(query, options)
  Regexp.new(query, !options.match_case)
end

- (Object) replace_selection_if_match(doc, start_pos, query, replace)

Replaces the current selection, if it matches the query completely.



101
102
103
104
105
106
107
108
109
110
111
# File 'plugins/document_search/lib/document_search/commands.rb', line 101

def replace_selection_if_match(doc, start_pos, query, replace)
  scanner = StringScanner.new(doc.selected_text)
  scanner.check(query)
  if (not scanner.matched?) || (scanner.matched_size != doc.selected_text.length)
    return 0
  end
  matched_text = doc.get_range(start_pos, scanner.matched_size)
  replacement_text = matched_text.gsub(query, replace)
  doc.replace(start_pos, scanner.matched_size, replacement_text)
  replacement_text.length
end

- (Object) select_next_match(doc, start_pos, query, wrap_around)

Selects the first match of query, starting from the start_pos.



41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
# File 'plugins/document_search/lib/document_search/commands.rb', line 41

def select_next_match(doc, start_pos, query, wrap_around)
  return false unless is_valid(query)
  scanner = StringScanner.new(doc.get_all_text)
  scanner.pos = start_pos
  if not scanner.scan_until(query)
    if not wrap_around
      return false
    end

    scanner.reset
    if not scanner.scan_until(query)
      return false
    end
  end

  selection_pos = scanner.pos - scanner.matched_size
  select_range_bytes(selection_pos, scanner.pos)
  true
end

- (Object) select_previous_match(doc, search_pos, query, wrap_around)

Selects the match that first precedes the search position.

The current implementation is brain-dead, but works: the document is scanned from the start up to the search position, retaining the most recent match. Many smarter, but more complicated strategies are possible; the best would be full reversal of the query regex, but that obviously has a lot of tricky aspects to it.



67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
# File 'plugins/document_search/lib/document_search/commands.rb', line 67

def select_previous_match(doc, search_pos, query, wrap_around)
  return false unless is_valid(query)
  previous_match = nil
  scanner = StringScanner.new(doc.get_all_text)
  scanner.pos = 0
  while scanner.scan_until(query)
    start_pos = scanner.pos - scanner.matched_size
    if start_pos < search_pos
      previous_match = [start_pos, scanner.pos]
    elsif previous_match
      select_range_bytes(*previous_match)
      return true
    elsif not wrap_around
      return false
    else
      break
    end
  end

  # Find the last match in the document.
  while scanner.scan_until(query)
    start_pos = scanner.pos - scanner.matched_size
    previous_match = [start_pos, scanner.pos]
  end

  if previous_match
    select_range_bytes(*previous_match)
    return true
  else
    return false
  end
end

- (Object) select_range(start, stop)

Selects the specified range and scrolls to the start.



114
115
116
117
118
119
120
121
122
123
124
125
# File 'plugins/document_search/lib/document_search/commands.rb', line 114

def select_range(start, stop)
  line     = doc.line_at_offset(start)
  lineoff  = start - doc.offset_at_line(line)
  if lineoff < doc.smallest_visible_horizontal_index
    horiz = lineoff
  else
    horiz = stop - doc.offset_at_line(line)
  end
  doc.set_selection_range(start, stop)
  doc.scroll_to_line(line)
  doc.scroll_to_horizontal_offset(horiz) if horiz
end

- (Object) select_range_bytes(start, stop)

Selects the specified byte range, mapping to character indices first.

This method is necessary, because Ruby (1.8) strings really work in terms of bytes, and thus our regex and scanning matches return byte ranges, while the editor view deals in terms of character ranges.



132
133
134
135
136
137
138
139
140
# File 'plugins/document_search/lib/document_search/commands.rb', line 132

def select_range_bytes(start, stop)
  text = doc.get_all_text
  # Unpack span up to start into array of Unicode chars and count for start_chars.
  start_chars = text.slice(0, start).unpack('U*').size
  # Do the same for the span between start and stop, and then use to compute stop_chars.
  char_span   = text.slice(start, stop - start).unpack('U*').size
  stop_chars  = start_chars + char_span
  select_range(start_chars, stop_chars)
end

- (Object) selection_byte_offsets

Returns the document selection range as byte offsets, adjusting for multi-byte characters.



25
26
27
28
29
30
31
32
33
34
35
36
37
38
# File 'plugins/document_search/lib/document_search/commands.rb', line 25

def selection_byte_offsets
  char_offsets = [doc.cursor_offset, doc.selection_offset]
  min_char_offset = char_offsets.min
  max_char_offset = char_offsets.max

  # For the min_byte_offset, get all document text before the selection, and count the bytes.
  min_byte_offset = doc.get_range(0, min_char_offset).size
  # If the selection is non-empty, count the bytes in the selection text, too.
  max_byte_offset = (min_byte_offset +
      (max_char_offset > min_char_offset ?
       doc.get_slice(min_char_offset, max_char_offset).size :
       0))
  [min_byte_offset, max_byte_offset]
end