Community
    • Login

    has a plugin like sublime plugin BracketHighlighter?

    Scheduled Pinned Locked Moved Notepad++ & Plugin Development
    brackethighligh
    14 Posts 4 Posters 13.3k Views
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • Scott SumnerS
      Scott Sumner @linpengcheng
      last edited by

      @linpengcheng

      I don’t believe so; you asked before 9 months ago: https://notepad-plus-plus.org/community/topic/13017/feature-request-keep-the-current-bracket-block-surrounded-by-a-polygonal-box

      It is possible to write that plugin yourself, or you could script it with one of the scripting plugins (Pythonscript, Luascript). I did some experimentation with doing it with Pythonscript some time ago; it worked but I didn’t really like the effect so I threw it away. But I did prove out that it was possible…

      1 Reply Last reply Reply Quote 0
      • linpengchengL
        linpengcheng
        last edited by

        I’ve tried luascript, doc and examples are few, not successful, probably I only use clojure language, mainly engaged in data analysis, not long this aspect

        1 Reply Last reply Reply Quote 0
        • linpengchengL
          linpengcheng
          last edited by

          I’ve tried luascript, doc and examples are few, not successful, probably I only use clojure language, mainly engaged in data analysis,

          I’m not lazy, I’m just not long for this technology, here’s My notepad++ for Clojure
          https://github.com/linpengcheng/ClojureBoxNpp

          If you have semi-finished products, can you give a github address, I try to improve it.

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

            @linpengcheng

            I didn’t like the effect because the boxing appeared too “cluttered”; here’s an example:

            Imgur

            I like the Sublime BracketHighligher way of doing it better…a single box around everything, not each individual line boxed. I don’t really see how it could be done similarly well in Notepad++.

            Maybe instead of boxing just use a solid background color for where the boxing would occur, but that might not play too well with other coloring/styling that is done by Notepad++ in programming language source…although in my example Python array above it would probably look good.

            1 Reply Last reply Reply Quote 0
            • linpengchengL
              linpengcheng
              last edited by

              it look very good! Can you push it to github?

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

                @linpengcheng

                I found an old version of the Pythonscript under discussion. It seems to work although it is almost guaranteed that YMMV. :-D

                I ran it and made a short video of what it does when the caret is moved around thru a code file: https://i.imgur.com/DUB4bSB.mp4

                A large portion of the code is concerned with multiple views handling and/or cloned document handling. Once that is sorted out, the box drawing is rather trivial.

                Enjoy!

                try:
                
                    BH__dict
                
                except NameError:
                
                    BH__dict = dict()
                
                    BH__dict['indic_for_box_at_caret'] = 10  # pick a free indicator number
                
                    def indicatorOptionsSet(indicator_number, indicator_style, rgb_color_tup, alpha, outline_alpha, draw_under_text, which_editor=editor):
                        which_editor.indicSetStyle(indicator_number, indicator_style)       # e.g. INDICATORSTYLE.ROUNDBOX
                        which_editor.indicSetFore(indicator_number, rgb_color_tup)
                        which_editor.indicSetAlpha(indicator_number, alpha)                 # integer
                        which_editor.indicSetOutlineAlpha(indicator_number, outline_alpha)  # integer
                        which_editor.indicSetUnder(indicator_number, draw_under_text)       # boolean
                
                    for editorX in (editor1, editor2):
                        indicatorOptionsSet(BH__dict['indic_for_box_at_caret'], INDICATORSTYLE.STRAIGHTBOX, (238,121,159), 0, 255, True, editorX)  # white box rimmed in "pale violet red 2"
                
                    BH__dict['last_modificationType_for_hack'] = None
                
                    def BH__containing_box_indices_into_string(str_containing_caret, caret_index_into_str):
                
                        class Stack:
                            def __init__(self): self.clear()
                            def isEmpty(self): return self.size() == 0
                            def push(self, item): self.items.append(item)
                            def pop(self): return None if self.size() == 0 else self.items.pop()
                            def peek(self): return None if self.size() == 0 else self.items[self.size() - 1]
                            def size(self): return len(self.items)
                            def clear(self): self.items = []
                
                        retval = (None, None)  # default to no valid box
                
                        get_opening_char_via_closing_char_dict = {
                            ')' : '(',
                            ']' : '[',
                            '}' : '{',
                            }
                        get_closing_char_via_opening_char_dict = dict((v, k) for (k, v) in get_opening_char_via_closing_char_dict.items())
                
                        closing_chars = get_opening_char_via_closing_char_dict.keys()
                        opening_chars = get_opening_char_via_closing_char_dict.values()
                
                        box_ending_index = -1
                        box_starting_index = -1
                
                        stack = Stack()
                
                        for j in range(caret_index_into_str, len(str_containing_caret)):
                            c = str_containing_caret[j]
                            if c in closing_chars:
                                if stack.isEmpty():
                                    box_ending_index = j
                                    break
                                else:
                                    if stack.peek() ==  get_opening_char_via_closing_char_dict[c]:
                                        stack.pop()
                                    else:
                                        break  # unbalanced
                            elif c in opening_chars:
                                stack.push(c)
                
                        if box_ending_index != -1:
                            stack.clear()
                            box_starting_index = -1
                            for j in range(caret_index_into_str - 1, -1, -1):
                                c = str_containing_caret[j]
                                if c in opening_chars:
                                    if stack.isEmpty():
                                        box_starting_index = j
                                        break
                                    else:
                                        if stack.peek() ==  get_closing_char_via_opening_char_dict[c]:
                                            stack.pop()
                                        else:
                                            break  # unbalanced
                                elif c in closing_chars:
                                    stack.push(c)
                
                        if box_ending_index != -1:
                            if box_starting_index != -1:
                                if str_containing_caret[box_ending_index] == get_closing_char_via_opening_char_dict[str_containing_caret[box_starting_index]]:
                                    retval = (box_starting_index, box_ending_index + 1)
                
                        return retval
                
                    def BH__callback_sci_MODIFIED(args):
                        global BH__dict
                        BH__dict['last_modificationType_for_hack'] = args['modificationType']
                
                    def BH__fileIsCloned(file_name_to_test):
                        retval = False
                        clone_detect_dict = {}
                        file_tup_list = notepad.getFiles()
                        for tup in file_tup_list:
                            (filename, _, _, _) = tup
                            if filename not in clone_detect_dict:
                                clone_detect_dict[filename] = 0
                            else:
                                clone_detect_dict[filename] += 1
                                if filename == file_name_to_test: break
                        if file_name_to_test in clone_detect_dict:
                            if clone_detect_dict[file_name_to_test] >= 1: retval = True
                        return retval
                
                    def BH__fileIsClonedAndIsActiveInBothViews(file_name_to_test):
                        retval = False
                        if editor1 and editor2:
                            # both views are in use
                            if BH__fileIsCloned(file_name_to_test):
                                curr_doc_index_main_view = notepad.getCurrentDocIndex(0)
                                curr_doc_index_2nd_view = notepad.getCurrentDocIndex(1)
                                main_view_active_doc_bool = False
                                secondary_view_active_doc_bool = False
                                file_tup_list = notepad.getFiles()
                                for tup in file_tup_list:
                                    (filename, _, index_in_view, view_number) = tup
                                    if filename == file_name_to_test:
                                        if view_number == 0:
                                            if index_in_view == curr_doc_index_main_view:
                                                main_view_active_doc_bool = True
                                        elif view_number == 1:
                                            if index_in_view == curr_doc_index_2nd_view:
                                                secondary_view_active_doc_bool = True
                                        if main_view_active_doc_bool and secondary_view_active_doc_bool:
                                            retval = True
                                            break
                        return retval
                
                    def BH__getViewableEditorAndRangeTupleListList(work_across_both_views):
                        retval = []
                        # retval looks like these examples:
                        #  [ ( editor, [ (0, 1000), (2020, 3000) ] ) ]
                        #  [ ( editor1, [ (0, 1000), (2020, 3000) ] ), ( editor2, [ (4000, 5000), (6020, 7000) ] ) ]
                        def consolidate_range_tuple_list(range_tup_list):
                            sorted_range_tup_list = sorted(range_tup_list)  # sort criteria is first element of tuple in list
                            saved_2element_list = list(sorted_range_tup_list[0])
                            for (start, end) in sorted_range_tup_list:
                                if start <= saved_2element_list[1]:
                                    saved_2element_list[1] = max(saved_2element_list[1], end)
                                else:
                                    yield tuple(saved_2element_list)
                                    saved_2element_list[0] = start
                                    saved_2element_list[1] = end
                            yield tuple(saved_2element_list)
                        def get_onscreen_pos_tup_list(which_editor):  # which_editor is editor1 or editor2 (or maybe even just plain editor)
                            # loosely based upon the N++ source for SmartHighlighter::highlightViewWithWord()
                            retval_tup_list = list()
                            temp_tup_list = []
                            MAXLINEHIGHLIGHT = 400
                            firstLine = which_editor.getFirstVisibleLine()
                            currentLine = firstLine
                            nbLineOnScreen = which_editor.linesOnScreen()
                            nrLines = min(nbLineOnScreen, MAXLINEHIGHLIGHT) + 1
                            lastLine = firstLine + nrLines
                            prevDocLineChecked = -1
                            break_out = False
                            while currentLine < lastLine:
                                docLine = which_editor.docLineFromVisible(currentLine)
                                if docLine != prevDocLineChecked:
                                    prevDocLineChecked = docLine
                                    startPos = which_editor.positionFromLine(docLine)
                                    endPos = which_editor.positionFromLine(docLine + 1)
                                    if endPos == -1:
                                        endPos = which_editor.getTextLength() - 1
                                        break_out = True
                                    if endPos > startPos: temp_tup_list.append((startPos, endPos))
                                    if break_out: break
                                currentLine += 1
                            if len(temp_tup_list) > 0:
                                retval_tup_list = list(consolidate_range_tuple_list(temp_tup_list))
                            return retval_tup_list
                        both_views_open = True if editor1 and editor2 else False
                        curr_file_active_in_both_views = BH__fileIsClonedAndIsActiveInBothViews(notepad.getCurrentFilename()) if both_views_open else False
                        if both_views_open:
                            ed1_range_tup_list = get_onscreen_pos_tup_list(editor1)
                            ed2_range_tup_list = get_onscreen_pos_tup_list(editor2)
                        if curr_file_active_in_both_views:
                            range_tup_list = list(consolidate_range_tuple_list(ed1_range_tup_list + ed2_range_tup_list))
                            retval.append((editor, range_tup_list))
                        elif both_views_open and work_across_both_views:
                            retval.append((editor1, ed1_range_tup_list))
                            retval.append((editor2, ed2_range_tup_list))
                        else:
                            range_tup_list = get_onscreen_pos_tup_list(editor)
                            retval.append((editor, range_tup_list))
                        return retval
                
                    def BH__callback_sci_UPDATEUI(args):
                
                        # hack, see https://notepad-plus-plus.org/community/topic/12360/vi-simulator-how-to-highlight-a-word/27, look for "16400" in code:
                        if args['updated'] == UPDATE.CONTENT and BH__dict['last_modificationType_for_hack'] == (MODIFICATIONFLAGS.CHANGEINDICATOR | MODIFICATIONFLAGS.USER): return
                
                        for (editorX, pos_range_tuple_list) in BH__getViewableEditorAndRangeTupleListList(True):
                
                            # clear out any existing highlighting in areas the user can currently see
                            for (start_pos, end_pos) in pos_range_tuple_list:
                                editorX.setIndicatorCurrent(BH__dict['indic_for_box_at_caret'])
                                editorX.indicatorClearRange(start_pos, end_pos - start_pos)
                
                            for (start_pos, end_pos) in pos_range_tuple_list:
                
                                if start_pos <= editorX.getCurrentPos() <= end_pos:
                
                                    (box_start_offset, box_end_offset) = BH__containing_box_indices_into_string(
                                        editorX.getTextRange(start_pos, end_pos),
                                        editorX.getCurrentPos() - start_pos
                                        )
                
                                    if box_start_offset != None:
                                        size_of_box_in_chars = box_end_offset - box_start_offset
                                        if size_of_box_in_chars <= 2:
                                            pass  # rather pointless to box in if the opening and closing delims are right next to each other
                                        else:
                                            editorX.setIndicatorCurrent(BH__dict['indic_for_box_at_caret'])
                                            editorX.indicatorFillRange(start_pos + box_start_offset, size_of_box_in_chars)
                
                    editor.callbackSync(BH__callback_sci_UPDATEUI, [SCINTILLANOTIFICATION.UPDATEUI])  # install callback
                    editor.callbackSync(BH__callback_sci_MODIFIED, [SCINTILLANOTIFICATION.MODIFIED])  # may not need to be "Sync", but for now we'll make it that way
                
                else:
                
                    editor.setSelectionMode(editor.getSelectionMode())  # force manual UPDATEUI to happen
                
                1 Reply Last reply Reply Quote 3
                • linpengchengL
                  linpengcheng
                  last edited by

                  @Scott Sumner :
                  thank you very much!

                  1 Reply Last reply Reply Quote 1
                  • linpengchengL
                    linpengcheng
                    last edited by

                    @Scott Sumner :
                    thank you very much! it’s very cool!
                    Screenshots of Clojure

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

                      @linpengcheng

                      Clojure seems to be a language with a lot of parentheses! (I know nothing about Clojure)

                      My opinion only, but I think the visual boxing effect could be made better by solid-coloring the box, perhaps in yellow:

                      Imgur

                      I did that by changing this line of the original script:

                      indicatorOptionsSet(BH__dict['indic_for_box_at_caret'], INDICATORSTYLE.STRAIGHTBOX, (238,121,159), 0, 255, True, editorX)
                      

                      to this:

                      indicatorOptionsSet(BH__dict['indic_for_box_at_caret'], INDICATORSTYLE.STRAIGHTBOX, (255, 255, 0), 100, 50, True, editorX)
                      

                      Some other notes I forgot to add earlier:

                      • I called the script BracketHighlighter.py
                      • a lot of the naming in the script starts with BH__ ; that stand for “Bracket Highlighter”
                      1 Reply Last reply Reply Quote 2
                      • linpengchengL
                        linpengcheng
                        last edited by linpengcheng

                        @Scott Sumner
                        thanks! It looks better. It is a very useful bracket tool, if you push it to github, it’s easier for more people to search for it.
                        parentheses are a well-known feature of the Lisp language. :-)
                        Clojure is a lisp on JVM. https://clojure.org/
                        hylang is clojure on python. http://docs.hylang.org/en/stable/

                        1 Reply Last reply Reply Quote 0
                        • guy038G
                          guy038
                          last edited by guy038

                          @scott-sumner and All,

                          Oh, Scott, I missed this nice script ! Luckily, thanks to @BENielsen post and your reply to him, I’m, now, aware of this “old” script and was able to test it !

                          First, I added the <> couple to the dictionary

                          changing the script’s code :

                                  get_opening_char_via_closing_char_dict = {
                                      ')' : '(',
                                      ']' : '[',
                                      '}' : '{',
                                      }
                          

                          into

                                  get_opening_char_via_closing_char_dict = {
                                      ')' : '(',
                                      ']' : '[',
                                      '}' : '{',
                          			'>' : '<',
                                      }
                          

                          I personally like the second visual effect, with the light yellow highlighting ! Again, an awesome Python script of yours !


                          Just two things, that I noticed :

                          • When a couple of the symbols, above, is empty, I mean, for instance, syntases as {} or <>, your script does not highlight these two symbols. Just anecdotal !

                          • More annoying : when one of the symbols, of a matched section, is not visible in main editor windows, the highlighting disappears, too !

                          Easy to test it : just write the two lines, below :

                          (test start
                          test end)
                          

                          And add, successively some blank lines, between these two lines, untill, either, the line (test start or the line test end) becomes invisible => The yellow highlighting, then, disappears !


                          Now, you knows me, don’t you ? I tried to simulate your Python script with an appropriate regex ! I looked for, more than a complete day ! Pheeeeeeeew !

                          The main difficulty was to take these four couples of symbols, into account, at the same time ! Moreover, because of possible nested sequence of characters, between two matched symbols, recursive regex syntaxes are really mandatory !

                          So, here is, below, the regex, in fre-spacing mode, which has an analogue behaviour as your Python script :

                          (?x)  (  [(]  (  [^](){}<>[]  |  (?1)  |  (?3)  |  (?5)  |  (?7)  )*  [)]  )  |  # looks for a () BLOCK, with any MATCHED block inside  OR
                                (  [{]  (  [^](){}<>[]  |  (?1)  |  (?3)  |  (?5)  |  (?7)  )*  [}]  )  |  # looks for a {} BLOCK, with any MATCHED block inside  OR
                                (  [[]  (  [^](){}<>[]  |  (?1)  |  (?3)  |  (?5)  |  (?7)  )*  []]  )  |  # looks for a [] BLOCK, with any MATCHED block inside  OR
                                (  [<]  (  [^](){}<>[]  |  (?1)  |  (?3)  |  (?5)  |  (?7)  )*  [>]  )     # looks for a <> BLOCK, with any MATCHED block inside
                          

                          Now :

                          • Select this text

                          • Copy it with Ctrl + C

                          • Open the Find dialog

                          • Paste it, with Ctrl + V, in the Find what: zone

                          • Select the Regular expression search mode

                          • Place the caret, at any location of your file

                          • Click on the Find Next button and enjoy !

                          Et voilà !


                          Notes :

                          • At beginning, the (?x syntax forces the free-spacing mode, which means that any space character is not taken in account, unless you use, either, the syntaxes [ ], \x20 or a space character preceded by the escape \ symbol

                          • Afterwards, you can see four analogue structures, surrounded by outer parentheses, There are alternatives, separated with the | symbol and stored as groups 1, 3, 5 and 7. In each of them, you notice that it looks for :

                            • The starting symbol of a well-matched block, between two square brackets, so [(], [{], [[] or [<], followed by, either :

                              • Any single allowed character, different of these four couples of symbols, so the characters ](){}<>[, inside a negative class [^....] of characters, so the regex [^](){}<>[]

                              • Any other well-matched block ( (...), {...}, [...] or <...> ) , thanks to a recursive call to one of the groups 1, 3, 5 or 7, as a subroutine, so the regex (?1)|(?3)|(?5)|(?7)

                            • These two syntaxes may be present or not, as many times as necessary, in case of juxtaposed chars/blocks, with the * quantifier

                            • The ending symbol of a well-matched block, between two square brackets too, so [)], [}], []] or [>]


                          Remarks :

                          • The short version of this regex, without the free-spacing mode, is :

                          ([(]([^](){}<>[]|(?1)|(?3)|(?5)|(?7))*[)])|([{]([^](){}<>[]|(?1)|(?3)|(?5)|(?7))*[}])|([[]([^](){}<>[]|(?1)|(?3)|(?5)|(?7))*[]])|([<]([^](){}<>[]|(?1)|(?3)|(?5)|(?7))*[>])

                          • Note that the [(] syntax may be replaced, as well, with the \( syntax and, of course, the [<] form is, simply, the regex < ! In that case, the short regex form becomes :

                          (\(([^](){}<>[]|(?1)|(?3)|(?5)|(?7))*\))|(\{([^](){}<>[]|(?1)|(?3)|(?5)|(?7))*\})|(\[([^](){}<>[]|(?1)|(?3)|(?5)|(?7))*\])|(<([^](){}<>[]|(?1)|(?3)|(?5)|(?7))*>)

                          However, there’s, also, an issue, with that regex ! It wrongly selects all the file contents :

                          • When text size, with multiple inner blocks, is over 32,000 characters about

                          • When text size, without any block, is over 35,800 characters about

                          Best Regards,

                          guy038

                          P.S. :

                          You may test my regex with the @linpengcheng clojure example, below, if you add the < and > symbols in dictionary :

                          (extend-type AsyncChannel  <bla<bla bla> bla >
                            Channel
                            (open? [ch] ( not (isClosed ch)))
                            (Close [ch] (.serverClose ch 1000))
                            (websocket? [ch] (.isWebsocket ch))
                            (send!
                              ([ch date] {abc} <def> (.send ch data ( not (websocket? ch))))
                              ([ch data close-after-send?] (.send ch data ( boolean close-after-send?))))
                              (on-receive [ch callback] (.setReceiveHandler ch callbac))
                              (in-close [ch callback] (.setCloseHandler ch callback)))
                          
                          Scott SumnerS 1 Reply Last reply Reply Quote 1
                          • Scott SumnerS
                            Scott Sumner @guy038
                            last edited by

                            @guy038

                            • I didn’t add < and > to the delimiters because in the programming languages I use most often, they are not delimiters, they are less-than and greater-than.
                            • Yes, I decided not to box in [], etal, because of the triviality – it is more annoying to see the box than to not see it in this case.
                            • The code was designed to consider only what is in the current viewing area, i.e., both the opening and closing delimiter need to be seen by the user. This is mainly for performance reasons (consider a huge file with the matching delimiters at the very start and very end of the file). If one wants to match delimiters that have longer distance between them, the native Notepad++ brace-matching commands can be used.
                            1 Reply Last reply Reply Quote 1
                            • chcgC
                              chcg
                              last edited by

                              @scott-sumner
                              Added your script under https://github.com/bruderstein/PythonScript/blob/master/scripts/Samples/BracketHighlighter.py. Hope that is ok for you. If you have more example scripts to add just let me know. Would be happy to add more of them.

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