How to add " after user inputs " into a textbox?

The title isn’t very helpful but I’m wanting a textbox to instantly add a " after typing a ".

User Input: Blah Blah Blah "Blah
Textbox: Blah Blah Blah ""Blah

It would also be nice to set the cursor (or focus) in between the double quotes

User Input: Blah Blah Blah "Blah
Textbox: Blah Blah Blah “Blah”

This should occur with ’ too
Brackets like [, {, <, and ( should have the exact same affect but instead of being duplicated it would be flipped

User Input: {H [E <L (P
Textbox: {H} [E] (P)

Again, after typing in the brackets it would move the cursor (or focus) in between

I’m not exactly good with this, but I will try to help here. I believe there is a CursorPosition property of a text box, so It may go something like this:

local adds = {
    ['"'] = '"',
    ["("] = ")",
    ["{"] = "}",
    ["["] = "]",
    ["<"] = ">"
}

local textbox = --textbox
local prevtext = ""
local conn

function createconnection()
    conn = textbox:GetPropertyChangedSignal("Text"):Connect(function()
        conn:Disconnect()
        local pos = textbox.CursorPosition
        local typed = string.sub(textbox.Text, pos-1, pos)
        
        if adds[typed] then
            local newtext = string.sub(textbox.Text, 1, pos)..adds[typed]
            if pos < string.len(textbox.Text) then
                newtext ..= string.sub(textbox.Text, pos+1, textbox.Text)
            end
            textbox.Text = newtext
        end
        
        prevtext = textbox.Text
        textbox.CursorPosition = pos
        createconnection()
    end)
end

createconnection()

Now, keep in mind that I haven’t tested this myself, so be prepared for some mistakes.

Okay, so I did just test this myself. It worked quite well and a few errors that needed fixing (like typing inside of a pair of quotation marks or with any character after where you were typing didn’t work, the positioning was off after fixing that, etc.) But, I have fixed it.

local adds = {
	['"'] = '"',
	["("] = ")",
	["{"] = "}",
	["["] = "]",
	["<"] = ">"
}

local textbox = script.Parent.TextBox
local prevtext = ""
local conn

function createconnection()
	conn = textbox:GetPropertyChangedSignal("Text"):Connect(function()
		conn:Disconnect()
		local pos = textbox.CursorPosition
		
		if string.len(textbox.Text) > string.len(prevtext) then
			local typed = string.sub(textbox.Text, pos-1, pos-1)

			if adds[typed] then
				local newtext = string.sub(textbox.Text, 1, pos-1)..adds[typed]
				if pos < string.len(textbox.Text) then
					newtext ..= string.sub(textbox.Text, pos, textbox.Text)
				end
				textbox.Text = newtext
			end
		end
		
		prevtext = textbox.Text
		textbox.CursorPosition = pos
		createconnection()
	end)
end

createconnection()

This works for the most part but when you type (" instead of outputting ("") it outputs (""
When typing blah"blah it breaks and says

invalid argument #3 to 'sub' (number expected, got string)

at

local newtext = string.sub(textbox.Text, 1, pos-1)..adds[typed]

It only errors here when i go backtrack to blahblah and add " in the middle of the two blahs

Alright, I fixed those errors you mentioned (one missing a =, another forgetting a string.len(), and another not checking for deleting letters.)

local adds = {
	['"'] = '"',
	["("] = ")",
	["{"] = "}",
	["["] = "]",
	["<"] = ">"
}

local textbox = script.Parent.TextBox
local prevtext = ""
local conn

function createconnection()
	conn = textbox:GetPropertyChangedSignal("Text"):Connect(function()
		conn:Disconnect()
		local pos = textbox.CursorPosition
		
		if string.len(textbox.Text) > string.len(prevtext) then
			local typed = string.sub(textbox.Text, pos-1, pos-1)

			if adds[typed] then
				local newtext = string.sub(textbox.Text, 1, pos-1)..adds[typed]
				if pos <= string.len(textbox.Text) then
					newtext ..= string.sub(textbox.Text, pos, string.len(textbox.Text))
				end
				textbox.Text = newtext
			end
		end
		
		prevtext = textbox.Text
		textbox.CursorPosition = pos
		createconnection()
	end)
end

createconnection()

Works perfectly thank you so much

Actually i just found a different error, when I type something in between the () like (t) and then delete the t instead or returning to () it instead returns ())))

local Game = game
local Script = script
local UserInputService = Game:GetService("UserInputService")
local TextBox = Script.Parent

local Connection

local function OnTextBoxTextChanged()
	Connection:Disconnect()
	TextBox.Text = string.gsub(TextBox.Text, "%p", {["\""] = "\"\"", ["'"] = "''", ["("] = "()", ["["] = "[]", ["{"] = "{}", ["<"] = "<>"})
	Connection = TextBox:GetPropertyChangedSignal("Text"):Connect(OnTextBoxTextChanged)
end

Connection = TextBox:GetPropertyChangedSignal("Text"):Connect(OnTextBoxTextChanged)

It works for the first input () but when you try to put " into it it returns ()" instead of ("")

It doesn’t do that for me. Here is the exact script I have:

local adds = {
	['"'] = '"',
	["("] = ")",
	["{"] = "}",
	["["] = "]",
	["<"] = ">"
}

local textbox = script.Parent.TextBox
local prevtext = ""
local conn

function createconnection()
	conn = textbox:GetPropertyChangedSignal("Text"):Connect(function()
		conn:Disconnect()
		local pos = textbox.CursorPosition
		
		if string.len(textbox.Text) > string.len(prevtext) then
			local typed = string.sub(textbox.Text, pos-1, pos-1)

			if adds[typed] then
				local newtext = string.sub(textbox.Text, 1, pos-1)..adds[typed]
				if pos <= string.len(textbox.Text) then
					newtext ..= string.sub(textbox.Text, pos, string.len(textbox.Text))
				end
				textbox.Text = newtext
			end
		end
		
		prevtext = textbox.Text
		textbox.CursorPosition = pos
		createconnection()
	end)
end

createconnection()

Here is the UI I used.
idkwhattocallit.rbxm (4.4 KB)

local Game = game
local Script = script
local UserInputService = Game:GetService("UserInputService")
local TextBox = Script.Parent

local Keys = {Quote = {"\"", "'"}, Nine = {")", ""}, LeftBracket = {"}", "]"}, Comma = {">", ""}}

local function OnInputBegan(InputObject)
	if not (TextBox:IsFocused()) then return end
	task.wait()
	local Position = TextBox.CursorPosition
	local Key = Keys[InputObject.KeyCode.Name]
	if not Key then return end
	local State = (UserInputService:IsKeyDown("LeftShift") or UserInputService:IsKeyDown("RightShift"))
	TextBox.Text = string.sub(TextBox.Text, 1, Position - 1)..(if State then Key[1] else Key[2])..string.sub(TextBox.Text, Position)
	TextBox.CursorPosition += 1
end

UserInputService.InputBegan:Connect(OnInputBegan)

https://gyazo.com/15cb452a89f22aef1388ac358c88694f

I forgot the

string.len(textbox.Text) > string.len(prevtext)

Got it working now thanks