···11+ Apache License
22+ Version 2.0, January 2004
33+ http://www.apache.org/licenses/
44+55+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
66+77+ 1. Definitions.
88+99+ "License" shall mean the terms and conditions for use, reproduction,
1010+ and distribution as defined by Sections 1 through 9 of this document.
1111+1212+ "Licensor" shall mean the copyright owner or entity authorized by
1313+ the copyright owner that is granting the License.
1414+1515+ "Legal Entity" shall mean the union of the acting entity and all
1616+ other entities that control, are controlled by, or are under common
1717+ control with that entity. For the purposes of this definition,
1818+ "control" means (i) the power, direct or indirect, to cause the
1919+ direction or management of such entity, whether by contract or
2020+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
2121+ outstanding shares, or (iii) beneficial ownership of such entity.
2222+2323+ "You" (or "Your") shall mean an individual or Legal Entity
2424+ exercising permissions granted by this License.
2525+2626+ "Source" form shall mean the preferred form for making modifications,
2727+ including but not limited to software source code, documentation
2828+ source, and configuration files.
2929+3030+ "Object" form shall mean any form resulting from mechanical
3131+ transformation or translation of a Source form, including but
3232+ not limited to compiled object code, generated documentation,
3333+ and conversions to other media types.
3434+3535+ "Work" shall mean the work of authorship, whether in Source or
3636+ Object form, made available under the License, as indicated by a
3737+ copyright notice that is included in or attached to the work
3838+ (an example is provided in the Appendix below).
3939+4040+ "Derivative Works" shall mean any work, whether in Source or Object
4141+ form, that is based on (or derived from) the Work and for which the
4242+ editorial revisions, annotations, elaborations, or other modifications
4343+ represent, as a whole, an original work of authorship. For the purposes
4444+ of this License, Derivative Works shall not include works that remain
4545+ separable from, or merely link (or bind by name) to the interfaces of,
4646+ the Work and Derivative Works thereof.
4747+4848+ "Contribution" shall mean any work of authorship, including
4949+ the original version of the Work and any modifications or additions
5050+ to that Work or Derivative Works thereof, that is intentionally
5151+ submitted to Licensor for inclusion in the Work by the copyright owner
5252+ or by an individual or Legal Entity authorized to submit on behalf of
5353+ the copyright owner. For the purposes of this definition, "submitted"
5454+ means any form of electronic, verbal, or written communication sent
5555+ to the Licensor or its representatives, including but not limited to
5656+ communication on electronic mailing lists, source code control systems,
5757+ and issue tracking systems that are managed by, or on behalf of, the
5858+ Licensor for the purpose of discussing and improving the Work, but
5959+ excluding communication that is conspicuously marked or otherwise
6060+ designated in writing by the copyright owner as "Not a Contribution."
6161+6262+ "Contributor" shall mean Licensor and any individual or Legal Entity
6363+ on behalf of whom a Contribution has been received by Licensor and
6464+ subsequently incorporated within the Work.
6565+6666+ 2. Grant of Copyright License. Subject to the terms and conditions of
6767+ this License, each Contributor hereby grants to You a perpetual,
6868+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
6969+ copyright license to reproduce, prepare Derivative Works of,
7070+ publicly display, publicly perform, sublicense, and distribute the
7171+ Work and such Derivative Works in Source or Object form.
7272+7373+ 3. Grant of Patent License. Subject to the terms and conditions of
7474+ this License, each Contributor hereby grants to You a perpetual,
7575+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
7676+ (except as stated in this section) patent license to make, have made,
7777+ use, offer to sell, sell, import, and otherwise transfer the Work,
7878+ where such license applies only to those patent claims licensable
7979+ by such Contributor that are necessarily infringed by their
8080+ Contribution(s) alone or by combination of their Contribution(s)
8181+ with the Work to which such Contribution(s) was submitted. If You
8282+ institute patent litigation against any entity (including a
8383+ cross-claim or counterclaim in a lawsuit) alleging that the Work
8484+ or a Contribution incorporated within the Work constitutes direct
8585+ or contributory patent infringement, then any patent licenses
8686+ granted to You under this License for that Work shall terminate
8787+ as of the date such litigation is filed.
8888+8989+ 4. Redistribution. You may reproduce and distribute copies of the
9090+ Work or Derivative Works thereof in any medium, with or without
9191+ modifications, and in Source or Object form, provided that You
9292+ meet the following conditions:
9393+9494+ (a) You must give any other recipients of the Work or
9595+ Derivative Works a copy of this License; and
9696+9797+ (b) You must cause any modified files to carry prominent notices
9898+ stating that You changed the files; and
9999+100100+ (c) You must retain, in the Source form of any Derivative Works
101101+ that You distribute, all copyright, patent, trademark, and
102102+ attribution notices from the Source form of the Work,
103103+ excluding those notices that do not pertain to any part of
104104+ the Derivative Works; and
105105+106106+ (d) If the Work includes a "NOTICE" text file as part of its
107107+ distribution, then any Derivative Works that You distribute must
108108+ include a readable copy of the attribution notices contained
109109+ within such NOTICE file, excluding those notices that do not
110110+ pertain to any part of the Derivative Works, in at least one
111111+ of the following places: within a NOTICE text file distributed
112112+ as part of the Derivative Works; within the Source form or
113113+ documentation, if provided along with the Derivative Works; or,
114114+ within a display generated by the Derivative Works, if and
115115+ wherever such third-party notices normally appear. The contents
116116+ of the NOTICE file are for informational purposes only and
117117+ do not modify the License. You may add Your own attribution
118118+ notices within Derivative Works that You distribute, alongside
119119+ or as an addendum to the NOTICE text from the Work, provided
120120+ that such additional attribution notices cannot be construed
121121+ as modifying the License.
122122+123123+ You may add Your own copyright statement to Your modifications and
124124+ may provide additional or different license terms and conditions
125125+ for use, reproduction, or distribution of Your modifications, or
126126+ for any such Derivative Works as a whole, provided Your use,
127127+ reproduction, and distribution of the Work otherwise complies with
128128+ the conditions stated in this License.
129129+130130+ 5. Submission of Contributions. Unless You explicitly state otherwise,
131131+ any Contribution intentionally submitted for inclusion in the Work
132132+ by You to the Licensor shall be under the terms and conditions of
133133+ this License, without any additional terms or conditions.
134134+ Notwithstanding the above, nothing herein shall supersede or modify
135135+ the terms of any separate license agreement you may have executed
136136+ with Licensor regarding such Contributions.
137137+138138+ 6. Trademarks. This License does not grant permission to use the trade
139139+ names, trademarks, service marks, or product names of the Licensor,
140140+ except as required for reasonable and customary use in describing the
141141+ origin of the Work and reproducing the content of the NOTICE file.
142142+143143+ 7. Disclaimer of Warranty. Unless required by applicable law or
144144+ agreed to in writing, Licensor provides the Work (and each
145145+ Contributor provides its Contributions) on an "AS IS" BASIS,
146146+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147147+ implied, including, without limitation, any warranties or conditions
148148+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149149+ PARTICULAR PURPOSE. You are solely responsible for determining the
150150+ appropriateness of using or redistributing the Work and assume any
151151+ risks associated with Your exercise of permissions under this License.
152152+153153+ 8. Limitation of Liability. In no event and under no legal theory,
154154+ whether in tort (including negligence), contract, or otherwise,
155155+ unless required by applicable law (such as deliberate and grossly
156156+ negligent acts) or agreed to in writing, shall any Contributor be
157157+ liable to You for damages, including any direct, indirect, special,
158158+ incidental, or consequential damages of any character arising as a
159159+ result of this License or out of the use or inability to use the
160160+ Work (including but not limited to damages for loss of goodwill,
161161+ work stoppage, computer failure or malfunction, or any and all
162162+ other commercial damages or losses), even if such Contributor
163163+ has been advised of the possibility of such damages.
164164+165165+ 9. Accepting Warranty or Additional Liability. While redistributing
166166+ the Work or Derivative Works thereof, You may choose to offer,
167167+ and charge a fee for, acceptance of support, warranty, indemnity,
168168+ or other liability obligations and/or rights consistent with this
169169+ License. However, in accepting such obligations, You may act only
170170+ on Your own behalf and on Your sole responsibility, not on behalf
171171+ of any other Contributor, and only if You agree to indemnify,
172172+ defend, and hold each Contributor harmless for any liability
173173+ incurred by, or claims asserted against, such Contributor by reason
174174+ of your accepting any such warranty or additional liability.
175175+176176+ END OF TERMS AND CONDITIONS
177177+178178+ APPENDIX: How to apply the Apache License to your work.
179179+180180+ To apply the Apache License to your work, attach the following
181181+ boilerplate notice, with the fields enclosed by brackets "[]"
182182+ replaced with your own identifying information. (Don't include
183183+ the brackets!) The text should be enclosed in the appropriate
184184+ comment syntax for the file format. We also recommend that a
185185+ file or class name and description of purpose be included on the
186186+ same "printed page" as the copyright notice for easier
187187+ identification within third-party archives.
188188+189189+ Copyright [yyyy] [name of copyright owner]
190190+191191+ Licensed under the Apache License, Version 2.0 (the "License");
192192+ you may not use this file except in compliance with the License.
193193+ You may obtain a copy of the License at
194194+195195+ http://www.apache.org/licenses/LICENSE-2.0
196196+197197+ Unless required by applicable law or agreed to in writing, software
198198+ distributed under the License is distributed on an "AS IS" BASIS,
199199+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200200+ See the License for the specific language governing permissions and
201201+ limitations under the License.
+342
config/lua/ivy/treesitter-indentexpr.lua
···11+-- copied from @nvim-treesitter https://github.com/nvim-treesitter/nvim-treesitter/blob/6620ae1c44dfa8623b22d0cbf873a9e8d073b849/lua/nvim-treesitter/indent.lua
22+-- license: APACHE-2.0
33+44+local ts = vim.treesitter
55+66+local M = {}
77+88+M.comment_parsers = {
99+ comment = true,
1010+ luadoc = true,
1111+ javadoc = true,
1212+ jsdoc = true,
1313+ phpdoc = true,
1414+}
1515+1616+local function getline(lnum)
1717+ return vim.api.nvim_buf_get_lines(0, lnum - 1, lnum, false)[1] or ""
1818+end
1919+2020+---@param lnum integer
2121+---@return integer
2222+local function get_indentcols_at_line(lnum)
2323+ local _, indentcols = getline(lnum):find("^%s*")
2424+ return indentcols or 0
2525+end
2626+2727+---@param root TSNode
2828+---@param lnum integer
2929+---@param col? integer
3030+---@return TSNode?
3131+local function get_first_node_at_line(root, lnum, col)
3232+ col = col or get_indentcols_at_line(lnum)
3333+ return root:descendant_for_range(lnum - 1, col, lnum - 1, col + 1)
3434+end
3535+3636+---@param root TSNode
3737+---@param lnum integer
3838+---@param col? integer
3939+---@return TSNode?
4040+local function get_last_node_at_line(root, lnum, col)
4141+ col = col or (#getline(lnum) - 1)
4242+ return root:descendant_for_range(lnum - 1, col, lnum - 1, col + 1)
4343+end
4444+4545+---@param bufnr integer
4646+---@param node TSNode
4747+---@param delimiter string
4848+---@return TSNode? child
4949+---@return boolean? is_end
5050+local function find_delimiter(bufnr, node, delimiter)
5151+ for child, _ in node:iter_children() do
5252+ if child:type() == delimiter then
5353+ local linenr = child:start()
5454+ local line = vim.api.nvim_buf_get_lines(bufnr, linenr, linenr + 1, false)[1]
5555+ local end_char = { child:end_() }
5656+ local trimmed_after_delim
5757+ local escaped_delimiter = delimiter:gsub("[%-%.%+%[%]%(%)%$%^%%%?%*]", "%%%1")
5858+ trimmed_after_delim = assert(line):sub(end_char[2] + 1):gsub("[%s" .. escaped_delimiter .. "]*", "")
5959+ return child, #trimmed_after_delim == 0
6060+ end
6161+ end
6262+end
6363+6464+---Memoize a function using hash_fn to hash the arguments.
6565+---@generic F: function
6666+---@param fn F
6767+---@param hash_fn fun(...): any
6868+---@return F
6969+local function memoize(fn, hash_fn)
7070+ local cache = setmetatable({}, { __mode = "kv" }) ---@type table<any,any>
7171+7272+ return function(...)
7373+ local key = hash_fn(...)
7474+ if cache[key] == nil then
7575+ local v = fn(...) ---@type any
7676+ cache[key] = v ~= nil and v or vim.NIL
7777+ end
7878+7979+ local v = cache[key]
8080+ return v ~= vim.NIL and v or nil
8181+ end
8282+end
8383+8484+local get_indents = memoize(function(bufnr, root, lang)
8585+ ---@type table<string,table<string,table>>
8686+ local map = {
8787+ ["indent.auto"] = {},
8888+ ["indent.begin"] = {},
8989+ ["indent.end"] = {},
9090+ ["indent.dedent"] = {},
9191+ ["indent.branch"] = {},
9292+ ["indent.ignore"] = {},
9393+ ["indent.align"] = {},
9494+ ["indent.zero"] = {},
9595+ }
9696+9797+ local query = ts.query.get(lang, "indents")
9898+ if not query then
9999+ return map
100100+ end
101101+ for id, node, metadata in query:iter_captures(root, bufnr) do
102102+ if query.captures[id]:sub(1, 1) ~= "_" then
103103+ map[query.captures[id]][node:id()] = metadata or {}
104104+ end
105105+ end
106106+107107+ return map
108108+end, function(bufnr, root, lang)
109109+ return tostring(bufnr) .. root:id() .. "_" .. lang
110110+end)
111111+112112+---@param lnum integer (1-indexed)
113113+---@return integer
114114+function M.get_indent(lnum)
115115+ local bufnr = vim.api.nvim_get_current_buf()
116116+ local parser = ts.get_parser(bufnr)
117117+ if not parser or not lnum then
118118+ return -1
119119+ end
120120+121121+ parser:parse({ vim.fn.line("w0") - 1, vim.fn.line("w$") })
122122+123123+ -- Get language tree with smallest range around node that's not a comment parser
124124+ local root, lang_tree ---@type TSNode, vim.treesitter.LanguageTree
125125+ parser:for_each_tree(function(tstree, tree)
126126+ if not tstree or M.comment_parsers[tree:lang()] then
127127+ return
128128+ end
129129+ local local_root = tstree:root()
130130+ if ts.is_in_node_range(local_root, lnum - 1, 0) then
131131+ if not root or root:byte_length() >= local_root:byte_length() then
132132+ root = local_root
133133+ lang_tree = tree
134134+ end
135135+ end
136136+ end)
137137+138138+ -- Not likely, but just in case...
139139+ if not root then
140140+ return 0
141141+ end
142142+143143+ local q = get_indents(vim.api.nvim_get_current_buf(), root, lang_tree:lang())
144144+ local node ---@type TSNode?
145145+ if getline(lnum):find("^%s*$") then
146146+ local prevlnum = vim.fn.prevnonblank(lnum)
147147+ local indentcols = get_indentcols_at_line(prevlnum)
148148+ local prevline = vim.trim(getline(prevlnum))
149149+ -- The final position can be trailing spaces, which should not affect indentation
150150+ node = get_last_node_at_line(root, prevlnum, indentcols + #prevline - 1)
151151+ if node and node:type():match("comment") then
152152+ -- The final node we capture of the previous line can be a comment node, which should also be ignored
153153+ -- Unless the last line is an entire line of comment, ignore the comment range and find the last node again
154154+ local first_node = get_first_node_at_line(root, prevlnum, indentcols)
155155+ local _, scol, _, _ = node:range()
156156+ if first_node and first_node:id() ~= node:id() then
157157+ -- In case the last captured node is a trailing comment node, re-trim the string
158158+ prevline = vim.trim(prevline:sub(1, scol - indentcols))
159159+ -- Add back indent as indent of prevline was trimmed away
160160+ local col = indentcols + #prevline - 1
161161+ node = get_last_node_at_line(root, prevlnum, col)
162162+ end
163163+ end
164164+ if node and q["indent.end"][node:id()] then
165165+ node = get_first_node_at_line(root, lnum)
166166+ end
167167+ else
168168+ node = get_first_node_at_line(root, lnum)
169169+ end
170170+171171+ local indent_size = vim.fn.shiftwidth()
172172+ local indent = 0
173173+ local _, _, root_start = root:start()
174174+ if root_start ~= 0 then
175175+ -- injected tree
176176+ indent = vim.fn.indent(root:start() + 1)
177177+ end
178178+179179+ -- tracks to ensure multiple indent levels are not applied for same line
180180+ local is_processed_by_row = {} --- @type table<integer,boolean>
181181+182182+ if node and q["indent.zero"][node:id()] then
183183+ return 0
184184+ end
185185+186186+ while node do
187187+ -- do 'autoindent' if not marked as @indent
188188+ if
189189+ not q["indent.begin"][node:id()]
190190+ and not q["indent.align"][node:id()]
191191+ and q["indent.auto"][node:id()]
192192+ and node:start() < lnum - 1
193193+ and lnum - 1 <= node:end_()
194194+ then
195195+ return -1
196196+ end
197197+198198+ -- Do not indent if we are inside an @ignore block.
199199+ -- If a node spans from L1,C1 to L2,C2, we know that lines where L1 < line <= L2 would
200200+ -- have their indentations contained by the node.
201201+ if
202202+ not q["indent.begin"][node:id()]
203203+ and q["indent.ignore"][node:id()]
204204+ and node:start() < lnum - 1
205205+ and lnum - 1 <= node:end_()
206206+ then
207207+ return 0
208208+ end
209209+210210+ local srow, _, erow = node:range()
211211+212212+ local is_processed = false
213213+214214+ if
215215+ not is_processed_by_row[srow]
216216+ and ((q["indent.branch"][node:id()] and srow == lnum - 1) or (q["indent.dedent"][node:id()] and srow ~= lnum - 1))
217217+ then
218218+ indent = indent - indent_size
219219+ is_processed = true
220220+ end
221221+222222+ -- do not indent for nodes that starts-and-ends on same line and starts on target line (lnum)
223223+ local should_process = not is_processed_by_row[srow]
224224+ local is_in_err = false
225225+ if should_process then
226226+ local parent = node:parent()
227227+ is_in_err = parent and parent:has_error() or false
228228+ end
229229+ if
230230+ should_process
231231+ and (
232232+ q["indent.begin"][node:id()]
233233+ and (srow ~= erow or is_in_err or q["indent.begin"][node:id()]["indent.immediate"])
234234+ and (srow ~= lnum - 1 or q["indent.begin"][node:id()]["indent.start_at_same_line"])
235235+ )
236236+ then
237237+ indent = indent + indent_size
238238+ is_processed = true
239239+ end
240240+241241+ if is_in_err and not q["indent.align"][node:id()] then
242242+ -- only when the node is in error, promote the
243243+ -- first child's aligned indent to the error node
244244+ -- to work around ((ERROR "X" . (_)) @aligned_indent (#set! "delimiter" "AB"))
245245+ -- matching for all X, instead set do
246246+ -- (ERROR "X" @aligned_indent (#set! "delimiter" "AB") . (_))
247247+ -- and we will fish it out here.
248248+ for c in node:iter_children() do
249249+ if q["indent.align"][c:id()] then
250250+ q["indent.align"][node:id()] = q["indent.align"][c:id()]
251251+ break
252252+ end
253253+ end
254254+ end
255255+ -- do not indent for nodes that starts-and-ends on same line and starts on target line (lnum)
256256+ if should_process and q["indent.align"][node:id()] and (srow ~= erow or is_in_err) and (srow ~= lnum - 1) then
257257+ local metadata = q["indent.align"][node:id()]
258258+ local o_delim_node, o_is_last_in_line ---@type TSNode?, boolean?
259259+ local c_delim_node, c_is_last_in_line ---@type TSNode?, boolean?, boolean?
260260+ local indent_is_absolute = false
261261+ if metadata["indent.open_delimiter"] then
262262+ o_delim_node, o_is_last_in_line = find_delimiter(bufnr, node, metadata["indent.open_delimiter"])
263263+ else
264264+ o_delim_node = node
265265+ end
266266+ if metadata["indent.close_delimiter"] then
267267+ c_delim_node, c_is_last_in_line = find_delimiter(bufnr, node, metadata["indent.close_delimiter"])
268268+ else
269269+ c_delim_node = node
270270+ end
271271+272272+ if o_delim_node then
273273+ local o_srow, o_scol = o_delim_node:start()
274274+ local c_srow = nil --- @type integer?
275275+ if c_delim_node then
276276+ c_srow = c_delim_node:start()
277277+ end
278278+ if o_is_last_in_line then
279279+ -- hanging indent (previous line ended with starting delimiter)
280280+ -- should be processed like indent
281281+ if should_process then
282282+ indent = indent + indent_size * 1
283283+ if c_is_last_in_line then
284284+ -- If current line is outside the range of a node marked with `@aligned_indent`
285285+ -- Then its indent level shouldn't be affected by `@aligned_indent` node
286286+ if c_srow and c_srow < lnum - 1 then
287287+ indent = math.max(indent - indent_size, 0)
288288+ end
289289+ end
290290+ end
291291+ else
292292+ -- aligned indent
293293+ if c_is_last_in_line and c_srow and o_srow ~= c_srow and c_srow < lnum - 1 then
294294+ -- If current line is outside the range of a node marked with `@aligned_indent`
295295+ -- Then its indent level shouldn't be affected by `@aligned_indent` node
296296+ indent = math.max(indent - indent_size, 0)
297297+ else
298298+ indent = o_scol + (metadata["indent.increment"] or 1)
299299+ indent_is_absolute = true
300300+ end
301301+ end
302302+ -- deal with the final line
303303+ local avoid_last_matching_next = false
304304+ if c_srow and c_srow ~= o_srow and c_srow == lnum - 1 then
305305+ -- delims end on current line, and are not open and closed same line.
306306+ -- then this last line may need additional indent to avoid clashes
307307+ -- with the next. `indent.avoid_last_matching_next` controls this behavior,
308308+ -- for example this is needed for function parameters.
309309+ avoid_last_matching_next = metadata["indent.avoid_last_matching_next"] or false
310310+ end
311311+ if avoid_last_matching_next then
312312+ -- last line must be indented more in cases where
313313+ -- it would be same indent as next line (we determine this as one
314314+ -- width more than the open indent to avoid confusing with any
315315+ -- hanging indents)
316316+ if indent <= vim.fn.indent(o_srow + 1) + indent_size then
317317+ indent = indent + indent_size * 1
318318+ else
319319+ indent = indent
320320+ end
321321+ end
322322+ is_processed = true
323323+ if indent_is_absolute then
324324+ -- don't allow further indenting by parent nodes, this is an absolute position
325325+ return indent
326326+ end
327327+ end
328328+ end
329329+330330+ is_processed_by_row[srow] = is_processed_by_row[srow] or is_processed
331331+332332+ node = node:parent()
333333+ end
334334+335335+ return indent
336336+end
337337+338338+function M.indent()
339339+ return M.get_indent(vim.v.lnum)
340340+end
341341+342342+return M
+3-2
config/plugin/treesitter.lua
···3737 if not ok then
3838 return
3939 end
4040- vim.o.foldmethod = "expr"
4141- vim.o.foldexpr = "v:lua.vim.treesitter.foldexpr()"
4040+ vim.wo.foldmethod = "expr"
4141+ vim.wo.foldexpr = "v:lua.vim.treesitter.foldexpr()"
4242+ vim.bo.indentexpr = "v:lua.require('ivy.treesitter-indentexpr').indent()"
4243end)