aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/locale.conf6
-rw-r--r--core/locales/locale.ar.conf6
-rw-r--r--core/locales/locale.de.conf6
-rw-r--r--core/locales/locale.es.conf6
-rw-r--r--core/locales/locale.fr.conf6
-rw-r--r--core/locales/locale.it.conf6
-rw-r--r--core/locales/locale.pl.conf6
-rw-r--r--core/locales/locale.ru.conf6
-rw-r--r--core/locales/locale.sv.conf6
-rw-r--r--core/locales/locale.zh.conf6
-rw-r--r--docs/api.md139
-rw-r--r--docs/manual.md9
-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
-rw-r--r--test/modules/textadept/history/1 (renamed from test/modules/history/1)0
-rw-r--r--test/modules/textadept/history/2 (renamed from test/modules/history/2)0
-rw-r--r--test/test.lua278
22 files changed, 578 insertions, 190 deletions
diff --git a/core/locale.conf b/core/locale.conf
index e1c3292f..d5f60436 100644
--- a/core/locale.conf
+++ b/core/locale.conf
@@ -198,6 +198,12 @@ Enclose in Brackets = Enclose in _Brackets
Enclose in Braces = Enclose in B_races
Move Selected Lines Up = _Move Selected Lines Up
Move Selected Lines Down = Move Selected Lines Do_wn
+# Menu items for navigating through position history.
+History = _History
+Navigate Backward = Navigate _Backward
+Navigate Forward = Navigate _Forward
+Record Location = _Record Location
+Clear History = _Clear History
# Menu item for opening user preferences.
Preferences = Pre_ferences
# Menu items for searching and replacing text within buffers and files.
diff --git a/core/locales/locale.ar.conf b/core/locales/locale.ar.conf
index 73f922cd..953d3d38 100644
--- a/core/locales/locale.ar.conf
+++ b/core/locales/locale.ar.conf
@@ -198,6 +198,12 @@ Enclose in Brackets = احتويه في أقواس م_ربعة
Enclose in Braces = احتويه في أقواس م_نحنية
Move Selected Lines Up = _حرّكه لأعلى
Move Selected Lines Down = ح_رّكه لأسفل
+# Menu items for navigating through position history.
+History = _History
+Navigate Backward = Navigate _Backward
+Navigate Forward = Navigate _Forward
+Record Location = _Record Location
+Clear History = _Clear History
# Menu item for opening user preferences.
Preferences = Pre_ferences
# Menu items for searching and replacing text within buffers and files.
diff --git a/core/locales/locale.de.conf b/core/locales/locale.de.conf
index 01d65e38..cda2c964 100644
--- a/core/locales/locale.de.conf
+++ b/core/locales/locale.de.conf
@@ -198,6 +198,12 @@ Enclose in Brackets = in eckige Klammern setzen
Enclose in Braces = in geschweifte Klammern setzen
Move Selected Lines Up = Ausgewählte Zeilen nach oben verschieben
Move Selected Lines Down = Ausgewählte Zeilen nach unten verschieben
+# Menu items for navigating through position history.
+History = _History
+Navigate Backward = Navigate _Backward
+Navigate Forward = Navigate _Forward
+Record Location = _Record Location
+Clear History = _Clear History
# Menu item for opening user preferences.
Preferences = Einstellungen
# Menu items for searching and replacing text within buffers and files.
diff --git a/core/locales/locale.es.conf b/core/locales/locale.es.conf
index af3c1c55..354a33fc 100644
--- a/core/locales/locale.es.conf
+++ b/core/locales/locale.es.conf
@@ -198,6 +198,12 @@ Enclose in Brackets = Encerrar entre corc_hetes
Enclose in Braces = Encerrar entre _llaves
Move Selected Lines Up = Mover lí_neas seleccionadas arriba
Move Selected Lines Down = Mo_ver líneas seleccionadas abajo
+# Menu items for navigating through position history.
+History = _History
+Navigate Backward = Navigate _Backward
+Navigate Forward = Navigate _Forward
+Record Location = _Record Location
+Clear History = _Clear History
# Menu item for opening user preferences.
Preferences = _Preferencias
# Menu items for searching and replacing text within buffers and files.
diff --git a/core/locales/locale.fr.conf b/core/locales/locale.fr.conf
index 14628df3..b2fff92d 100644
--- a/core/locales/locale.fr.conf
+++ b/core/locales/locale.fr.conf
@@ -199,6 +199,12 @@ Enclose in Brackets = Entre c_rochets
Enclose in Braces = Entre _accolades
Move Selected Lines Up = Déplacer les lignes vers le _haut
Move Selected Lines Down = Déplacer les lignes vers le _bas
+# Menu items for navigating through position history.
+History = _History
+Navigate Backward = Navigate _Backward
+Navigate Forward = Navigate _Forward
+Record Location = _Record Location
+Clear History = _Clear History
# Menu item for opening user preferences.
Preferences = Pre_ferences
# Menu items for searching and replacing text within buffers and files.
diff --git a/core/locales/locale.it.conf b/core/locales/locale.it.conf
index 62550785..91d4958a 100644
--- a/core/locales/locale.it.conf
+++ b/core/locales/locale.it.conf
@@ -198,6 +198,12 @@ Enclose in Brackets = Includi tra parentesi _quadre
Enclose in Braces = Includi tra parentesi _graffe
Move Selected Lines Up = Muovi le linee in _alto
Move Selected Lines Down = Muovi le linee in _basso
+# Menu items for navigating through position history.
+History = _History
+Navigate Backward = Navigate _Backward
+Navigate Forward = Navigate _Forward
+Record Location = _Record Location
+Clear History = _Clear History
# Menu item for opening user preferences.
Preferences = Pre_ferences
# Menu items for searching and replacing text within buffers and files.
diff --git a/core/locales/locale.pl.conf b/core/locales/locale.pl.conf
index b714004c..bbe34282 100644
--- a/core/locales/locale.pl.conf
+++ b/core/locales/locale.pl.conf
@@ -199,6 +199,12 @@ Enclose in Brackets = Zamknij w nawiasie k_wadratowym
Enclose in Braces = Zamknij w nawiasie k_lamrowym
Move Selected Lines Up = Przenieś wiersze w _górę
Move Selected Lines Down = Przenieś wiersze w _dół
+# Menu items for navigating through position history.
+History = _History
+Navigate Backward = Navigate _Backward
+Navigate Forward = Navigate _Forward
+Record Location = _Record Location
+Clear History = _Clear History
# Menu item for opening user preferences.
Preferences = Pre_ferences
# Menu items for searching and replacing text within buffers and files.
diff --git a/core/locales/locale.ru.conf b/core/locales/locale.ru.conf
index c144df68..e2b41aed 100644
--- a/core/locales/locale.ru.conf
+++ b/core/locales/locale.ru.conf
@@ -198,6 +198,12 @@ Enclose in Brackets = Заключить в к_вадратные скобки
Enclose in Braces = Заключить в _фигурные скобки
Move Selected Lines Up = Переместить выделенные строки _вверх
Move Selected Lines Down = Переместить выделнные строки в_низ
+# Menu items for navigating through position history.
+History = _History
+Navigate Backward = Navigate _Backward
+Navigate Forward = Navigate _Forward
+Record Location = _Record Location
+Clear History = _Clear History
# Menu item for opening user preferences.
Preferences = Настройки
# Menu items for searching and replacing text within buffers and files.
diff --git a/core/locales/locale.sv.conf b/core/locales/locale.sv.conf
index a1b64933..d56d2456 100644
--- a/core/locales/locale.sv.conf
+++ b/core/locales/locale.sv.conf
@@ -198,6 +198,12 @@ Enclose in Brackets = Sätt inom _hakparenteser
Enclose in Braces = Sätt inom _krullparenteser
Move Selected Lines Up = _Flytta markerade rader upp
Move Selected Lines Down = Flytta markerade rader _ner
+# Menu items for navigating through position history.
+History = _History
+Navigate Backward = Navigate _Backward
+Navigate Forward = Navigate _Forward
+Record Location = _Record Location
+Clear History = _Clear History
# Menu item for opening user preferences.
Preferences = Inställningar
# Menu items for searching and replacing text within buffers and files.
diff --git a/core/locales/locale.zh.conf b/core/locales/locale.zh.conf
index f20c48aa..dbce6e55 100644
--- a/core/locales/locale.zh.conf
+++ b/core/locales/locale.zh.conf
@@ -198,6 +198,12 @@ Enclose in Brackets = 用方括号包裹(_B)
Enclose in Braces = 用花括号包裹(_R)
Move Selected Lines Up = 上移选中行(_M)
Move Selected Lines Down = 下移选中行(_W)
+# Menu items for navigating through position history.
+History = _History
+Navigate Backward = Navigate _Backward
+Navigate Forward = Navigate _Forward
+Record Location = _Record Location
+Clear History = _Clear History
# Menu item for opening user preferences.
Preferences = Pre_ferences
# Menu items for searching and replacing text within buffers and files.
diff --git a/docs/api.md b/docs/api.md
index 57808bb4..9fe7c066 100644
--- a/docs/api.md
+++ b/docs/api.md
@@ -20,6 +20,7 @@
1. [textadept.bookmarks](#textadept.bookmarks)
1. [textadept.editing](#textadept.editing)
1. [textadept.file_types](#textadept.file_types)
+1. [textadept.history](#textadept.history)
1. [textadept.keys](#textadept.keys)
1. [textadept.macros](#textadept.macros)
1. [textadept.menu](#textadept.menu)
@@ -6872,6 +6873,70 @@ Map of first-line patterns to their associated lexer names.
Each pattern is matched against the first line in the file.
---
+<a id="textadept.history"></a>
+## The `textadept.history` Module
+---
+
+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 recorded.
+
+### Fields defined by `textadept.history`
+
+<a id="textadept.history.maximum_history_size"></a>
+#### `textadept.history.maximum_history_size` (number)
+
+The maximum number of history records to keep per view.
+ The default value is `100`.
+
+<a id="textadept.history.minimum_line_distance"></a>
+#### `textadept.history.minimum_line_distance` (number)
+
+The minimum number of lines between distinct history records.
+ The default value is `3`.
+
+
+### Functions defined by `textadept.history`
+
+<a id="textadept.history.back"></a>
+#### `textadept.history.back`()
+
+Navigates backwards through the current view's history.
+
+<a id="textadept.history.clear"></a>
+#### `textadept.history.clear`()
+
+Clears all view history.
+
+<a id="textadept.history.forward"></a>
+#### `textadept.history.forward`()
+
+Navigates forwards through the current view's history.
+
+<a id="textadept.history.record"></a>
+#### `textadept.history.record`(*filename, line, column, soft*)
+
+Records the given location in the current view's history.
+
+Parameters:
+
+* *`filename`*: Optional string filename, buffer type, or identifier of the
+ buffer to store. If `nil`, uses the current buffer.
+* *`line`*: Optional Integer line number to store. If `nil`, uses the current
+ line.
+* *`column`*: Optional integer column number on line *line* to store. If
+ `nil`, uses the current column.
+* *`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`.
+
+
+---
<a id="textadept.keys"></a>
## The `textadept.keys` Module
---
@@ -6897,41 +6962,45 @@ Ctrl+Shift+W |⌘⇧W |M-^W |Close all files
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
diff --git a/docs/manual.md b/docs/manual.md
index ceb57973..c06b2d05 100644
--- a/docs/manual.md
+++ b/docs/manual.md
@@ -1135,6 +1135,13 @@ You can auto-enclose selected text between any typed punctuation character
[`textadept.editing.auto_pairs`]: api.html#textadept.editing.auto_pairs
[`textadept.editing.auto_enclose`]: api.html#textadept.editing.auto_enclose
+#### Navigate Through History
+
+Textadept records buffer positions within views over time and allows for
+navigating through that history. Navigate backward or forward via `Alt+,` or
+`Alt+.`, respectively, on Windows, Linux, and BSD; `^,` or `^.`, respectively,
+on macOS; and `M-,` or `M-.`, respectively, in the terminal version.
+
#### Goto Line
You can jump to a specific line in the current buffer via `Ctrl+J` on Windows,
@@ -1898,6 +1905,7 @@ lexers |Removed |N/A<sup>b</sup>
find\_incremental() |Replaced|[incremental][]<sup>c</sup>
find\_incremental\_keys |Removed |
N/A |Added |[highlight_all_matches][]
+**textadept.history** |Added |[textadept.history][]
**textadept.snippets** | |
\_insert() |Renamed |[insert()][]
\_previous() |Renamed |[previous()][]
@@ -1946,6 +1954,7 @@ section below.
[highlight_words]: api.html#textadept.editing.highlight_words
[incremental]: api.html#ui.find.incremental
[highlight_all_matches]: api.html#ui.find.highlight_all_matches
+[textadept.history]: api.html#textadept.history
[insert()]: api.html#textadept.snippets.insert
[previous()]: api.html#textadept.snippets.previous
[cancel_current()]: api.html#textadept.snippets.cancel_current
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}
},
diff --git a/test/modules/history/1 b/test/modules/textadept/history/1
index 7b050800..7b050800 100644
--- a/test/modules/history/1
+++ b/test/modules/textadept/history/1
diff --git a/test/modules/history/2 b/test/modules/textadept/history/2
index 7b050800..7b050800 100644
--- a/test/modules/history/2
+++ b/test/modules/textadept/history/2
diff --git a/test/test.lua b/test/test.lua
index be2d432f..1a0a2185 100644
--- a/test/test.lua
+++ b/test/test.lua
@@ -2508,6 +2508,166 @@ function test_ui_find_replace_all()
buffer:close(true)
end
+function test_history()
+ local filename1 = _HOME .. '/test/modules/textadept/history/1'
+ io.open_file(filename1)
+ textadept.history.clear() -- clear initial buffer switch record
+ buffer:goto_line(5)
+ textadept.history.back() -- should not do anything
+ assert_equal(buffer.filename, filename1)
+ assert_equal(buffer:line_from_position(buffer.current_pos), 5)
+ buffer:add_text('foo')
+ buffer:goto_line(5 + textadept.history.minimum_line_distance + 1)
+ textadept.history.back()
+ assert_equal(buffer.filename, filename1)
+ assert_equal(buffer:line_from_position(buffer.current_pos), 5)
+ assert_equal(buffer.current_pos, buffer.line_end_position[5])
+ textadept.history.forward() -- should stay put (no edits have been made since)
+ assert_equal(buffer.filename, filename1)
+ assert_equal(buffer:line_from_position(buffer.current_pos), 5)
+ buffer:new_line()
+ buffer:add_text('bar') -- close changes should update current history
+ local filename2 = _HOME .. '/test/modules/textadept/history/2'
+ io.open_file(filename2)
+ buffer:goto_line(10)
+ buffer:add_text('baz')
+ textadept.history.back() -- should ignore initial file load and go back to file 1
+ assert_equal(buffer.filename, filename1)
+ assert_equal(buffer:line_from_position(buffer.current_pos), 6)
+ textadept.history.back() -- should stay put (updated history from line 5 to line 6)
+ assert_equal(buffer.filename, filename1)
+ assert_equal(buffer:line_from_position(buffer.current_pos), 6)
+ textadept.history.forward()
+ assert_equal(buffer.filename, filename2)
+ assert_equal(buffer:line_from_position(buffer.current_pos), 10)
+ textadept.history.back()
+ buffer:goto_line(15)
+ buffer:clear() -- erases forward history to file 2
+ textadept.history.forward() -- should not do anything
+ assert_equal(buffer.filename, filename1)
+ assert_equal(buffer:line_from_position(buffer.current_pos), 15)
+ textadept.history.back()
+ assert_equal(buffer.filename, filename1)
+ assert_equal(buffer:line_from_position(buffer.current_pos), 6)
+ textadept.history.forward()
+ view:goto_buffer(1)
+ assert_equal(buffer.filename, filename2)
+ buffer:goto_line(20)
+ buffer:add_text('quux')
+ view:goto_buffer(-1)
+ assert_equal(buffer.filename, filename1)
+ buffer:undo() -- undo delete of '\n'
+ buffer:undo() -- undo add of 'foo'
+ buffer:redo() -- re-add 'foo'
+ textadept.history.back() -- undo and redo should not affect history
+ assert_equal(buffer.filename, filename2)
+ assert_equal(buffer:line_from_position(buffer.current_pos), 20)
+ textadept.history.back()
+ assert_equal(buffer.filename, filename1)
+ assert_equal(buffer:line_from_position(buffer.current_pos), 15)
+ textadept.history.back()
+ assert_equal(buffer.filename, filename1)
+ assert_equal(buffer:line_from_position(buffer.current_pos), 6)
+ buffer:target_whole_document()
+ buffer:replace_target(string.rep('\n', buffer.line_count)) -- whole buffer replacements should not affect history (e.g. clang-format)
+ textadept.history.forward()
+ assert_equal(buffer.filename, filename1)
+ assert_equal(buffer:line_from_position(buffer.current_pos), 15)
+ view:goto_buffer(1)
+ assert_equal(buffer.filename, filename2)
+ buffer:close(true)
+ textadept.history.back() -- should re-open file 2
+ assert_equal(buffer.filename, filename2)
+ assert_equal(buffer:line_from_position(buffer.current_pos), 20)
+ buffer:close(true)
+ buffer:close(true)
+
+ assert_raises(function() textadept.history.record(1) end, 'string/nil expected, got number')
+ assert_raises(function() textadept.history.record('', true) end, 'number/nil expected, got boolean')
+ assert_raises(function() textadept.history.record('', 1, '') end, 'number/nil expected, got string')
+end
+
+function test_history_soft_records()
+ local filename1 = _HOME .. '/test/modules/textadept/history/1'
+ io.open_file(filename1)
+ textadept.history.clear() -- clear initial buffer switch record
+ buffer:goto_line(5)
+ local filename2 = _HOME .. '/test/modules/textadept/history/2'
+ io.open_file(filename2)
+ buffer:goto_line(10)
+ textadept.history.back()
+ assert_equal(buffer.filename, filename1)
+ assert_equal(buffer:line_from_position(buffer.current_pos), 5)
+ buffer:goto_line(15)
+ textadept.history.forward() -- should update soft record from line 5 to 15
+ assert_equal(buffer.filename, filename2)
+ assert_equal(buffer:line_from_position(buffer.current_pos), 10)
+ buffer:goto_line(20)
+ textadept.history.back() -- should update soft record from line 10 to 20
+ assert_equal(buffer.filename, filename1)
+ assert_equal(buffer:line_from_position(buffer.current_pos), 15)
+ textadept.history.forward()
+ assert_equal(buffer.filename, filename2)
+ assert_equal(buffer:line_from_position(buffer.current_pos), 20)
+ buffer:goto_line(10)
+ buffer:add_text('foo') -- should update soft record from line 20 to 10 and make it hard
+ textadept.history.back()
+ assert_equal(buffer.filename, filename1)
+ assert_equal(buffer:line_from_position(buffer.current_pos), 15)
+ textadept.history.forward()
+ assert_equal(buffer.filename, filename2)
+ assert_equal(buffer:line_from_position(buffer.current_pos), 10)
+ buffer:goto_line(20)
+ buffer:add_text('bar') -- should create a new record
+ textadept.history.back()
+ assert_equal(buffer.filename, filename2)
+ assert_equal(buffer:line_from_position(buffer.current_pos), 10)
+ buffer:close(true)
+ buffer:close(true)
+end
+
+function test_history_per_view()
+ local filename1 = _HOME .. '/test/modules/textadept/history/1'
+ io.open_file(filename1)
+ textadept.history.clear() -- clear initial buffer switch record
+ buffer:goto_line(5)
+ buffer:add_text('foo')
+ buffer:goto_line(10)
+ buffer:add_text('bar')
+ view:split()
+ textadept.history.back() -- no history for this view
+ assert_equal(buffer.filename, filename1)
+ assert_equal(buffer:line_from_position(buffer.current_pos), 10)
+ local filename2 = _HOME .. '/test/modules/textadept/history/2'
+ io.open_file(filename2)
+ buffer:goto_line(15)
+ buffer:add_text('baz')
+ buffer:goto_line(20)
+ textadept.history.back()
+ assert_equal(buffer.filename, filename2)
+ assert_equal(buffer:line_from_position(buffer.current_pos), 15)
+ textadept.history.back()
+ assert_equal(buffer.filename, filename1)
+ assert_equal(buffer:line_from_position(buffer.current_pos), 10)
+ textadept.history.back() -- no more history for this view
+ assert_equal(buffer.filename, filename1)
+ assert_equal(buffer:line_from_position(buffer.current_pos), 10)
+ ui.goto_view(-1)
+ textadept.history.back()
+ assert_equal(buffer.filename, filename1)
+ assert_equal(buffer:line_from_position(buffer.current_pos), 5)
+ textadept.history.forward()
+ assert_equal(buffer.filename, filename1)
+ assert_equal(buffer:line_from_position(buffer.current_pos), 10)
+ textadept.history.forward() -- no more history for this view
+ assert_equal(buffer.filename, filename1)
+ assert_equal(buffer:line_from_position(buffer.current_pos), 10)
+ view:unsplit()
+ view:goto_buffer(1)
+ buffer:close(true)
+ buffer:close(true)
+end
+
function test_macro_record_play_save_load()
textadept.macros.save() -- should not do anything
textadept.macros.play() -- should not do anything
@@ -3870,124 +4030,6 @@ function test_file_diff_interactive()
if different_files then buffer:close(true) end
end
-function test_history()
- local history = require('history')
- history.disable_listening() -- clear preexisting history
- history.enable_listening()
- local filename1 = _HOME .. '/test/modules/history/1'
- io.open_file(filename1)
- buffer:goto_line(5)
- history.back() -- should not do anything (ignore initial file load)
- assert_equal(buffer.filename, filename1)
- assert_equal(buffer:line_from_position(buffer.current_pos), 5)
- buffer:add_text('foo')
- buffer:goto_line(5 + history.minimum_line_distance + 1)
- history.back()
- assert_equal(buffer.filename, filename1)
- assert_equal(buffer:line_from_position(buffer.current_pos), 5)
- assert_equal(buffer.current_pos, buffer.line_end_position[5])
- history.forward() -- should stay put (no edits have been made since)
- assert_equal(buffer.filename, filename1)
- assert_equal(buffer:line_from_position(buffer.current_pos), 5)
- buffer:new_line()
- buffer:add_text('bar') -- close changes should update current history
- local filename2 = _HOME .. '/test/modules/history/2'
- io.open_file(filename2)
- buffer:goto_line(10)
- buffer:add_text('baz')
- history.back() -- should ignore initial file load and go back to file 1
- assert_equal(buffer.filename, filename1)
- assert_equal(buffer:line_from_position(buffer.current_pos), 6)
- history.back() -- should stay put (updated history from line 5 to line 6)
- assert_equal(buffer.filename, filename1)
- assert_equal(buffer:line_from_position(buffer.current_pos), 6)
- history.forward()
- assert_equal(buffer.filename, filename2)
- assert_equal(buffer:line_from_position(buffer.current_pos), 10)
- history.back()
- buffer:goto_line(15)
- buffer:clear() -- erases forward history to file 2
- history.forward() -- should not do anything
- assert_equal(buffer.filename, filename1)
- assert_equal(buffer:line_from_position(buffer.current_pos), 15)
- history.back()
- assert_equal(buffer.filename, filename1)
- assert_equal(buffer:line_from_position(buffer.current_pos), 6)
- history.forward()
- view:goto_buffer(1)
- assert_equal(buffer.filename, filename2)
- buffer:goto_line(20)
- buffer:add_text('quux')
- view:goto_buffer(-1)
- assert_equal(buffer.filename, filename1)
- buffer:undo() -- undo delete of '\n'
- buffer:undo() -- undo add of 'foo'
- buffer:redo() -- re-add 'foo'
- history.back() -- undo and redo should not affect history
- assert_equal(buffer.filename, filename2)
- assert_equal(buffer:line_from_position(buffer.current_pos), 20)
- history.back()
- assert_equal(buffer.filename, filename1)
- assert_equal(buffer:line_from_position(buffer.current_pos), 15)
- history.back()
- assert_equal(buffer.filename, filename1)
- assert_equal(buffer:line_from_position(buffer.current_pos), 6)
- buffer:target_whole_document()
- buffer:replace_target(string.rep('\n', buffer.line_count)) -- whole buffer replacements should not affect history (e.g. clang-format)
- history.forward()
- assert_equal(buffer.filename, filename1)
- assert_equal(buffer:line_from_position(buffer.current_pos), 15)
- view:goto_buffer(1)
- assert_equal(buffer.filename, filename2)
- buffer:close(true)
- history.forward() -- should re-open file 2
- assert_equal(buffer.filename, filename2)
- assert_equal(buffer:line_from_position(buffer.current_pos), 20)
- buffer:close(true)
- buffer:close(true)
-end
-
-function test_history_per_view()
- local history = require('history')
- history.disable_listening() -- clear preexisting history
- history.enable_listening()
- local filename1 = _HOME .. '/test/modules/history/1'
- io.open_file(filename1)
- buffer:goto_line(5)
- buffer:add_text('foo')
- buffer:goto_line(10)
- buffer:add_text('bar')
- view:split()
- history.back() -- no history for this view
- assert_equal(buffer.filename, filename1)
- assert_equal(buffer:line_from_position(buffer.current_pos), 10)
- local filename2 = _HOME .. '/test/modules/history/2'
- io.open_file(filename2)
- buffer:goto_line(15)
- buffer:add_text('baz')
- buffer:goto_line(20)
- history.back()
- assert_equal(buffer.filename, filename2)
- assert_equal(buffer:line_from_position(buffer.current_pos), 15)
- history.back() -- no more history for this view
- assert_equal(buffer.filename, filename2)
- assert_equal(buffer:line_from_position(buffer.current_pos), 15)
- ui.goto_view(-1)
- history.back()
- assert_equal(buffer.filename, filename1)
- assert_equal(buffer:line_from_position(buffer.current_pos), 5)
- history.forward()
- assert_equal(buffer.filename, filename1)
- assert_equal(buffer:line_from_position(buffer.current_pos), 10)
- history.forward() -- no more history for this view
- assert_equal(buffer.filename, filename1)
- assert_equal(buffer:line_from_position(buffer.current_pos), 10)
- view:unsplit()
- view:goto_buffer(1)
- buffer:close(true)
- buffer:close(true)
-end
-
function test_spellcheck()
local spellcheck = require('spellcheck')
local SPELLING_ID = 1 -- not accessible