Re: [code] Sometimes replacing in textadept is too slow

From: Alexander Misel <alexander_misel.att.live.cn>
Date: Sat, 14 Sep 2019 15:08:46 +0000

I made it faster by caching all matches in a line before replacing. This solves the problem for recalculating text positions many times. Regex replacement is also supported. The speed is acceptable, and the code is longer. With function bg_find the same as before.
local function replace_all(ftext, rtext)
  if ftext == '' then return end

  -- init flags
  local flags = 0
  if M.match_case then flags = flags + buffer.FIND_MATCHCASE end
  if M.whole_word then flags = flags + buffer.FIND_WHOLEWORD end
  local bg_replace
  local replace_cache = {
    line_num = -1,
    list = {}
  }
  if M.regex then
    flags = flags + buffer.FIND_REGEXP
    rtext = rtext:gsub('%f[\\]\\u(%x%x%x%x)', function(code)
      return utf8.char(tonumber(code, 16))
    end)
    bg_replace = function(rtext)
      local count = #replace_cache.list
      if count == 0 then return end
      local new_str = replace_cache.list[1].rtext
      for i = 2, count do
        buffer:set_target_range(replace_cache.list[i-1].final, replace_cache.list[i].start)
        new_str = new_str .. buffer.target_text .. replace_cache.list[i].rtext
      end
      buffer:set_target_range(replace_cache.list[1].start, replace_cache.list[count].final)
      buffer:replace_target(new_str)
    end
  else
    bg_replace = function()
      local count = #replace_cache.list
      if count == 0 then return end
      local new_str = rtext
      for i = 2, count do
        buffer:set_target_range(replace_cache.list[i-1].final, replace_cache.list[i].start)
        new_str = new_str .. buffer.target_text .. rtext
      end
      buffer:set_target_range(replace_cache.list[1].start, replace_cache.list[count].final)
      buffer:replace_target(new_str)
    end
  end
  buffer.search_flags = flags

  local count = 0
  -- common code in replace
  local replace_helper = function (pos)
    local new_match = {
      start = buffer.target_start,
      final = buffer.target_end
    }
    if M.regex then
      new_match.rtext = rtext:gsub('%f[\\]\\(%d%d?)', function(code)
        return buffer.tag[tonumber(code)]
      end)
    end
    local line_num = buffer:line_from_position(pos)
    if replace_cache.line_num >= 0 and line_num ~= replace_cache.line_num then
      bg_replace()
    end
    table.insert(replace_cache.list, new_match)
    count = count + 1
    return new_match.final
  end

  -- start replace action
  buffer:begin_undo_action()

  if buffer.selection_empty then
    buffer:target_whole_document()
    local s = 0
    local pos = bg_find(ftext)
    while pos ~= -1 do
      s = replace_helper(pos)
      buffer:set_target_range(s, buffer.length)
      pos = bg_find(ftext)
    end
    bg_replace()
  else
    local s, e = buffer.selection_start, buffer.selection_end
    buffer.indicator_current = INDIC_REPLACE
    buffer:indicator_fill_range(e, 1)
    local EOF = buffer.selection_end == buffer.length -- no indic at EOF
    buffer:set_target_range(s, e)
    local pos = bg_find(ftext)
    while pos ~= -1 and (EOF or pos < e) do
      s = replace_helper(pos)
      if EOF then
        buffer:set_target_range(s, buffer.length)
      else
        e = buffer:indicator_end(INDIC_REPLACE, s)
        buffer:set_target_range(s, e)
      end
      pos = bg_find(ftext)
    end
    bg_replace()
    e = buffer:indicator_end(INDIC_REPLACE, s)
    buffer:indicator_clear_range(e, 1)
  end
  ui.statusbar_text = string.format('%d %s', count, _L['replacement(s) made'])
  buffer:end_undo_action()
end

________________________________
From: Mitchell <m.att.foicica.com>
Sent: Tuesday, September 10, 2019 3:46
To: code.att.foicica.com <code.att.foicica.com>
Subject: Re: [code] Sometimes replacing in textadept is too slow

Hi Alexander,

On Sat, 7 Sep 2019, Alexander Misel wrote:

> I just worked out a slightly faster implementation that could finish replacing in a few seconds (it the long line is not wrapped). Maybe it could be improved.
> [snip]

I'll look into this when I have some time, but it looks to be sourced from existing code that I wouldn't want to duplicate in two places in *modules/textadept/find.lua*. If you really need the speed, you can

   events.connect(events.REPLACE_ALL, your_replace_all, 1)

and have your `your_relace_all()` function return `true` in order to prevent Textadept's default `replace_all` from running.

Cheers,
Mitchell

--
You are subscribed to code.att.foicica.com.
To change subscription settings, send an e-mail to code+help.att.foicica.com.
To unsubscribe, send an e-mail to code+unsubscribe.att.foicica.com.
-- 
You are subscribed to code.att.foicica.com.
To change subscription settings, send an e-mail to code+help.att.foicica.com.
To unsubscribe, send an e-mail to code+unsubscribe.att.foicica.com.
Received on Sat 14 Sep 2019 - 11:08:46 EDT

This archive was generated by hypermail 2.2.0 : Sun 15 Sep 2019 - 06:37:14 EDT