forked from rgieseke/textredux
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathbuffer_list.lua
More file actions
221 lines (195 loc) · 7.24 KB
/
Copy pathbuffer_list.lua
File metadata and controls
221 lines (195 loc) · 7.24 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
-- Copyright 2011-2012 Nils Nordman <nino at nordman.org>
-- Copyright 2012-2014 Robert Gieseke <rob.g@web.de>
-- License: MIT (see LICENSE)
--[[--
The buffer list module provides a text based replacement for the standard
Textadept buffer list.
## Usage
Use the @{textredux.hijack} function or load the buffer list
in your `~/.textadept/init.lua`:
local textredux = require('textredux')
keys["ctrl+b"] = textredux.buffer_list.show
## Features
- Close a buffer from the buffer list (bound to `Ctrl + D` by default)
- Close all files currently shown in the buffer list.
(bound to `Ctrl + Shift + D` and `Meta + D` in Curses by default)
- The list of buffers is sorted and the current buffer is pre-selected
- The buffers to show can be specified using a function
@module textredux.buffer_list
]]
local reduxlist = require('textredux.core.list')
local M = {}
local CURSES = UI == 'terminal'
--- The Textredux list instance used by the buffer list.
M.list = nil
--[[-- The key bindings for the buffer list.
You can modify this to customize the key bindings to your liking. The key
bindings are passed directly to the Textredux list, so note that the
first argument to any function will be the Textredux list itself.
You can read more about the Textredux list's keys in the
[list documentation](./textredux.core.list.html#keys).
If you like to add a custom key binding for closing a buffer or all buffers
in the current selection you can bind the @{close_buffer} and @{close_selected}
functions to a key of your choice. For other actions it's likely that you want to
obtain the currently selected buffer - you can use the @{currently_selected_buffer}
function for that.
]]
M.keys = {
['ctrl+d'] = function(list)
M.close_buffer(list)
end, -- Default for `close buffer`
[CURSES and 'meta+d' or 'ctrl+D'] = function(list)
M.close_selected(list)
end,
}
local buffer_source
local function shorten_home_dir(directory)
if not directory then return end
local home_dir = os.getenv('HOME') or os.getenv('UserProfile')
if not home_dir then return directory end
return directory:gsub(home_dir, '~')
end
local function buffer_title(buf)
local title = (buf.filename or ''):match('[\\/]([^/\\]+)$')
return title or buf.filename or buf._type or _L['Untitled']
end
local function buffer_directory(buf)
if not buf.filename then return nil end
return shorten_home_dir(buf.filename:match('^(.+[\\/])[^/\\]+$'))
end
local function get_buffer_items()
local items = {}
for _, buf in ipairs(buffer_source()) do
if M.list.buffer.target ~= buf then
local modified = buf.modify and '*' or ''
items[#items + 1] = {
buffer_title(buf) .. modified,
buffer_directory(buf),
buffer = buf,
}
end
end
table.sort(items, function(a, b)
-- Keep named-directory items and nil-directory items as separate groups so
-- that mixed comparisons never produce contradictory orderings (which would
-- violate the strict weak ordering required by table.sort).
if a[2] and not b[2] then return true end
if not a[2] and b[2] then return false end
if a[2] == b[2] then return a[1] < b[1] end
return a[2] < b[2]
end)
return items
end
local function on_selection(list, item)
list:close()
local target = item.buffer
if buffer ~= target then view:goto_buffer(target) end
buffer:vertical_center_caret()
end
--[[-- Returns the currently selected buffer in the list.
@param list The Textredux list instance used by the buffer list. If not
provided, then the global list is used automatically.
@return The currently selected buffer, if any.
@return The currently selected buffer's name, if any.
]]
function M.currently_selected_buffer(list)
list = list or M.list
if not list then error('`list` must be provided', 2) end
local item = list:get_current_selection()
if item then return item.buffer, item[1] end
end
--[[-- Closes the currently selected buffer in the buffer list.
@param list The Textredux list instance used by the buffer list. This function
is ordinarily invoked as the result of a key binding, and you should thus not
need to specify this yourself. If list isn't provided, the global list is
automatically used.
]]
function M.close_buffer(list)
list = list or M.list
if not list then error('`list` must be provided', 2) end
local sel_buffer, name = M.currently_selected_buffer(list)
if sel_buffer then
ui.statusbar_text = 'Closing ' .. name .. '..'
local current_pos = buffer.current_pos
local current_search = list:get_current_search()
view:goto_buffer(sel_buffer)
local closed = sel_buffer:close()
list.items = get_buffer_items()
list:show()
if closed then
list:set_current_search(current_search)
buffer:goto_pos(math.min(current_pos, buffer.length + 1))
buffer:home()
ui.statusbar_text = 'Closed ' .. name
else
ui.statusbar_text = ''
end
end
end
--[[-- Closes all currently selected/filtered files in the buffer list.
@param list The Textredux list instance used by the buffer list. This function
is ordinarily invoked as the result of a key binding, and you should thus not
need to specify this yourself. If list isn't provided, the global list is
automatically used.
]]
function M.close_selected(list)
list = list or M.list
if not list then error('`list` must be provided', 2) end
local current_search = list:get_current_search()
if not current_search then return end
while true do
local sel_buffer, _ = M.currently_selected_buffer(list)
if not sel_buffer then break end
local current_pos = buffer.current_pos
view:goto_buffer(sel_buffer)
local closed = sel_buffer:close()
if not closed then break end -- user cancelled (e.g. unsaved-changes dialog)
list.items = get_buffer_items()
list:show()
list:set_current_search(current_search)
buffer:goto_pos(math.min(current_pos, buffer.length + 1))
buffer:home()
end
list:set_current_search('')
ui.statusbar_text = 'All selected buffers closed'
end
--- Shows a list of the specified buffers, or _BUFFERS if not specified.
-- @param buffers Either nil, in which case all buffers within _BUFFERS
-- are displayed, or a function returning a table of buffers to display.
function M.show(buffers)
buffer_source = buffers or function()
return _BUFFERS
end
if not M.list then
M.list = reduxlist.new('Buffer list')
M.list.headers = { 'Name', 'Directory' }
M.list.on_selection = on_selection
for k, v in pairs(M.keys) do
M.list.keys[k] = v
end
end
M.list.items = get_buffer_items()
local active_buffer
for index, item in ipairs(M.list.items) do
if item.buffer == buffer then
active_buffer = index
break
end
end
M.list:show()
if active_buffer then
local data = M.list.buffer.data
-- Ensure the target item has been rendered.
-- For long lists that exceed lines_on_screen, items are loaded lazily.
while active_buffer > data.shown_items and data.shown_items < #data.matching_items do
M.list:_load_more_items()
end
local line = data.items_start_line + active_buffer
M.list.buffer:goto_line(line - 1)
end
local short_cut = CURSES and '[Meta+D]' or '[Ctrl+Shift+D]'
ui.statusbar_text = '[Enter] = open, [Ctrl+D] = close selected, '
.. short_cut
.. ' = close all buffers in current list'
end
return M