orbitalquark / textadept

Textadept is a fast, minimalist, and remarkably extensible cross-platform text editor for programmers.
https://orbitalquark.github.io/textadept
MIT License
653 stars 39 forks source link

Word auto-complete to ignore comments #285

Closed eric-hemasystems closed 2 years ago

eric-hemasystems commented 2 years ago

I wanted to try to improve the "word" auto-complete so that it doesn't include words that are in comments. I think most of the time when you want auto-complete you are wanting code identifiers but the fact that words in comments are in the completion list adds a good bit of noise.

I think I would add it as an additional condition to this conditional. Even did a bit of a monkey-patch in my local configuration to test out the idea:

textadept.editing.autocompleters.word = function()
  local list, matches = {}, {}
  local s = buffer:word_start_position(buffer.current_pos, true)
  if s == buffer.current_pos then return end
  local word_part = buffer:text_range(s, buffer.current_pos)
  for _, buffer in ipairs(_BUFFERS) do
    if buffer == _G.buffer or textadept.editing.autocomplete_all_words then
      buffer.search_flags = buffer.FIND_WORDSTART |
        (not buffer.auto_c_ignore_case and buffer.FIND_MATCHCASE or 0)
      buffer:target_whole_document()
      while buffer:search_in_target(word_part) ~= -1 do
        local e = buffer:word_end_position(buffer.target_end, true)
        local style = buffer:name_of_style(buffer.style_at[buffer.target_start])
        local match = buffer:text_range(buffer.target_start, e)
        if style ~= 'comment' and #match > #word_part and not matches[match] then
          list[#list + 1], matches[match] = match, true
        end
        buffer:set_target_range(e, buffer.length + 1)
      end
    end
  end
  return #word_part, list
end

This is exactly the stock auto-completer only that extra style check is in place. I dislike monkey-patches due to their fragility and maintenance hassles so wanted to see if there was a way to upstream this. I'm happy to put together a PR to make this happen but wanted to see if you were open to the idea and if you had a preferred approach. With regard to approach I see a few options:

  1. Incorporate what I did in my monkey-patch into the stock code.
  2. Add an option for this. This would allow folks to opt-into this behavior. The option might simply a simple boolean (yes or not to include comments in word searches). Or it could be something more flexible like a list of styles to ignore when doing a word auto-complete (and I would make put comment in my configuration).
  3. Provide an option for a callback so the callback can inject whatever conditional logic it wants.

With that last option there might be a dummy filter on textadept.editing.autocompleters.word_filter that has the following function signature:

function(buffer, match, target_start, target_end)

By default this option would be a function that just unconditionally return true. But for my own config I could do the following to override that:

textadept.editing.autocompleters.word_filter = function(buffer, match, target_start, target_end)
  local style = buffer:name_of_style(buffer.style_at[target_start])
  return style ~= 'comment'
end

Let me know if any of this is any interest and I can work up a PR. If not I'll just keep my monkey-patch and close the issue. Thanks!

orbitalquark commented 2 years ago

I personally use word completion from comments all the time because I write lots of documentation. Searching buffers for words is already relatively expensive, and looking up the name of a style is even more so. Additional checks against a table of styles is something I'd prefer to avoid by default, and the editing module is already pretty populated with options. The autocompleter framework is quite modular, so I think I would actually recommend just changing the textadept.editing.autocompleters.word function from your ~/.textadept/init.lua. Whenever Textadept tries to perform 'word' autocompletion, it will call your completer with its optional check for styles. (You can even simplify the autocompleter function by removing the 'autocomplete_all_words' option if you don't care about it.) Furthermore, I don't think that code has changed in years, so you wouldn't be missing out on anything when updating Textadept.

Would that be okay for you?

eric-hemasystems commented 2 years ago

The autocompleter framework is quite modular, so I think I would actually recommend just changing the textadept.editing.autocompleters.word function from your ~/.textadept/init.lua.

Yes, that is what I did with the above monkey-patch. Sounds like you don't want the extra configuration which is understandable so will just occasionally check on the word function to see if any updates need to be incorporated into my version.

Just FYI, I don't notice any performance issue with that style lookup. Looks like you have this thing so blazing fast even if I do things not recommended it's still works great!

Since I have you on the subject of auto-complete, I like to auto-complete both by word and snippet (and maybe other things in the future). I didn't see any way to support multiple auto-complete lists at the same time so wrote my own function (below for reference) but wanted to confirm I just didn't miss something obvious.

-- Operates like (and based on) the standard autocomplete list but instead can
-- accept multiple autocomplete functions that will be combined together.
local function multi_autocomplete(...)
  local list = {}
  local len_entered

  for _, name in ipairs({...}) do
    if not textadept.editing.autocompleters[assert_type(name, 'string', 1)] then goto continue end
    local len, cur_list = textadept.editing.autocompleters[name]()
    len_entered = len
    table.move(cur_list, 1, #cur_list, #list + 1, list)
    ::continue::
  end

  if not len_entered or not list or #list == 0 then return end
  buffer.auto_c_order = buffer.ORDER_PERFORMSORT
  buffer:auto_c_show(len_entered, table.concat(list, string.char(buffer.auto_c_separator)))
  return true
end
orbitalquark commented 2 years ago

That looks fine to me. The only concern might be if the autocompleters return mis-matched len results. I think all of the autocompleters (perhaps with the exeption of CSS) operate on the same set of word characters, so this probably won't be an issue, but it might be something to keep in mind.