aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormitchell <70453897+667e-11@users.noreply.github.com>2020-03-17 16:46:33 -0400
committermitchell <70453897+667e-11@users.noreply.github.com>2020-03-17 16:46:33 -0400
commit9b02a146612e1a2e15b7bb8d2a5185f1d3925572 (patch)
treecfa28780929cb53715b2ea137f376c0fc0671e9f
parent92bf8d4488d50e31b32d593ca78973b557cc8df7 (diff)
downloadtextadept-9b02a146612e1a2e15b7bb8d2a5185f1d3925572.tar.gz
textadept-9b02a146612e1a2e15b7bb8d2a5185f1d3925572.zip
Added per-mode command entry history.
-rw-r--r--doc/manual.md4
-rw-r--r--modules/textadept/command_entry.lua39
-rw-r--r--test/test.lua62
3 files changed, 98 insertions, 7 deletions
diff --git a/doc/manual.md b/doc/manual.md
index c8769162..91cb0f8b 100644
--- a/doc/manual.md
+++ b/doc/manual.md
@@ -1344,6 +1344,7 @@ simply use `split`. Finally, these commands are runnable on startup using the
`-e` and `--execute` command line switches.
Pressing `Ctrl+H` (`^H` | `M-H` or `M-S-H`) shows help for the current command.
+Pressing `Up` or `Down` cycles through command history.
![Command Entry](images/commandentry.png)
@@ -1366,7 +1367,8 @@ available completions. Use the arrow keys to make a selection and press `Enter`
Executing Lua commands is just one of the many "modes" the command entry has.
The [command entry API documentation][] has more information on modes and how to
-create new ones.
+create new ones. Each mode has its own history that can be cycled through using
+the `Up` or `Down` keys.
[command entry API documentation]: api.html#ui.command_entry
diff --git a/modules/textadept/command_entry.lua b/modules/textadept/command_entry.lua
index 347f6ab0..168b53f5 100644
--- a/modules/textadept/command_entry.lua
+++ b/modules/textadept/command_entry.lua
@@ -6,13 +6,33 @@ local M = ui.command_entry
--[[ This comment is for LuaDoc.
---
-- Textadept's Command Entry.
--- It supports multiple modes that each have their own functionality, such as
+-- It supports multiple modes that each have their own functionality (such as
-- running Lua code, searching for text incrementally, and filtering text
--- through shell commands.
+-- through shell commands) and history.
-- @field height (number)
-- The height in pixels of the command entry.
module('ui.command_entry')]]
+-- Command history per mode.
+-- The current mode is in the `mode` field.
+-- @class table
+-- @name history
+local history = {}
+
+-- Cycles through command history for the current mode.
+-- @param prev Flag that indicates whether to cycle to the previous command or
+-- the next one.
+local function cycle_history(prev)
+ if M:auto_c_active() then M[prev and 'line_up' or 'line_down'](M) return end
+ local mode_history = history[history.mode]
+ if not mode_history or prev and mode_history.pos <= 1 then return end
+ if not prev and mode_history.pos >= #mode_history then return end
+ M:line_delete()
+ local i, bound = prev and -1 or 1, prev and 1 or #mode_history
+ mode_history.pos = math[prev and 'max' or 'min'](mode_history.pos + i, bound)
+ M:add_text(mode_history[mode_history.pos])
+end
+
---
-- A metatable with typical platform-specific key bindings for text entries.
-- This metatable may be used to add basic editing and movement keys to command
@@ -31,6 +51,9 @@ M.editing_keys = {__index = {
[not OSX and 'cz' or 'mz'] = function() M:undo() end,
[not OSX and 'cZ' or 'mZ'] = function() M:redo() end,
[not OSX and 'cy' or '\0'] = function() M:redo() end,
+ up = function() cycle_history(true) end, down = cycle_history,
+ [(OSX or CURSES) and 'cp' or '\0'] = function() cycle_history(true) end,
+ [(OSX or CURSES) and 'cn' or '\0'] = cycle_history,
-- Movement keys.
[(OSX or CURSES) and 'cf' or '\0'] = function() M:char_right() end,
[(OSX or CURSES) and 'cb' or '\0'] = function() M:char_left() end,
@@ -104,7 +127,7 @@ local function complete_lua()
if (not ok or type(result) ~= 'table') and symbol ~= '' then return end
local cmpls = {}
part = '^' .. part
- local sep = string.char(buffer.auto_c_type_separator)
+ local sep = string.char(M.auto_c_type_separator)
local XPM = textadept.editing.XPM_IMAGES
if not ok or symbol == 'buffer' then
local sci = _SCINTILLA
@@ -180,11 +203,17 @@ function M.run(f, keys, lexer, height)
keys['\n'] = function()
if M:auto_c_active() then return false end -- allow Enter to autocomplete
M.focus() -- hide
- if f then f((M:get_text())) end
+ if not f then return end
+ local mode_history = history[history.mode]
+ mode_history[#mode_history + 1] = M:get_text()
+ mode_history.pos = #mode_history + 1
+ f((M:get_text()))
end
end
if not getmetatable(keys) then setmetatable(keys, M.editing_keys) end
- M:select_all()
+ if f and not history[f] then history[f] = {pos = 0} end
+ history.mode = f
+ M:set_text('')
M.focus()
M:set_lexer(lexer or 'text')
M.height = M:text_height(0) * (height or 1)
diff --git a/test/test.lua b/test/test.lua
index a584cacc..b884f188 100644
--- a/test/test.lua
+++ b/test/test.lua
@@ -1081,8 +1081,8 @@ function test_command_entry_run()
end
local function run_lua_command(command)
- ui.command_entry:set_text(command)
ui.command_entry.run()
+ ui.command_entry:set_text(command)
assert_equal(ui.command_entry:get_lexer(), 'lua')
events.emit(events.KEYPRESS, not CURSES and 0xFF0D or 343) -- \n
end
@@ -1131,6 +1131,11 @@ local function assert_lua_autocompletion(text, first_item)
events.emit(events.KEYPRESS, string.byte('\t'))
assert_equal(ui.command_entry:auto_c_active(), true)
assert_equal(ui.command_entry.auto_c_current_text, first_item)
+ events.emit(events.KEYPRESS, not CURSES and 0xFF54 or 300) -- down
+ events.emit(events.KEYPRESS, not CURSES and 0xFF52 or 301) -- up
+ assert_equal(ui.command_entry:get_text(), text) -- no history cycling
+ assert_equal(ui.command_entry:auto_c_active(), true)
+ assert_equal(ui.command_entry.auto_c_current_text, first_item)
ui.command_entry:auto_c_cancel()
end
@@ -1146,6 +1151,61 @@ function test_command_entry_complete_lua()
ui.command_entry:focus() -- hide
end
+function test_command_entry_history()
+ local one, two = function() end, function() end
+
+ ui.command_entry.run(one)
+ events.emit(events.KEYPRESS, not CURSES and 0xFF52 or 301) -- up
+ assert_equal(ui.command_entry:get_text(), '') -- no prior history
+ events.emit(events.KEYPRESS, not CURSES and 0xFF54 or 300) -- down
+ assert_equal(ui.command_entry:get_text(), '') -- no further history
+ ui.command_entry:add_text('foo')
+ events.emit(events.KEYPRESS, not CURSES and 0xFF0D or 343) -- \n
+
+ ui.command_entry.run(two)
+ events.emit(events.KEYPRESS, not CURSES and 0xFF52 or 301) -- up
+ assert_equal(ui.command_entry:get_text(), '') -- no prior history
+ events.emit(events.KEYPRESS, not CURSES and 0xFF54 or 300) -- down
+ assert_equal(ui.command_entry:get_text(), '') -- no further history
+ ui.command_entry:add_text('bar')
+ events.emit(events.KEYPRESS, not CURSES and 0xFF0D or 343) -- \n
+
+ ui.command_entry.run(one)
+ assert_equal(ui.command_entry:get_text(), '')
+ events.emit(events.KEYPRESS, not CURSES and 0xFF52 or 301) -- up
+ assert_equal(ui.command_entry:get_text(), 'foo')
+ events.emit(events.KEYPRESS, not CURSES and 0xFF52 or 301) -- up
+ assert_equal(ui.command_entry:get_text(), 'foo') -- no prior history
+ events.emit(events.KEYPRESS, not CURSES and 0xFF54 or 300) -- down
+ assert_equal(ui.command_entry:get_text(), 'foo') -- no further history
+ ui.command_entry:set_text('baz')
+ events.emit(events.KEYPRESS, not CURSES and 0xFF0D or 343) -- \n
+
+ ui.command_entry.run(one)
+ events.emit(events.KEYPRESS, not CURSES and 0xFF52 or 301) -- up
+ assert_equal(ui.command_entry:get_text(), 'baz')
+ events.emit(events.KEYPRESS, not CURSES and 0xFF52 or 301) -- up
+ assert_equal(ui.command_entry:get_text(), 'foo')
+ events.emit(events.KEYPRESS, not CURSES and 0xFF54 or 300) -- down
+ assert_equal(ui.command_entry:get_text(), 'baz')
+ events.emit(events.KEYPRESS, not CURSES and 0xFF52 or 301) -- up, 'foo'
+ events.emit(events.KEYPRESS, not CURSES and 0xFF0D or 343) -- \n
+
+ ui.command_entry.run(one)
+ events.emit(events.KEYPRESS, not CURSES and 0xFF52 or 301) -- up
+ assert_equal(ui.command_entry:get_text(), 'foo')
+ events.emit(events.KEYPRESS, not CURSES and 0xFF52 or 301) -- up
+ assert_equal(ui.command_entry:get_text(), 'baz')
+ events.emit(events.KEYPRESS, not CURSES and 0xFF52 or 301) -- up
+ assert_equal(ui.command_entry:get_text(), 'foo')
+ events.emit(events.KEYPRESS, not CURSES and 0xFF1B or 7) -- esc
+
+ ui.command_entry.run(two)
+ events.emit(events.KEYPRESS, not CURSES and 0xFF52 or 301) -- up
+ assert_equal(ui.command_entry:get_text(), 'bar')
+ events.emit(events.KEYPRESS, not CURSES and 0xFF1B or 7) -- esc
+end
+
function test_editing_auto_pair()
buffer.new()
-- Single selection.