diff options
Diffstat (limited to 'core')
-rw-r--r-- | core/args.lua | 4 | ||||
-rw-r--r-- | core/events.lua | 1 | ||||
-rw-r--r-- | core/file_io.lua | 57 | ||||
-rw-r--r-- | core/keys.lua | 4 | ||||
-rw-r--r-- | core/lfs_ext.lua | 18 | ||||
-rw-r--r-- | core/ui.lua | 88 |
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) |