Community
    • Login

    Persistent highlight of characters (e.g. no-break space)

    Scheduled Pinned Locked Moved Help wanted · · · – – – · · ·
    14 Posts 3 Posters 1.2k 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.
    • EkopalypseE
      Ekopalypse
      last edited by

      I guess we can make a script which might do what you want.
      So am I right you want to have

      • dynamic marking on every document
      • bookmarking the line with the same marks as npp uses to be able to use F2 to jump to it

      To be honest, I never tried to use two indicators on the same character,
      so I’m not 100% sure we can use the INDIC_ROUNDBOX and the INDIC_SQUIGGLE together.

      eugenesvkE 1 Reply Last reply Reply Quote 2
      • EkopalypseE
        Ekopalypse @eugenesvk
        last edited by

        @eugenesvk

        just a test to see if two indicators can be used together.
        YES we can.

        038af67a-24d9-42fd-8da3-169c280dc2f2-image.png

        1 Reply Last reply Reply Quote 3
        • eugenesvkE
          eugenesvk @Ekopalypse
          last edited by

          Thank you for a prompt response!

          @Ekopalypse said in Persistent highlight of characters (e.g. no-break space):

          • dynamic marking on every document

          Just to clarify: not every document I have opened, but on any document in the current tab. I mean that this highlighting would be independent of any syntax/user-defined language etc. And ideally also only for the visible text buffer just like the script cited above does as I’ve noticed in Sublime that this king of highlighting could be a performance-killing feature, which isn’t great

          • bookmarking the line with the same marks as npp uses to be able to use F2 to jump to it

          Yes, these marks (a very nice feature for navigation)

          To be honest, I never tried to use two [indicators]

          Just to clarify, I don’t need BOTH at the same time, I wanted to be able to use one for one group and another for another group (e.g. I’d prefer an underline for non zero-width chars so it’s not as visible as a box).

          I just wanted to differentiate between whatever the mark style is (I actually don’t know what exactly it is, is it always INDIC_ROUNDBOX that turns into a vertical line for zero-width chars?) and a user-defined style that you mention (INDIC_SQUIGGLE) as I thought these could be two different implementations:

          1. the mark style could be invoked via a “open search → mark menu → insert search string → select mark all” type of command and the other style
          2. the other style would be applied “directly” to the regex strings found by the script
          EkopalypseE 1 Reply Last reply Reply Quote 0
          • EkopalypseE
            Ekopalypse @eugenesvk
            last edited by Ekopalypse

            @eugenesvk said in Persistent highlight of characters (e.g. no-break space):

            Just to clarify: not every document I have opened, but on any document in the current tab.

            Yes, I meant the same. Regardless of the file type and the assigned lexer,
            when you run the script it should color what it finds.

            Only this visual area - ok

            By mark style I guess you refer to the find dialog mark tab and its marking function, correct?
            I assume it is using INDIC_FULLBOX.

            the mark style could be invoked via a “open search → mark menu → insert search string → select mark all” type of command and the other style

            I don’t know if I understand this correctly.
            I can change the general behavior of the builtin marking style,
            to be a squiggle line for example but I cannot add
            another marking feature to the find dialog.

            What a script can do, from ui point of view is,

            • it can be executed via context menu (right click within a document, not the tab)
            • it can be executed from within the pythonscript plugin menu
            • it can be executed via keyboard short cut.

            From functional point of view a script can be written to either

            • ask for the search string and maybe indicator and do the job
            • do multiple searches within the script with different indicators per regex for example

            As it is already ~midnight here I will follow up on this tomorrow.

            eugenesvkE 1 Reply Last reply Reply Quote 2
            • eugenesvkE
              eugenesvk @Ekopalypse
              last edited by eugenesvk

              Only this visual area - ok

              Yep, if it’s dynamic, I don’t need to highlight the (invisible) chars in the invisible area :)

              @Ekopalypse said in Persistent highlight of characters (e.g. no-break space):

              By mark style I guess you refer to the find dialog mark tab and its marking function, correct?

              Yes

              to be a squiggle line for example but I cannot add
              another marking feature to the find dialog

              That’s fine, I’m not looking for such a UI change. I was just describing a potential way to achieve the highligting, just maybe the only way to automate the search&mark operation would’ve been to record a macro with the steps mentioned above. I just don’t know enough about what’s possible via scripting, that’s why I mentioned the mark style separately

              What you describe next about the script is perfectly fine as the only implementation, and I’m not even looking for the extra dynamic user-input functionality like this

              ask for the search string and maybe indicator and do the job

              (though you maybe didn’t mean it as you’ve put it in functionality, not UI)
              I’d be perfectly fine with defining regex groups of characters and their respective indicator type+color in the script itself (similarly to how EnhanceUDLLexer script defines regex groups and colors, but also adding an indicator type), as I’d only set it once

              (By the way, I didn’t know you could execute script also from a context menu, thanks for mentioning it, would I need to add some type of reference to this script in the contextMenu.xml config file?)

              As it is already ~midnight here I will follow up on this tomorrow

              Thanks a bunch!

              EkopalypseE 1 Reply Last reply Reply Quote 0
              • EkopalypseE
                Ekopalypse @eugenesvk
                last edited by

                @eugenesvk

                Ok lunch break gave me some time to already play with it.
                What about this

                # -*- coding: utf-8 -*-
                
                from Npp import editor, editor1, editor2, notepad, SCINTILLANOTIFICATION, INDICATORSTYLE
                import ctypes
                from ctypes import wintypes
                from collections import OrderedDict
                regexes = OrderedDict()
                
                # ------------------------------------------------- configuration area ---------------------------------------------------
                #
                # List of defined/used indicators
                #   Npp uses ids 21-31, lexer should use 0-7 which leaves plugins to use 8-20
                #   As other plugins might make usage of indicators as well,
                #   it needs to be tested which are free to use.
                #   Each indicator can be defined with the following attributes
                #       - an uique id starting from 8 to 20 (required)
                #       - an indicator style (required) (see https://www.scintilla.org/ScintillaDoc.html#Indicators which are available)
                #       - a foreground color (optional) (color are tuples containing the red, green and blue value like (255,0,0) )
                #       - an alpha transparency value (optional) (range 0-255)
                #       - an outline alpha transparency value (optional) (range 0-255)
                #       - a boolean value which makes indicator drawn under text or over(default) (optional)
                #   example:
                #      indicators = [(8, INDICATORSTYLE.ROUNDBOX),
                #                    (9, INDICATORSTYLE.SQUIGGLE, (100,215,100)),
                #                    (10, INDICATORSTYLE.GRADIENT, None, 55, None, False)
                #                      
                # Definition of colors and regular expressions
                #   Note, the order in which regular expressions will be processed
                #   is determined by its creation, that is, the first definition is processed first, then the 2nd, and so on
                #
                #   The basic structure always looks like this
                #
                #   regexes[a] = (b, c)
                #
                #   regexes = an ordered dictionary which ensures that the regular expressions are always processed in the same order
                #   a = an indicator id
                #   b = raw byte string, describes the regular expression. Example r'\w+'
                #   c = number of the match group to be used
                
                
                # Examples:
                #   All occurances of word editor, except editor1 and editor2
                #   should be indicated by a roundbox by using
                #   the indicator id 8 and the results from match group 1
                regexes[8] = (r'editor1|editor2|(editor)', 1)
                
                #   All numbers should be indicated by an squiggled lined,
                #   using indicator id 9  and the results from matchgroup 0.
                regexes[9] = (r'\d', 0)
                
                indicator_list = [(8, INDICATORSTYLE.ROUNDBOX),
                                  (9, INDICATORSTYLE.SQUIGGLE, (255,0,0)),]
                
                # ------------------------------------------------ /configuration area ---------------------------------------------------
                
                try:
                    AutoMarker().main()
                except NameError:
                    user32 = ctypes.WinDLL('user32')
                
                    class SingletonAutoMarker(type):
                        '''
                            Ensures, more or less, that only one
                            instance of the main class can be instantiated
                        '''
                        _instance = None
                        def __call__(cls, *args, **kwargs):
                            if cls._instance is None:
                                cls._instance = super(SingletonAutoMarker, cls).__call__(*args, **kwargs)
                            return cls._instance
                
                
                    class AutoMarker(object):
                        ''' Colors within visual area based on found matches. '''
                        __metaclass__ = SingletonAutoMarker
                
                        def __init__(self):
                            '''
                                Instantiated the class,
                                because of __metaclass__ = ... usage, is called once only.
                            '''
                            editor.callbackSync(self.on_updateui, [SCINTILLANOTIFICATION.UPDATEUI])
                            self.npp_hwnd = user32.FindWindowW(u'Notepad++', None)
                            self.editor1_hwnd = None
                            self.editor2_hwnd = None
                            self.__enum_scintilla_hwnds()
                            self.configure()
                
                
                        def __enum_scintilla_hwnds(self):
                            '''
                                Helper function
                                Enumerates the window handles for editor1 and editor2
                
                                Args:
                                    None
                                Returns:
                                    None
                            '''
                            EnumWindowsProc = ctypes.WINFUNCTYPE(wintypes.BOOL, wintypes.HWND, wintypes.LPARAM)
                
                            def foreach_window(hwnd, lParam):
                                curr_class = ctypes.create_unicode_buffer(256)
                                user32.GetClassNameW(hwnd, curr_class, 256)
                
                                if (curr_class.value == u'Scintilla') and (user32.GetParent(hwnd) == self.npp_hwnd):
                                    if self.editor1_hwnd is None:
                                        self.editor1_hwnd = hwnd
                                    elif self.editor2_hwnd is None:
                                        self.editor2_hwnd = hwnd
                                        return False
                                return True
                
                            user32.EnumChildWindows(self.npp_hwnd, EnumWindowsProc(foreach_window), 0)
                
                
                        @staticmethod
                        def paint_it(indicator_id, pos, length):
                            '''
                                This is where the actual coloring takes place.
                                Color, the position of the first character and
                                the length of the text to be colored must be provided.
                                Coloring occurs only if the position is not within the excluded range.
                
                                Args:
                                    indicator_id = integer, expected in range of 0-16777215
                                    pos = integer,  denotes the start position
                                    length = integer, denotes how many chars need to be colored.
                                Returns:
                                    None
                            '''
                            editor.setIndicatorCurrent(indicator_id)
                            editor.indicatorFillRange(pos, length)
                            editor.markerAdd(editor.lineFromPosition(pos), 24)
                
                
                        def style(self):
                            '''
                                Calculates the text area to be searched for in the current document.
                                Calls up the regexes to find the position and
                                calculates the length of the text to be colored.
                                Deletes the old indicators before setting new ones.
                
                                Args:
                                    None
                                Returns:
                                    None
                            '''
                            start_line = editor.docLineFromVisible(editor.getFirstVisibleLine())
                            end_line = editor.docLineFromVisible(start_line + editor.linesOnScreen())
                            start_position = editor.positionFromLine(start_line)
                            end_position = editor.getLineEndPosition(end_line)
                            
                            for indicator in indicator_list:
                                editor.setIndicatorCurrent(indicator[0])
                                editor.indicatorClearRange(indicator[0], editor.getTextLength())
                            
                            for id, _regex in regexes.items():
                                editor.research(_regex[0],
                                                lambda m: self.paint_it(id,
                                                                        m.span(_regex[1])[0],
                                                                        m.span(_regex[1])[1] - m.span(_regex[1])[0]),
                                                0,
                                                start_position,
                                                end_position)
                
                
                        def configure(self):
                            '''
                                Define basic indicator settings and the needed regexes.
                
                                Args:
                                    None
                                Returns:
                                    None
                            '''
                            def set_indicator_attributes(indicator_number, 
                                                         indicator_style, 
                                                         fore=None, 
                                                         alpha=None,
                                                         outline_alpha=None, 
                                                         under=None):
                                ''' helper function to set indicators '''
                                editor1.indicSetStyle(indicator_number, indicator_style)
                                editor2.indicSetStyle(indicator_number, indicator_style)
                                editor1.indicSetAlpha(indicator_number, alpha or 55)
                                editor2.indicSetAlpha(indicator_number, alpha or 55)
                                editor1.indicSetFore(indicator_number, fore or (100,215,100))
                                editor2.indicSetFore(indicator_number, fore or (100,215,100))
                                editor1.indicSetOutlineAlpha(indicator_number, outline_alpha or 255)
                                editor2.indicSetOutlineAlpha(indicator_number, outline_alpha or 255)
                                editor1.indicSetUnder(indicator_number, under or True)
                                editor2.indicSetUnder(indicator_number, under or True)
                
                            for indicator in indicator_list:
                                set_indicator_attributes(*indicator)
                
                
                        def on_updateui(self, args):
                            '''
                                Callback which gets called every time scintilla
                                (aka the editor) changed something within the document.
                
                                Triggers the styling function if the document is of interest.
                
                                Args:
                                    provided by scintilla but none are of interest
                                Returns:
                                    None
                            '''
                            self.style()
                
                
                        def main(self):
                            '''
                                Main function entry point.
                                Simulates updateui event to enforce
                                checking the document for the first time uasge.
                
                                Args:
                                    None
                                Returns:
                                    None
                            '''
                            self.on_updateui(None)
                
                    AutoMarker().main()
                

                Concerning the contect menu, yes, you have to add something like

                <Item FolderName="SUBFOLDERNAME" PluginEntryName="Python Script" PluginCommandItemName="SCRIPTNAME_WITHOUT_EXTENSION" ItemNameAs="NAME_HOW_IT_SHOULD_APPEAR"/>

                eugenesvkE 1 Reply Last reply Reply Quote 2
                • eugenesvkE
                  eugenesvk @Ekopalypse
                  last edited by

                  @Ekopalypse
                  Thanks a lot for the script, it’s working great!

                  Just a few questions:

                  1. Is it possible to make the script toggle itself? This way I’d can click a menu button to turn it on/off
                  2. Also, is there a chance to add the bookmark as an option for a regex group as I don’t need them for all the characters, but only for some?
                  3. During performance testing I’ve noticed that ~100 of characters would make it choke and slow down caret movement refresh rate, i.e. if I hold the Right key (without any edits and without moving the screen buffer), the caret would disappear and appear only when I release it (the position would change). I was wondering whether this is due to it auto-updating too fast or someting and if the script could maybe poll for changes slower? Granted, in real documents I never have so many of characters for this script to work with, so it’s not a big issue
                  EkopalypseE 1 Reply Last reply Reply Quote 0
                  • EkopalypseE
                    Ekopalypse @eugenesvk
                    last edited by Ekopalypse

                    @eugenesvk

                    # -*- coding: utf-8 -*-
                    
                    from Npp import editor, editor1, editor2, notepad, SCINTILLANOTIFICATION, INDICATORSTYLE
                    import ctypes
                    from ctypes import wintypes
                    from collections import OrderedDict
                    regexes = OrderedDict()
                    
                    # ------------------------------------------------- configuration area ---------------------------------------------------
                    #
                    # List of defined/used indicators
                    #   Npp uses ids 21-31, lexer should use 0-7 which leaves plugins to use 8-20
                    #   As other plugins might make usage of indicators as well,
                    #   it needs to be tested which are free to use.
                    #   Each indicator can be defined with the following attributes
                    #       - an uique id starting from 8 to 20 (required)
                    #       - an indicator style (required) (see https://www.scintilla.org/ScintillaDoc.html#Indicators which are available)
                    #       - a foreground color (optional) (color are tuples containing the red, green and blue value like (255,0,0) )
                    #       - an alpha transparency value (optional) (range 0-255)
                    #       - an outline alpha transparency value (optional) (range 0-255)
                    #       - a boolean value which makes indicator drawn under text or over(default) (optional)
                    #   example:
                    #      indicators = [(8, INDICATORSTYLE.ROUNDBOX),
                    #                    (9, INDICATORSTYLE.SQUIGGLE, (100,215,100)),
                    #                    (10, INDICATORSTYLE.GRADIENT, None, 55, None, False)
                    #                      
                    # Definition of colors and regular expressions
                    #   Note, the order in which regular expressions will be processed
                    #   is determined by its creation, that is, the first definition is processed first, then the 2nd, and so on
                    #
                    #   The basic structure always looks like this
                    #
                    #   regexes[a] = (b, c, d)
                    #
                    #   regexes = an ordered dictionary which ensures that the regular expressions are always processed in the same order
                    #   a = an indicator id
                    #   b = raw byte string, describes the regular expression. Example r'\w+'
                    #   c = number of the match group to be used
                    #   d = boolean value which indicates whether line should be bookmarked
                    
                    # Examples:
                    #   All occurances of word editor, except editor1 and editor2
                    #   should be indicated by a roundbox by using
                    #   the indicator id 8 and the results from match group 1
                    regexes[8] = (r'editor1|editor2|(editor)', 1, False)
                    
                    #   All numbers should be indicated by an squiggled lined,
                    #   using indicator id 9  and the results from matchgroup 0.
                    regexes[9] = (r'\d', 0, True)
                    
                    indicator_list = [(8, INDICATORSTYLE.ROUNDBOX),
                                      (9, INDICATORSTYLE.SQUIGGLE, (255,0,0)),]
                    
                    # ------------------------------------------------ /configuration area ---------------------------------------------------
                    
                    try:
                        AutoMarker().main()
                    except NameError:
                        user32 = ctypes.WinDLL('user32')
                    
                        class SingletonAutoMarker(type):
                            '''
                                Ensures, more or less, that only one
                                instance of the main class can be instantiated
                            '''
                            _instance = None
                            def __call__(cls, *args, **kwargs):
                                if cls._instance is None:
                                    cls._instance = super(SingletonAutoMarker, cls).__call__(*args, **kwargs)
                                return cls._instance
                    
                    
                        class AutoMarker(object):
                            ''' Colors within visual area based on found matches. '''
                            __metaclass__ = SingletonAutoMarker
                    
                            def __init__(self):
                                '''
                                    Instantiated the class,
                                    because of __metaclass__ = ... usage, is called once only.
                                '''
                                editor.callbackSync(self.on_updateui, [SCINTILLANOTIFICATION.UPDATEUI])
                                self.npp_hwnd = user32.FindWindowW(u'Notepad++', None)
                                self.editor1_hwnd = None
                                self.editor2_hwnd = None
                                self.do_mark = False
                                self.__enum_scintilla_hwnds()
                                self.configure()
                    
                    
                            def __enum_scintilla_hwnds(self):
                                '''
                                    Helper function
                                    Enumerates the window handles for editor1 and editor2
                    
                                    Args:
                                        None
                                    Returns:
                                        None
                                '''
                                EnumWindowsProc = ctypes.WINFUNCTYPE(wintypes.BOOL, wintypes.HWND, wintypes.LPARAM)
                    
                                def foreach_window(hwnd, lParam):
                                    curr_class = ctypes.create_unicode_buffer(256)
                                    user32.GetClassNameW(hwnd, curr_class, 256)
                    
                                    if (curr_class.value == u'Scintilla') and (user32.GetParent(hwnd) == self.npp_hwnd):
                                        if self.editor1_hwnd is None:
                                            self.editor1_hwnd = hwnd
                                        elif self.editor2_hwnd is None:
                                            self.editor2_hwnd = hwnd
                                            return False
                                    return True
                    
                                user32.EnumChildWindows(self.npp_hwnd, EnumWindowsProc(foreach_window), 0)
                    
                    
                            @staticmethod
                            def paint_it(indicator_id, pos, length, bookmark_it):
                                '''
                                    This is where the actual coloring takes place.
                                    Color, the position of the first character and
                                    the length of the text to be colored must be provided.
                                    Coloring occurs only if the position is not within the excluded range.
                    
                                    Args:
                                        indicator_id = integer, expected in range of 0-16777215
                                        pos = integer,  denotes the start position
                                        length = integer, denotes how many chars need to be colored.
                                    Returns:
                                        None
                                '''
                                editor.setIndicatorCurrent(indicator_id)
                                editor.indicatorFillRange(pos, length)
                                if bookmark_it:
                                    editor.markerAdd(editor.lineFromPosition(pos), 24)
                    
                    
                            def style(self):
                                '''
                                    Calculates the text area to be searched for in the current document.
                                    Calls up the regexes to find the position and
                                    calculates the length of the text to be colored.
                                    Deletes the old indicators before setting new ones.
                    
                                    Args:
                                        None
                                    Returns:
                                        None
                                '''
                                start_line = editor.docLineFromVisible(editor.getFirstVisibleLine())
                                end_line = editor.docLineFromVisible(start_line + editor.linesOnScreen())
                                start_position = editor.positionFromLine(start_line)
                                end_position = editor.getLineEndPosition(end_line)
                                
                                for indicator in indicator_list:
                                    editor.setIndicatorCurrent(indicator[0])
                                    editor.indicatorClearRange(indicator[0], editor.getTextLength())
                                
                                for id, _regex in regexes.items():
                                    editor.research(_regex[0],
                                                    lambda m: self.paint_it(id,
                                                                            m.span(_regex[1])[0],
                                                                            m.span(_regex[1])[1] - m.span(_regex[1])[0],
                                                                            _regex[2]),
                                                    0,
                                                    start_position,
                                                    end_position)
                    
                    
                            def configure(self):
                                '''
                                    Define basic indicator settings and the needed regexes.
                    
                                    Args:
                                        None
                                    Returns:
                                        None
                                '''
                                def set_indicator_attributes(indicator_number, 
                                                             indicator_style, 
                                                             fore=None, 
                                                             alpha=None,
                                                             outline_alpha=None, 
                                                             under=None):
                                    ''' helper function to set indicators '''
                                    editor1.indicSetStyle(indicator_number, indicator_style)
                                    editor2.indicSetStyle(indicator_number, indicator_style)
                                    editor1.indicSetAlpha(indicator_number, alpha or 55)
                                    editor2.indicSetAlpha(indicator_number, alpha or 55)
                                    editor1.indicSetFore(indicator_number, fore or (100,215,100))
                                    editor2.indicSetFore(indicator_number, fore or (100,215,100))
                                    editor1.indicSetOutlineAlpha(indicator_number, outline_alpha or 255)
                                    editor2.indicSetOutlineAlpha(indicator_number, outline_alpha or 255)
                                    editor1.indicSetUnder(indicator_number, under or True)
                                    editor2.indicSetUnder(indicator_number, under or True)
                    
                                for indicator in indicator_list:
                                    set_indicator_attributes(*indicator)
                    
                    
                            def on_updateui(self, args):
                                '''
                                    Callback which gets called every time scintilla
                                    (aka the editor) changed something within the document.
                    
                                    Triggers the styling function if the document is of interest.
                    
                                    Args:
                                        provided by scintilla but none are of interest
                                    Returns:
                                        None
                                '''
                                if self.do_mark:
                                    self.style()
                    
                    
                            def main(self):
                                '''
                                    Main function entry point.
                                    Simulates updateui event to enforce
                                    checking the document for the first time uasge.
                    
                                    Args:
                                        None
                                    Returns:
                                        None
                                '''
                                self.do_mark = not self.do_mark
                                self.on_updateui(None)
                    
                        AutoMarker().main()
                    

                    Point 1 + 2 should be implemented now,

                    Concerning point 3, this might have several causes.
                    One, from what I recall is a change within scintilla.
                    If scintilla receives too many keyboard inputs it won’t repaint until a key release has been received BUT it could be python as well.
                    Second, if you have really long lines this is an internal known scintilla issue.

                    What I did was changing one regex to search for \w+ and using the script itself and it seem to be reasonable fast if I stayed on up/down arrow key.

                    One other thing, the bookmarking is somewhat slow and I don’t know why.
                    Currently, well some month already, I’m developing a new pythonscript plugin,
                    one which uses python3 and if I do bookmarking with this plugin,
                    it instantly does it.

                    eugenesvkE 1 Reply Last reply Reply Quote 2
                    • eugenesvkE
                      eugenesvk @Ekopalypse
                      last edited by

                      @Ekopalypse
                      You’re right, emoving the bookmarks and shortening the lines did indeed solve all of the performance issues even in a file with 100k lines of 25 highlighted characters each! Not only does the caret not disappear, but even holding PageDown has only a minor negative effect.
                      That’s awesome!!!
                      Thanks again for rewriting your script to add this functionality

                      Will just paste my config that I used in the script in case someone might find it useful in their whitespacing quests ;)

                      #'ur' not supported in Python 3, but the scripting engine is Python2
                      regexes[ 8] = (ur'\u00A0+|\u202F+'	, 0, False)	#¦ ¦no-break; ¦ ¦ no-break narrow
                      regexes[ 9] = (ur'\u2003+'        	, 0, False)	#¦ ¦ Em
                      regexes[10] = (ur'\u2002+'        	, 0, False)	#¦ ¦ En
                      regexes[11] = (ur'\u2007+'        	, 0, False)	#¦ ¦ Figure
                      regexes[12] = (ur'\u2008+'        	, 0, False)	#¦ ¦ Punctuation
                      regexes[13] = (ur'\u2009+'        	, 0, False)	#¦ ¦ Thin
                      regexes[14] = (ur'\u200A+'        	, 0, False)	#¦ ¦ Hair
                      regexes[15] = (ur'\u1680+|\u180E+|\u2000+|\u2001+|\u2004+|\u2005+|\u2006+|\u205F+|\u3000+', 0, False) #Others non zero-width: ¦ ¦Ogham Mark, ¦᠎¦Mongolian Vowel Separator, ¦ ¦En Quad, ¦ ¦Em Quad, ¦ ¦3-per-em (thick), ¦ ¦4-per-em (mid), ¦ ¦6-per-em, ¦ ¦Medium Mathematical, ¦ ¦Ideographic
                      #15¦ ¦Ogh ¦᠎¦Mong ¦ ¦En ¦ ¦Em Quad ¦ ¦3-per-em ¦ ¦4 ¦ ¦6 ¦ ¦Math ¦ ¦Ideographic
                      ZeroWidth = [
                      	ur'\u00AD+',                                	#Zero1: >­< Soft Hyphen
                      	ur'\u200B+|\u200C+|\u200D+|\u200E+|\u200F+',	#Zero2: >​< ZWS >‌< ZWNJ >‍< ZWJoiner >‎< LTR Mark, >‏< RTL Mark
                      	ur'\u202A+|\u202B+|\u202C+|\u202D+|\u202E+',	#Zero3: >‪< LTR Embedding >‫< RTL Embedding, >‬< Pop Directional, >‭< LTR Override, >‮< RTL Override
                      	ur'\u2060+|\u2061+|\u2062+|\u2063+|\u2064+|\u2066+|\u2067+|\u2068+|\u2069+|\u206A+|\u206B+|\u206C+|\u206D+|\u206E+|\u206F+',
                      	#Zero4: >⁠< Word J >⁡< FunctionApp >⁢< Invisible Times >⁣< I Sep >⁤< I+ >⁦< LTR Isolate >⁧< RTL I >⁨< First Strong I >⁩< Pop Directional I >< Inhibit Symmetric Swapping, >< Activate Symmetric Swapping, >< Inhibit Arabic Form Shaping, >< Activate Arabic Form Shaping, >< National Digit, >< Nominal Digit
                      	ur'\uFEFF+',                	#Zero5: ¦¦ ZW No-break
                      	ur'\uFFF9+|\uFFFA+|\uFFFB+']	#Zero6: >< Interlinear Annotation Anchor >< IA Separator >< IA Terminator
                      ZWCombo = '|'.join([str for str in (ZeroWidth)])
                      regexes[16] = (ZWCombo	, 0, False)
                      
                      indicator_list = [
                      	( 8, INDICATORSTYLE.PLAIN         	, (  0, 255,   0)),
                      	( 9, INDICATORSTYLE.DASH          	, (255,  83, 112)),
                      	(10, INDICATORSTYLE.SQUIGGLE      	, (255,  83, 112)),
                      	(11, INDICATORSTYLE.PLAIN         	, (255, 130,  41)),
                      	(12, INDICATORSTYLE.POINTCHARACTER	, (  0, 200, 255)),
                      	(13, INDICATORSTYLE.POINTCHARACTER	, (  0, 255,   0)),
                      	(14, INDICATORSTYLE.POINTCHARACTER	, (255,  83, 112)),
                      	(15, INDICATORSTYLE.PLAIN         	, (  0,   0,   0)),
                      	(16, INDICATORSTYLE.BOX           	, (255,  83, 112))]
                      
                      1 Reply Last reply Reply Quote 2
                      • Alan KilbornA
                        Alan Kilborn
                        last edited by

                        @Ekopalypse said:

                        the bookmarking is somewhat slow and I don’t know why

                        A discussion of boomark processing with Pythonscript being slow can be found here:
                        https://community.notepad-plus-plus.org/topic/15159/bookmark-multiple-lines

                        EkopalypseE 1 Reply Last reply Reply Quote 2
                        • EkopalypseE
                          Ekopalypse @Alan Kilborn
                          last edited by

                          @Alan-Kilborn

                          I remember we already had that discussion once, right?
                          I never tracked down what the issue is but ok, PS is using SendMessage
                          instead of Direct_Function and this might slow down a little bit but
                          why only with markerAdd method? I mean, shouldn’t the indicator calls
                          be affected as well? I my example I’ve done multiple times more indicator
                          calls than markerAdd calls.
                          I just recorded a screecast, can be seen here.

                          The code in question is this

                          cdef void mark_spaces():
                              cdef size_t _ptr, i, j, linenumber
                              _ptr = SendMessageW(nppData._scintillaMainHandle, SCI_GETCHARACTERPOINTER, 0, 0)
                              SendMessageW(nppData._scintillaMainHandle, SCI_INDICSETSTYLE, 8, 7)
                              SendMessageW(nppData._scintillaMainHandle, SCI_INDICSETFORE, 8, 0x64d764)
                              SendMessageW(nppData._scintillaMainHandle, SCI_INDICSETALPHA, 8, 55)
                              SendMessageW(nppData._scintillaMainHandle, SCI_INDICSETOUTLINEALPHA, 8, 255)
                          
                              i = 0
                              j = -1
                              for c in <char*>_ptr:
                                  j += 1
                                  if c == b'e':
                                      i += 1
                                      linenumber = SendMessageW(nppData._scintillaMainHandle,SCI_LINEFROMPOSITION, j, 0)
                                      SendMessageW(nppData._scintillaMainHandle, SCI_SETINDICATORCURRENT, 8, 0)
                                      SendMessageW(nppData._scintillaMainHandle, SCI_INDICATORFILLRANGE, j, 1)
                                      SendMessageW(nppData._scintillaMainHandle,SCI_MARKERADD, linenumber, 24)
                                      
                              text = f'{i} matches found!'
                              SendMessageW(nppData._nppHandle, NPPM_SETSTATUSBAR, STATUSBAR_DOC_TYPE, <LPARAM><LPCWSTR>text)
                              
                          cdef void beNotified(SCNotification *notifyCode):
                              global notification
                              if notifyCode.nmhdr.code == SCN_UPDATEUI:
                                  mark_spaces()
                          

                          with each updateui notification the whole document is scanned
                          and I’m using SendMessage exclusively. But no GIL and thread.

                          Alan KilbornA 1 Reply Last reply Reply Quote 1
                          • Alan KilbornA
                            Alan Kilborn @Ekopalypse
                            last edited by

                            @Ekopalypse said in Persistent highlight of characters (e.g. no-break space):

                            I remember we already had that discussion once, right?

                            Probably. :-)

                            I’m not sure what the code is you’re showing. Maybe it is just out of my swim lane. :-)

                            EkopalypseE 1 Reply Last reply Reply Quote 0
                            • EkopalypseE
                              Ekopalypse @Alan Kilborn
                              last edited by

                              @Alan-Kilborn

                              I’m not sure what the code is you’re showing.

                              It’s cythonized python code :)
                              Basically this
                              editor.callbackSync(self.on_updateui, [SCINTILLANOTIFICATION.UPDATEUI])
                              is equivalent to

                              cdef void beNotified(SCNotification *notifyCode):
                                  global notification
                                  if notifyCode.nmhdr.code == SCN_UPDATEUI:
                                      mark_spaces()
                              

                              With pythonscript on_updateui would handle the notifcation and
                              here it is mark_spaces.

                              mark_spces uses SendMessage method to do the communication with
                              scintilla to show and proof that this message can be used to some
                              certain of degree, so this can’t be the only reason. I assume it has to do
                              with locking/releasing GIL and threads but I’m not going to dig deeper
                              as I’m working on a replacement for PS - so from my point of view it doesn’t make sense to spent time on it. :)

                              1 Reply Last reply Reply Quote 3
                              • Alan KilbornA Alan Kilborn referenced this topic on
                              • Alan KilbornA Alan Kilborn referenced this topic on
                              • First post
                                Last post
                              The Community of users of the Notepad++ text editor.
                              Powered by NodeBB | Contributors