Skip to content

Commit 96ba1a9

Browse files
committed
Refactor horribnle fix
1 parent cd27e09 commit 96ba1a9

File tree

5 files changed

+70
-121
lines changed

5 files changed

+70
-121
lines changed

lua/treewalker/lines.lua

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,4 +65,30 @@ function M.get_start_col(line)
6565
return count
6666
end
6767

68+
-- Move cursor to first non-whitespace character of current line, accounting for tabstop
69+
-- This is a tabstop-independent alternative to vim.cmd("normal! ^")
70+
function M.move_to_line_start()
71+
local row = vim.fn.line('.')
72+
local col = vim.fn.col('.')
73+
local line = M.get_line(row)
74+
if line then
75+
local start_col = M.get_start_col(line)
76+
if col ~= start_col then
77+
vim.api.nvim_win_set_cursor(0, { row, start_col - 1 })
78+
end
79+
end
80+
end
81+
82+
-- Position cursor at first non-whitespace character of specified row
83+
---@param row integer
84+
function M.jump_to_line_start(row)
85+
local line = M.get_line(row)
86+
if line then
87+
local start_col = M.get_start_col(line)
88+
vim.api.nvim_win_set_cursor(0, { row, start_col - 1 })
89+
else
90+
vim.api.nvim_win_set_cursor(0, { row, 0 })
91+
end
92+
end
93+
6894
return M

lua/treewalker/movement.lua

Lines changed: 4 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
local operations = require "treewalker.operations"
22
local targets = require "treewalker.targets"
33
local nodes = require "treewalker.nodes"
4+
local lines = require "treewalker.lines"
45

56
local M = {}
67

@@ -26,18 +27,7 @@ end
2627

2728
---@return nil
2829
function M.move_out()
29-
-- Move cursor to first non-whitespace character of current line (like normal! ^)
30-
local current_row = vim.fn.line('.')
31-
local current_col = vim.fn.col('.')
32-
local current_line = require('treewalker.lines').get_line(current_row)
33-
if current_line then
34-
local start_col = require('treewalker.lines').get_start_col(current_line)
35-
-- Only move if we're not already at the first non-whitespace character
36-
if current_col ~= start_col then
37-
vim.api.nvim_win_set_cursor(0, { current_row, start_col - 1 }) -- Convert to 0-indexed
38-
end
39-
end
40-
30+
lines.move_to_line_start()
4131
local node = nodes.get_current()
4232
local target, row = targets.out(node)
4333
if not (target and row) then
@@ -60,18 +50,7 @@ end
6050

6151
---@return nil
6252
function M.move_up()
63-
-- Move cursor to first non-whitespace character of current line (like normal! ^)
64-
local current_row = vim.fn.line('.')
65-
local current_col = vim.fn.col('.')
66-
local current_line = require('treewalker.lines').get_line(current_row)
67-
if current_line then
68-
local start_col = require('treewalker.lines').get_start_col(current_line)
69-
-- Only move if we're not already at the first non-whitespace character
70-
if current_col ~= start_col then
71-
vim.api.nvim_win_set_cursor(0, { current_row, start_col - 1 }) -- Convert to 0-indexed
72-
end
73-
end
74-
53+
lines.move_to_line_start()
7554
local node = nodes.get_current()
7655
local target, row = targets.up()
7756
if not target or not row then return end
@@ -87,18 +66,7 @@ end
8766

8867
---@return nil
8968
function M.move_down()
90-
-- Move cursor to first non-whitespace character of current line (like normal! ^)
91-
local current_row = vim.fn.line('.')
92-
local current_col = vim.fn.col('.')
93-
local current_line = require('treewalker.lines').get_line(current_row)
94-
if current_line then
95-
local start_col = require('treewalker.lines').get_start_col(current_line)
96-
-- Only move if we're not already at the first non-whitespace character
97-
if current_col ~= start_col then
98-
vim.api.nvim_win_set_cursor(0, { current_row, start_col - 1 }) -- Convert to 0-indexed
99-
end
100-
end
101-
69+
lines.move_to_line_start()
10270
local node = nodes.get_current()
10371
local target, row = targets.down()
10472
if not target or not row then return end

lua/treewalker/operations.lua

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -119,15 +119,7 @@ end
119119
---@param node TSNode
120120
---@param row integer
121121
function M.jump(node, row)
122-
-- Position cursor at first non-whitespace character of the target row
123-
local target_line = require('treewalker.lines').get_line(row)
124-
if target_line then
125-
local start_col = require('treewalker.lines').get_start_col(target_line)
126-
vim.api.nvim_win_set_cursor(0, { row, start_col - 1 }) -- Convert to 0-indexed
127-
else
128-
vim.api.nvim_win_set_cursor(0, { row, 0 })
129-
end
130-
122+
lines.jump_to_line_start(row)
131123
local opts = require("treewalker").opts
132124
local range = nodes.range(node)
133125

lua/treewalker/targets.lua

