Persistent highlight of characters (e.g. no-break space)
-
just a test to see if two indicators can be used together.
YES we can. -
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:
- 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
- the other style would be applied “directly” to the regex strings found by the script
-
@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.
-
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 dialogThat’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!
-
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"/>
-
@Ekopalypse
Thanks a lot for the script, it’s working great!Just a few questions:
- Is it possible to make the script toggle itself? This way I’d can click a menu button to turn it on/off
- 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?
- 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
-
# -*- 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. -
@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 functionalityWill 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))]
-
@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 -
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. -
@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. :-)
-
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 tocdef 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. :) -
-