aboutsummaryrefslogtreecommitdiffhomepage
path: root/modules
diff options
context:
space:
mode:
authorGravatar mitchell <70453897+orbitalquark@users.noreply.github.com>2020-10-08 14:01:36 -0400
committerGravatar mitchell <70453897+orbitalquark@users.noreply.github.com>2020-10-08 14:01:36 -0400
commit1f367593686bd1619ad94b669a65559ef8afb582 (patch)
tree9016c21963f64bf7318193b13c5131825821d3b3 /modules
parentfe67095963a162ff7e24680936fb7cf1cb730bbb (diff)
Added `textadept.history` module for recording and navigating position history.
Diffstat (limited to 'modules')
-rw-r--r--modules/lua/ta_api7
-rw-r--r--modules/lua/ta_tags8
-rw-r--r--modules/textadept/history.lua175
-rw-r--r--modules/textadept/init.lua1
-rw-r--r--modules/textadept/keys.lua81
-rw-r--r--modules/textadept/macros.lua2
-rw-r--r--modules/textadept/menu.lua8
7 files changed, 245 insertions, 37 deletions
diff --git a/modules/lua/ta_api b/modules/lua/ta_api
index dd8632a6..67fbe022 100644
--- a/modules/lua/ta_api
+++ b/modules/lua/ta_api
@@ -370,6 +370,7 @@ autocomplete_all_words textadept.editing.autocomplete_all_words (bool)\nAutocomp
autocomplete_snippets _M.ansi_c.autocomplete_snippets (boolean)\nWhether or not to include snippets in autocompletion lists.\nThe default value is `true`.
autocomplete_snippets _M.lua.autocomplete_snippets (boolean)\nWhether or not to include snippets in autocompletion lists.\nThe default value is `false`.
autocompleters textadept.editing.autocompleters (table)\nMap of autocompleter names to autocompletion functions.\nNames are typically lexer names and autocompletion functions typically\nautocomplete symbols.\nAutocompletion functions must return two values: the number of characters\nbehind the caret that are used as the prefix of the entity to be\nautocompleted, and a list of completions to be shown. Autocompletion lists\nare sorted automatically.\n@see autocomplete
+back textadept.history.back()\nNavigates backwards through the current view's history.
back_space_un_indents buffer.back_space_un_indents (bool)\nUn-indent text when backspacing within indentation.\nThe default value is `false`.
back_tab buffer.back_tab(buffer)\nUn-indents the text on the selected lines.\n@param buffer A buffer.
begin_undo_action buffer.begin_undo_action(buffer)\nStarts a sequence of actions to be undone or redone as a single action.\nMay be nested.\n@param buffer A buffer.
@@ -419,6 +420,7 @@ char_right_rect_extend buffer.char_right_rect_extend(buffer)\nMoves the caret ri
choose_caret_x buffer.choose_caret_x(buffer)\nIdentifies the current horizontal caret position as the caret's preferred\nhorizontal position when moving between lines.\n@param buffer A buffer.\n@see caret_sticky
clear buffer.clear(buffer)\nDeletes the selected text or the character at the caret.\n@param buffer A buffer.
clear textadept.bookmarks.clear()\nClears all bookmarks in the current buffer.
+clear textadept.history.clear()\nClears all view history.
clear_all buffer.clear_all(buffer)\nDeletes the buffer's text.\n@param buffer A buffer.
clear_document_style buffer.clear_document_style(buffer)\nClears all styling and folding information.\n@param buffer A buffer.
clear_registered_images view.clear_registered_images(view)\nClears all images registered using `view.register_image()` and\n`view.register_rgba_image()`.\n@param view A view.
@@ -539,6 +541,7 @@ fold_on_zero_sum_lines lexer.fold_on_zero_sum_lines (boolean)\nWhether or not to
fold_parent buffer.fold_parent (table, Read-only)\nTable of fold point line numbers per child line number.\nA line number of `-1` means no line was found.
folding lexer.folding (boolean)\nWhether or not folding is enabled for the lexers that support it.\nThis option is disabled by default.\nThis is an alias for `lexer.property['fold'] = '1|0'`.
fontselect ui.dialogs.fontselect(options)\nPrompts the user with a font selection dialog defined by dialog options\ntable *options*, returning the font selected (including style and size).\nIf the user canceled the dialog, returns `nil`.\n@param options Table of key-value option pairs for the option select dialog.\n\n * `title`: The dialog's title text.\n * `text`: The font preview text.\n * `font_name`: The initially selected font name.\n * `font_size`: The initially selected font size. The default value is `12`.\n * `font_style`: The initially selected font style. The available options\n are `"regular"`, `"bold"`, `"italic"`, and `"bold italic"`. The default\n value is `"regular"`.\n * `float`: Show the dialog on top of all desktop windows. The default value\n is `false`.\n@usage ui.dialogs.fontselect{title = 'Font', font_name = 'Monospace',\n font_size = 10}\n@return selected font, including style and size
+forward textadept.history.forward()\nNavigates forwards through the current view's history.
functions _SCINTILLA.functions (table)\nMap of Scintilla function names to tables containing their IDs, return types,\nwParam types, and lParam types. Types are as follows:\n\n + `0`: Void.\n + `1`: Integer.\n + `2`: Length of the given lParam string.\n + `3`: Integer position.\n + `4`: Color, in "0xBBGGRR" format.\n + `5`: Boolean `true` or `false`.\n + `6`: Bitmask of Scintilla key modifiers and a key value.\n + `7`: String parameter.\n + `8`: String return value.
get_cur_line buffer.get_cur_line(buffer)\nReturns the current line's text and the caret's position on that line.\n@param buffer A buffer.\n@return string, number
get_default_fold_display_text view.get_default_fold_display_text(view)\nReturns the default fold display text.\n@param view A view.
@@ -567,6 +570,7 @@ hide_lines view.hide_lines(view, start_line, end_line)\nHides the range of lines
highlight_all_matches ui.find.highlight_all_matches (boolean)\nWhether or not to highlight all occurrences of found text in the current\nbuffer.\nThe default value is `false`.
highlight_guide view.highlight_guide (number)\nThe indentation guide column number to also highlight when highlighting\nmatching braces, or `0` to stop indentation guide highlighting.
highlight_words textadept.editing.highlight_words (number)\nThe word highlight mode.\n\n* `textadept.editing.HIGHLIGHT_CURRENT`\n Automatically highlight all instances of the current word.\n* `textadept.editing.HIGHLIGHT_SELECTED`\n Automatically highlight all instances of the selected word.\n* `textadept.editing.HIGHLIGHT_NONE`\n Do not automatically highlight words.\n\nThe default value is `textadept.editing.HIGHLIGHT_NONE`.
+history textadept.history (module)\nRecords buffer positions within Textadept views over time and allows for\nnavigating through that history.\n\nThis module listens for text edit events and buffer switch events. Each time\nan insertion or deletion occurs, its location is recorded in the current\nview's location history. If the edit is close enough to the previous record,\nthe previous record is amended. Each time a buffer switch occurs, the before\nand after locations are recorded.
home buffer.home(buffer)\nMoves the caret to the beginning of the current line.\n@param buffer A buffer.
home_display buffer.home_display(buffer)\nMoves the caret to the beginning of the current wrapped line.\n@param buffer A buffer.
home_display_extend buffer.home_display_extend(buffer)\nMoves the caret to the beginning of the current wrapped line, extending the\nselected text to the new position.\n@param buffer A buffer.
@@ -692,10 +696,12 @@ marker_symbol_defined view.marker_symbol_defined(view, marker)\nReturns the symb
match_case ui.find.match_case (bool)\nMatch search text case sensitively.\nThe default value is `false`.
match_case_label_text ui.find.match_case_label_text (string, Write-only)\nThe text of the "Match case" label.\nThis is primarily used for localization.
maximized ui.maximized (bool)\nWhether or not Textadept's window is maximized.
+maximum_history_size textadept.history.maximum_history_size (number)\nThe maximum number of history records to keep per view.\nThe default value is `100`.
menu textadept.menu (module)\nDefines the menus used by Textadept.\nMenus are simply tables of menu items and submenus and may be edited in\nplace. A menu item itself is a table whose first element is a menu label and\nwhose second element is a menu command to run. Submenus have `title` keys\nassigned to string text.
menu ui.menu(menu_table)\nLow-level function for creating a menu from table *menu_table* and returning\nthe userdata.\nYou probably want to use the higher-level `textadept.menu.menubar`,\n`textadept.menu.context_menu`, or `textadept.menu.tab_context_menu` tables.\nEmits a `MENU_CLICKED` event when a menu item is selected.\n@param menu_table A table defining the menu. It is an ordered list of tables\n with a string menu item, integer menu ID, and optional GDK keycode and\n modifier mask. The latter two are used to display key shortcuts in the\n menu. '_' characters are treated as a menu mnemonics. If the menu item is\n empty, a menu separator item is created. Submenus are just nested\n menu-structure tables. Their title text is defined with a `title` key.\n@usage ui.menu{ {'_New', 1}, {'_Open', 2}, {''}, {'_Quit', 4} }\n@usage ui.menu{ {'_New', 1, string.byte('n'), 4} } -- 'Ctrl+N'\n@see events.MENU_CLICKED\n@see textadept.menu.menubar\n@see textadept.menu.context_menu\n@see textadept.menu.tab_context_menu
menubar textadept.menu.menubar (table)\nThe default main menubar.\nIndividual menus, submenus, and menu items can be retrieved by name in\naddition to table index number.
menubar ui.menubar (table)\nA table of menus defining a menubar. (Write-only).\nThis is a low-level field. You probably want to use the higher-level\n`textadept.menu.menubar`.\n@see textadept.menu.menubar
+minimum_line_distance textadept.history.minimum_line_distance (number)\nThe minimum number of lines between distinct history records.\nThe default value is `3`.
mode keys.mode (string)\nThe current key mode.\nWhen non-`nil`, all key bindings defined outside of `keys[mode]` are\nignored.\nThe default value is `nil`.
modify buffer.modify (bool, Read-only)\nWhether or not the buffer has unsaved changes.
modify_rule lexer.modify_rule(lexer, id, rule)\nReplaces in lexer *lexer* the existing rule identified by string *id* with\npattern *rule*.\n@param lexer The lexer to modify.\n@param id The id associated with this rule.\n@param rule The LPeg pattern of the rule.
@@ -771,6 +777,7 @@ range lexer.range(s, e, single_line, escapes, balanced)\nCreates and returns a p
read spawn_proc:read(arg)\nReads and returns stdout from process *spawn_proc*, according to string\nformat or number *arg*.\nSimilar to Lua's `io.read()` and blocks for input. *spawn_proc* must still be\nrunning. If an error occurs while reading, returns `nil`, an error code, and\nan error message.\nEnsure any read operations read all stdout available, as the stdout callback\nfunction passed to `os.spawn()` will not be called until the stdout buffer is\nclear.\n@param arg Optional argument similar to those in Lua's `io.read()`, but "n"\n is not supported. The default value is "l", which reads a line.\n@return string of bytes read
read_only buffer.read_only (bool)\nWhether or not the buffer is read-only.\nThe default value is `false`.
recent_files io.recent_files (table)\nList of recently opened files, the most recent being towards the top.
+record textadept.history.record(filename, line, column, soft)\nRecords the given location in the current view's history.\n@param filename Optional string filename, buffer type, or identifier of the\n buffer to store. If `nil`, uses the current buffer.\n@param line Optional Integer line number to store. If `nil`, uses the current\n line.\n@param column Optional integer column number on line *line* to store. If\n `nil`, uses the current column.\n@param soft Optional flag that indicates whether or not this record should be\n skipped when navigating backward towards it, and updated when navigating\n away from it. The default value is `false`.
record textadept.macros.record()\nToggles between starting and stopping macro recording.
rectangular_selection_anchor buffer.rectangular_selection_anchor (number)\nThe rectangular selection's anchor position.
rectangular_selection_anchor_virtual_space buffer.rectangular_selection_anchor_virtual_space (number)\nThe amount of virtual space for the rectangular selection's anchor.
diff --git a/modules/lua/ta_tags b/modules/lua/ta_tags
index 1102149a..b0634779 100644
--- a/modules/lua/ta_tags
+++ b/modules/lua/ta_tags
@@ -372,6 +372,7 @@ autocomplete_all_words _HOME/modules/textadept/editing.lua /^module('textadept.e
autocomplete_snippets _HOME/modules/ansi_c/init.lua /^module('_M.ansi_c')]]$/;" F class:_M.ansi_c
autocomplete_snippets _HOME/modules/lua/init.lua /^module('_M.lua')]]$/;" F class:_M.lua
autocompleters _HOME/modules/textadept/editing.lua /^M.autocompleters = {}$/;" t class:textadept.editing
+back _HOME/modules/textadept/history.lua /^function M.back()$/;" f class:textadept.history
back_space_un_indents _HOME/core/.buffer.luadoc /^module('buffer')$/;" F class:buffer
back_tab _HOME/core/.buffer.luadoc /^function back_tab(buffer) end$/;" f class:buffer
begin_undo_action _HOME/core/.buffer.luadoc /^function begin_undo_action(buffer) end$/;" f class:buffer
@@ -421,6 +422,7 @@ char_right_rect_extend _HOME/core/.buffer.luadoc /^function char_right_rect_exte
choose_caret_x _HOME/core/.buffer.luadoc /^function choose_caret_x(buffer) end$/;" f class:buffer
clear _HOME/core/.buffer.luadoc /^function clear(buffer) end$/;" f class:buffer
clear _HOME/modules/textadept/bookmarks.lua /^function M.clear() buffer:marker_delete_all(M.MARK_BOOKMARK) end$/;" f class:textadept.bookmarks
+clear _HOME/modules/textadept/history.lua /^function M.clear()$/;" f class:textadept.history
clear_all _HOME/core/.buffer.luadoc /^function clear_all(buffer) end$/;" f class:buffer
clear_document_style _HOME/core/.buffer.luadoc /^function clear_document_style(buffer) end$/;" f class:buffer
clear_registered_images _HOME/core/.view.luadoc /^function clear_registered_images(view) end$/;" f class:view
@@ -541,6 +543,7 @@ fold_on_zero_sum_lines _HOME/lexers/lexer.lua /^module('lexer')]=]$/;" F class:l
fold_parent _HOME/core/.buffer.luadoc /^module('buffer')$/;" F class:buffer
folding _HOME/lexers/lexer.lua /^module('lexer')]=]$/;" F class:lexer
fontselect _HOME/core/.ui.dialogs.luadoc /^function fontselect(options) end$/;" f class:ui.dialogs
+forward _HOME/modules/textadept/history.lua /^function M.forward()$/;" f class:textadept.history
functions _HOME/core/iface.lua /^M.functions = {add_ref_document={2376,0,0,1},add_selection={2573,0,3,3},add_styled_text={2002,0,2,9},add_tab_stop={2676,0,3,1},add_text={2001,0,2,7},add_undo_action={2560,0,1,1},allocate={2446,0,3,0},allocate_extended_styles={2553,1,1,0},allocate_sub_styles={4020,1,1,1},annotation_clear_all={2547,0,0,0},append_text={2282,0,2,7},assign_cmd_key={2070,0,6,1},auto_c_active={2102,5,0,0},auto_c_cancel={2101,0,0,0},auto_c_complete={2104,0,0,0},auto_c_pos_start={2103,3,0,0},auto_c_select={2108,0,0,7},auto_c_show={2100,0,1,7},auto_c_stops={2105,0,0,7},back_tab={2328,0,0,0},begin_undo_action={2078,0,0,0},brace_bad_light={2352,0,3,0},brace_bad_light_indicator={2499,0,5,3},brace_highlight={2351,0,3,3},brace_highlight_indicator={2498,0,5,3},brace_match={2353,3,3,1},brace_match_next={2369,3,3,3},call_tip_active={2202,5,0,0},call_tip_cancel={2201,0,0,0},call_tip_pos_start={2203,3,0,0},call_tip_set_hlt={2204,0,3,3},call_tip_show={2200,0,3,7},can_paste={2173,5,0,0},can_redo={2016,5,0,0},can_undo={2174,5,0,0},cancel={2325,0,0,0},change_insertion={2672,0,2,7},change_lexer_state={2617,1,3,3},char_left={2304,0,0,0},char_left_extend={2305,0,0,0},char_left_rect_extend={2428,0,0,0},char_position_from_point={2561,3,1,1},char_position_from_point_close={2562,3,1,1},char_right={2306,0,0,0},char_right_extend={2307,0,0,0},char_right_rect_extend={2429,0,0,0},choose_caret_x={2399,0,0,0},clear={2180,0,0,0},clear_all={2004,0,0,0},clear_all_cmd_keys={2072,0,0,0},clear_cmd_key={2071,0,6,0},clear_document_style={2005,0,0,0},clear_registered_images={2408,0,0,0},clear_representation={2667,0,7,0},clear_selections={2571,0,0,0},clear_tab_stops={2675,0,3,0},colorize={4003,0,3,3},contracted_fold_next={2618,3,3,0},convert_eols={2029,0,1,0},copy={2178,0,0,0},copy_allow_line={2519,0,0,0},copy_range={2419,0,3,3},copy_text={2420,0,2,7},count_characters={2633,1,3,3},count_code_units={2715,1,3,3},create_document={2375,1,3,1},create_loader={2632,1,3,1},cut={2177,0,0,0},del_line_left={2395,0,0,0},del_line_right={2396,0,0,0},del_word_left={2335,0,0,0},del_word_right={2336,0,0,0},del_word_right_end={2518,0,0,0},delete_back={2326,0,0,0},delete_back_not_line={2344,0,0,0},delete_range={2645,0,3,2},describe_key_word_sets={4017,0,0,8},describe_property={4016,0,7,8},description_of_style={4032,0,3,8},doc_line_from_visible={2221,3,3,0},document_end={2318,0,0,0},document_end_extend={2319,0,0,0},document_start={2316,0,0,0},document_start_extend={2317,0,0,0},drop_selection_n={2671,0,3,0},edit_toggle_overtype={2324,0,0,0},empty_undo_buffer={2175,0,0,0},encoded_from_utf8={2449,0,7,8},end_undo_action={2079,0,0,0},ensure_visible={2232,0,3,0},ensure_visible_enforce_policy={2234,0,3,0},eol_annotation_clear_all={2744,0,0,0},expand_children={2239,0,3,1},find_column={2456,3,3,3},find_indicator_flash={2641,0,3,3},find_indicator_hide={2642,0,0,0},find_indicator_show={2640,0,3,3},find_text={2150,3,1,11},fold_all={2662,0,1,0},fold_children={2238,0,3,1},fold_line={2237,0,3,1},form_feed={2330,0,0,0},format_range={2151,3,5,12},free_sub_styles={4023,0,0,0},get_cur_line={2027,3,2,8},get_default_fold_display_text={2723,0,0,8},get_hotspot_active_back={2495,4,0,0},get_hotspot_active_fore={2494,4,0,0},get_line={2153,0,3,8},get_line_sel_end_position={2425,3,3,0},get_line_sel_start_position={2424,3,3,0},get_next_tab_stop={2677,1,3,1},get_sel_text={2161,0,0,8},get_styled_text={2015,3,0,10},get_text={2182,0,2,8},get_text_range={2162,3,0,10},goto_line={2024,0,3,0},goto_pos={2025,0,3,0},grab_focus={2400,0,0,0},hide_lines={2227,0,3,3},hide_selection={2163,0,5,0},home={2312,0,0,0},home_display={2345,0,0,0},home_display_extend={2346,0,0,0},home_extend={2313,0,0,0},home_rect_extend={2430,0,0,0},home_wrap={2349,0,0,0},home_wrap_extend={2450,0,0,0},indicator_all_on_for={2506,1,3,0},indicator_clear_range={2505,0,3,2},indicator_end={2509,3,3,3},indicator_fill_range={2504,0,3,2},indicator_start={2508,3,3,3},indicator_value_at={2507,1,3,3},insert_text={2003,0,3,7},is_range_word={2691,5,3,3},line_copy={2455,0,0,0},line_cut={2337,0,0,0},line_delete={2338,0,0,0},line_down={2300,0,0,0},line_down_extend={2301,0,0,0},line_down_rect_extend={2426,0,0,0},line_duplicate={2404,0,0,0},line_end={2314,0,0,0},line_end_display={2347,0,0,0},line_end_display_extend={2348,0,0,0},line_end_extend={2315,0,0,0},line_end_rect_extend={2432,0,0,0},line_end_wrap={2451,0,0,0},line_end_wrap_extend={2452,0,0,0},line_from_position={2166,3,3,0},line_length={2350,1,3,0},line_reverse={2354,0,0,0},line_scroll={2168,0,1,1},line_scroll_down={2342,0,0,0},line_scroll_up={2343,0,0,0},line_transpose={2339,0,0,0},line_up={2302,0,0,0},line_up_extend={2303,0,0,0},line_up_rect_extend={2427,0,0,0},lines_join={2288,0,0,0},lines_split={2289,0,1,0},load_lexer_library={4007,0,0,7},lower_case={2340,0,0,0},margin_text_clear_all={2536,0,0,0},marker_add={2043,1,3,3},marker_add_set={2466,0,3,1},marker_define={2040,0,3,1},marker_define_pixmap={2049,0,3,7},marker_define_rgba_image={2626,0,3,7},marker_delete={2044,0,3,3},marker_delete_all={2045,0,3,0},marker_delete_handle={2018,0,1,0},marker_enable_highlight={2293,0,5,0},marker_get={2046,1,3,0},marker_handle_from_line={2732,1,3,3},marker_line_from_handle={2017,3,1,0},marker_next={2047,3,3,1},marker_number_from_line={2733,3,3,3},marker_previous={2048,3,3,1},marker_symbol_defined={2529,1,3,0},move_caret_inside_view={2401,0,0,0},move_selected_lines_down={2621,0,0,0},move_selected_lines_up={2620,0,0,0},multi_edge_add_line={2694,0,1,4},multi_edge_clear_all={2695,0,0,0},multiple_select_add_each={2689,0,0,0},multiple_select_add_next={2688,0,0,0},name_of_style={4030,0,3,8},new_line={2329,0,0,0},null={2172,0,0,0},page_down={2322,0,0,0},page_down_extend={2323,0,0,0},page_down_rect_extend={2434,0,0,0},page_up={2320,0,0,0},page_up_extend={2321,0,0,0},page_up_rect_extend={2433,0,0,0},para_down={2413,0,0,0},para_down_extend={2414,0,0,0},para_up={2415,0,0,0},para_up_extend={2416,0,0,0},paste={2179,0,0,0},point_x_from_position={2164,1,0,3},point_y_from_position={2165,1,0,3},position_after={2418,3,3,0},position_before={2417,3,3,0},position_from_line={2167,3,3,0},position_from_point={2022,3,1,1},position_from_point_close={2023,3,1,1},position_relative={2670,3,3,1},position_relative_code_units={2716,3,3,3},private_lexer_call={4013,1,1,1},property_names={4014,0,0,8},property_type={4015,1,7,0},redo={2011,0,0,0},register_image={2405,0,1,7},register_rgba_image={2627,0,1,7},release_all_extended_styles={2552,0,0,0},release_document={2377,0,0,1},replace_sel={2170,0,0,7},replace_target={2194,1,2,7},replace_target_re={2195,1,2,7},rotate_selection={2606,0,0,0},scroll_caret={2169,0,0,0},scroll_range={2569,0,3,3},scroll_to_end={2629,0,0,0},scroll_to_start={2628,0,0,0},search_anchor={2366,0,0,0},search_in_target={2197,3,2,7},search_next={2367,3,1,7},search_prev={2368,3,1,7},select_all={2013,0,0,0},selection_duplicate={2469,0,0,0},set_chars_default={2444,0,0,0},set_default_fold_display_text={2722,0,0,7},set_empty_selection={2556,0,3,0},set_fold_margin_color={2290,0,5,4},set_fold_margin_hi_color={2291,0,5,4},set_hotspot_active_back={2411,0,5,4},set_hotspot_active_fore={2410,0,5,4},set_length_for_encode={2448,0,3,0},set_save_point={2014,0,0,0},set_sel={2160,0,3,3},set_sel_back={2068,0,5,4},set_sel_fore={2067,0,5,4},set_selection={2572,0,3,3},set_styling={2033,0,2,3},set_styling_ex={2073,0,2,7},set_target_range={2686,0,3,3},set_text={2181,0,0,7},set_visible_policy={2394,0,1,1},set_whitespace_back={2085,0,5,4},set_whitespace_fore={2084,0,5,4},set_x_caret_policy={2402,0,1,1},set_y_caret_policy={2403,0,1,1},show_lines={2226,0,3,3},start_record={3001,0,0,0},start_styling={2032,0,3,1},stop_record={3002,0,0,0},stuttered_page_down={2437,0,0,0},stuttered_page_down_extend={2438,0,0,0},stuttered_page_up={2435,0,0,0},stuttered_page_up_extend={2436,0,0,0},style_clear_all={2050,0,0,0},style_reset_default={2058,0,0,0},swap_main_anchor_caret={2607,0,0,0},tab={2327,0,0,0},tags_of_style={4031,0,3,8},target_as_utf8={2447,0,0,8},target_from_selection={2287,0,0,0},target_whole_document={2690,0,0,0},text_height={2279,1,3,0},text_width={2276,1,3,7},toggle_caret_sticky={2459,0,0,0},toggle_fold={2231,0,3,0},toggle_fold_show_text={2700,0,3,7},undo={2176,0,0,0},upper_case={2341,0,0,0},use_pop_up={2371,0,1,0},user_list_show={2117,0,1,7},vc_home={2331,0,0,0},vc_home_display={2652,0,0,0},vc_home_display_extend={2653,0,0,0},vc_home_extend={2332,0,0,0},vc_home_rect_extend={2431,0,0,0},vc_home_wrap={2453,0,0,0},vc_home_wrap_extend={2454,0,0,0},vertical_center_caret={2619,0,0,0},visible_from_doc_line={2220,3,3,0},word_end_position={2267,3,3,5},word_left={2308,0,0,0},word_left_end={2439,0,0,0},word_left_end_extend={2440,0,0,0},word_left_extend={2309,0,0,0},word_part_left={2390,0,0,0},word_part_left_extend={2391,0,0,0},word_part_right={2392,0,0,0},word_part_right_extend={2393,0,0,0},word_right={2310,0,0,0},word_right_end={2441,0,0,0},word_right_end_extend={2442,0,0,0},word_right_extend={2311,0,0,0},word_start_position={2266,3,3,5},wrap_count={2235,1,3,0},zoom_in={2333,0,0,0},zoom_out={2334,0,0,0},}$/;" t class:_SCINTILLA
get_cur_line _HOME/core/.buffer.luadoc /^function get_cur_line(buffer) end$/;" f class:buffer
get_default_fold_display_text _HOME/core/.view.luadoc /^function get_default_fold_display_text(view) end$/;" f class:view
@@ -569,6 +572,7 @@ hide_lines _HOME/core/.view.luadoc /^function hide_lines(view, start_line, end_l
highlight_all_matches _HOME/modules/textadept/find.lua /^module('ui.find')]]$/;" F class:ui.find
highlight_guide _HOME/core/.view.luadoc /^module('view')$/;" F class:view
highlight_words _HOME/modules/textadept/editing.lua /^module('textadept.editing')]]$/;" F class:textadept.editing
+history _HOME/modules/textadept/history.lua /^module('textadept.history')]]$/;" m class:textadept
home _HOME/core/.buffer.luadoc /^function home(buffer) end$/;" f class:buffer
home_display _HOME/core/.buffer.luadoc /^function home_display(buffer) end$/;" f class:buffer
home_display_extend _HOME/core/.buffer.luadoc /^function home_display_extend(buffer) end$/;" f class:buffer
@@ -694,10 +698,12 @@ marker_symbol_defined _HOME/core/.view.luadoc /^function marker_symbol_defined(v
match_case _HOME/modules/textadept/find.lua /^module('ui.find')]]$/;" F class:ui.find
match_case_label_text _HOME/modules/textadept/find.lua /^module('ui.find')]]$/;" F class:ui.find
maximized _HOME/core/ui.lua /^module('ui')]]$/;" F class:ui
+maximum_history_size _HOME/modules/textadept/history.lua /^module('textadept.history')]]$/;" F class:textadept.history
menu _HOME/core/ui.lua /^local menu$/;" f class:ui
menu _HOME/modules/textadept/menu.lua /^module('textadept.menu')]]$/;" m class:textadept
menubar _HOME/core/ui.lua /^local menubar$/;" t class:ui
menubar _HOME/modules/textadept/menu.lua /^local default_menubar = {$/;" t class:textadept.menu
+minimum_line_distance _HOME/modules/textadept/history.lua /^module('textadept.history')]]$/;" F class:textadept.history
mode _HOME/core/keys.lua /^module('keys')]]$/;" F class:keys
modify _HOME/core/.buffer.luadoc /^module('buffer')$/;" F class:buffer
modify_rule _HOME/lexers/lexer.lua /^function M.modify_rule(lexer, id, rule)$/;" f class:lexer
@@ -773,6 +779,7 @@ range _HOME/lexers/lexer.lua /^function M.range(s, e, single_line, escapes, bala
read _HOME/core/.os.luadoc /^function spawn_proc:read(arg) end$/;" f class:spawn_proc
read_only _HOME/core/.buffer.luadoc /^module('buffer')$/;" F class:buffer
recent_files _HOME/core/file_io.lua /^io.recent_files = {}$/;" t class:io
+record _HOME/modules/textadept/history.lua /^function M.record(filename, line, column, soft)$/;" f class:textadept.history
record _HOME/modules/textadept/macros.lua /^function M.record()$/;" f class:textadept.macros
rectangular_selection_anchor _HOME/core/.buffer.luadoc /^module('buffer')$/;" F class:buffer
rectangular_selection_anchor_virtual_space _HOME/core/.buffer.luadoc /^module('buffer')$/;" F class:buffer
@@ -941,6 +948,7 @@ textadept _HOME/modules/textadept/init.lua /^module('textadept')]]$/;" m
textadept.bookmarks _HOME/modules/textadept/bookmarks.lua /^module('textadept.bookmarks')]]$/;" m
textadept.editing _HOME/modules/textadept/editing.lua /^module('textadept.editing')]]$/;" m
textadept.file_types _HOME/modules/textadept/file_types.lua /^module('textadept.file_types')]]$/;" m
+textadept.history _HOME/modules/textadept/history.lua /^module('textadept.history')]]$/;" m
textadept.keys _HOME/modules/textadept/keys.lua /^module('textadept.keys')]]$/;" m
textadept.macros _HOME/modules/textadept/macros.lua /^module('textadept.macros')]]$/;" m
textadept.menu _HOME/modules/textadept/menu.lua /^module('textadept.menu')]]$/;" m
diff --git a/modules/textadept/history.lua b/modules/textadept/history.lua
new file mode 100644
index 00000000..4729322c
--- /dev/null
+++ b/modules/textadept/history.lua
@@ -0,0 +1,175 @@
+-- Copyright 2019-2020 Mitchell. See LICENSE.
+
+local M = {}
+
+--[[ This comment is for LuaDoc.
+---
+-- Records buffer positions within Textadept views over time and allows for
+-- navigating through that history.
+--
+-- This module listens for text edit events and buffer switch events. Each time
+-- an insertion or deletion occurs, its location is recorded in the current
+-- view's location history. If the edit is close enough to the previous record,
+-- the previous record is amended. Each time a buffer switch occurs, the before
+-- and after locations are also recorded.
+-- @field minimum_line_distance (number)
+-- The minimum number of lines between distinct history records.
+-- The default value is `3`.
+-- @field maximum_history_size (number)
+-- The maximum number of history records to keep per view.
+-- The default value is `100`.
+module('textadept.history')]]
+
+M.minimum_line_distance = 3
+M.maximum_history_size = 100
+
+-- Map of views to their history records.
+-- Each record has a `pos` field that points to the current history position in
+-- the associated view.
+-- @class table
+-- @name view_history
+local view_history = setmetatable({}, {__index = function(t, view)
+ t[view] = {pos = 0}
+ return t[view]
+end})
+
+-- Listens for text insertion and deletion events and records their locations.
+events.connect(events.MODIFIED, function(position, mod_type, text, length)
+ local buffer = buffer
+ -- Only interested in text insertion or deletion.
+ if mod_type & buffer.MOD_INSERTTEXT > 0 then
+ if length == buffer.length then return end -- ignore file loading
+ position = position + length
+ elseif mod_type & buffer.MOD_DELETETEXT > 0 then
+ if buffer.length == 0 then return end -- ignore replacing buffer contents
+ else
+ return
+ end
+ -- Ignore undo/redo.
+ if mod_type & (buffer.PERFORMED_UNDO | buffer.PERFORMED_REDO) > 0 then
+ return
+ end
+ M.record(nil, buffer:line_from_position(position), buffer.column[position])
+end)
+
+-- Do not record positions during buffer switches when jumping backwards or
+-- forwards.
+local jumping = false
+
+-- Jumps to the current position in the current view's history after adjusting
+-- that position backwards or forwards.
+local function goto_record()
+ jumping = true
+ local history = view_history[view]
+ local record = history[history.pos]
+ local filename, line, column = record.filename, record.line, record.column
+ if lfs.attributes(filename) then
+ io.open_file(filename)
+ else
+ for _, buffer in ipairs(_BUFFERS) do
+ if buffer.filename == filename or buffer._type == filename or
+ not buffer.filename and not buffer._type and
+ filename == _L['Untitled'] then
+ view:goto_buffer(buffer)
+ break
+ end
+ end
+ end
+ buffer:goto_pos(buffer:find_column(line, column))
+ jumping = false
+end
+
+---
+-- Navigates backwards through the current view's history.
+-- @name back
+function M.back()
+ local history = view_history[view]
+ if #history == 0 then return end -- nothing to do
+ local record = history[history.pos]
+ local line = buffer:line_from_position(buffer.current_pos)
+ if buffer.filename ~= record.filename or
+ math.abs(record.line - line) > M.minimum_line_distance then
+ -- When navigated away from the most recent record, and if that record is
+ -- not a soft record, jump back to it first, then navigate backwards.
+ if not record.soft then goto_record() return end
+ -- Otherwise, update the soft record with the current position and
+ -- immediately navigate backwards.
+ M.record(record.filename, nil, nil, record.soft)
+ end
+ if history.pos > 1 then history.pos = history.pos - 1 end
+ goto_record()
+end
+
+---
+-- Navigates forwards through the current view's history.
+-- @name forward
+function M.forward()
+ local history = view_history[view]
+ if history.pos == #history then return end -- nothing to do
+ local record = history[history.pos]
+ if record.soft then M.record(record.filename, nil, nil, record.soft) end
+ history.pos = history.pos + 1
+ goto_record()
+end
+
+---
+-- Records the given location in the current view's history.
+-- @param filename Optional string filename, buffer type, or identifier of the
+-- buffer to store. If `nil`, uses the current buffer.
+-- @param line Optional Integer line number to store. If `nil`, uses the current
+-- line.
+-- @param column Optional integer column number on line *line* to store. If
+-- `nil`, uses the current column.
+-- @param soft Optional flag that indicates whether or not this record should be
+-- skipped when navigating backward towards it, and updated when navigating
+-- away from it. The default value is `false`.
+-- @name record
+function M.record(filename, line, column, soft)
+ if not assert_type(filename, 'string/nil', 1) then
+ filename = buffer.filename or buffer._type or _L['Untitled']
+ end
+ if not assert_type(line, 'number/nil', 2) then
+ line = buffer:line_from_position(buffer.current_pos)
+ end
+ if not assert_type(column, 'number/nil', 3) then
+ column = buffer.column[buffer.current_pos]
+ end
+ local history = view_history[view]
+ if #history > 0 then
+ local record = history[history.pos]
+ if filename == record.filename and
+ (math.abs(record.line - line) <= M.minimum_line_distance or
+ record.soft) then
+ -- If the most recent record is close enough (distance-wise), or if that
+ -- record is a soft record, update it instead of recording a new one.
+ record.line, record.column = line, column
+ record.soft = soft and record.soft
+ return
+ end
+ end
+ if history.pos < #history then
+ for i = history.pos + 1, #history do history[i] = nil end -- clear forward
+ end
+ history[#history + 1] = {
+ filename = filename, line = line, column = column, soft = soft
+ }
+ if #history > M.maximum_history_size then table.remove(history, 1) end
+ history.pos = #history
+end
+
+-- Softly record positions when switching between buffers.
+local function record_switch()
+ if not jumping then M.record(nil, nil, nil, true) end
+end
+events.connect(events.BUFFER_BEFORE_SWITCH, record_switch)
+events.connect(events.BUFFER_AFTER_SWITCH, record_switch)
+events.connect(events.FILE_OPENED, record_switch)
+
+---
+-- Clears all view history.
+-- @name clear
+function M.clear()
+ for view in pairs(view_history) do view_history[view] = {pos = 0} end
+end
+
+return M
diff --git a/modules/textadept/init.lua b/modules/textadept/init.lua
index a70afb6a..435b34dd 100644
--- a/modules/textadept/init.lua
+++ b/modules/textadept/init.lua
@@ -14,6 +14,7 @@ require('textadept.command_entry')
M.editing = require('textadept.editing')
M.file_types = require('textadept.file_types')
require('textadept.find')
+M.history = require('textadept.history')
M.macros = require('textadept.macros')
M.run = require('textadept.run')
M.session = require('textadept.session')
diff --git a/modules/textadept/keys.lua b/modules/textadept/keys.lua
index e9cc2c29..25db537b 100644
--- a/modules/textadept/keys.lua
+++ b/modules/textadept/keys.lua
@@ -25,41 +25,45 @@ local M = {}
-- None |None |None |Load session...
-- None |None |None |Save session...
-- Ctrl+Q |⌘Q |^Q |Quit
--- **Edit** | | |
--- Ctrl+Z<br/>Alt+Bksp |⌘Z |^Z^(†)<br/>M-Z|Undo
--- Ctrl+Y<br/>Ctrl+Shift+Z |⌘⇧Z |^Y<br/>M-S-Z |Redo
--- Ctrl+X<br/>Shift+Del |⌘X<br/>⇧⌦|^X |Cut
--- Ctrl+C<br/>Ctrl+Ins |⌘C |^C |Copy
--- Ctrl+V<br/>Shift+Ins |⌘V |^V |Paste
--- Ctrl+Shift+V |⌘⇧V |M-V |Paste Reindent
--- Ctrl+D |⌘D |None |Duplicate line
--- Del |⌦<br/>^D |Del<br/>^D |Delete
--- Alt+Del |^⌦ |M-Del<br/>M-D |Delete word
--- Ctrl+A |⌘A |M-A |Select all
--- Ctrl+M |^M |M-M |Match brace
--- Ctrl+Enter |^Esc |M-Enter^(‡) |Complete word
--- Ctrl+/ |^/ |M-/ |Toggle block comment
--- Ctrl+T |^T |^T |Transpose characters
--- Ctrl+Shift+J |^J |M-J |Join lines
--- Ctrl+&#124; |⌘&#124; |^\ |Filter text through
--- Ctrl+Shift+M |^⇧M |M-S-M |Select between delimiters
--- Ctrl+< |⌘< |M-< |Select between XML tags
--- Ctrl+> |⌘> |None |Select in XML tag
--- Ctrl+Shift+D |⌘⇧D |M-S-W |Select word
--- Ctrl+Shift+N |⌘⇧N |M-S-N |Select line
--- Ctrl+Shift+P |⌘⇧P |M-S-P |Select paragraph
--- Ctrl+Alt+U |^U |M-^U |Upper case selection
--- Ctrl+Alt+Shift+U |^⇧U |M-^L |Lower case selection
--- Alt+< |^< |M-> |Enclose as XML tags
--- Alt+> |^> |None |Enclose as single XML tag
--- Alt+" |^" |None |Enclose in double quotes
--- Alt+' |^' |None |Enclose in single quotes
--- Alt+( |^( |M-) |Enclose in parentheses
--- Alt+[ |^[ |M-] |Enclose in brackets
--- Alt+{ |^{ |M-} |Enclose in braces
--- Ctrl+Shift+Up |^⇧⇡ |S-^Up |Move selected lines up
--- Ctrl+Shift+Down |^⇧⇣ |S-^Down |Move selected lines down
--- Ctrl+P |⌘, |M-~ |Preferences
+-- **Edit** | | |
+-- Ctrl+Z<br/>Alt+Bksp |⌘Z |^Z^(†)<br/>M-Z|Undo
+-- Ctrl+Y<br/>Ctrl+Shift+Z|⌘⇧Z |^Y<br/>M-S-Z |Redo
+-- Ctrl+X<br/>Shift+Del |⌘X<br/>⇧⌦|^X |Cut
+-- Ctrl+C<br/>Ctrl+Ins |⌘C |^C |Copy
+-- Ctrl+V<br/>Shift+Ins |⌘V |^V |Paste
+-- Ctrl+Shift+V |⌘⇧V |M-V |Paste Reindent
+-- Ctrl+D |⌘D |None |Duplicate line
+-- Del |⌦<br/>^D |Del<br/>^D |Delete
+-- Alt+Del |^⌦ |M-Del<br/>M-D |Delete word
+-- Ctrl+A |⌘A |M-A |Select all
+-- Ctrl+M |^M |M-M |Match brace
+-- Ctrl+Enter |^Esc |M-Enter^(‡) |Complete word
+-- Ctrl+/ |^/ |M-/ |Toggle block comment
+-- Ctrl+T |^T |^T |Transpose characters
+-- Ctrl+Shift+J |^J |M-J |Join lines
+-- Ctrl+&#124; |⌘&#124; |^\ |Filter text through
+-- Ctrl+Shift+M |^⇧M |M-S-M |Select between delimiters
+-- Ctrl+< |⌘< |M-< |Select between XML tags
+-- Ctrl+> |⌘> |None |Select in XML tag
+-- Ctrl+Shift+D |⌘⇧D |M-S-W |Select word
+-- Ctrl+Shift+N |⌘⇧N |M-S-N |Select line
+-- Ctrl+Shift+P |⌘⇧P |M-S-P |Select paragraph
+-- Ctrl+Alt+U |^U |M-^U |Upper case selection
+-- Ctrl+Alt+Shift+U |^⇧U |M-^L |Lower case selection
+-- Alt+< |^< |M-> |Enclose as XML tags
+-- Alt+> |^> |None |Enclose as single XML tag
+-- Alt+" |^" |None |Enclose in double quotes
+-- Alt+' |^' |None |Enclose in single quotes
+-- Alt+( |^( |M-) |Enclose in parentheses
+-- Alt+[ |^[ |M-] |Enclose in brackets
+-- Alt+{ |^{ |M-} |Enclose in braces
+-- Ctrl+Shift+Up |^⇧⇡ |S-^Up |Move selected lines up
+-- Ctrl+Shift+Down |^⇧⇣ |S-^Down |Move selected lines down
+-- Alt+, |^, |M-, |Navigate backward
+-- Alt+. |^. |M-. |Navigate forward
+-- None |None |None |Record location
+-- None |None |None |Clear navigation history
+-- Ctrl+P |⌘, |M-~ |Preferences
-- **Search** | | |
-- Ctrl+F |⌘F |M-F<br/>M-S-F|Find
-- Ctrl+G<br/>F3 |⌘G |M-G |Find next
@@ -336,7 +340,12 @@ local bindings = {
{'ctrl+shift+up', 'ctrl+shift+up', 'ctrl+shift+up'},
[buffer.move_selected_lines_down] =
{'ctrl+shift+down', 'ctrl+shift+down', 'ctrl+shift+down'},
- -- Preferences
+ -- History.
+ [textadept.history.back] = {'alt+,', 'ctrl+,', 'meta+,'},
+ [textadept.history.forward] = {'alt+.', 'ctrl+.', 'meta+.'},
+ -- TODO: textadept.history.record
+ -- TODO: textadept.history.clear
+ -- Preferences.
[m_edit[_L['Preferences']][2]] = {'ctrl+p', 'cmd+,', 'meta+~'},
-- Search.
diff --git a/modules/textadept/macros.lua b/modules/textadept/macros.lua
index 2a165c36..3428ef11 100644
--- a/modules/textadept/macros.lua
+++ b/modules/textadept/macros.lua
@@ -1,4 +1,4 @@
--- Copyright 2018 Mitchell. See LICENSE.
+-- Copyright 2018-2020 Mitchell. See LICENSE.
--[[ This comment is for LuaDoc.
---
diff --git a/modules/textadept/menu.lua b/modules/textadept/menu.lua
index b69a9f7b..1c7aa4b1 100644
--- a/modules/textadept/menu.lua
+++ b/modules/textadept/menu.lua
@@ -131,6 +131,14 @@ local default_menubar = {
{_L['Move Selected Lines Up'], buffer.move_selected_lines_up},
{_L['Move Selected Lines Down'], buffer.move_selected_lines_down}
},
+ {
+ title = _L['History'],
+ {_L['Navigate Backward'], textadept.history.back},
+ {_L['Navigate Forward'], textadept.history.forward},
+ {_L['Record Location'], textadept.history.record},
+ SEPARATOR,
+ {_L['Clear History'], textadept.history.clear}
+ },
SEPARATOR,
{_L['Preferences'], function() io.open_file(_USERHOME .. '/init.lua') end}
},