@@ -2103,6 +2103,29 @@ const escape_defaults = merge!(
21032103 )
21042104
21052105
2106+ # Helper function to check and remove paired brackets/quotes
2107+ # Returns true if paired delimiters were removed, false otherwise
2108+ function try_remove_paired_delimiter (buf:: IOBuffer )
2109+ left_brackets = (' (' , ' {' , ' [' , ' "' , ' \' ' , ' `' )
2110+ right_brackets = (' )' , ' }' , ' ]' , ' "' , ' \' ' , ' `' )
2111+
2112+ if ! eof (buf) && position (buf) > 0
2113+ # Peek at char to the left
2114+ p = position (buf)
2115+ left_char = char_move_left (buf)
2116+ seek (buf, p)
2117+
2118+ i = findfirst (isequal (left_char), left_brackets)
2119+ if i != = nothing && peek (buf, Char) == right_brackets[i]
2120+ # Remove both the left and right bracket/quote
2121+ edit_delete (buf)
2122+ edit_backspace (buf)
2123+ return true
2124+ end
2125+ end
2126+ return false
2127+ end
2128+
21062129# Keymap for automatic bracket/quote insertion and completion
21072130const bracket_insert_keymap = AnyDict ()
21082131let
@@ -2125,37 +2148,33 @@ let
21252148 return c
21262149 end
21272150
2128- # Check if there's an unmatched opening quote before the cursor
2129- function has_unmatched_quote (buf:: IOBuffer , quote_char:: Char )
2130- pos = position (buf)
2131- content = String (buf. data[1 : pos])
2132- isempty (content) && return false
2133-
2134- # Count unescaped quotes before cursor position
2135- count = 0
2136- i = 1
2137- while i <= length (content)
2138- if content[i] == quote_char
2139- # Check if escaped by counting preceding backslashes
2140- num_backslashes = 0
2141- j = i - 1
2142- while j >= 1 && content[j] == ' \\ '
2143- num_backslashes += 1
2144- j -= 1
2145- end
2146- # If even number of backslashes (including zero), the quote is not escaped
2147- if num_backslashes % 2 == 0
2148- count += 1
2149- end
2150- end
2151- i = nextind (content, i)
2151+ # Check if we should auto-close a quote (insert paired quotes)
2152+ # auto-close when "transparent" chars on both sides
2153+ # Transparent chars: whitespace, opening brackets ([{, closing brackets )]}, or nothing
2154+ function should_auto_close_quote (buf:: IOBuffer , quote_char:: Char )
2155+ # Check left side: BOF, whitespace, or opening bracket
2156+ left_ok = if position (buf) == 0
2157+ true
2158+ else
2159+ left_char = peek_char_left (buf)
2160+ isspace (left_char) || left_char in (' (' , ' [' , ' {' )
2161+ end
2162+
2163+ # Check right side: EOF, whitespace, or closing bracket
2164+ right_ok = if eof (buf)
2165+ true
2166+ else
2167+ right_char = peek (buf, Char)
2168+ isspace (right_char) || right_char in (' )' , ' ]' , ' }' )
21522169 end
2153- return isodd (count)
2170+
2171+ return left_ok && right_ok
21542172 end
21552173
21562174 # Left/right bracket pairs
21572175 bracket_pairs = ((' (' , ' )' ), (' {' , ' }' ), (' [' , ' ]' ))
2158- right_brackets_ws = (' )' , ' }' , ' ]' , ' ' , ' \t ' , ' \n ' )
2176+ # Characters that are "transparent" for bracket auto-closing
2177+ right_brackets_ws = (' )' , ' }' , ' ]' , ' ' , ' \t ' , ' \n ' , ' "' , ' \' ' , ' `' )
21592178
21602179 for (left, right) in bracket_pairs
21612180 # Left bracket: insert both and move cursor between them
@@ -2191,14 +2210,13 @@ let
21912210 elseif position (buf) > 0 && should_skip_closing_bracket (peek_char_left (buf), quote_char)
21922211 # Don't auto-close (e.g., for transpose or triple quotes)
21932212 edit_insert (buf, quote_char)
2194- elseif quote_char in (' "' , ' \' ' , ' `' ) && has_unmatched_quote (buf, quote_char)
2195- # For quotes, check if we're closing an existing string
2196- edit_insert (buf, quote_char)
2197- else
2198- # Insert both quotes
2213+ elseif should_auto_close_quote (buf, quote_char)
21992214 edit_insert (buf, quote_char)
22002215 edit_insert (buf, quote_char)
22012216 edit_move_left (buf)
2217+ else
2218+ # Just insert single quote
2219+ edit_insert (buf, quote_char)
22022220 end
22032221 refresh_line (s)
22042222 end
@@ -2221,18 +2239,8 @@ let
22212239 end
22222240
22232241 buf = buffer (s)
2224- left_brackets = (' (' , ' {' , ' [' , ' "' , ' \' ' , ' `' )
2225- right_brackets = (' )' , ' }' , ' ]' , ' "' , ' \' ' , ' `' )
2226-
2227- if ! eof (buf) && position (buf) > 0
2228- left_char = peek_char_left (buf)
2229- i = findfirst (isequal (left_char), left_brackets)
2230- if i != = nothing && peek (buf, Char) == right_brackets[i]
2231- # Remove both the left and right bracket/quote
2232- edit_delete (buf)
2233- edit_backspace (buf)
2234- return refresh_line (s)
2235- end
2242+ if try_remove_paired_delimiter (buf)
2243+ return refresh_line (s)
22362244 end
22372245 return edit_backspace (s)
22382246 end
0 commit comments