From f4528df26eb6528fb9af6dcb1b7e36632c25c24a Mon Sep 17 00:00:00 2001
From: "roy.crippen4" <roy.crippen4@archarithms.com>
Date: Sun, 14 Jul 2024 13:24:34 -0400
Subject: [PATCH 1/5] fix(#199): Auto-tag react fragments

Detects if `<>` was typed in a react file (`js`, `jsx`, or `tsx`)
and autocloses the fragment tag.
---
 lua/nvim-ts-autotag/internal.lua | 39 ++++++++++++++++++++++++++++++++
 1 file changed, 39 insertions(+)

diff --git a/lua/nvim-ts-autotag/internal.lua b/lua/nvim-ts-autotag/internal.lua
index 61d0397..123fb04 100644
--- a/lua/nvim-ts-autotag/internal.lua
+++ b/lua/nvim-ts-autotag/internal.lua
@@ -242,6 +242,42 @@ local function check_close_tag(close_slash_tag)
     return false
 end
 
+local function is_react_file()
+    local ft = vim.bo.ft
+    -- check filetypes first.
+    if ft == 'javascriptreact' or 'typescriptreact' then
+        return true
+    elseif ft ~= 'javascript' then
+        return false
+    end
+
+    local ok, buf_parser = pcall(vim.treesitter.get_parser)
+    if not ok then
+        return false
+    end
+
+    local tree = buf_parser:parse(true)
+    if not tree then
+        return false
+    end
+
+    local root = tree[1]:root()
+    -- parse the tree for jsx nodes
+    local query = vim.treesitter.query.parse('javascript', [[
+        (jsx_element)
+        (jsx_self_closing_element)
+    ]])
+
+    -- iterate over nodes to find jsx nodes
+    for _, node in query:iter_captures(root, 0, 0, -1) do
+        if node then
+            return true
+        end
+    end
+    return false
+end
+
+
 M.close_tag = function()
     local ok, buf_parser = pcall(vim.treesitter.get_parser)
     if not ok then
@@ -252,6 +288,9 @@ M.close_tag = function()
     if result == true and tag_name ~= nil then
         vim.api.nvim_put({ string.format("</%s>", tag_name) }, "", true, false)
         vim.cmd([[normal! F>]])
+    elseif is_react_file() then
+        vim.api.nvim_put({ "</>" }, "", true, false)
+        vim.cmd([[normal! F>]])
     end
 end
 

From 4ab5a1fd9fac6a6ed0d381a7a7d02d3cb3f561e3 Mon Sep 17 00:00:00 2001
From: "roy.crippen4" <roy.crippen4@archarithms.com>
Date: Sun, 14 Jul 2024 16:48:35 -0400
Subject: [PATCH 2/5] test(react): Testing for auto-close react fragments in
 `js` and `tsx` files

---
 sample/index.js               | 23 +++++++++++++++++++++++
 tests/specs/closetag_spec.lua | 33 +++++++++++++++++++++------------
 2 files changed, 44 insertions(+), 12 deletions(-)
 create mode 100644 sample/index.js

diff --git a/sample/index.js b/sample/index.js
new file mode 100644
index 0000000..072cb5d
--- /dev/null
+++ b/sample/index.js
@@ -0,0 +1,23 @@
+
+import React, { useCallback, useEffect } from 'react'
+
+const SamplePage = () => {
+  const [state, setstate] = useState(initialState)
+
+
+  return (
+    <div className="h-full">
+
+
+
+
+
+
+
+
+
+    </div>
+  )
+}
+
+export default SamplePage
diff --git a/tests/specs/closetag_spec.lua b/tests/specs/closetag_spec.lua
index fc9f127..c2bda45 100644
--- a/tests/specs/closetag_spec.lua
+++ b/tests/specs/closetag_spec.lua
@@ -144,11 +144,20 @@ local data = {
         filetype = "typescriptreact",
         linenr = 12,
         key = [[>]],
-        before = [[<]],
-        after = [[<>|</>]]
+        before = [[<|<div></div>]],
+        after = [[<>|</><div></div>]]
     },
     {
-        name = "17 vue auto close tag",
+        name = "17 javascript autoclose fragment",
+        filepath = "./sample/index.js",
+        filetype = "javascript",
+        linenr = 12,
+        key = [[>]],
+        before = [[<|<div></div>]],
+        after = [[<>|</><div></div>]]
+    },
+    {
+        name = "18 vue auto close tag",
         filepath = "./sample/index.vue",
         filetype = "vue",
         linenr = 4,
@@ -157,7 +166,7 @@ local data = {
         after = [[<Img>|</Img>]],
     },
     {
-        name = "18 vue not close on script",
+        name = "19 vue not close on script",
         filepath = "./sample/index.vue",
         filetype = "vue",
         linenr = 12,
@@ -166,7 +175,7 @@ local data = {
         after = [[const data:Array<string>| ]],
     },
     {
-        name = "19 php div ",
+        name = "20 php div ",
         filepath = "./sample/index.php",
         filetype = "php",
         linenr = 25,
@@ -184,7 +193,7 @@ local data = {
     --     after = [[<div>|</div> ]],
     -- },
     {
-        name = "20 lit template div",
+        name = "21 lit template div",
         filepath = "./sample/index.ts",
         filetype = "typescript",
         linenr = 3,
@@ -193,7 +202,7 @@ local data = {
         after = [[<div>|</div> ]],
     },
     {
-        name = "21 eruby template div",
+        name = "22 eruby template div",
         filepath = "./sample/index.html.erb",
         filetype = "eruby",
         linenr = 10,
@@ -202,7 +211,7 @@ local data = {
         after = [[<div>|</div> ]],
     },
     {
-        name = "22 eruby template ruby string",
+        name = "23 eruby template ruby string",
         filepath = "./sample/index.html.erb",
         filetype = "eruby",
         linenr = 10,
@@ -211,7 +220,7 @@ local data = {
         after = [[<%= <div>| %> ]],
     },
     {
-        name = "23 templ close tag",
+        name = "24 templ close tag",
         filepath = "./sample/index.templ",
         filetype = "templ",
         linenr = 10,
@@ -220,7 +229,7 @@ local data = {
         after = [[<div>|</div>]],
     },
     {
-        name = "24 templ close tag",
+        name = "25 templ close tag",
         filepath = "./sample/index.templ",
         filetype = "templ",
         linenr = 10,
@@ -229,7 +238,7 @@ local data = {
         after = [[<div clas="laa">|</div>]],
     },
     {
-        name = "25 templ not close tag on close tag",
+        name = "26 templ not close tag on close tag",
         filepath = "./sample/index.templ",
         filetype = "templ",
         linenr = 10,
@@ -238,7 +247,7 @@ local data = {
         after = [[<div>aa</div>|]],
     },
     {
-        name = "26 templ not close on input tag",
+        name = "27 templ not close on input tag",
         filepath = "./sample/index.templ",
         filetype = "templ",
         linenr = 10,

From cbe184f12d7de8139c957e31d1b6271a9892a3dd Mon Sep 17 00:00:00 2001
From: "roy.crippen4" <roy.crippen4@archarithms.com>
Date: Sun, 14 Jul 2024 16:49:42 -0400
Subject: [PATCH 3/5] fix/refactor: Auto-close react fragments

Refactor: Moved detection functions into `utils.lua`
Fix: Improved react detection logic in `.js` files
---
 lua/nvim-ts-autotag/internal.lua | 38 +-------------------
 lua/nvim-ts-autotag/utils.lua    | 62 ++++++++++++++++++++++++++++++++
 2 files changed, 63 insertions(+), 37 deletions(-)

diff --git a/lua/nvim-ts-autotag/internal.lua b/lua/nvim-ts-autotag/internal.lua
index 0eced70..98ce103 100644
--- a/lua/nvim-ts-autotag/internal.lua
+++ b/lua/nvim-ts-autotag/internal.lua
@@ -242,42 +242,6 @@ local function check_close_tag(close_slash_tag)
     return false
 end
 
-local function is_react_file()
-    local ft = vim.bo.ft
-    -- check filetypes first.
-    if ft == "javascriptreact" or "typescriptreact" then
-        return true
-    elseif ft ~= "javascript" then
-        return false
-    end
-
-    local ok, buf_parser = pcall(vim.treesitter.get_parser)
-    if not ok then
-        return false
-    end
-
-    local tree = buf_parser:parse(true)
-    if not tree then
-        return false
-    end
-
-    local root = tree[1]:root()
-    -- parse the tree for jsx nodes
-    local query = vim.treesitter.query.parse("javascript", [[
-        (jsx_element)
-        (jsx_self_closing_element)
-    ]])
-
-    -- iterate over nodes to find jsx nodes
-    for _, node in query:iter_captures(root, 0, 0, -1) do
-        if node then
-            return true
-        end
-    end
-    return false
-end
-
-
 M.close_tag = function()
     local ok, buf_parser = pcall(vim.treesitter.get_parser)
     if not ok then
@@ -288,7 +252,7 @@ M.close_tag = function()
     if result == true and tag_name ~= nil then
         vim.api.nvim_put({ string.format("</%s>", tag_name) }, "", true, false)
         vim.cmd([[normal! F>]])
-    elseif is_react_file() then
+    elseif utils.is_react_file() and utils.is_react_fragment() then
         vim.api.nvim_put({ "</>" }, "", true, false)
         vim.cmd([[normal! F>]])
     end
diff --git a/lua/nvim-ts-autotag/utils.lua b/lua/nvim-ts-autotag/utils.lua
index ab5e276..b2c03f8 100644
--- a/lua/nvim-ts-autotag/utils.lua
+++ b/lua/nvim-ts-autotag/utils.lua
@@ -2,6 +2,68 @@ local log = require("nvim-ts-autotag._log")
 local get_node_text = vim.treesitter.get_node_text
 local M = {}
 
+---@return boolean
+function M.is_react_file()
+    local ft = vim.bo.ft
+    -- check filetypes first.
+    if ft == "javascriptreact" or ft == "typescriptreact" then
+        return true
+    elseif ft ~= "javascript" then
+        return false
+    end
+
+    local ok, buf_parser = pcall(vim.treesitter.get_parser)
+    if not ok then
+        return false
+    end
+
+    local tree = buf_parser:parse(true)
+    if not tree then
+        return false
+    end
+
+    local root = tree[1]:root()
+    local queries = { 'jsx_element', 'jsx_self_closing_element' }
+
+    for _, query in ipairs(queries) do
+        if M.node_exists(root, query) then
+            return true
+        end
+    end
+
+    return false
+end
+
+
+
+---@return boolean
+function M.is_react_fragment()
+    local line = vim.fn.getline(".")
+    local col = vim.fn.col(".") - 2
+    local strpart = vim.fn.strpart(line, col)
+    local char_at_cursor = vim.fn.strcharpart(strpart, 0, 1) ---@type string
+    return char_at_cursor == "<"
+end
+
+---@param node TSNode
+---@param query string
+---@return boolean
+function M.node_exists(node, query)
+    if node:type() == query then
+        return true
+    end
+
+    for child in node:iter_children() do
+        if M.node_exists(child, query) then
+            return true
+        end
+    end
+
+    return false
+end
+
+
+
 M.get_node_text = function(node)
     local _, txt = pcall(get_node_text, node, vim.api.nvim_get_current_buf())
     return vim.split(txt, "\n") or {}

From 2553ac552069736910dfd2a87ebb4ff2150c6549 Mon Sep 17 00:00:00 2001
From: roycrippen4 <54562558+roycrippen4@users.noreply.github.com>
Date: Sun, 14 Jul 2024 17:35:03 -0400
Subject: [PATCH 4/5] Update lua/nvim-ts-autotag/utils.lua

Co-authored-by: Price Hiller <price@orion-technologies.io>
---
 lua/nvim-ts-autotag/utils.lua | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/lua/nvim-ts-autotag/utils.lua b/lua/nvim-ts-autotag/utils.lua
index b2c03f8..0fdd0b7 100644
--- a/lua/nvim-ts-autotag/utils.lua
+++ b/lua/nvim-ts-autotag/utils.lua
@@ -11,7 +11,8 @@ function M.is_react_file()
     elseif ft ~= "javascript" then
         return false
     end
-
+-- If we are in a `javascript` file, then check the content to see if the
+-- current file counts as a react file
     local ok, buf_parser = pcall(vim.treesitter.get_parser)
     if not ok then
         return false

From 33a38a53b160e382a7b61e096e7835bc833e9e6d Mon Sep 17 00:00:00 2001
From: "roy.crippen4" <roy.crippen4@archarithms.com>
Date: Sun, 14 Jul 2024 17:36:32 -0400
Subject: [PATCH 5/5] chore: formatting and clarification comment

---
 lua/nvim-ts-autotag/utils.lua | 10 +++-------
 tests/specs/closetag_spec.lua |  4 ++--
 2 files changed, 5 insertions(+), 9 deletions(-)

diff --git a/lua/nvim-ts-autotag/utils.lua b/lua/nvim-ts-autotag/utils.lua
index 0fdd0b7..6b5fbdc 100644
--- a/lua/nvim-ts-autotag/utils.lua
+++ b/lua/nvim-ts-autotag/utils.lua
@@ -11,8 +11,8 @@ function M.is_react_file()
     elseif ft ~= "javascript" then
         return false
     end
--- If we are in a `javascript` file, then check the content to see if the
--- current file counts as a react file
+    -- If we are in a `javascript` file, then check the content to see if the
+    -- current file counts as a react file
     local ok, buf_parser = pcall(vim.treesitter.get_parser)
     if not ok then
         return false
@@ -24,7 +24,7 @@ function M.is_react_file()
     end
 
     local root = tree[1]:root()
-    local queries = { 'jsx_element', 'jsx_self_closing_element' }
+    local queries = { "jsx_element", "jsx_self_closing_element" }
 
     for _, query in ipairs(queries) do
         if M.node_exists(root, query) then
@@ -35,8 +35,6 @@ function M.is_react_file()
     return false
 end
 
-
-
 ---@return boolean
 function M.is_react_fragment()
     local line = vim.fn.getline(".")
@@ -63,8 +61,6 @@ function M.node_exists(node, query)
     return false
 end
 
-
-
 M.get_node_text = function(node)
     local _, txt = pcall(get_node_text, node, vim.api.nvim_get_current_buf())
     return vim.split(txt, "\n") or {}
diff --git a/tests/specs/closetag_spec.lua b/tests/specs/closetag_spec.lua
index c2bda45..4e2284d 100644
--- a/tests/specs/closetag_spec.lua
+++ b/tests/specs/closetag_spec.lua
@@ -145,7 +145,7 @@ local data = {
         linenr = 12,
         key = [[>]],
         before = [[<|<div></div>]],
-        after = [[<>|</><div></div>]]
+        after = [[<>|</><div></div>]],
     },
     {
         name = "17 javascript autoclose fragment",
@@ -154,7 +154,7 @@ local data = {
         linenr = 12,
         key = [[>]],
         before = [[<|<div></div>]],
-        after = [[<>|</><div></div>]]
+        after = [[<>|</><div></div>]],
     },
     {
         name = "18 vue auto close tag",