aboutsummaryrefslogtreecommitdiff
path: root/core/ext/find.lua
blob: 8ff5236dca75757113ba7c38c2d696d4215dd090 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
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
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
-- Copyright 2007 Mitchell mitchell<att>caladbolg.net. See LICENSE.

local find = textadept.find

---
-- [Local table] Text escape sequences with their associated characters.
-- @class table
-- @name escapes
local escapes = {
  ['\\a'] = '\a', ['\\b'] = '\b', ['\\f'] = '\f', ['\\n'] = '\n',
  ['\\r'] = '\r', ['\\t'] = '\t', ['\\v'] = '\v', ['\\\\'] = '\\'
}

---
-- Finds and selects text in the current buffer.
-- This is used by the find dialog. It is recommended to use the buffer:find()
-- function for scripting.
-- @param text The text to find.
-- @param flags Search flags. This is a number mask of 3 flags: match case (2),
--   whole word (4), and Lua pattern (8) joined with binary AND.
-- @param next Flag indicating whether or not the search direction is forward.
-- @param nowrap Flag indicating whether or not the search won't wrap.
-- @param wrapped Utility flag indicating whether or not the search has wrapped
--   for displaying useful statusbar information. This flag is used and set
--   internally, and should not be set otherwise.
function find.find(text, flags, next, nowrap, wrapped)
  local buffer = buffer
  local increment, result
  text = text:gsub('\\[abfnrtv\\]', escapes)
  find.captures = nil
  if buffer.current_pos == buffer.anchor then
    increment = 0
  elseif not wrapped then
    increment = next and 1 or -1
  end
  if flags < 8 then
    if next then
      buffer:goto_pos(buffer.current_pos + increment)
      buffer:search_anchor()
      result = buffer:search_next(flags, text)
    else
      buffer:goto_pos(buffer.anchor - increment)
      buffer:search_anchor()
      result = buffer:search_prev(flags, text)
    end
    if result then buffer:scroll_caret() end
  else -- lua pattern search
    local buffer_text = buffer:get_text(buffer.length)
    local results = { buffer_text:find(text, buffer.anchor + increment) }
    if #results > 0 then
      result = results[1]
      find.captures = { unpack(results, 3) }
      buffer:set_sel(results[2], result - 1)
    else
      result = -1
    end
  end
  if result == -1 and not nowrap and not wrapped then -- wrap the search
    local anchor, pos = buffer.anchor, buffer.current_pos
    if next or flags >= 8 then
      buffer:goto_pos(0)
    else
      buffer:goto_pos(buffer.length)
    end
    textadept.statusbar_text = 'Search wrapped'
    result = find.find(text, flags, next, true, true)
    if not result then
      textadept.statusbar_text = 'No results found'
      buffer:goto_pos(anchor)
    end
    return result
  elseif result ~= -1 and not wrapped then
    textadept.statusbar_text = ''
  end
  return result ~= -1
end

---
-- Replaces found text.
-- This function is used by the find dialog. It is not recommended to call it
-- via scripts.
-- textadept.find.find is called first, to select any found text. The selected
-- text is then replaced by the specified replacement text.
-- @param rtext The text to replace found text with. It can contain Lua escape
--   sequences to use text captured by a Lua pattern. (%n where 1 <= n <= 9.)
function find.replace(rtext)
  if #buffer:get_sel_text() == 0 then return end
  local buffer = buffer
  buffer:target_from_selection()
  if find.captures then
    for i, v in ipairs(find.captures) do
      rtext = rtext:gsub('[^%%]?[^%%]?%%'..i, v) -- not entirely correct
    end
  end
  buffer:replace_target( rtext:gsub('\\[abfnrtv\\]', escapes) )
end

---
-- Replaces all found text.
-- This function is used by the find dialog. It is not recommended to call it
-- via scripts.
-- @param ftext The text to find.
-- @param rtext The text to replace found text with.
-- @param flags The number mask identical to the one in 'find'.
-- @see find.find
function find.replace_all(ftext, rtext, flags)
  buffer:goto_pos(0)
  local count = 0
  while( find.find(ftext, flags, true, true) ) do
    find.replace(rtext)
    count = count + 1
  end
  textadept.statusbar_text = tostring(count)..' replacement(s) made'
end