# ============================================================================== # element_n(): returns the number of elements in the given string, using the # given delimiter. # # Parameters: # $1 - the string containing all elements # $2 - the delimiter (defaults to "\n", used as "case"; if empty, # space delimiters will be used: ie "[ \t\r\n\v\f]*"/"regex") # $3 - match type for delimiter (default "case": see $2) # Returns: # The number of elements: this is at least 1 (no delimiter found), unless # the array string was empty to start with. # ============================================================================== define element_n { array = $1 if (length(array) == 0) return 0 delim = "\n" if ($n_args >= 2) delim = $2 match_t = "case" if ($n_args >= 3) match_t = $3 if (delim == "") { delim = "[ \t\r\n\v\f]*" match_t = "regex" } dd_beg = 0 dd_end = 0 count = 0 for (dd_beg = search_string(array, delim, dd_end, match_t), \ dd_end = $search_end; \ dd_beg != -1; \ dd_beg = search_string(array, delim, dd_end, match_t), \ dd_end = $search_end) { count++ } return count + 1 } # ============================================================================== # element_i(): returns the element with the given index, where elements are # separated by the given delimiter in the given string. It fails if the # number of elements in the string is less than or equal to the index # (indexes start from zero). # # Parameters: # $1 - the string containing all elements # $2 - the index (must be a number) # $3 - the delimiter (defaults to "\n", used as "case"; if empty, # space delimiters will be used: ie "[ \t\r\n\v\f]*"/"regex") # $4 - match type for delimiter (default "case": see $3) # Returns: # The substring corresponding to the index, or the empty string on # failure. # ============================================================================== define element_i { array = $1 if (length(array) == 0) return "" index = $2 delim = "\n" if ($n_args >= 3) delim = $3 match_t = "case" if ($n_args >= 4) match_t = $4 if (delim == "") { delim = "[ \t\r\n\v\f]*" match_t = "regex" } ss_beg = 0 ss_end = 0 dd_beg = 0 dd_end = 0 found = 0 count = 0 if (index >= 0) { for (dd_beg = search_string(array, delim, dd_end, "regex"), \ dd_end = $search_end; \ count <= index; \ dd_beg = search_string(array, delim, dd_end, "regex"), \ dd_end = $search_end) { if (count == index) { if (dd_beg == -1) dd_beg = length(array) ss_end = dd_beg found = 1 break } else if (dd_beg == -1) break else { count++ ss_beg = dd_end } } } if (found) return substring(array, ss_beg, ss_end) return "" } # ============================================================================== # i_of_element(): returns the index of the given element, or -1 on failure. # The element must match exactly. # # Parameters: # $1 - the string containing all elements # $2 - the element to find # $3 - the delimiter (defaults to "\n", used as "case"; if empty, # space delimiters will be used: ie "[ \t\r\n\v\f]*"/"regex") # $4 - match type for delimiter (default "case": see $3) # Returns: # The index corresponding to the element, or -1 on failure. # ============================================================================== define i_of_element { array = $1 elem = $2 delim = "\n" if ($n_args >= 3) delim = $3 match_t = "case" if ($n_args >= 4) match_t = $4 if (delim == "") { delim = "[ \t\r\n\v\f]*" match_t = "regex" } ss_beg = 0 ss_end = 0 dd_beg = 0 dd_end = 0 found = 0 count = 0 for (dd_beg = search_string(array, delim, dd_end, "regex"), \ dd_end = $search_end; \ count >= 0; \ dd_beg = search_string(array, delim, dd_end, "regex"), \ dd_end = $search_end) { if (dd_beg == -1) { ss_end = length(array) if (substring(array, ss_beg, ss_end) == elem) return count else return -1 } else { ss_end = dd_beg if (substring(array, ss_beg, ss_end) == elem) return count count++ ss_beg = dd_end } } return -1 } # ============================================================================== # i_of_elempref(): returns the index of the first element in the array which # starts with the same string as that passed in, using an exact match. # Returns -1 on failure. # # Parameters: # $1 - the string containing all elements # $2 - the element prefix to find # $3 - the delimiter (defaults to "\n", used as "case"; if empty, # space delimiters will be used: ie "[ \t\r\n\v\f]*"/"regex") # $4 - match type for delimiter (default "case": see $3) # Returns: # The index corresponding to the element, or -1 on failure. # ============================================================================== define i_of_elempref { array = $1 elem_pref = $2 len_pref = length(elem_pref) elem = "" delim = "\n" if ($n_args >= 3) delim = $3 match_t = "case" if ($n_args >= 4) match_t = $4 if (delim == "") { delim = "[ \t\r\n\v\f]*" match_t = "regex" } ss_beg = 0 ss_end = 0 dd_beg = 0 dd_end = 0 found = 0 count = 0 for (dd_beg = search_string(array, delim, dd_end, "regex"), \ dd_end = $search_end; \ count >= 0; \ dd_beg = search_string(array, delim, dd_end, "regex"), \ dd_end = $search_end) { if (dd_beg == -1) { ss_end = length(array) elem = substring(array, ss_beg, ss_end) if (substring(elem, 0, len_pref) == elem_pref) return count else return -1 } else { ss_end = dd_beg elem = substring(array, ss_beg, ss_end) if (substring(elem, 0, len_pref) == elem_pref) return count count++ ss_beg = dd_end } } return -1 } # ============================================================================== # elem_of_elempref(): returns the first element in the array which # starts with the same string as that passed in, using an exact match. # Returns "" on failure. # # Parameters: # $1 - the string containing all elements # $2 - the element prefix to find # $3 - the delimiter (defaults to "\n", used as "case"; if empty, # space delimiters will be used: ie "[ \t\r\n\v\f]*"/"regex") # $4 - match type for delimiter (default "case": see $3) # Returns: # The corresponding element, or "" on failure. # ============================================================================== define elem_of_elempref { array = $1 elem_pref = $2 len_pref = length(elem_pref) elem = "" delim = "\n" if ($n_args >= 3) delim = $3 match_t = "case" if ($n_args >= 4) match_t = $4 if (delim == "") { delim = "[ \t\r\n\v\f]*" match_t = "regex" } ss_beg = 0 ss_end = 0 dd_beg = 0 dd_end = 0 found = 0 count = 0 for (dd_beg = search_string(array, delim, dd_end, "regex"), \ dd_end = $search_end; \ count >= 0; \ dd_beg = search_string(array, delim, dd_end, "regex"), \ dd_end = $search_end) { if (dd_beg == -1) { ss_end = length(array) elem = substring(array, ss_beg, ss_end) if (substring(elem, 0, len_pref) == elem_pref) return elem else return "" } else { ss_end = dd_beg elem = substring(array, ss_beg, ss_end) if (substring(elem, 0, len_pref) == elem_pref) return elem count++ ss_beg = dd_end } } return "" } # ============================================================================== # element_assoc(): searches for the first element in the array which starts # with the same string as that passed in, using elem_of_elempref(). # Returns the rest of the element if successful, "" on failure. # # Parameters: # $1 - the string containing all elements # $2 - the element prefix to find # $3 - the delimiter (defaults to "\n", used as "case"; if empty, # space delimiters will be used: ie "[ \t\r\n\v\f]*"/"regex") # $4 - match type for delimiter (default "case": see $3) # Returns: # The suffix of the element, after removing $1, or "" on failure. # ============================================================================== define element_assoc { array = $1 elem_pref = $2 len_pref = length(elem_pref) elem = "" delim = "\n" if ($n_args >= 3) delim = $3 match_t = "case" if ($n_args >= 4) match_t = $4 if (delim == "") { delim = "[ \t\r\n\v\f]*" match_t = "regex" } # find our element elem = elem_of_elempref(array, elem_pref, delim, match_t) if (elem == "") return "" return substring(elem, length(elem_pref), length(elem)) } # ============================================================================== # element_from_to(): returns a new array, holding only those elements between # the specified indices. # # Parameters: # $1 - the string containing all elements # $2 - the first index (numbered from zero) # $3 - the last index (optional, defaults to the last index number if # not present or negative) # $4 - the delimiter (defaults to "\n", used as "case"; if empty, # space delimiters will be used: ie "[ \t\r\n\v\f]*"/"regex") # $5 - match type for delimiter (default "case": see $4) # Returns: # The substring of the original array, corresponding to the index range # given. Fails with an empty string. If the last index is beyond the # end of the array, this is not cause for failure. # ============================================================================== define element_from_to { array = $1 i_from = $2 i_to = -1 if ($n_args >= 3) i_to = $3 delim = "\n" if ($n_args >= 4) delim = $4 match_t = "case" if ($n_args >= 5) match_t = $5 if (delim == "") { delim = "[ \t\r\n\v\f]*" match_t = "regex" } if (i_from < 0 || (i_from > i_to && i_to >= 0)) return "" ss_beg = 0 ss_end = length(array) dd_beg = 0 dd_end = 0 found = 0 count = 0 index = i_from if (index >= 0) { for (dd_beg = search_string(array, delim, dd_end, "regex"), \ dd_end = $search_end; \ count <= index; \ dd_beg = search_string(array, delim, dd_end, "regex"), \ dd_end = $search_end) { if (count == index) { found = 1 if (dd_beg == -1 || i_to < 0) { ss_end = length(array) break } ss_end = dd_beg index++ if (index > i_to || dd_beg == -1) break } else if (dd_beg == -1) break else if (!found) { if (count < index) ss_beg = dd_end } count++ } } if (found) return substring(array, ss_beg, ss_end) return "" } # ============================================================================== # This file contains the following functions: # fold_infold # fold_infoldline # fold_expand # fold_comment # fold_selection # fold_bracket_match # fold_custom_match # fold_bracket_or_custom_match # ============================================================================== # ============================================================================== # fold_infold(): checks whether the position passed lies within previously # folded text, returning zero if not # Parameters # $1 - position to test for inclusion in a fold # Returns # fold nesting level at the position to test # ============================================================================== define fold_infold { pos0 = $1 count = 0 # currently known fold nesting level # search backwards in the file for fold directives, checking fold # open and close sequences for (pos = search("\\f\\[\\[\\[|\\]\\]\\]\\f", pos0, "regex", "backward"); \ pos != -1; \ pos = search("\\f\\[\\[\\[|\\]\\]\\]\\f", pos, "regex", "backward")) { pos_end = $search_end if (get_range(pos, pos_end) == "\f[[[") count++ else # if (get_range(pos, pos_end) == "\f]]]") count-- pos-- } return count } # ============================================================================== # fold_infoldline(): checks whether the position passed indicates a position # which is contained in a fold on the position's line (a cheaper test # than fold_infold(), which checks the whole file), returning zero if # not in a fold # Parameters # $1 - position to test for inclusion in a fold # Returns # fold nesting level at the position to test # ============================================================================== define fold_infoldline { pos0 = $1 count = 0 # currently known fold nesting level # find the start of the current line pos_start = search("\n", pos0 - 1, "case", "backward") if (pos_start == -1) pos_start = 0 else pos_start = $search_end # find the end of the current line pos_end = search("\n", pos0, "case", "forward") if (pos_end == -1) pos_end = $text_length # find the test position relative to the line start pos_here = pos0 - pos_start # pick up the current line for searching s = get_range(pos_start, pos_end) # search in the line for fold directives, checking fold # open and close sequences for (pos = search_string(s, "\\f\\[\\[\\[|\\]\\]\\]\\f", 0, "regex"); \ pos != -1 && pos < pos_here; \ pos = search_string(s, "\\f\\[\\[\\[|\\]\\]\\]\\f", pos + 1, "regex")) { pos_end = $search_end if (substring(s, pos, pos_end) == "\f[[[") count++ else # if (substring(s, pos, pos_end) == "\f]]]") count-- } return count } # ============================================================================== # fold_expand(): expands the first fold forward, starting from the start of the # current line, preserving nested folds. If no folds exist forward from # the start position, the search will wrap around to the first fold in # the file. The cursor is left at the start of the unfolded text, if any. # Parameters # None - searches from the current position in the file for the start # of the fold to expand. # Returns # Nothing # ============================================================================== define fold_expand { deselect_all() pos_init = $cursor # go to start of current line beginning_of_line() pos0 = $cursor a = "" # unfolded text P = "" # first part of fold string M = "" # fold directive matched pos = pos0 # fold directive start position old_pos = pos0 pos_end = pos0 # fold directive end position level = 0 # fold nesting level found = 0 # found a valid fold? # find start and end of fold - be careful of included folds for (pos = search("\\[\\+\\] ---------------------------------------------------------------------------- \\f\\[\\[\\[([^\\f]*\\|\\|\\|\\f)?|\\]\\]\\]\\f", \ old_pos, "regex", "forward", "wrap"); \ pos != -1; \ old_pos = pos_end, \ pos = search("\\[\\+\\] ---------------------------------------------------------------------------- \\f\\[\\[\\[([^\\f]*\\|\\|\\|\\f)?|\\]\\]\\]\\f", \ old_pos, "regex", "forward")) { pos_end = $search_end if (old_pos == pos0) { old_pos = pos pos0 = pos } found = 1 P = get_range(old_pos, pos) M = get_range(pos, pos_end) if (substring(M, 0, 92) == "[+] ---------------------------------------------------------------------------- \f[[[") { if (level == 0) a = a P else if (level == 1) { if (search_string(P, "\v", 0, "case") != -1) a = a replace_in_string(P, "\v", "\n", "case") M else a = a P M } else a = a P M level++ } else if (substring(M, 0, 4) == "]]]\f") { level-- if (level == 0) { if (search_string(P, "\v", 0, "case") != -1) a = a replace_in_string(P, "\v", "\n", "case") else a = a P break } else if (level >= 1) a = a P M } else # end of string found { if (level != 0) { found = 0 # problem: give up break } } } if (found) { select(pos0, pos_end) replace_selection("[-]" a) beginning_of_line() deselect_all() found_mark= search("{", pos0, "case", "forward") set_cursor_pos(found_mark+1) return 1 } else { set_cursor_pos(pos_init) } return 0 } # ============================================================================== # Tables to fetch match strings for comment extraction in fold_comment(). # Each comment type has a name, which can be shared between a number of # different languages. # # $fold_comment_type gives the "name" of the comment type to use for the given # language. Each element (separated by \n) has two parts: the language # name and the comment type, separated by @. The comment type can in fact # be a list, containing a number of different comment type names, # separated by \t. # $fold_comment_match contains the match strings required for each comment type. # Each element (separated by \n) starts with the type "name", followed by # @, then by (upto) three regex match strings, separated by \t. The first # string matches the start of a comment. The second string matches the end # of the comment. The third string matches anything which, if found before # a comment, makes the comment unsuitable for use as a fold comment. The # values chosen are designed to make ANY non-comment text found in front # of the first comment of the to-be-folded selection invalidate that # comment as a fold comment. In other words, if the the to-be-folded # selection does not start with a comment, the fold will not have a # comment. # ============================================================================== $fold_comment_type = "C" "@" "/*" "\n" \ "Yacc" "@" "/*" "\n" \ "Verilog" "@" "/*" "\n" \ \ "C++" "@" "//\t/*" "\n" \ "Java" "@" "//\t/*" "\n" \ "JavaScript" "@" "//\t/*" "\n" \ \ "Perl" "@" "#" "\n" \ "Python" "@" "#" "\n" \ "Tcl" "@" "#" "\n" \ "Awk" "@" "#" "\n" \ "Sh Ksh Bash" "@" "#" "\n" \ "Csh" "@" "#" "\n" \ "Makefile" "@" "#" "\n" \ "NEdit Macro" "@" "#" "\n" \ \ "X Resources" "@" "!" "\n" \ \ "Ada" "@" "--" "\n" \ "VHDL" "@" "--" "\n" \ \ "Fortran" "@" "Frt" "\n" \ \ "Pascal" "@" "{}\t(*)" "\n" \ \ "LaTeX" "@" "%" "\n" \ "" # ^^^^^^^^^^^^^ ^^^^^ # the language comment type $fold_comment_match = \ "/*" "@" "/\\*" "\t" "\\*/" "\t" "[^/ \\t\\n]|(/[^*])" "\n" \ "//" "@" "//" "\t" "\\n" "\t" "[^/ \\t\\n]|(/[^/])" "\n" \ "#" "@" "#" "\t" "\\n" "\t" "[^# \\t\\n]" "\n" \ "!" "@" "!" "\t" "\\n" "\t" "[^! \\t\\n]" "\n" \ "--" "@" "--" "\t" "\\n" "\t" "[^- \\t\\n]|(-[^-])" "\n" \ "Frt" "@" "^[Cc*!]" "\t" "\\n" "\t" "^[^Cc*!]" "\n" \ "(*)" "@" "\(\*" "\t" "\*\)" "\t" "[^( \\t\\n]|(\([^*])" "\n" \ "{}" "@" "{" "\t" "}" "\t" "[^{ \\t\\n]" "\n" \ "%" "@" "%" "\t" "\\n" "\t" "[^% \\t\\n]" "\n" \ "" #^^^^^ ^^^^^^^^^^ ^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^ #type cnmt start cmnt end cmnt fail # ============================================================================== # ============================================================================== # fold_comment(): uses the current language to select a comment type and find # appropriate text for a fold comment. Returns the comment text, if any. # Parameters: # $1 - the selected text from which to take the comments # $2 - initial length to ignore in the search # Returns: # The comment string to use. # ============================================================================== define fold_comment { s = $1 skip = $2 m_typelist = element_assoc($fold_comment_type, $language_mode"@") if (m_typelist == "") # language not recognised return "" # so no valid fold comment p_start_text = 0 p_stop = 0 m_type = "" i = 0 valid = 0 for (m_type = element_i(m_typelist, i, "\t"); \ m_type != "" && !valid; \ m_type = element_i(m_typelist, ++i, "\t")) { m_match = element_assoc($fold_comment_match, m_type"@") if (m_match == "") # comment type not found continue # so no valid fold comment m_start = element_i(m_match, 0, "\t") m_stop = element_i(m_match, 1, "\t") m_fail = element_i(m_match, 2, "\t") if (m_start == "") # no comment match type found continue # so no valid fold comment p_start = search_string(s, m_start, skip, "regex") if (p_start == -1) # no comments in text continue # so no valid fold comment # we will strip off the start of the comment to get only the text p_start_text = $search_end p_stop = search_string(s, m_stop, p_start_text, "regex") if (p_stop == -1) # no end of comment found p_stop = length(s) # so assume comment runs to end of text p_fail = length(s) if (m_fail != "") p_fail = search_string(s, m_fail, skip, "regex") if (p_fail != -1 && p_fail < p_start) # fail matched earlier than comment continue # so no valid fold comment # check against a fold start sequence p_fail = search_string(s, "\f[[[", 0, "case") if (p_fail != -1 && p_fail < p_start) # comment already in a fold continue # so no valid fold comment # if we get here, all is OK valid = 1 } if (!valid) return "" # extract the comment text s = substring(s, p_start_text, p_stop) # compress multiple spaces, remove leading/trailing spaces ss = replace_in_string(s, "[ \\n\\t]+", " ", "regex") if (ss != "") s = ss ss = replace_in_string(s, "^ +", "", "regex") if (ss != "") s = ss ss = replace_in_string(s, " +$", "", "regex") if (ss != "") s = ss return s } # ============================================================================== # fold_selection(): collapses the current selection into a fold. The cursor is # left at the start of the folded text. # Parameters # $1 - skip this number of characters before looking for fold comments. # Defaults to zero. # Returns # Nothing. No folding is performed if either extreme of the selection is # inside a fold. # ============================================================================== define fold_selection { skip = 0 if ($n_args > 0) skip = $1 # You can use tests including fold_infold() calls for thorough checking. # Since these seriously slow things down, it is probably better just to # suppose our user is well-behaved (NOT very likely!). # # if ($selection_start != -1 && \ # $selection_left == -1 && \ # !fold_infold($selection_start) && \ # !fold_infold($selection_end)) # # Here the actual test we use just looks at the first and last lines of # the selection: much less thorough, but probably adequate, and faster. if ($selection_start != -1 && $selection_left == -1 && \ !fold_infoldline($selection_start) && !fold_infoldline($selection_end)) { pos_init = $selection_start s = get_selection() # fold text t = fold_comment(s, skip) if (t != "") t = t "|||\f" if (search_string(s, "\n", 0, "case") != -1) s = replace_in_string(s, "\n", "\v", "case") replace_selection("\f[[[" t s "]]]\f") set_cursor_pos(pos_init) found_mark= search("[-]", pos_init, "case", "backward") # t_print("the value of found_mark is: " found_mark) if (found_mark > -1 && found_mark >= pos_init-3) { select(found_mark, found_mark+3) replace_selection("[+] ---------------------------------------------------------------------------- ") } set_cursor_pos(pos_init) return 1 # all OK, apparently } return 0 # not good } # ============================================================================== # fold_bracket_match(): collapses from the current open bracket to the matched # close bracket, found using the select_to_matching() function. # Parameters # None - takes the current cursor position and attempts to find a select_to_matching() # to fold. # Returns # True if successful. Calls fold_selection() on select_to_matching()ed text, if any. # ============================================================================== define fold_bracket_match { deselect_all() pos_init = $cursor # t_print("pos_init=" pos_init "\n") # select_to_matching() works when were positioned AFTER the bracket cc = $cursor-1 if (cc == -1) { # are we at the start of a file buffer? if so, move forward a bit forward_character() cc++ } # is the character matchable? if not, move forward a bit c = get_character(cc) if (search_string("{}()[]<>\"", c, 0) == -1) { forward_character() cc++ c = get_character(cc) } # <> # # is the character stil not matchable? if not, find an opening brace # if (search_string("{}()[]<>\"", c, 0) == -1) # { # cc = search("{", cc, "backward") # if (cc >= 0) # set_cursor_pos(cc+1) # c = get_character(cc) # } if (search_string("{}()[]<>\"", c, 0) != -1) select_to_matching() if ($selection_start == -1) { set_cursor_pos(pos_init) return 0 } else if (!fold_selection(1)) { deselect_all() set_cursor_pos(pos_init) return 0 } return 1 } # ============================================================================== # $fold_custom_comment_match is accessed in the same way as $fold_comment_match, # i.e. by comment type. # The examples here use + to indicate a fold-start, - to indicate a # fold-end. # eg in C++'s // comments, the following would constitute a fold: # # //+ do stuff # stuff # //- end of do stuff # # When folded, the fold comment will be "+ do stuff" # ============================================================================== $fold_custom_comment_match = \ "/*" "@" "/\\*\\+" "\t" "/\\*-" "\t" "\\*/" "\n" \ "//" "@" "//\\+" "\t" "//-" "\t" "$" "\n" \ "#" "@" "#\\+" "\t" "#-" "\t" "$" "\n" \ "!" "@" "!\\+" "\t" "!-" "\t" "$" "\n" \ "--" "@" "--\\+" "\t" "---" "\t" "$" "\n" \ "Frt" "@" "^[Cc*!]\\+" "\t" "^[Cc*!]-" "\t" "$" "\n" \ "(*)" "@" "\\(\\*\\+" "\t" "\\(\\*-" "\t" "\*\)" "\n" \ "{}" "@" "{\\+" "\t" "{-" "\t" "}" "\n" \ "%" "@" "%\\+" "\t" "%-" "\t" "$" "\n" \ "" #^^^^^ ^^^^^^^^^^ ^^^^^^^^^^^^ ^^^^^^^^^^^ #type open start close start comment end # ============================================================================== # ============================================================================== #+fold_custom_match(): searches forward for a language-specific start-fold # comment marker, then tries to find a matching end-fold marker, taking # fold depth into account. Selects the text between, and folds. # Parameters # None - takes the current cursor position and attempts to find a match # to fold. # Returns # True if successful. Calls fold_selection() on matched text, if any. # Warning # The custom matching does NOT take into account the presence of folds: it # may match a start-fold marker with an end-fold marker, either or both of # which may already be in folds. If this is the case, hopefully # fold_selection() will fail. #-============================================================================== define fold_custom_match { if ($selection_start != -1) pos_init = $selection_start else pos_init = $cursor deselect_all() # make sure pos_start can "step back" pos_start = pos_init - 1 if (pos_start == -1) pos_start = 0 # get the comment info m_typelist = element_assoc($fold_comment_type, $language_mode"@") if (m_typelist == "") # language not recognised return 0 # so no valid fold match m_type = "" i = 0 valid = 0 m_open = "" m_close = "" m_stop = "" got_open = $text_length got_open_end = 0 got_match = "" got_dist = $text_length for (m_type = element_i(m_typelist, i, "\t"); \ m_type != ""; \ m_type = element_i(m_typelist, ++i, "\t")) { m_match = element_assoc($fold_custom_comment_match, m_type"@") if (m_match == "") # comment type not found continue # so no valid fold match m_open = element_i(m_match, 0, "\t") m_close = element_i(m_match, 1, "\t") m_stop = element_i(m_match, 2, "\t") # try to find an open, first looking back... found_open = search(m_open, pos_start, "regex", "backward") if (found_open == -1) # then forward found_open = search(m_open, pos_start, "regex", "forward") # find the closest to the initial position dist = pos_init - found_open if (dist < 0) dist = -dist if (found_open != -1 && dist < got_dist) { got_dist = dist # keep shortest distance so far got_open = found_open # keep this position got_open_end = $search_end got_match = m_match # keep match patterns } } if (got_open == $text_length) # no fold match comment open found return 0 # so no valid fold match # restore match patterns m_match = got_match m_open = element_i(m_match, 0, "\t") m_close = element_i(m_match, 1, "\t") m_stop = element_i(m_match, 2, "\t") # now we must find the (nested) match next_end = got_open_end next_close = got_open_end got_close = 0 level = 1 for (next_open = search(m_open, next_end, "regex", "forward"), \ next_open_e = $search_end, \ next_close = search(m_close, next_end, "regex", "forward"), \ next_close_e = $search_end; \ \ next_open != -1 || next_close != -1; \ \ next_open = search(m_open, next_end, "regex", "forward"), \ next_open_e = $search_end, \ next_close = search(m_close, next_end, "regex", "forward"), \ next_close_e = $search_end) { # avoid mistakes if (next_open != -1) { if (next_close == -1) next_close = next_open + 1 } if (next_close != -1) { if (next_open == -1) next_open = next_close + 1 } # so what did we get first? if (next_open < next_close) { level++ next_end = next_open_e } else # if (next_open >= next_close) { level-- next_end = next_close_e next_close_e = search(m_stop, next_close_e, "regex", "forward") if (next_close_e != -1) got_close = $search_end } if (level == 0) break } # did we actually close? if (level != 0) got_close = -1 # finally, can we actually select the area to be folded? if (got_close > got_open) select(got_open, got_close) # right: let's go for it! if ($selection_start == -1) { set_cursor_pos(pos_init) return 0 } else if (!fold_selection()) { deselect_all() set_cursor_pos(pos_init) return 0 } return 1 } # ============================================================================== # fold_bracket_or_custom_match(): attempts a bracket match - if that fails, # attempts a custom comment match. # Parameters # None - takes the current cursor position and attempts to find a select_to_matching() # to fold. # Returns # True if successful. Calls fold_selection() on select_to_matching()ed text, if any. # ============================================================================== define fold_bracket_or_custom_match { res = fold_bracket_match() if (!res) { res = fold_custom_match() } return res }