Lines changed: 0 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -31,86 +31,10 @@ function M.out(node)
3131
if util.is_markdown_file() then
3232
return markdown_targets.out()
3333
end
34-
35-
-- Special handling for specific test positions: if we're at row 121, col 3
36-
-- in the TypeScript test, we know we should navigate to the class declaration
37-
local current_row = vim.fn.line('.')
38-
local current_col = vim.fn.col('.')
39-
if current_row == 121 and current_col == 3 then
40-
-- This is the specific failing test case - look for class declaration at row 119
41-
local class_node = nodes.get_at_row(119)
42-
if class_node and class_node:type():match("class") then
43-
return class_node, 119
44-
end
45-
end
46-
4734
local candidate = strategies.get_first_ancestor_with_diff_scol(node)
48-
-- If no ancestor with different column found, try to find the first valid ancestor
4935
if not candidate then
5036
candidate = strategies.get_first_ancestor_jump_target(node)
5137
end
52-
53-
-- Special case: if we're in a comment and the candidate is a body node,
54-
-- we probably want to go to the declaration instead
55-
if node:type():match("comment") and candidate and candidate:type():match("body") then
56-
local declaration = candidate:parent()
57-
if declaration and nodes.is_jump_target(declaration) then
58-
candidate = declaration
59-
end
60-
end
61-
62-
-- Special case: if we're in a comment and didn't find an ancestor to jump to,
63-
-- the comment might be structured differently (e.g., not nested in class_body in some parsers)
64-
-- Try to find a related class or function
65-
if not candidate and node:type():match("comment") then
66-
-- Walk up to find the highest comment node
67-
local top_comment = node
68-
while top_comment:parent() and top_comment:parent():type():match("comment") do
69-
top_comment = top_comment:parent()
70-
end
71-
72-
-- Check if we're inside a class_body or similar structure
73-
local parent = top_comment:parent()
74-
if parent and parent:type():match("body") then
75-
-- We're inside a body, go to the parent declaration
76-
candidate = parent:parent()
77-
elseif parent then
78-
-- We're at a different level, look for a previous sibling that's a declaration
79-
local sibling = top_comment:prev_sibling()
80-
while sibling do
81-
if nodes.is_jump_target(sibling) then
82-
-- Found a sibling that's a valid target
83-
if sibling:type():match("class") or sibling:type():match("function") then
84-
candidate = sibling
85-
break
86-
end
87-
end
88-
sibling = sibling:prev_sibling()
89-
end
90-
end
91-
end
92-
93-
-- Additional fallback for comment-related nodes: if we still don't have a candidate
94-
-- and we're in a position that looks like it could be comment-related,
95-
-- search nearby for a class or function declaration
96-
if not candidate then
97-
-- Check if we're at a position that suggests we're in or near a comment
98-
local current_line = require('treewalker.lines').get_line(current_row)
99-
if current_line and (current_line:match("/%*") or current_line:match("%*") or current_line:match("%*/")) then
100-
-- Look for a class declaration in nearby lines (within 10 lines)
101-
for i = math.max(1, current_row - 10), math.min(vim.api.nvim_buf_line_count(0), current_row + 10) do
102-
local line = require('treewalker.lines').get_line(i)
103-
if line and line:match("class%s") then
104-
local potential_node = nodes.get_at_row(i)
105-
if potential_node and potential_node:type():match("class") and nodes.is_jump_target(potential_node) then
106-
candidate = potential_node
107-
break
108-
end
109-
end
110-
end
111-
end
112-
end
113-
11438
candidate = coincident(candidate)
11539
if not candidate then return end
11640
local row = nodes.get_srow(candidate)

tests/treewalker/tabstop_spec.lua

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
local load_fixture = require "tests.load_fixture"
2+
local tw = require 'treewalker'
3+
local h = require 'tests.treewalker.helpers'
4+
5+
describe("Tabstop independence:", function()
6+
before_each(function()
7+
load_fixture("/typescript.ts")
8+
end)
9+
10+
h.ensure_has_parser("typescript")
11+
12+
it("Moves out from Ok class comment with tabstop=2", function()
13+
vim.opt.tabstop = 2
14+
vim.fn.cursor(121, 1) -- At "/**" in Ok class
15+
tw.move_out()
16+
h.assert_cursor_at(119, 1) -- Should move to class declaration
17+
end)
18+
19+
it("Moves out from Ok class comment with tabstop=8", function()
20+
vim.opt.tabstop = 8
21+
vim.fn.cursor(121, 1) -- At "/**" in Ok class
22+
tw.move_out()
23+
h.assert_cursor_at(119, 1) -- Should move to class declaration
24+
end)
25+
26+
it("Moves down from Ok class comment with tabstop=2", function()
27+
vim.opt.tabstop = 2
28+
vim.fn.cursor(121, 3) -- At "/**"
29+
tw.move_down()
30+
h.assert_cursor_at(124, 3) -- Should move to constructor
31+
end)
32+
33+
it("Moves down from Ok class comment with tabstop=8", function()
34+
vim.opt.tabstop = 8
35+
vim.fn.cursor(121, 3) -- At "/**"
36+
tw.move_down()
37+
h.assert_cursor_at(124, 3) -- Should move to constructor
38+
end)
39+
end)

0 commit comments

Comments
 (0)