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 @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