Community
    • Login

    [vi simulator] how to highlight a word

    Scheduled Pinned Locked Moved Notepad++ & Plugin Development
    55 Posts 5 Posters 61.3k Views
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • dailD
      dail
      last edited by

      Is it possible to invoke the npp’s methods?

      In some cases yes but in this case no.

      I was thinking why I need to reinvent the wheel.

      Luckily it is a small wheel.

      Here is the Notepad++ code that does the smarthighlighting. Unfortunately it isn’t as simple as copy/paste.

      If you want to see a “leaner” version of how to do something similar to smarthighlighting take at this Lua code. This isn’t perfect but works under alot of circumstances. You can even incorporate some of the things Notepad++ is doing as well. I understand you are writing it in C but most of the lines of Lua translates into a single line of C.

      For example something like:

      editor.FirstVisibleLine
      

      Is equivalent to:

      SendMessage(nppData._scintillaMainHandle, SCI_GETFIRSTVISIBLELINE, 0, 0);
      

      The one caveat being editor:findtext() which you can take a look at SCI_FINDTEXT

      Scott SumnerS 1 Reply Last reply Reply Quote 0
      • bycn82 bbbbB
        bycn82 bbbb
        last edited by

        Anyway! I will just use the Ctrl+F3.
        Don’t have much time to work on a editor plugin.

        I use command line for most of time.

        1 Reply Last reply Reply Quote 0
        • Scott SumnerS
          Scott Sumner @dail
          last edited by

          @dail said:

          smarthighlighting take at this Lua code

          Hi dail, I took a look at the code found at the link you provided, and although I don’t use Luascript with Notepad++ I think I can follow the logic well-enough. Knowing Pythonscript and how the callback mechanism works there, it appears your intent in this code is to fire it off every time an “update UI” event is generated. The problem is (and again I’m thinking of my Pythonscript familiarity) I think that the code itself will cause more of those “update UI” events to be generated, thus spiraling somewhat out of control (at least while the caret is on a “word”), calling your callback many many times. Am I missing something here?

          dailD 1 Reply Last reply Reply Quote 1
          • dailD
            dail @Scott Sumner
            last edited by

            Hi @Scott-Sumner

            That is actually a really good question. I’ve always gone under the assumption that it doesn’t fire off another “updateUI” event.

            Looking at the Scintilla documentation for SCN_UPDATEUI states:

            Either the text or styling of the document has changed or the selection range or scroll position has changed.

            Note: “style” is separate from “indicators”

            There are separate notifications when indicators have changes (i.e. SCN_MODIFIED). If you take a look at the Notepad++ source code you will also see that it does the “highlighting” during the SCN_UPDATEUI event. See this line of code

            So it appears that doing this is safe.

            Scott SumnerS 1 Reply Last reply Reply Quote 1
            • Scott SumnerS
              Scott Sumner @dail
              last edited by

              @dail

              So for some fun I ported the code to Pythonscript on my lunch break. Due to limited time, I left out the callback stuff, and am just putting the caret on a word and running the script and seeing all occurrences in the currently visible window of the editor tab become highlighted. However, I have another script that tells me which callbacks are happening (I run that first). When I invoke the “highlight” script, I see MODIFIED callbacks occur with SC_PERFORMED_USER | SC_MOD_CHANGEINDICATOR flags set (which seems correct), but I also see UPDATEUI callbacks occurring as well, after the MODIFIED ones. Thus this leads me back to suspecting that it would just generate something of a big callback loop if actually set up as part of a UPDATEUI callback; I’ll try that out soon.

              Claudia FrankC 1 Reply Last reply Reply Quote 0
              • Claudia FrankC
                Claudia Frank @Scott Sumner
                last edited by

                @Scott-Sumner

                Hi Scott,

                dail is correct, as long as you take care that your callback function doesn’t generate ui updates it is save to use.
                I’m using it in my updated regextester script for some weeks now without a problem.

                Cheers
                Claudia

                1 Reply Last reply Reply Quote 0
                • Scott SumnerS
                  Scott Sumner
                  last edited by

                  So last night I actually tried it out with it installed as an update-ui callback. After installation, putting the caret inside a word results in the FIRST occurrence of that word flashing rapidly. It seems like what I feared would happen is actually happening–something in itself is triggering multiple re-calling of the callback, and it is clearing and setting the indicator over and over.

                  Maybe there is something wrong with my Pythonscript port of this code; it is short enough so I have included it below. Perhaps someone can see a deficiency? I see dail used “return False” a few places in the original code; I never heard of a callback returning a boolean; however I included it in the port (along with some “else” placements which would permit removing the return statements without affecting functionality).

                  Anyway, here’s the code:

                  INDICATOR_TO_USE = 12
                  editor1.indicSetStyle(INDICATOR_TO_USE, INDICATORSTYLE.ROUNDBOX)
                  editor2.indicSetStyle(INDICATOR_TO_USE, INDICATORSTYLE.ROUNDBOX)
                  editor1.indicSetAlpha(INDICATOR_TO_USE, 55)
                  editor2.indicSetAlpha(INDICATOR_TO_USE, 55)
                  editor1.indicSetOutlineAlpha(INDICATOR_TO_USE, 255)
                  editor2.indicSetOutlineAlpha(INDICATOR_TO_USE, 255)
                  
                  def callback_sci_UPDATEUI(args):
                  
                  	def getRangeOnScreen():
                  		firstLine = editor.getFirstVisibleLine()
                  		lastLine = firstLine + editor.linesOnScreen()
                  		startPos = editor.positionFromLine(firstLine)
                  		endPos = editor.getLineEndPosition(lastLine)
                  		return (startPos, endPos)
                  
                  	def clearIndicatorOnScreen():
                  		(s, e) = getRangeOnScreen()
                  		editor.indicatorClearRange(s, e - s)
                  
                  	editor.setIndicatorCurrent(INDICATOR_TO_USE)
                  
                  	if not editor.getSelectionEmpty():
                  		clearIndicatorOnScreen()
                  		return False
                  	else:
                  		startWord = editor.wordStartPosition(editor.getCurrentPos(), True)
                  		endWord = editor.wordEndPosition(startWord, True)
                  
                  		if startWord == endWord:
                  			clearIndicatorOnScreen()
                  			return False
                  		else:
                  			word = editor.getTextRange(startWord, endWord)
                  
                  			clearIndicatorOnScreen()
                  
                  			(startPos, endPos) = getRangeOnScreen()
                  			temp = editor.findText(FINDOPTION.WHOLEWORD | FINDOPTION.MATCHCASE, startPos, endPos, word)
                  			while temp != None:
                  				(s, e) = temp
                  				editor.indicatorFillRange(s, e - s)
                  				temp = editor.findText(FINDOPTION.WHOLEWORD | FINDOPTION.MATCHCASE, e, endPos, word)
                  
                  editor.callback(callback_sci_UPDATEUI, [SCINTILLANOTIFICATION.UPDATEUI])
                  
                  1 Reply Last reply Reply Quote 0
                  • dailD
                    dail
                    last edited by

                    This is some interesting results. If I get some time I will play around with it as well with Lua. It is odd you are getting this behavior. The example Lua I posted I’ve been using for months just fine. It might be worth looking a bit more into the Notepad++ “smarthilighter” since it does pretty much the same thing but works fine.

                    I see dail used “return False” a few places in the original code

                    This is purely a LuaScript thing. (It doesn’t actually do anything but I recommend returning false from a LuaScript callback for forwards-compatibility reasons).

                    There is one other caveat to keep in mind. PythonScript callbacks are asynchronous (whereas LuaScript callbacks are purely synchronous). Not sure if this is affecting anything but definitely worth keeping in mind.

                    1 Reply Last reply Reply Quote 0
                    • Scott SumnerS
                      Scott Sumner
                      last edited by

                      The Pythonscript docs say that I can use editor.callbackSync() to make it synchronous, but then it goes on to say that if I do that, I can’t call editor.findText(), which this script uses. :(

                      Claudia FrankC 1 Reply Last reply Reply Quote 0
                      • Claudia FrankC
                        Claudia Frank @Scott Sumner
                        last edited by Claudia Frank

                        Hello Scott,

                        you are right, using asynchronous callback leads to the update flickering.
                        I’m not quite sure why this happens.
                        I’m using the synchronous callback together with the research function and this
                        seems to work well. Your code would look like

                        INDICATOR_TO_USE = 12
                        editor1.indicSetStyle(INDICATOR_TO_USE, INDICATORSTYLE.ROUNDBOX)
                        editor2.indicSetStyle(INDICATOR_TO_USE, INDICATORSTYLE.ROUNDBOX)
                        editor1.indicSetAlpha(INDICATOR_TO_USE, 55)
                        editor2.indicSetAlpha(INDICATOR_TO_USE, 55)
                        editor1.indicSetOutlineAlpha(INDICATOR_TO_USE, 255)
                        editor2.indicSetOutlineAlpha(INDICATOR_TO_USE, 255)
                        
                        import re
                        
                        def callback_sci_UPDATEUI(args):
                            print 'callback_sci_UPDATEUI'
                            
                            def match_found(m):
                                editor.setIndicatorCurrent(INDICATOR_TO_USE)
                                editor.indicatorFillRange(m.span(0)[0], m.span(0)[1] - m.span(0)[0])
                            
                            def getRangeOnScreen():
                                print 'getRangeOnScreen'
                                firstLine = editor.getFirstVisibleLine()
                                lastLine = firstLine + editor.linesOnScreen()
                                startPos = editor.positionFromLine(firstLine)
                                endPos = editor.getLineEndPosition(lastLine)
                                return (startPos, endPos)
                        
                            def clearIndicatorOnScreen():
                                print 'clearIndicatorOnScreen'
                                (s, e) = getRangeOnScreen()
                                editor.indicatorClearRange(s, e - s)
                        
                            editor.setIndicatorCurrent(INDICATOR_TO_USE)
                        
                            if not editor.getSelectionEmpty():
                                clearIndicatorOnScreen()
                                return False
                            else:
                                startWord = editor.wordStartPosition(editor.getCurrentPos(), True)
                                endWord = editor.wordEndPosition(startWord, True)
                        
                                if startWord == endWord:
                                    clearIndicatorOnScreen()
                                    return False
                                else:
                                    word = editor.getTextRange(startWord, endWord)
                                    print 'word:{}'.format(word)
                                    clearIndicatorOnScreen()
                        
                                    (startPos, endPos) = getRangeOnScreen()
                                    # temp = editor.findText(FINDOPTION.WHOLEWORD | FINDOPTION.MATCHCASE, startPos, endPos, word)
                                    editor.research(word, match_found, re.IGNORECASE) 
                                    # while temp != None:
                                        # (s, e) = temp
                                        # editor.indicatorFillRange(s, e - s)
                                        # temp = editor.findText(FINDOPTION.WHOLEWORD | FINDOPTION.MATCHCASE, e, endPos, word)
                        
                        editor.callbackSync(callback_sci_UPDATEUI, [SCINTILLANOTIFICATION.UPDATEUI])
                        

                        I know you could have done this yourself but thought …

                        Cheers
                        Claudia

                        1 Reply Last reply Reply Quote 1
                        • dailD
                          dail
                          last edited by

                          I’m not familiar enough with the internal Scintilla code but I think it is making sure it doesn’t get stuck in one of these types of loops. I added a bit of extra code to my plugin to log exactly when notifications are getting received. It shows when it enters and leaves the notifications. As shown below, it enters the SCN_UPDATEUI, at which point it calls my Lua callback which adds 3 indicators, thus the 3 SCN_MODIFIED pairs, then leaves the SCN_UPDATEUI notification.

                          ->SCN_UPDATEUI
                          ->SCN_MODIFIED
                          <-SCN_MODIFIED
                          ->SCN_MODIFIED
                          <-SCN_MODIFIED
                          ->SCN_MODIFIED
                          <-SCN_MODIFIED
                          <-SCN_UPDATEUI
                          

                          I would say between this and the fact that @Claudia-Frank successfully used the synchronous callbacks, means that your code was receiving these notifications out of order due to the asynchronous callbacks (which is a problem I ran into with the PythonScript a while ago).

                          1 Reply Last reply Reply Quote 0
                          • Scott SumnerS
                            Scott Sumner
                            last edited by

                            Claudia and dail,

                            Thanks for the follow-up on this. This started out as just a curiosity to me, but in the end I learned a lot and I liked the functionality of this little script so much that I integrated it into my startup.py so that it is a permanent part of my Notepad++. I even tweaked it so that the highlight at the caret uses a second indicator to give it more emphasis than the other matches within the viewing window. Thanks again.

                            1 Reply Last reply Reply Quote 1
                            • Scott SumnerS
                              Scott Sumner
                              last edited by

                              After using what I came up with for a while, I happened to notice that when the current document also exists as clone (that is, is in both views), when the caret is on a word the UPDATEUI callback fires continually. The intended highlighting works, but with the callback firing continually, this is not good. I suspected this when the editing tabs in both views disappeared. I put in some “I’m here” output to the pythonscript console window at the top of the callback, and suspicions were confirmed.

                              What I’m unsure of is how to fix it. I went back to @Claudia-Frank 's original script above and made sure the same problem exists there and that it wasn’t something I introduced. Claudia or @dail , any pointers in the right direction for a fix would be appreciated.

                              dailD Claudia FrankC 2 Replies Last reply Reply Quote 0
                              • dailD
                                dail @Scott Sumner
                                last edited by

                                @Scott-Sumner

                                I had never noticed that before (mostly because I rarely clone documents) but I am seeing the same thing when using Lua. I already tried a few things to fix it but did not have any luck.

                                1 Reply Last reply Reply Quote 0
                                • Claudia FrankC
                                  Claudia Frank @Scott Sumner
                                  last edited by

                                  @Scott-Sumner

                                  Hi Scott,

                                  not at home, so just a quick info.
                                  Afaik, cloned documents are just two references to the same object
                                  therefore your experience can be explained. What I would try to do
                                  is to see if it can be made unique again like checking if
                                  file, which triggers the coloring, is different from the cloned one.
                                  Like current document call, it gives back bufferid, view and file position.
                                  Maybe create an identifier out of it and check this before callback gets executed.

                                  I will give it a try, once I’m at home. ~ 5-6 hours

                                  Cheers
                                  Claudia

                                  Scott SumnerS 1 Reply Last reply Reply Quote 0
                                  • Scott SumnerS
                                    Scott Sumner @Claudia Frank
                                    last edited by

                                    @Claudia-Frank

                                    You’ve inspired me to try out some things which ended up a real “hack”. Basically I need to stop the endless chain of calls, so I measured the time gap between them (typically 4-5 milliseconds). Then I added in some code such that if an UPDATEUI is triggered within 10ms of the previous one, the new one does an immediate return, thus preventing the infinite re-triggering. It seems to work, but if you come up with something more elegant, I’d like to see it. :)

                                    Claudia FrankC 1 Reply Last reply Reply Quote 0
                                    • Claudia FrankC
                                      Claudia Frank @Scott Sumner
                                      last edited by Claudia Frank

                                      @Scott-Sumner

                                      Hi Scott,

                                      after analyzing the issue I think the solution is quite simple.
                                      Note, I’m still using linux and wine - I hope it acts the same
                                      as with real windows.

                                      From what I see, if you have an cloned document you get additional
                                      updateui calls, but all with update flag 0x1 and I assume those can be
                                      safely ignored. See list of available messages and meaning.

                                      SC_UPDATE_CONTENT       0x01 	Contents, styling or markers have been changed.
                                      SC_UPDATE_SELECTION 	0x02 	Selection has been changed.
                                      SC_UPDATE_V_SCROLL      0x04 	Scrolled vertically.
                                      SC_UPDATE_H_SCROLL      0x08 	Scrolled horizontally.
                                      

                                      So the solution I tried is

                                      def updateui_callback(args):
                                          if args['updated'] == 2:
                                              ... do your stuff ...
                                      

                                      seems to work. What do you think? Is it elegant ;-)

                                      Cheers
                                      Claudia

                                      dailD 1 Reply Last reply Reply Quote 0
                                      • dailD
                                        dail @Claudia Frank
                                        last edited by

                                        @Claudia-Frank

                                        That was the first thing I tried, however there are times when you need to use SC_UPDATE_CONTENT. For example putting the cursor in the middle of a word and pressing space will not re-adjust the highlighting of the word.

                                        Claudia FrankC 1 Reply Last reply Reply Quote 0
                                        • Claudia FrankC
                                          Claudia Frank @dail
                                          last edited by

                                          @dail

                                          Hi dail, I see what you mean but I thought for Scotts purpose it might be a possible solution.
                                          Digging deeper it seems to be a bug - but unsure what caused it (npp, scintilla).
                                          When setting an identifier we could see, that notification are sent from the editor which has the
                                          cursor (formerly I thought it is some kind of circular reference).
                                          Another workaround, which keeps the SC_UPDATE_CONTENT functionality is to record
                                          the last modification type and compare it against the updated value. But this, of course, would
                                          mean that we can trust the ordering of the incoming notifications. From my limited tests I’ve
                                          done, it seems that it could work.

                                          So, for python, I did

                                          last_mod_type = -1
                                          
                                          def callback_sci_MODIFIED(args):
                                              global last_mod_type
                                              last_mod_type = args['modificationType']
                                          

                                          and within updateui callback a check
                                          to see if these nonsense notifications are receeived. If so, skip it.

                                          def callback_sci_UPDATEUI(args):
                                              if args['updated'] == 1 and last_mod_type == 16400:
                                                  return
                                          

                                          What do you think?

                                          Cheers
                                          Claudia

                                          1 Reply Last reply Reply Quote 0
                                          • Scott SumnerS
                                            Scott Sumner
                                            last edited by

                                            @Claudia-Frank , @dail ,

                                            I’ve been continuing to work this as I have time, just for “fun” but I admit it is turning away from the fun side as I discover more. Claudia’s latest fix suggestion seems to work; so it’s not that. I’ve noticed some other things which cast doubt upon the whole base concept; that being to only highlight what the user can see on screen in an editor tab.

                                            The editor.getFirstVisibleLine() and/or editor.linesOnScreen() don’t seem to tell the whole story as to what is visible. The following at a minimum seem to mess with the accuracy of the return values of those functions: “Wrap” enabled, “Folding” in the folded-state, “Hide Lines” with lines hidden. The new “scroll beyond EOF” feature in 7.x, when enabled, causes a minor problem, but can be compensated for. But I’ve found no way to get correct screen line/position ranges with the wrap/folding/hidden features “on”. Maybe I’m missing something?

                                            1 Reply Last reply Reply Quote 0
                                            • First post
                                              Last post
                                            The Community of users of the Notepad++ text editor.
                                            Powered by NodeBB | Contributors