Find and Add To Selection In 7.7



  • @Ekopalypse

    Thank you, that helped! I’ve used the NppExecplugin and the sequence:

    1. Select a word.
    2. Send SCI_TARGETWHOLEDOCUMENT (message code 2690) to set Scintilla’s target.
    3. Send SCI_MULTIPLESELECTADDEACH (message code 2689) to multi-edit all instances of the word.


  • @dinkumoil

    if different findoption should be used you can add a step 2a. SCI_SETSEARCHFLAGS



  • @Ekopalypse

    Thanks for that additional hint, much appreciated!

    Here is my complete NppExec script:

    sci_sendmsg SCI_GETSELECTIONEMPTY
    
    if $(MSG_RESULT) == 1 then
      sci_sendmsg SCI_WORDRIGHTEXTEND
    endif
    
    sci_sendmsg SCI_SETSEARCHFLAGS SCFIND_WHOLEWORD 
    
    sci_sendmsg 2690
    sci_sendmsg 2689
    

    It automatically selects the word right from the cursor but only if there is no active selection.



  • @dinkumoil - nice one, thanks for sharing.
    One question, as I don’t have NppExec currently installed,
    is it really needed to do SCI_WORDRIGHTEXTEND?

    From the docs it states If the current selection is empty then select word around caret



  • @Ekopalypse said:

    is it really needed to do SCI_WORDRIGHTEXTEND?

    According to my tests - yes.

    If the SCI_WORDRIGHTEXTEND part is removed from the script, executing it (without an active selection) selects the word right from the cursor and marks all other instances of the word in the whole text but there are no multiple cursors.



  • @dinkumoil - thanks for clarifying.



  • It automatically selects the word right from the cursor but only if there is no active selection.

    I tried the NppExec script and I found that if I run it without a selection but with the caret inside or at the start of a word, it will select from the caret to the right to just before the start of the next word, and then fail to give multiple carets; example with caret sitting on second s of highlighted sci_sendmsg:

    Imgur

    Starting with an entire word preselected (here the 2nd sci_sendmsg in the file) however works just fine:

    Imgur

    Perhaps I miss something about the case where there is no selected text first…



  • @all

    Improved version of my NppExec script:

    sci_sendmsg SCI_GETSELECTIONEMPTY
    
    if $(MSG_RESULT) == 1 then
      sci_sendmsg SCI_GETCURRENTPOS
      set local $(CurPos) ~ $(MSG_RESULT)
    
      sci_sendmsg SCI_WORDSTARTPOSITION $(CurPos) 1
    
      if $(MSG_RESULT) != $(CurPos) then
        sci_sendmsg SCI_WORDLEFT
      endif
    
      sci_sendmsg SCI_WORDRIGHTEXTEND
    endif
    
    sci_sendmsg SCI_SETSEARCHFLAGS SCFIND_WHOLEWORD 
    
    sci_sendmsg 2690
    sci_sendmsg 2689
    

    If the cursor is placed in the middle of a word without an active selection when executing the script, it is moved to the next word boundary to its left before selecting the word to the right of its new position, i.e. the word in whose mid it was placed before.

    @Alan-Kilborn
    Oh, I’ve posted before I could even see your posting…



  • @dinkumoil said:

    Oh, I’ve posted before I could even see your posting…

    I tried the NppExec script just above and had the same result as the earlier NppExec script when no selection before running…



  • @Alan-Kilborn said:

    I tried the NppExec script just above and had the same result as the earlier NppExec script when no selection before running…

    It works at my site. Did you restart Notepad++ after updating the script?



  • @dinkumoil

    Did you restart Notepad++ after updating the script?

    No, but I didn’t “save” it, I was just running it as a NppExec temporary script…



  • Totally just for fun, I managed to replicate the functionality with Pythonscript in its current (1.4) form:

    import ctypes
    from ctypes.wintypes import HWND
    from ctypes import byref, wintypes, Structure, sizeof
    
    def get_focused_window():
        class GUITHREADINFO(Structure):
            _fields_ = [
                ("cbSize", wintypes.DWORD),
                ("flags", wintypes.DWORD),
                ("hwndActive", wintypes.HWND),
                ("hwndFocus", wintypes.HWND),  # <--- what we really want
                ("hwndCapture", wintypes.HWND),
                ("hwndMenuOwner", wintypes.HWND),
                ("hwndMoveSize", wintypes.HWND),
                ("hwndCaret", wintypes.HWND),
                ("rcCaret", wintypes.RECT)
                ]
        guiThreadInfo = GUITHREADINFO(cbSize=sizeof(GUITHREADINFO))
        ctypes.windll.user32.GetGUIThreadInfo(0, byref(guiThreadInfo))
        return guiThreadInfo.hwndFocus
    
    current_view_hwnd = get_focused_window()
    
    SciLexer = ctypes.WinDLL('SciLexer.dll', use_last_error = True)
    Scintilla_DirectFunction = SciLexer.Scintilla_DirectFunction
    direct_pointer = SendMessage(current_view_hwnd, 2185, 0, 0)
    
    editor.setSearchFlags(FINDOPTION.WHOLEWORD | FINDOPTION.MATCHCASE)
    
    if editor.getSelectionEmpty():
        start_of_word_pos = editor.wordStartPosition(editor.getCurrentPos(), True)
        end_of_word_pos = editor.wordEndPosition(start_of_word_pos, True)
        if start_of_word_pos != end_of_word_pos:
            editor.setSelection(end_of_word_pos, start_of_word_pos)
    
    Scintilla_DirectFunction(direct_pointer, 2689, 1, 0)


  • Hmm, I found a strange pitfall with my code.

    I use for testing the same snippet of source code @dail used in his posting in the other thread:

    static std::string getWordAt(GUI::ScintillaWindow *window, int pos) {
      int word_start = window->Call(SCI_WORDSTARTPOSITION, pos, true);
      int word_end = window->Call(SCI_WORDENDPOSITION, pos, true);
      return getRange(window, word_start, word_end);
    }
    

    If I place the cursor before the word pos near the end of line 1 and press my keyboard shortcut everything works fine. If I do the same with placeing it before the word int near the end of line 1, the script selects the word int and the following space character and nothing more happens. It looks like the sci_sendmsg SCI_WORDRIGHTEXTEND gives a wrong result.

    I will try to figure out what’s going on…

    EDIT: It’s because int is followed by a space character and pos by opening parenthesis. This would make this feature nearly useless. Gonna do further investigations…



  • @dinkumoil

    Yea, if you’ll take note, I had an intentional big amount of spaces to the right of the word I had my caret in for my testing. :)



  • @Alan-Kilborn

    Does your Python script work as intended or is it a problem with the algorithm used?



  • @dinkumoil

    Not sure if I understand the q… The PS doesn’t use the “word-right-extend” stuff, which seems to be where the trouble lies with the NppExec scripts presented to this point…

    I didn’t do exhaustive testing on the PS, but yea, seems to work as intended.



  • Got it. SCI_WORDRIGHTEXTEND has to be replaced by SCI_WORDRIGHTENDEXTEND - of course. :(

    Here is the updated (and working) version of the script:

    sci_sendmsg SCI_GETSELECTIONEMPTY
    
    if $(MSG_RESULT) == 1 then
      sci_sendmsg SCI_GETCURRENTPOS
      set local $(CurPos) ~ $(MSG_RESULT)
    
      sci_sendmsg SCI_WORDSTARTPOSITION $(CurPos) 1
    
      if $(MSG_RESULT) != $(CurPos) then
        sci_sendmsg SCI_WORDLEFT
      endif
    
      sci_sendmsg SCI_WORDRIGHTENDEXTEND
    endif
    
    sci_sendmsg SCI_SETSEARCHFLAGS SCFIND_WHOLEWORD 
    
    sci_sendmsg 2690
    sci_sendmsg 2689
    


  • @dinkumoil said:

    (and working) version of the script:

    CONFIRMED! :)



  • @cipher-1024 said:

    I was hoping to easily go one ‘find’ at a time

    I guess we ignored that part of the request. :)



  • Quite a while ago (before Notepad++ updated Scintilla) I found the “find and add next” functionality very useful in other editors and knew that Scintilla supported it in newer versions. I dug through the Scintilla source code and translated it to this LuaScript code:

    npp.AddShortcut("Selection Add Next", "Ctrl+D", function()
        -- From SciTEBase.cxx
        local flags = SCFIND_MATCHCASE -- can use 0
        editor:SetTargetRange(0, editor.TextLength)
        editor.SearchFlags = flags
    
        -- From Editor.cxx
        if editor.SelectionEmpty or not editor.MultipleSelection then
            local startWord = editor:WordStartPosition(editor.CurrentPos, true)
            local endWord = editor:WordEndPosition(startWord, true)
            editor:SetSelection(startWord, endWord)
        else
            local i = editor.MainSelection
            local s = editor:textrange(editor.SelectionNStart[i], editor.SelectionNEnd[i])
            local searchRanges = {{editor.SelectionNEnd[i], editor.TargetEnd}, {editor.TargetStart, editor.SelectionNStart[i]}}
            for _, range in pairs(searchRanges) do
                editor:SetTargetRange(range[1], range[2])
                if editor:SearchInTarget(s) ~= -1 then
                    editor:AddSelection(editor.TargetStart, editor.TargetEnd)
                    editor:ScrollRange(editor.TargetStart, editor.TargetEnd)
                    break
                end
            end
        end
    end)
    

    In theory this should be the exact functionality (well, very close at least) that SCI_MULTIPLESELECTADDNEXT provides. Should be translatable to PythonScript or NppExec as this above code was working with Scintilla v3.5.6


Log in to reply