aboutsummaryrefslogtreecommitdiff
path: root/core
diff options
context:
space:
mode:
Diffstat (limited to 'core')
-rw-r--r--core/args.lua4
-rw-r--r--core/events.lua1
-rw-r--r--core/file_io.lua57
-rw-r--r--core/keys.lua4
-rw-r--r--core/lfs_ext.lua18
-rw-r--r--core/ui.lua88
6 files changed, 97 insertions, 75 deletions
diff --git a/core/args.lua b/core/args.lua
index c27da28e..834ba973 100644
--- a/core/args.lua
+++ b/core/args.lua
@@ -50,9 +50,7 @@ local function process(arg)
local switch = switches[arg[i]]
if switch then
local f, n = table.unpack(switch)
- local args = {}
- for j = i + 1, i + n do args[#args + 1] = arg[j] end
- f(table.unpack(args))
+ f(table.unpack(arg, i + 1, i + n))
i = i + n
else
io.open_file(lfs.abspath(arg[i], arg[-1]))
diff --git a/core/events.lua b/core/events.lua
index ceba125e..5b7c390a 100644
--- a/core/events.lua
+++ b/core/events.lua
@@ -270,6 +270,7 @@ local handlers = {}
-- @see disconnect
-- @name connect
function M.connect(event, f, index)
+ -- Note: cannot assert() here since _L is undefined early in init process.
if not event then error(_L['Undefined event name']) end
if not handlers[event] then handlers[event] = {} end
if handlers[event][f] then M.disconnect(event, f) end
diff --git a/core/file_io.lua b/core/file_io.lua
index 5f3ab513..709bf1c8 100644
--- a/core/file_io.lua
+++ b/core/file_io.lua
@@ -109,8 +109,11 @@ function io.open_file(filenames)
if not filenames then return end
for i = 1, #filenames do
local filename = lfs.abspath((filenames[i]:gsub('^file://', '')))
- for j, buffer in ipairs(_BUFFERS) do
- if filename == buffer.filename then view:goto_buffer(j) goto continue end
+ for j = 1, #_BUFFERS do
+ if filename == _BUFFERS[j].filename then
+ view:goto_buffer(j) -- already open
+ goto continue
+ end
end
local text = ''
@@ -118,7 +121,7 @@ function io.open_file(filenames)
if f then
text = f:read('*a')
f:close()
- if not text then return end -- filename exists, but cannot read it
+ if not text then goto continue end -- filename exists, but cannot read it
elseif lfs.attributes(filename) then
error(err)
end
@@ -152,10 +155,13 @@ function io.open_file(filenames)
events.emit(events.FILE_OPENED, filename)
-- Add file to recent files list, eliminating duplicates.
- for j, file in ipairs(io.recent_files) do
- if file == filename then table.remove(io.recent_files, j) break end
- end
table.insert(io.recent_files, 1, filename)
+ for j = 2, #io.recent_files do
+ if io.recent_files[j] == filename then
+ table.remove(io.recent_files, j)
+ break
+ end
+ end
::continue::
end
end
@@ -244,8 +250,8 @@ end
-- @name save_all_files
function io.save_all_files()
local current_buffer = _BUFFERS[buffer]
- for i, buffer in ipairs(_BUFFERS) do
- if buffer.filename and buffer.modify then
+ for i = 1, #_BUFFERS do
+ if _BUFFERS[i].filename and _BUFFERS[i].modify then
view:goto_buffer(i)
io.save_file()
end
@@ -306,17 +312,17 @@ events_connect(events.RESUME, update_modified_file)
-- Prompts the user to reload the current file if it has been externally
-- modified.
events_connect(events.FILE_CHANGED, function()
- local msg = ('"%s"\n%s'):format(buffer.filename:iconv('UTF-8', _CHARSET),
- _L['has been modified. Reload it?'])
local button = ui.dialogs.msgbox{
title = _L['Reload?'], text = _L['Reload modified file?'],
- informative_text = msg, icon = 'gtk-dialog-question',
- button1 = _L['_Yes'], button2 = _L['_No']
+ informative_text = string.format('"%s"\n%s',
+ buffer.filename:iconv('UTF-8', _CHARSET),
+ _L['has been modified. Reload it?']),
+ icon = 'gtk-dialog-question', button1 = _L['_Yes'], button2 = _L['_No']
}
if button == 1 then io.reload_file() end
end)
--- Closes the initial "Untitled" buffer.
+-- Closes the initial "Untitled" buffer when another buffer is opened.
events_connect(events.FILE_OPENED, function()
local buf = _BUFFERS[1]
if #_BUFFERS == 2 and not (buf.filename or buf._type or buf.modify) then
@@ -330,12 +336,12 @@ end)
-- @see recent_files
-- @name open_recent_file
function io.open_recent_file()
- local utf8_filenames = {}
- for _, filename in ipairs(io.recent_files) do
- utf8_filenames[#utf8_filenames + 1] = filename:iconv('UTF-8', _CHARSET)
+ local utf8_list = {}
+ for i = 1, #io.recent_files do
+ utf8_list[#utf8_list + 1] = io.recent_files[i]:iconv('UTF-8', _CHARSET)
end
local button, i = ui.dialogs.filteredlist{
- title = _L['Open'], columns = _L['File'], items = utf8_filenames,
+ title = _L['Open'], columns = _L['File'], items = utf8_list,
width = CURSES and ui.size[1] - 2 or nil
}
if button == 1 and i then io.open_file(io.recent_files[i]) end
@@ -442,10 +448,10 @@ function io.snapopen(paths, filter, exclude_FILTER, opts)
end
local utf8_list = {}
for i = 1, #paths do
- lfs.dir_foreach(paths[i], function(file)
+ lfs.dir_foreach(paths[i], function(filename)
if #utf8_list >= io.SNAPOPEN_MAX then return false end
- file = file:gsub('^%.[/\\]', ''):iconv('UTF-8', _CHARSET)
- utf8_list[#utf8_list + 1] = file
+ filename = filename:gsub('^%.[/\\]', '')
+ utf8_list[#utf8_list + 1] = filename:iconv('UTF-8', _CHARSET)
end, filter, exclude_FILTER)
end
if #utf8_list >= io.SNAPOPEN_MAX then
@@ -462,8 +468,11 @@ function io.snapopen(paths, filter, exclude_FILTER, opts)
string_output = true, width = CURSES and ui.size[1] - 2 or nil
}
if opts then for k, v in pairs(opts) do options[k] = v end end
- local button, files = ui.dialogs.filteredlist(options)
- if button ~= _L['_OK'] or not files then return end
- for i = 1, #files do files[i] = files[i]:iconv(_CHARSET, 'UTF-8') end
- io.open_file(files)
+ local button, utf8_filenames = ui.dialogs.filteredlist(options)
+ if button ~= _L['_OK'] or not utf8_filenames then return end
+ local filenames = {}
+ for i = 1, #utf8_filenames do
+ filenames[i] = utf8_filenames[i]:iconv(_CHARSET, 'UTF-8')
+ end
+ io.open_file(filenames)
end
diff --git a/core/keys.lua b/core/keys.lua
index 927491e2..ce4a2cc0 100644
--- a/core/keys.lua
+++ b/core/keys.lua
@@ -155,7 +155,7 @@ local keychain = {}
-- @name keychain
M.keychain = setmetatable({}, {
__index = keychain,
- __newindex = function() error("'keys.keychain' is read-only") end,
+ __newindex = function() error("read-only table") end,
__len = function() return #keychain end
})
@@ -253,7 +253,7 @@ local function keypress(code, shift, control, alt, meta)
end
-- PROPAGATE otherwise.
end
-events.connect(events.KEYPRESS, keypress, 1)
+events.connect(events.KEYPRESS, keypress)
---
-- Map of key bindings to commands, with language-specific key tables assigned
diff --git a/core/lfs_ext.lua b/core/lfs_ext.lua
index 43fe6ebc..b2b89a88 100644
--- a/core/lfs_ext.lua
+++ b/core/lfs_ext.lua
@@ -103,17 +103,17 @@ function lfs.dir_foreach(dir, f, filter, exclude_FILTER, n, include_dirs, level)
if ext then for i = 1, #ext do ext[ext[i]] = true end end
end
local dir_sep, lfs_attributes = not WIN32 and '/' or '\\', lfs.attributes
- for file in lfs.dir(dir) do
- if not file:find('^%.%.?$') then -- ignore . and ..
- file = dir..(dir ~= '/' and dir_sep or '')..file
- local type = lfs_attributes(file, 'mode')
- if type == 'directory' and not exclude(file, filter.folders) then
- if include_dirs and f(file..dir_sep) == false then return end
+ for basename in lfs.dir(dir) do
+ if not basename:find('^%.%.?$') then -- ignore . and ..
+ local filename = dir..(dir ~= '/' and dir_sep or '')..basename
+ local mode = lfs_attributes(filename, 'mode')
+ if mode == 'directory' and not exclude(filename, filter.folders) then
+ if include_dirs and f(filename..dir_sep) == false then return end
if not n or level < n then
- lfs.dir_foreach(file, f, filter, nil, n, include_dirs, level + 1)
+ lfs.dir_foreach(filename, f, filter, nil, n, include_dirs, level + 1)
end
- elseif type == 'file' and not exclude(file, filter) then
- if f(file) == false then return end
+ elseif mode == 'file' and not exclude(filename, filter) then
+ if f(filename) == false then return end
end
end
end
diff --git a/core/ui.lua b/core/ui.lua
index 1dd403a9..de1ed2fd 100644
--- a/core/ui.lua
+++ b/core/ui.lua
@@ -46,8 +46,11 @@ local theme_props = {}
-- @see ui._print
local function _print(buffer_type, ...)
local print_buffer
- for _, buffer in ipairs(_BUFFERS) do
- if buffer._type == buffer_type then print_buffer = buffer break end
+ for i = 1, #_BUFFERS do
+ if _BUFFERS[i]._type == buffer_type then
+ print_buffer = _BUFFERS[i]
+ break
+ end
end
if not print_buffer then
if not ui.tabs then view:split() end
@@ -55,11 +58,12 @@ local function _print(buffer_type, ...)
print_buffer._type = buffer_type
events.emit(events.FILE_OPENED)
elseif not ui.SILENT_PRINT then
- local index = _BUFFERS[print_buffer]
- for i, view in ipairs(_VIEWS) do
- if view.buffer._type == buffer_type then ui.goto_view(i) break end
+ for i = 1, #_VIEWS do
+ if _VIEWS[i].buffer._type == buffer_type then ui.goto_view(i) break end
+ end
+ if view.buffer._type ~= buffer_type then
+ view:goto_buffer(_BUFFERS[print_buffer])
end
- if view.buffer._type ~= buffer_type then view:goto_buffer(index) end
end
local args, n = {...}, select('#', ...)
for i = 1, n do args[i] = tostring(args[i]) end
@@ -95,6 +99,7 @@ ui.dialogs = setmetatable({}, {__index = function(_, k)
-- @param options Table of key-value command line options for gtdialog.
-- @return Lua objects depending on the dialog kind
return function(options)
+ -- Set up dialog defaults and convert any 1-based indices to 0-based ones.
if not options.button1 then options.button1 = _L['_OK'] end
local select = options.select
if type(select) == 'number' then
@@ -107,7 +112,7 @@ ui.dialogs = setmetatable({}, {__index = function(_, k)
for option, value in pairs(options) do
if value then
args[#args + 1] = '--'..option:gsub('_', '-')
- if value ~= true then args[#args + 1] = value end
+ if type(value) ~= 'boolean' then args[#args + 1] = value end
end
end
-- Call gtdialog, stripping any trailing newline in the standard output.
@@ -118,9 +123,11 @@ ui.dialogs = setmetatable({}, {__index = function(_, k)
if result == '' then return nil end
if not CURSES then result = result:iconv(_CHARSET, 'UTF-8') end
if k == 'filesave' or not options.select_multiple then return result end
- local files = {}
- for file in result:gmatch('[^\n]+') do files[#files + 1] = file end
- return files
+ local filenames = {}
+ for filename in result:gmatch('[^\n]+') do
+ filenames[#filenames + 1] = filename
+ end
+ return filenames
elseif k == 'filteredlist' or k == 'optionselect' or
k:find('input') and result:match('^[^\n]+\n?(.*)$'):find('\n') then
local button, value = result:match('^([^\n]+)\n?(.*)$')
@@ -149,16 +156,17 @@ end})
-- Prompts the user to select a buffer to switch to.
-- @name switch_buffer
function ui.switch_buffer()
- local columns, items = {_L['Name'], _L['File']}, {}
- for _, buffer in ipairs(_BUFFERS) do
+ local columns, utf8_list = {_L['Name'], _L['File']}, {}
+ for i = 1, #_BUFFERS do
+ local buffer = _BUFFERS[i]
local filename = buffer.filename or buffer._type or _L['Untitled']
filename = filename:iconv('UTF-8', _CHARSET)
local basename = buffer.filename and filename:match('[^/\\]+$') or filename
- items[#items + 1] = (buffer.modify and '*' or '')..basename
- items[#items + 1] = filename
+ utf8_list[#utf8_list + 1] = (buffer.modify and '*' or '')..basename
+ utf8_list[#utf8_list + 1] = filename
end
local button, i = ui.dialogs.filteredlist{
- title = _L['Switch Buffers'], columns = columns, items = items,
+ title = _L['Switch Buffers'], columns = columns, items = utf8_list,
width = CURSES and ui.size[1] - 2 or nil
}
if button == 1 and i then view:goto_buffer(i) end
@@ -185,20 +193,26 @@ end
-- is `false`.
-- @name goto_file
function ui.goto_file(filename, split, preferred_view, sloppy)
- local patt = '^'..filename..'$'
+ local patt = '^'..filename..'$' -- TODO: escape filename properly
if sloppy then patt = filename:match('[^/\\]+$')..'$' end
if #_VIEWS == 1 and split and not (view.buffer.filename or ''):find(patt) then
view:split()
else
local other_view = _VIEWS[preferred_view]
- for i, v in ipairs(_VIEWS) do
- if (v.buffer.filename or ''):find(patt) then ui.goto_view(i) return end
- if not other_view and v ~= view then other_view = i end
+ for i = 1, #_VIEWS do
+ if (_VIEWS[i].buffer.filename or ''):find(patt) then
+ ui.goto_view(i)
+ return
+ end
+ if not other_view and _VIEWS[i] ~= view then other_view = i end
end
if other_view then ui.goto_view(other_view) end
end
- for i, buffer in ipairs(_BUFFERS) do
- if (buffer.filename or ''):find(patt) then view:goto_buffer(i) return end
+ for i = 1, #_BUFFERS do
+ if (_BUFFERS[i].filename or ''):find(patt) then
+ view:goto_buffer(i)
+ return
+ end
end
io.open_file(filename)
end
@@ -256,11 +270,12 @@ events_connect(events.VIEW_NEW, function()
'[', ']', '/', '\\', 'Z', 'Y', 'X', 'C', 'V', 'A', 'L', 'T', 'D', 'U'
}
local ctrl_shift_keys = {'L', 'T', 'U', 'Z'}
- for _, key in ipairs(ctrl_keys) do
- buffer:clear_cmd_key(string.byte(key) + bit32.lshift(buffer.MOD_CTRL, 16))
+ for i = 1, #ctrl_keys do
+ buffer:clear_cmd_key(string.byte(ctrl_keys[i]) +
+ bit32.lshift(buffer.MOD_CTRL, 16))
end
- for _, key in ipairs(ctrl_shift_keys) do
- buffer:clear_cmd_key(string.byte(key) +
+ for i = 1, #ctrl_shift_keys do
+ buffer:clear_cmd_key(string.byte(ctrl_shift_keys[i]) +
bit32.lshift(buffer.MOD_CTRL + buffer.MOD_SHIFT, 16))
end
-- Since BUFFER_NEW loads themes and settings on startup, only load them for
@@ -338,10 +353,6 @@ events_connect(events.UPDATE_UI, function()
col, lexer, eol, tabs, enc, bom)
end)
--- Updates the statusbar and titlebar for a new Scintilla document.
-events_connect(events.BUFFER_NEW, function() events.emit(events.UPDATE_UI) end)
-events_connect(events.BUFFER_NEW, set_title)
-
-- Save buffer properties.
events_connect(events.BUFFER_BEFORE_SWITCH, function()
local buffer = buffer
@@ -374,6 +385,7 @@ local function update_bars()
buffer:private_lexer_call(SETDIRECTPOINTER, buffer.direct_pointer)
events.emit(events.UPDATE_UI)
end
+events_connect(events.BUFFER_NEW, update_bars)
events_connect(events.BUFFER_AFTER_SWITCH, update_bars)
events_connect(events.VIEW_AFTER_SWITCH, update_bars)
@@ -410,18 +422,20 @@ events_connect(events.RESET_AFTER,
-- Prompts for confirmation if any buffers are modified.
events_connect(events.QUIT, function()
- local list = {}
- for _, buffer in ipairs(_BUFFERS) do
- if buffer.modify then
- local filename = buffer.filename or buffer._type or _L['Untitled']
- list[#list + 1] = filename:iconv('UTF-8', _CHARSET)
+ local utf8_list = {}
+ for i = 1, #_BUFFERS do
+ if _BUFFERS[i].modify then
+ local filename = _BUFFERS[i].filename or _BUFFERS[i]._type or
+ _L['Untitled']
+ utf8_list[#utf8_list + 1] = filename:iconv('UTF-8', _CHARSET)
end
end
- local cancel = #list > 0 and ui.dialogs.msgbox{
+ local cancel = #utf8_list > 0 and ui.dialogs.msgbox{
title = _L['Quit without saving?'],
text = _L['The following buffers are unsaved:'],
- informative_text = table.concat(list, '\n'), icon = 'gtk-dialog-question',
- button1 = _L['_Cancel'], button2 = _L['Quit _without saving']
+ informative_text = table.concat(utf8_list, '\n'),
+ icon = 'gtk-dialog-question', button1 = _L['_Cancel'],
+ button2 = _L['Quit _without saving']
} ~= 2
if cancel then return true end -- prevent quit
end)