IDE script to auto-enter closing paren and quote

This IDE script will intelligently close a quote or paren while you are typing in Xojo, emulating a feature found in other editors. If you enter a quote or opening paren, it will enter the closing character and position your cursor between. If you then “close” it again, it will remove the unneeded character and move the cursor for you.

I tied this to a Keyboard Maestro macro triggered by entering (, ), or ". When triggered by ), it delays by 0.2 seconds before running the script.

Enjoy.

//
// FUNCTIONS
//

Function IsEOLChar (char As String ) As Boolean
	static eolChars() as string = array( chr( 10 ), chr( 13 ) )
	return eolChars.IndexOf( char ) <> -1
End Function

//
// MAIN
//

if SelLength <> 0 then
	return
end if

dim pos as integer = SelStart
dim code as string = Text
dim codeLen as integer = code.Len

if code.Trim = "" then
	return
end if

//
// If we didn't just type the expected char then return
//
dim typedChar as string = code.Mid( pos, 1 )
dim typedIsParen as boolean = typedChar = "("  or typedChar = ")"
dim typedIsQuote as boolean = typedChar = """"

if not typedIsParen and not typedIsQuote then
	return
end if



dim isContinueChar as boolean = false
dim inComment as boolean = false
dim inQuote as boolean = false

dim codeLineStart as integer = 1

for i as integer = 1 to pos - 1
	dim thisChar as string = code.Mid( i, 1 )
	dim twoChars as string = code.Mid( i, 2 )
	dim fiveChars as string = code.Mid( i - 1 , 5 )
	
	if IsEOLChar( thisChar ) then
		inComment = false
		if isContinueChar then
			isContinueChar = false
		else
			codeLineStart = i + 1
		end if
		
	elseif inComment then
		//
		// Continue
		//
		
	elseif thisChar = """" then
		inQuote = not inQuote
		
	elseif inQuote then
		//
		// Continue
		//
		
	elseif thisChar = "'" or twoChars = "//" or fiveChars.Trim = "REM" then
		inComment = true
		
	elseif thisChar = "_" then
		isContinueChar = true
		
	elseif isContinueChar and thisChar <> " " and thisChar <> &u09 then
		isContinueChar = false
		
	end if
next

if inComment then
	return
end if

dim codeLineLength as integer = codeLen - codeLineStart + 1

//
// Get the length of this code block
//
for i as integer = pos to codeLen
	dim thisChar as string = code.Mid( i, 1 )
	dim twoChars as string = codeLine.Mid( i, 2 )
	dim fiveChars as string = codeLine.Mid( i - 1 , 5 )
	
	if IsEOLChar( thisChar ) then
		if isContinueChar then
			isContinueChar = false
			inComment = false
		else
			codeLineLength = i - codeLineStart
			exit
		end if
		
	elseif inComment then
		//
		// Continue
		//
		
	elseif thisChar = """" then
		inQuote = not inQuote
		
	elseif inQuote then
		//
		// Continue
		//
		
	elseif thisChar = "'" or twoChars = "//" or fiveChars.Trim = "REM" then
		inComment = true
		
	elseif thisChar = "_" then
		isContinueChar = true
		
	elseif isContinueChar and thisChar <> " " then
		isContinueChar = false
		
	end if
next

dim codeLine as string = code.Mid( codeLineStart, codeLineLength )
dim codePos as integer = pos - codeLineStart + 1

//
// See if the opening and closing parens already balance
// and get the nextChar
//
dim nextChar as string
dim nextCharCodePos as integer = codeLineLength + 1
dim parenCount as integer
dim quoteCount as integer
isContinueChar = false
inComment = false
inQuote = false

for i as integer = 1 to codeLineLength
	dim thisChar as string = codeLine.Mid( i, 1 )
	dim twoChars as string = codeLine.Mid( i, 2 )
	dim fiveChars as string = codeLine.Mid( i - 1 , 5 )
	
	if IsEOLChar( thisChar ) then
		inComment = false
		isContinueChar = false
		continue
		
	elseif inComment then
		continue
		
	elseif thisChar = """" then
		if inQuote then // Close quote
			quoteCount = quoteCount - 1
			inQuote = false
		else // Open quote 
			quoteCount = quoteCount + 1
			inQuote = true
		end if
		isContinueChar = false
		
	elseif inQuote then
		continue
		
	elseif thisChar = "'" or twoChars = "//" or fiveChars.Trim = "REM" then
		inComment = true
		
	elseif thisChar = "_" then
		isContinueChar = true
		
	elseif thisChar = "(" then
		parenCount = parenCount + 1
		isContinueChar = false
		
	elseif thisChar = ")" then
		parenCount = parenCount - 1
		isContinueChar = false
		
	elseif thisChar <> " " then
		isContinueChar = false
		
	end if
	
	//
	// Won't get here if it's EOL or inComment or inQuote
	//
	if not isContinueChar and _
			nextChar = "" and _
			i > codePos and _
			( typedIsQuote or ( thisChar <> " " and thisChar <> &u09 ) ) and _
			true then
	nextChar = thisChar
	nextCharCodePos = i
	end if
next

if ( typedIsQuote and quoteCount = 0 ) or ( typedIsParen and parenCount = 0 ) then
	//
	// Everything is already in balance, so let's not screw it up
	//
	return
end if

//
// We've met all the conditions, so do it
//
if ( typedChar = ")" and nextChar = ")" and parenCount < 0 ) or ( typedChar = """" and nextChar = """" and quoteCount > 0 ) then
	//
	// They have doubled up so removed the just typed character
	//
	SelStart = SelStart - 1
	SelLength = 1
	SelText = ""
	SelStart = codeLineStart + nextCharCodePos - 2
	
elseif typedChar = ")" then
	//
	// Leave it alone
	//
	
else // quote or open paren
	//
	// Insert the closing char
	//
	dim chars as string = if( typedIsQuote, """""", "()" )
	
	SelStart = SelStart - 1
	SelLength = 1
	SelText = chars
	SelStart = SelStart - 1
	
end if