1
1
--- @class VirtualIndent
2
2
--- @field private _ns_id number extmarks namespace id
3
+ --- @field private _headline_lib any Treesitter headline library
4
+ --- @field private _bufnr integer Buffer VirtualIndent is attached to
5
+ --- @field private _attached boolean Whether or not VirtualIndent is attached for its buffer
6
+ --- @field private _bufnrs {integer : boolean } Buffers with VirtualIndent attached
7
+ --- @field private _timer uv_timer_t Timer used for tracking ` org_indent_mode`
3
8
local VirtualIndent = {
4
- enabled = false ,
5
- lib = {},
9
+ _ns_id = vim . api . nvim_create_namespace ( ' orgmode.ui.indent ' ) ,
10
+ _bufnrs = {},
6
11
}
7
12
8
- function VirtualIndent :new ()
9
- if self .enabled then
10
- return self
13
+ --- Creates a new instance of VirtualIndent for a given buffer or returns the existing instance if
14
+ --- one exists
15
+ --- @param bufnr ? integer Buffer to use for VirtualIndent when attached
16
+ --- @return VirtualIndent
17
+ function VirtualIndent :new (bufnr )
18
+ bufnr = bufnr or vim .api .nvim_get_current_buf ()
19
+
20
+ local curr_instance = VirtualIndent ._bufnrs [bufnr ]
21
+ if curr_instance then
22
+ return curr_instance
11
23
end
12
- self ._ns_id = vim .api .nvim_create_namespace (' orgmode.ui.indent' )
13
- self .lib .headline = require (' orgmode.treesitter.headline' )
14
- self .enabled = true
15
- return self
24
+
25
+ local new = {}
26
+ setmetatable (new , self )
27
+ self .__index = self
28
+
29
+ new ._bufnr = bufnr
30
+ new ._headline_lib = require (' orgmode.treesitter.headline' )
31
+ new ._attached = false
32
+ VirtualIndent ._bufnrs [new ._bufnr ] = new
33
+ new ._timer = vim .uv .new_timer ()
34
+ return new
16
35
end
17
36
18
- function VirtualIndent :_delete_old_extmarks (buffer , start_line , end_line )
37
+ function VirtualIndent :_delete_old_extmarks (start_line , end_line )
19
38
local old_extmarks = vim .api .nvim_buf_get_extmarks (
20
- buffer ,
39
+ self . _bufnr ,
21
40
self ._ns_id ,
22
41
{ start_line , 0 },
23
42
{ end_line , 0 },
24
43
{ type = ' virt_text' }
25
44
)
26
45
for _ , ext in ipairs (old_extmarks ) do
27
- vim .api .nvim_buf_del_extmark (buffer , self ._ns_id , ext [1 ])
46
+ vim .api .nvim_buf_del_extmark (self . _bufnr , self ._ns_id , ext [1 ])
28
47
end
29
48
end
30
49
31
50
function VirtualIndent :_get_indent_size (line )
32
- local headline = self .lib . headline .from_cursor ({ line + 1 , 1 })
51
+ local headline = self ._headline_lib .from_cursor ({ line + 1 , 1 })
33
52
34
53
if headline then
35
54
local headline_line , _ , _ = headline .headline :start ()
@@ -42,13 +61,12 @@ function VirtualIndent:_get_indent_size(line)
42
61
return 0
43
62
end
44
63
45
- --- @param bufnr number buffer id
46
64
--- @param start_line number start line number to set the indentation , 0-based inclusive
47
65
--- @param end_line number end line number to set the indentation , 0-based inclusive
48
66
--- @param ignore_ts ? boolean whether or not to skip the treesitter start & end lookup
49
- function VirtualIndent :set_indent (bufnr , start_line , end_line , ignore_ts )
67
+ function VirtualIndent :set_indent (start_line , end_line , ignore_ts )
50
68
ignore_ts = ignore_ts or false
51
- local headline = self .lib . headline .from_cursor ({ start_line + 1 , 1 })
69
+ local headline = self ._headline_lib .from_cursor ({ start_line + 1 , 1 })
52
70
if headline and not ignore_ts then
53
71
local parent = headline .headline :parent ()
54
72
start_line = parent :start ()
@@ -57,13 +75,13 @@ function VirtualIndent:set_indent(bufnr, start_line, end_line, ignore_ts)
57
75
if start_line > 0 then
58
76
start_line = start_line - 1
59
77
end
60
- self :_delete_old_extmarks (bufnr , start_line , end_line )
78
+ self :_delete_old_extmarks (start_line , end_line )
61
79
for line = start_line , end_line do
62
80
local indent = self :_get_indent_size (line )
63
81
64
82
if indent > 0 then
65
83
-- NOTE: `ephemeral = true` is not implemented for `inline` virt_text_pos :(
66
- vim .api .nvim_buf_set_extmark (bufnr , self ._ns_id , line , 0 , {
84
+ vim .api .nvim_buf_set_extmark (self . _bufnr , self ._ns_id , line , 0 , {
67
85
virt_text = { { string.rep (' ' , indent ), ' OrgIndent' } },
68
86
virt_text_pos = ' inline' ,
69
87
right_gravity = false ,
@@ -72,22 +90,56 @@ function VirtualIndent:set_indent(bufnr, start_line, end_line, ignore_ts)
72
90
end
73
91
end
74
92
75
- --- @param bufnr ? number buffer id
76
- function VirtualIndent :attach (bufnr )
77
- bufnr = bufnr or 0
78
- self :set_indent (0 , 0 , vim .api .nvim_buf_line_count (bufnr ) - 1 , true )
93
+ --- Begins a timer to check `vim.b.org_indent_mode` and correctly attach or detatch VirtualIndent as
94
+ --- necessary
95
+ function VirtualIndent :start_watch_org_indent ()
96
+ self ._timer :start (
97
+ 50 ,
98
+ 50 ,
99
+ vim .schedule_wrap (function ()
100
+ local success , indent_mode_enabled = pcall (vim .api .nvim_buf_get_var , self ._bufnr , ' org_indent_mode' )
101
+ if success and indent_mode_enabled then
102
+ if not self ._attached then
103
+ self :attach ()
104
+ end
105
+ elseif self ._attached then
106
+ self :detach ()
107
+ end
108
+ end )
109
+ )
110
+ end
111
+
112
+ --- Stops the current VirtualIndent instance from reacting to changes in `vim.b.org_indent_mode`
113
+ function VirtualIndent :stop_watch_org_indent ()
114
+ self ._timer :stop ()
115
+ end
116
+
117
+ --- Enables virtual indentation in registered buffer
118
+ function VirtualIndent :attach ()
119
+ self ._attached = true
120
+ self :set_indent (0 , vim .api .nvim_buf_line_count (self ._bufnr ) - 1 , true )
121
+ self :start_watch_org_indent ()
79
122
80
- vim .api .nvim_buf_attach (bufnr , false , {
123
+ vim .api .nvim_buf_attach (self . _bufnr , false , {
81
124
on_lines = function (_ , _ , _ , start_line , _ , end_line )
125
+ if not self ._attached then
126
+ return true
127
+ end
82
128
-- HACK: By calling `set_indent` twice, once synchronously and once in `vim.schedule` we get smooth usage of the
83
129
-- virtual indent in most cases and still properly handle undo redo. Unfortunately this is called *early* when
84
130
-- `undo` or `redo` is used causing the padding to be incorrect for some headlines.
85
- self :set_indent (bufnr , start_line , end_line )
131
+ self :set_indent (self . _bufnr , start_line , end_line )
86
132
vim .schedule (function ()
87
- self :set_indent (bufnr , start_line , end_line )
133
+ self :set_indent (self . _bufnr , start_line , end_line )
88
134
end )
89
135
end ,
90
136
})
91
137
end
92
138
139
+ function VirtualIndent :detach ()
140
+ self ._attached = false
141
+ vim .api .nvim_buf_set_var (self ._bufnr , ' org_indent_mode' , false )
142
+ self :_delete_old_extmarks (0 , vim .api .nvim_buf_line_count (self ._bufnr ) - 1 )
143
+ end
144
+
93
145
return VirtualIndent
0 commit comments