diff options
author | 2011-07-29 18:27:06 -0400 | |
---|---|---|
committer | 2011-07-29 18:27:06 -0400 | |
commit | 36675f10b0cefaee7e9451b3eec3dfd715888625 (patch) | |
tree | 5a2135ef56b3e52bdf63fb4a82d4cec8c5e41eab | |
parent | 32da7ac1a36d2285daa3a0ee280f1730415f04cf (diff) |
Eliminated the need for keys.conf and keys.osx.conf.
Thanks to Robert Gieseke for the idea and prototype code.
-rw-r--r-- | core/.buffer.luadoc | 1 | ||||
-rw-r--r-- | core/gui.lua | 2 | ||||
-rw-r--r-- | doc/manual/9_Preferences.md | 35 | ||||
-rw-r--r-- | modules/lua/api | 8 | ||||
-rw-r--r-- | modules/lua/tags | 4 | ||||
-rw-r--r-- | modules/textadept/adeptsense.lua | 18 | ||||
-rw-r--r-- | modules/textadept/init.lua | 2 | ||||
-rw-r--r-- | modules/textadept/keys.conf | 165 | ||||
-rw-r--r-- | modules/textadept/keys.lua | 320 | ||||
-rw-r--r-- | modules/textadept/keys.osx.conf | 165 | ||||
-rw-r--r-- | modules/textadept/menu.lua | 408 | ||||
-rw-r--r-- | modules/textadept/session.lua | 28 |
12 files changed, 508 insertions, 648 deletions
diff --git a/core/.buffer.luadoc b/core/.buffer.luadoc index 01219b6e..93fe8abe 100644 --- a/core/.buffer.luadoc +++ b/core/.buffer.luadoc @@ -2408,6 +2408,7 @@ function buffer.set_lexer(buffer, lang) end -- Replacement for buffer.get_lexer_language(buffer). -- @param buffer The focused buffer. function buffer.get_lexer(buffer) end + --- -- Returns the name of the style associated with a style number. -- @param buffer The focused buffer. diff --git a/core/gui.lua b/core/gui.lua index 09540f7e..e0a739b4 100644 --- a/core/gui.lua +++ b/core/gui.lua @@ -98,7 +98,7 @@ connect(events.VIEW_NEW, function() local ctrl_keys = { '[', ']', '/', '\\', 'Z', 'Y', 'X', 'C', 'V', 'A', 'L', 'T', 'D', 'U' } - local ctrl_shift_keys = { 'L', 'T', 'U' } + local ctrl_shift_keys = { 'L', 'T', 'U', 'Z' } for _, key in ipairs(ctrl_keys) do buffer:clear_cmd_key(string.byte(key), c.SCMOD_CTRL) end diff --git a/doc/manual/9_Preferences.md b/doc/manual/9_Preferences.md index 8e6b0a48..3a19e55b 100644 --- a/doc/manual/9_Preferences.md +++ b/doc/manual/9_Preferences.md @@ -75,35 +75,16 @@ the snippet. ##### Key Commands -The default set of key commands is located in `modules/textadept/keys.conf` -(`modules/textadept/keys.osx.conf` on Mac OSX) and is loaded by the -`_m.textadept.menu` module. It is not recommended to edit Textadept's -`keys.conf` or `keys.osx.conf` for changing the key bindings since your changes -could be overwritten when updating Textadept. Instead, make a copy in your -`~/.textadept/` folder and modify that. - -Key commands need to be defined manually for keychains or custom functions. You -can do this in your `~/.textadept/init.lua` or from a file loaded by -`~/.textadept/init.lua`. For example maybe you want `Ctrl+Shift+A, N` (`⌘⇧A, N` -on Mac OSX) to create a new buffer instead of `Ctrl+N` (`⌘N`): - - keys.cA = {} - keys.cA.n = new_buffer +It is not recommended to edit Textadept's `modules/textadept/keys.lua` for +changing the key bindings since your changes could be overwritten when updating +Textadept. Instead, modify `keys` from within your `~/.textadept/init.lua` or +from a file loaded by `~/.textadept/init.lua`. For example maybe you want +`Ctrl+Shift+N` (`⌘⇧N` on Mac OSX) to create a new buffer instead of `Ctrl+N` +(`⌘N`): + + keys.cN = new_buffer keys.cn = nil -Earlier versions of Textadept manually defined all key commands in -`modules/textadept/keys.lua` (`keys.cn = new_buffer`, `keys.co = io.open_file`, -etc.). Now the `menu` module does this automatically by reading the keys in the -`keys.conf` or `keys.osx.conf` files mentioned earlier. Of course you can still -use Lua to define commands as demonstrated above. In fact, if you choose to -exclude loading the menu like in an earlier example above, you will have to -specify your own set of key commands! (I happen to do this and keep a copy of -`~/.textadept/modules/textadept/keys.lua` that is loaded by my -`~/.textadept/init.lua`.) Please note that key commands are _not_ handled by the -menu; they are still handled as they always have been. Therefore the keys shown -in the menu could differ from the actual set of key commands if you use Lua to -redefine them. Menu keys are shown purely for cosmetic, not functional reasons. - ## Locale Most messages displayed by Textadept are localized. `core/locale.conf` contains diff --git a/modules/lua/api b/modules/lua/api index 4bf67f90..55cb8b22 100644 --- a/modules/lua/api +++ b/modules/lua/api @@ -853,6 +853,7 @@ comment_string _m.textadept.editing.comment_string [table]\nComment strings for compile _m.textadept.run.compile()\nCompiles the file as specified by its extension in the compile_command table.\n@see compile_command\n compile_command _m.textadept.run.compile_command [table]\nFile extensions and their associated 'compile' actions. Each key is a file\nextension whose value is a either a command line string to execute or a\nfunction returning one. This table is typically populated by language-specific\nmodules.\n complete _m.textadept.adeptsense.complete(sense, only_fields, only_functions)\nShows an autocompletion list for the symbol behind the caret.\n@param sense The adeptsense returned by adeptsense.new().\n@param only_fields If true, returns list of only fields; defaults to false.\n@param only_functions If true, returns list of only functions; defaults\nto false.\n@return true on success or false.\n@see get_symbol\n@see get_completions\n +complete_symbol _m.textadept.adeptsense.complete_symbol()\nCompletes the symbol at the current position based on the current lexer's\nAdeptsense. This should be called by key commands and menus instead of\n`complete`.\n completions _m.textadept.adeptsense.completions [table]\nContains lists of possible completions for known symbols. Each symbol key\nhas a table value that contains a list of field completions with a `fields`\nkey and a list of functions completions with a `functions` key. This table\nis normally populated by load_ctags(), but can also be set by the user.\n concat table.concat(table [, sep [, i [, j]]])\nGiven an array where all elements are strings or numbers, returns\n`table[i]..sep..table[i+1] ··· sep..table[j]`. The default value for `sep`\nis the empty string, the default for `i` is 1, and the default for `j` is\nthe length of the table. If `i` is greater than `j`, returns the empty string.\n connect events.connect(event, f, index)\nAdds a handler function to an event.\n@param event The string event name. It is arbitrary and need not be defined\nanywhere.\n@param f The Lua function to add.\n@param index Optional index to insert the handler into.\n@return Index of handler.\n@see disconnect\n @@ -1092,7 +1093,7 @@ java _G.snippets.java [table]\nContainer for Java-specific snippets.\n java _m.java [module]\nThe java module. It provides utilities for editing Java code. User tags\nare loaded from _USERHOME/modules/java/tags and user apis are loaded from\n_USERHOME/modules/java/api.\n join_lines _m.textadept.editing.join_lines()\nJoins the current line with the line below.\n keys _G.keys [module]\nManages key commands in Textadept.\n -keys _m.textadept.keys [module]\nDefines additional key commands for Textadept. The primary key commands\nare loaded from _USERHOME/keys.conf, _HOME/modules/textadept/keys.conf,\n_USERHOME/keys.osx.conf, or _HOME/modules/textadept/keys.osx.conf depending\non the platform by _m.textadept.menu. This module, like _m.textadept.menu,\nshould be 'require'ed last.\n +keys _m.textadept.keys [module]\nDefines key commands for Textadept. This set of key commands is pretty\nstandard among other text editors. This module, should be 'require'ed last,\nbut before _m.textadept.menu.\n keys_unicode buffer.keys_unicode [bool]\nInterpret keyboard input as Unicode.\n layout_cache buffer.layout_cache [number]\nThe degree of caching of layout information.\n * `_SCINTILLA.constants.SC_CACHE_NONE` (0): No lines are cached.\n * `_SCINTILLA.constants.SC_CACHE_CARET` (1): The line containing the\n text caret. This is the default.\n * `_SCINTILLA.constants.SC_CACHE_PAGE` (2): Visible lines plus the line\n containing the caret.\n * `_SCINTILLA.constants.SC_CACHE_DOCUMENT` (3): All lines in the\n document.\n\n ldexp math.ldexp(m, e)\nReturns *m2^e* (`e` should be an integer).\n @@ -1205,7 +1206,7 @@ math _G.math [module]\nLua math module.\n max math.max(x, ···)\nReturns the maximum value among its arguments.\n max_line_state buffer.max_line_state [number]\nThe last line number that has line state. (Read-only)\n maxn table.maxn(table)\nReturns the largest positive numerical index of the given table, or zero if\nthe table has no positive numerical indices. (To do its job this function\ndoes a linear traversal of the whole table.)\n -menu _m.textadept.menu [module]\nProvides dynamic menus for Textadept. It also loads key commands\nfrom _USERHOME/keys.conf, _HOME/modules/textadept/keys.conf,\n_USERHOME/keys.osx.conf, or _HOME/modules/textadept/keys.osx.conf depending on\nthe platform. This module, like _m.textadept.keys, should be 'require'ed last.\n +menu _m.textadept.menu [module]\nProvides dynamic menus for Textadept. This module should be 'require'ed last,\nafter _m.textadept.keys since it looks up defined key commands to show them\nin menus.\n menubar _m.textadept.menu.menubar [table]\nContains the main menubar.\n menubar gui.menubar [table]\nA table of GTK menus defining a menubar. (Write-only)\n mime_types _m.textadept.mime_types [module]\nHandles file-specific settings.\n @@ -1282,6 +1283,8 @@ print_magnification buffer.print_magnification [number]\nThe print magnification print_wrap_mode buffer.print_wrap_mode [number]\nPrinting line wrap mode.\n * `_SCINTILLA.constants.SC_WRAP_NONE` (0): Each line of text generates\n one line of output and the line is truncated if it is too long to fit\n into the print area.\n * `_SCINTILLA.constants.SC_WRAP_WORD` (1): Wraps printed output so that\n all characters fit into the print rectangle. Tries to wrap only\n between words as indicated by white space or style changes although\n if a word is longer than a line, it will be wrapped before the line\n end. This is the default.\n * `_SCINTILLA.constants.SC_WRAP_CHAR` (2).\n\n private_lexer_call buffer.private_lexer_call(buffer, operation, data)\nFor private communication between an application and a known lexer.\n@param buffer The focused buffer.\n@param operation An operation number.\n@param data Number data.\n process args.process()\nProcesses command line arguments. Add command line switches with\nargs.register(). Any unrecognized arguments are treated as filepaths and\nopened. Generates an 'arg_none' event when no args are present.\n@see register\n +prompt_load _m.textadept.session.prompt_load()\nPrompts the user for a Textadept session to load.\n +prompt_save _m.textadept.session.prompt_save()\nPrompts the user to save the current Textadept session to a file.\n properties _SCINTILLA.properties [table]\nScintilla properties.\n property buffer.property [table]\nTable of keyword:value string pairs used by a lexer for some optional\nfeatures. (Write-only)\n property_int buffer.property_int [table]\nInterprets `buffer.property[keyword]` as an integer if found or returns\n0. (Read-only)\n @@ -1440,6 +1443,7 @@ setupvalue debug.setupvalue(func, up, value)\nThis function assigns the value `v setvbuf file:setvbuf(mode [, size])\nSets the buffering mode for an output file. There are three available\nmodes: "no": no buffering; the result of any output operation appears\nimmediately. "full": full buffering; output operation is performed only\nwhen the buffer is full (or when you explicitly `flush` the file (see\n`io.flush`)). "line": line buffering; output is buffered until a newline is\noutput or there is any input from some special files (such as a terminal\ndevice). For the last two cases, `size` specifies the size of the buffer,\nin bytes. The default is an appropriate size.\n shebangs _m.textadept.mime_types.shebangs [table]\nShebang words and their associated lexers.\n show_apidoc _m.textadept.adeptsense.show_apidoc(sense)\nShows a calltip with API documentation for the symbol behind the caret.\n@param sense The adeptsense returned by adeptsense.new().\n@return true on success or false.\n@see get_symbol\n@see get_apidoc\n +show_documentation _m.textadept.adeptsense.show_documentation()\nShows API documentation for the symbol at the current position based on the\ncurrent lexer's Adeptsense. This should be called by key commands and menus\ninstead of `show_apidoc`.\n show_lines buffer.show_lines(buffer, start_line, end_line)\nMake a range of lines visible. This has no effect on fold levels or fold\nflags. start_line can not be hidden.\n@param buffer The focused buffer.\n@param start_line The start line.\n@param end_line The end line.\n sin math.sin(x)\nReturns the sine of `x` (assumed to be in radians).\n singular _m.rails.singular\nA map of plural controller names to their singulars. Add key-value pairs to\nthis if singularize() is incorrectly converting your plural controller name\nto its singular model name.\n diff --git a/modules/lua/tags b/modules/lua/tags index 1429945b..acce66eb 100644 --- a/modules/lua/tags +++ b/modules/lua/tags @@ -878,6 +878,7 @@ comment_string _ 0;" t class:_m.textadept.editing compile _ 0;" f class:_m.textadept.run compile_command _ 0;" t class:_m.textadept.run complete _ 0;" f class:_m.textadept.adeptsense +complete_symbol _ 0;" f class:_m.textadept.adeptsense completions _ 0;" t class:_m.textadept.adeptsense concat _ 0;" f class:table connect _ 0;" f class:events @@ -1322,6 +1323,8 @@ print_magnification _ 0;" F class:buffer print_wrap_mode _ 0;" F class:buffer private_lexer_call _ 0;" f class:buffer process _ 0;" f class:args +prompt_load _ 0;" f class:_m.textadept.session +prompt_save _ 0;" f class:_m.textadept.session properties _ 0;" t class:_SCINTILLA property _ 0;" F class:buffer property_int _ 0;" F class:buffer @@ -1480,6 +1483,7 @@ setupvalue _ 0;" f class:debug setvbuf _ 0;" f class:file shebangs _ 0;" t class:_m.textadept.mime_types show_apidoc _ 0;" f class:_m.textadept.adeptsense +show_documentation _ 0;" f class:_m.textadept.adeptsense show_lines _ 0;" f class:buffer sin _ 0;" f class:math singular _ 0;" F class:_m.rails diff --git a/modules/textadept/adeptsense.lua b/modules/textadept/adeptsense.lua index d7c3a9a5..37ee20bc 100644 --- a/modules/textadept/adeptsense.lua +++ b/modules/textadept/adeptsense.lua @@ -889,3 +889,21 @@ syntax = { senses[lang] = sense return sense end + +--- +-- Completes the symbol at the current position based on the current lexer's +-- Adeptsense. +-- This should be called by key commands and menus instead of `complete`. +function complete_symbol() + local m = _m[buffer:get_lexer()] + if m and m.sense then m.sense:complete() end +end + +--- +-- Shows API documentation for the symbol at the current position based on the +-- current lexer's Adeptsense. +-- This should be called by key commands and menus instead of `show_apidoc`. +function show_documentation() + local m = _m[buffer:get_lexer()] + if m and m.sense then m.sense:show_apidoc() end +end diff --git a/modules/textadept/init.lua b/modules/textadept/init.lua index 01493380..12b085c4 100644 --- a/modules/textadept/init.lua +++ b/modules/textadept/init.lua @@ -18,5 +18,5 @@ require 'textadept.snapopen' require 'textadept.snippets' -- These need to be loaded last. -require 'textadept.menu' require 'textadept.keys' +require 'textadept.menu' diff --git a/modules/textadept/keys.conf b/modules/textadept/keys.conf deleted file mode 100644 index 4b7dd023..00000000 --- a/modules/textadept/keys.conf +++ /dev/null @@ -1,165 +0,0 @@ -% Windows and Linux menu key commands. -% This set of key commands is pretty standard among other text editors. -% Define additional key commands in _USERHOME/modules/textadept/keys.lua. - -% Unassigned keys (~ denotes keys reserved by the operating system): -% c: A B C N p qQ T ~ V X Y ) ] } * \n -% a: aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpP QrRsStTuUvVwWxXyYzZ_ ) ] } *+-/=~~\n\s -% ca: aAbBcCdDeE F hH jJkK LmMnN pPqQ t v xXy zZ_"'()[]{}<>* / - -% CTRL = 'c' (Control ^) -% ALT = 'a' (Alt) -% META = [unused] -% SHIFT = 's' (Shift ⇧) -% ADD = '' -% Control, Alt, Shift, and 'a' = 'caA' -% Control, Alt, Shift, and '\t' = 'cas\t' - -% Multiple keys separated by spaces can be assigned to the same command. -% Keychains are not supported. Please define chains manually. - -% The commands below can either be in English or localized for your locale. - -% File -gtk-new = cn -gtk-open = co -Open Recent... = cao -Reload = cO -gtk-save = cs -gtk-save-as = cS -gtk-close = cw -Close All = cW -Load Session... = -Save Session... = -gtk-quit = aq - -% Edit -gtk-undo = cz -gtk-redo = cy cZ -gtk-cut = cx -gtk-copy = cc -gtk-paste = cv -Duplicate Line = cd -gtk-delete = del -gtk-select-all = ca -Match Brace = cm -Complete Word = c\n -Delete Word = adel -Highlight Word = cH -Toggle Block Comment = c/ -Transpose Characters = ct -Join Lines = cJ -% Select -Select to Matching Brace = cM -Select between XML Tags = c< -Select in XML Tag = c> -Select in Double Quotes = c" -Select in Single Quotes = c' -Select in Parentheses = c( -Select in Brackets = c[ -Select in Braces = c{ -Select Word = cD -Select Line = cL -Select Paragraph = cP -Select Indented Block = cI -Select Style = cY -% Selection -Upper Case Selection = cau -Lower Case Selection = caU -Enclose as XML Tags = a< -Enclose as Single XML Tag = a> -Enclose in Single Quotes = a' -Enclose in Double Quotes = a" -Enclose in Parentheses = a( -Enclose in Brackets = a[ -Enclose in Braces = a{ -Grow Selection = c+ -Shrink Selection = c_ -Move Selected Lines Up = csup -Move Selected Lines Down = csdown - -% Search -gtk-find = cf -Find Next = cg f3 -Find Previous = cG sf3 -Replace = cr -Replace All = cR -Find Incremental = caf -Find in Files = cF -Goto Next File Found = cag -Goto Previous File Found = caG -gtk-jump-to = cj - -% Tools -Command Entry = ce -Select Command = cE -Run = car -Compile = caR -Filter Through = c| -% Adeptsense -Complete Symbol = c\s -Show Documentation = ch -% Snippets -Insert Snippet... = ck -Expand Snippet/Next Placeholder = \t -Previous Snippet Placeholder = s\t -Cancel Snippet = cK -% Bookmark -Toggle Bookmark = cf2 -Clear Bookmarks = csf2 -Next Bookmark = f2 -Previous Bookmark = sf2 -Goto Bookmark... = af2 -% Snapopen -Snapopen User Home = cu -Snapopen Textadept Home = -Snapopen Current Directory = caO -Show Style = ci - -% Buffer -Next Buffer = c\t cpgdn -Previous Buffer = cs\t cpgup -Switch to Buffer... = cb -% Indentation -Tab width: 2 = -Tab width: 3 = -Tab width: 4 = -Tab width: 8 = -Toggle Use Tabs = caT -Convert Indentation = cai -% EOL Mode -CRLF = -CR = -LF = -% Encoding -UTF-8 Encoding = -ASCII Encoding = -ISO-8859-1 Encoding = -MacRoman Encoding = -UTF-16 Encoding = -Select Lexer... = cal -Refresh Syntax Highlighting = f5 - -% View -Next View = ca\t -Previous View = cas\t -Split View Vertical = caS -Split View Horizontal = cas -Unsplit View = caw -Unsplit All Views = caW -Grow View = ca+ ca= -Shrink View = ca- -Toggle Current Fold = -Toggle View EOL = ca\n ca\n\r -Toggle Wrap Mode = ca\\ -Toggle Show Indent Guides = caI -Toggle View Whitespace = ca\s -Toggle Virtual Space = caV -Zoom In = c= -Zoom Out = c- -Reset Zoom = c0 - -% Help -Show Manual = f1 -Show LuaDoc = sf1 -gtk-about = diff --git a/modules/textadept/keys.lua b/modules/textadept/keys.lua index e39fa796..8aeffbd2 100644 --- a/modules/textadept/keys.lua +++ b/modules/textadept/keys.lua @@ -1,41 +1,307 @@ -- Copyright 2007-2011 Mitchell mitchell<att>caladbolg.net. See LICENSE. local L = locale.localize +local gui = gui --- --- Defines additional key commands for Textadept. --- The primary key commands are loaded from _USERHOME/keys.conf, --- _HOME/modules/textadept/keys.conf, _USERHOME/keys.osx.conf, or --- _HOME/modules/textadept/keys.osx.conf depending on the platform by --- _m.textadept.menu. --- This module, like _m.textadept.menu, should be 'require'ed last. +-- Defines key commands for Textadept. +-- This set of key commands is pretty standard among other text editors. +-- This module, should be 'require'ed last, but before _m.textadept.menu. module('_m.textadept.keys', package.seeall) -local keys = keys +local keys, _buffer, _view = keys, buffer, view +local m_textadept, m_editing = _m.textadept, _m.textadept.editing +local c, OSX = _SCINTILLA.constants, OSX +-- Utility functions. +utils = { + enclose_as_xml_tags = function() + enclose('<', '>') + local buffer = buffer + local pos = buffer.current_pos + while buffer.char_at[pos - 1] ~= 60 do pos = pos - 1 end -- '<' + buffer:insert_text(-1, '</'..buffer:text_range(pos, buffer.current_pos)) + end, + find_in_files = function() + gui.find.in_files = true + gui.find.focus() + end, + select_command = function() _m.textadept.menu.select_command() end, + snapopen_filedir = function() + if buffer.filename then + m_textadept.snapopen.open(buffer.filename:match('^(.+)[/\\]')) + end + end, + show_style = function() + local buffer = buffer + local style = buffer.style_at[buffer.current_pos] + local text = string.format("%s %s\n%s %s (%d)", L('Lexer'), + buffer:get_lexer(), L('Style'), + buffer:get_style_name(style), style) + buffer:call_tip_show(buffer.current_pos, text) + end, + set_indentation = function(i) + buffer.indent, buffer.tab_width = i, i + events.emit(events.UPDATE_UI) -- for updating statusbar + end, + toggle_property = function(property, i) + local state = buffer[property] + if type(state) == 'boolean' then + buffer[property] = not state + elseif type(state) == 'number' then + buffer[property] = buffer[property] == 0 and (i or 1) or 0 + end + events.emit(events.UPDATE_UI) -- for updating statusbar + end, + set_encoding = function(encoding) + buffer:set_encoding(encoding) + events.emit(events.UPDATE_UI) -- for updating statusbar + end, + set_eol_mode = function(mode) + buffer.eol_mode = mode + buffer:convert_eo_ls(mode) + events.emit(events.UPDATE_UI) -- for updating statusbar + end, + unsplit_all = function() while view:unsplit() do end end, + grow = function() if view.size then view.size = view.size + 10 end end, + shrink = function() if view.size then view.size = view.size - 10 end end, + toggle_current_fold = function() + local buffer = buffer + buffer:toggle_fold(buffer:line_from_position(buffer.current_pos)) + end, + reset_zoom = function() buffer.zoom = 0 end, + open_webpage = function(url) + local cmd + if WIN32 then + cmd = string.format('start "" "%s"', url) + local p = io.popen(cmd) + if not p then error(L('Error loading webpage:')..url) end + else + cmd = string.format(OSX and 'open "file://%s"' or 'xdg-open "%s" &', url) + if os.execute(cmd) ~= 0 then error(L('Error loading webpage:')..url) end + end + end +} +-- The following buffer functions need to be constantized in order for menu +-- items to identify the key associated with the functions. +local menu_buffer_functions = { + 'undo', 'redo', 'cut', 'copy', 'paste', 'line_duplicate', 'clear', + 'select_all', 'upper_case', 'lower_case', 'move_selected_lines_up', + 'move_selected_lines_down', 'zoom_in', 'zoom_out', 'colourise' +} +local function constantize_menu_buffer_functions() + local buffer = buffer + for _, f in ipairs(menu_buffer_functions) do buffer[f] = buffer[f] end +end +events.connect(events.BUFFER_NEW, constantize_menu_buffer_functions) +if not RESETTING then constantize_menu_buffer_functions() end + +--[[ + Windows and Linux menu key commands. + + Unassigned keys (~ denotes keys reserved by the operating system): + c: A B C H N p qQ T ~ V X Y ) ] } * \n + a: aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpP QrRsStTuUvVwWxXyYzZ_ ) ] } *+-/=~~\n\s + ca: aAbBcCdDeE F h jJkK LmMnN pPqQ t v xXy zZ_"'()[]{}<>* / + + CTRL = 'c' (Control ^) + ALT = 'a' (Alt) + META = [unused] + SHIFT = 's' (Shift ⇧) + ADD = '' + Control, Alt, Shift, and 'a' = 'caA' + Control, Alt, Shift, and '\t' = 'cas\t' + + Mac OSX menu key commands. + + Unassigned keys (~ denotes keys reserved by the operating system): + c: A B C ~ JkK ~M N p ~ tT U V Xy ) ] } * ~~\n~~ + ca: aAbBcC~DeE F ~HiIjJkK L~MnN pPq~rRsStTuUvVwWxXyYzZ_"'()[]{}<>*+-/= \n~~ + m: cC D gG H J K L oO qQ v xXyYzZ_ ) ] } * / + + CTRL = 'c' (Command ⌘) + ALT = 'a' (Alt/option ⌥) + META = 'm' (Control ^) + SHIFT = 's' (Shift ⇧) + ADD = '' + Command, Alt, Shift, and 'a' = 'caA' + Command, Alt, Shift, and '\t' = 'cas\t' +]]-- + +-- File. +keys.cn = new_buffer +keys.co = io.open_file +keys.cao = io.open_recent_file +keys.cO = _buffer.reload +keys.cs = _buffer.save +keys.cS = _buffer.save_as +keys.cw = _buffer.close +keys.cW = io.close_all +-- TODO: m_textadept.sessions.prompt_load +-- TODO: m_textadept.sessions.prompt_save +keys[not OSX and 'aq' or 'cq'] = quit + +-- Edit. +keys.cz = _buffer.undo +if not OSX then keys.cy = _buffer.redo end +keys.cZ = _buffer.redo +keys.cx = _buffer.cut +keys.cc = _buffer.copy +keys.cv = _buffer.paste +keys.cd = _buffer.line_duplicate +keys.del = _buffer.clear +keys.ca = _buffer.select_all +keys[not OSX and 'cm' or 'mm'] = m_editing.match_brace +keys[not OSX and 'c\n' or 'mesc'] = { m_editing.autocomplete_word, '%w_' } +keys[not OSX and 'adel' or 'mdel'] = { m_editing.current_word, 'delete' } +keys[not OSX and 'caH' or 'cH'] = m_editing.highlight_word +keys['c/'] = m_editing.block_comment +keys[not OSX and 'ct' or 'mt'] = m_editing.transpose_chars +keys[not OSX and 'cJ' or 'mj'] = m_editing.join_lines +-- Select. +keys[not OSX and 'cM' or 'mM'] = { m_editing.match_brace, 'select' } +keys['c<'] = { m_editing.select_enclosed, '>', '<' } +keys['c>'] = { m_editing.select_enclosed, '<', '>' } +keys["c'"] = { m_editing.select_enclosed, "'", "'" } +keys['c"'] = { m_editing.select_enclosed, '"', '"' } +keys['c('] = { m_editing.select_enclosed, '(', ')' } +keys['c['] = { m_editing.select_enclosed, '[', ']' } +keys['c{'] = { m_editing.select_enclosed, '{', '}' } +keys.cD = { m_editing.current_word, 'select' } +keys.cL = m_editing.select_line +keys.cP = m_editing.select_paragraph +keys.cI = m_editing.select_indented_block +keys.cY = m_editing.select_style +-- Selection. +keys[not OSX and 'cau' or 'mu'] = _buffer.upper_case +keys[not OSX and 'caU' or 'mU'] = _buffer.lower_case +keys[not OSX and 'a<' or 'm<'] = utils.enclose_as_xml_tags +keys[not OSX and 'a>' or 'm>'] = { m_editing.enclose, '<', ' />' } +keys[not OSX and "a'" or "m'"] = { m_editing.enclose, "'", "'" } +keys[not OSX and 'a"' or 'm"'] = { m_editing.enclose, '"', '"' } +keys[not OSX and 'a(' or 'm('] = { m_editing.enclose, '(', ')' } +keys[not OSX and 'a[' or 'm['] = { m_editing.enclose, '[', ']' } +keys[not OSX and 'a{' or 'm{'] = { m_editing.enclose, '{', '}' } +keys['c+'] = { m_editing.grow_selection, 1 } +keys['c_'] = { m_editing.grow_selection, -1 } +keys[not OSX and 'csup' or 'msup'] = _buffer.move_selected_lines_up +keys[not OSX and 'csdown' or 'msdown'] = _buffer.move_selected_lines_down + +-- Search. +keys.cf = gui.find.focus +keys.cg = gui.find.find_next +if not OSX then keys.f3 = gui.find.find_next end +keys.cG = gui.find.find_prev +if not OSX then keys.sf3 = gui.find.find_prev end +keys.cr = gui.find.replace +keys.cR = gui.find.replace_all +keys.caf = gui.find.find_incremental +keys.cF = utils.find_in_files +keys.cag = { gui.find.goto_file_in_list, true } +keys.caG = { gui.find.goto_file_in_list, false } +keys.cj = m_editing.goto_line + +-- Tools. +keys.ce = gui.command_entry.focus +keys.cE = utils.select_command +keys[not OSX and 'car' or 'mr'] = m_textadept.run.run +keys[not OSX and 'caR' or 'mR'] = m_textadept.run.compile +keys['c|'] = m_textadept.filter_through.filter_through +-- Adeptsense. +keys[not OSX and 'c ' or 'aesc'] = m_textadept.adeptsense.complete_symbol +keys[not OSX and 'ch' or 'mh'] = m_textadept.adeptsense.show_documentation +-- Snippets. +keys[not OSX and 'ck' or 'a\t'] = m_textadept.snippets._select +keys['\t'] = m_textadept.snippets._insert +keys['s\t'] = m_textadept.snippets._previous +keys[not OSX and 'cK' or 'as\t'] = m_textadept.snippets._cancel_current +-- Bookmark. +keys.cf2 = m_textadept.bookmarks.toggle +keys.csf2 = m_textadept.bookmarks.clear +keys.f2 = m_textadept.bookmarks.goto_next +keys.sf2 = m_textadept.bookmarks.goto_prev +keys.af2 = m_textadept.bookmarks.goto +-- Snapopen. +keys.cu = { m_textadept.snapopen.open, _USERHOME } +-- TODO: { m_textadept.snapopen.open, _HOME } +keys.caO = utils.snapopen_filedir +keys.ci = utils.show_style + +-- Buffer. +keys[not OSX and 'c\t' or 'm`'] = { _view.goto_buffer, _view, 1, false } +if not OSX then keys.cpgdn = keys['c\t'] end +keys[not OSX and 'cs\t' or 'm~'] = { _view.goto_buffer, _view, -1, false } +if not OSX then keys.cpgup = keys['cs\t'] end +keys.cb = gui.switch_buffer +-- Indentation. +-- TODO: { utils.set_indentation, 2 } +-- TODO: { utils.set_indentation, 3 } +-- TODO: { utils.set_indentation, 4 } +-- TODO: { utils.set_indentation, 8 } +keys[not OSX and 'caT' or 'mT'] = { utils.toggle_property, 'use_tabs' } +keys[not OSX and 'cai' or 'mi'] = m_editing.convert_indentation +-- EOL Mode. +-- TODO: { utils.set_eol_mode, c.SC_EOL_CRLF } +-- TODO: { utils.set_eol_mode, c.SC_EOL_CR } +-- TODO: { utils.set_eol_mode, c.SC_EOL_LF } +-- Encoding. +-- TODO: { utils.set_encoding, 'UTF-8' } +-- TODO: { utils.set_encoding, 'ASCII' } +-- TODO: { utils.set_encoding, 'ISO-8859-1' } +-- TODO: { utils.set_encoding, 'MacRoman' } +-- TODO: { utils.set_encoding, 'UTF-16LE' } +keys.cal = m_textadept.mime_types.select_lexer +keys.f5 = { _buffer.colourise, _buffer, 0, -1 } + +-- View. +keys[not OSX and 'ca\t' or 'm\t'] = { gui.goto_view, 1, false } +keys[not OSX and 'cas\t' or 'ms\t'] = { gui.goto_view, -1, false } +keys[not OSX and 'caS' or 'mS'] = { _view.split, _view } +keys[not OSX and 'cas' or 'ms'] = { _view.split, _view, false } +keys[not OSX and 'caw' or 'mw'] = { _view.unsplit, _view } +keys[not OSX and 'caW' or 'mW'] = utils.unsplit_all +keys[not OSX and 'ca+' or 'm+'] = { utils.grow, 10 } +keys[not OSX and 'ca=' or 'm='] = { utils.grow, 10 } +keys[not OSX and 'ca-' or 'm-'] = { utils.shrink, 10 } +-- TODO: utils.toggle_current_fold +keys[not OSX and 'ca\n' or 'm\n'] = { utils.toggle_property, 'view_eol' } +if not OSX then keys['ca\n\r'] = keys['ca\n'] end +keys[not OSX and 'ca\\' or 'm\\'] = { utils.toggle_property, 'wrap_mode' } +keys[not OSX and 'caI' or 'mI'] = { utils.toggle_property, 'indentation_guides' } +keys[not OSX and 'ca ' or 'm '] = { utils.toggle_property, 'view_ws' } +keys[not OSX and 'caV' or 'mV'] = { utils.toggle_property, 'virtual_space_options', c.SCVS_USERACCESSIBLE } +keys['c='] = _buffer.zoom_in +keys['c-'] = _buffer.zoom_out +keys.c0 = utils.reset_zoom + +-- Help. +keys.f1 = { utils.open_webpage, _HOME..'/doc/manual/1_Introduction.html' } +keys.sf1 = { utils.open_webpage, _HOME..'/doc/index.html' } +-- TODO: { gui.dialog, 'ok-msgbox', '--title', 'Textadept' +-- '--informative-text', _RELEASE, '--no-cancel' } + +-- Movement commands. if OSX then - -- See keys.osx.conf for unassigned keys. keys.mk = function() buffer:line_end_extend() buffer:cut() end - local buffer = buffer - keys.mf = buffer.char_right - keys.mF = buffer.char_right_extend - keys.amf = buffer.word_right - keys.amF = buffer.word_right_extend - keys.mb = buffer.char_left - keys.mB = buffer.char_left_extend - keys.amb = buffer.word_left - keys.amB = buffer.word_left_extend - keys.mn = buffer.line_down - keys.mN = buffer.line_down_extend - keys.mp = buffer.line_up - keys.mP = buffer.line_up_extend - keys.ma = buffer.vc_home - keys.mA = buffer.vc_home_extend - keys.me = buffer.line_end - keys.mE = buffer.line_end_extend - keys.md = buffer.clear - keys.ml = buffer.vertical_centre_caret + keys.mf = _buffer.char_right + keys.mF = _buffer.char_right_extend + keys.amf = _buffer.word_right + keys.amF = _buffer.word_right_extend + keys.mb = _buffer.char_left + keys.mB = _buffer.char_left_extend + keys.amb = _buffer.word_left + keys.amB = _buffer.word_left_extend + keys.mn = _buffer.line_down + keys.mN = _buffer.line_down_extend + keys.mp = _buffer.line_up + keys.mP = _buffer.line_up_extend + keys.ma = _buffer.vc_home + keys.mA = _buffer.vc_home_extend + keys.me = _buffer.line_end + keys.mE = _buffer.line_end_extend + keys.md = _buffer.clear + keys.ml = _buffer.vertical_centre_caret end diff --git a/modules/textadept/keys.osx.conf b/modules/textadept/keys.osx.conf deleted file mode 100644 index 42492597..00000000 --- a/modules/textadept/keys.osx.conf +++ /dev/null @@ -1,165 +0,0 @@ -% Mac OSX menu key commands. -% This set of key commands is pretty standard among other text editors. -% Define additional key commands in _USERHOME/modules/textadept/keys.lua. - -% Unassigned keys (~ denotes keys reserved by the operating system): -% c: A B C ~ JkK ~M N p ~ tT U V Xy ) ] } * ~~\n~~ -% ca: aAbBcC~DeE F ~HiIjJkK L~MnN pPq~rRsStTuUvVwWxXyYzZ_"'()[]{}<>*+-/= \n~~ -% m: cC D gG H J K L oO qQ v xXyYzZ_ ) ] } * / - -% CTRL = 'c' (Command ⌘) -% ALT = 'a' (Alt/option ⌥) -% META = 'm' (Control ^) -% SHIFT = 's' (Shift ⇧) -% ADD = '' -% Command, Alt, Shift, and 'a' = 'caA' -% Command, Alt, Shift, and '\t' = 'cas\t' - -% Multiple keys separated by spaces can be assigned to the same command. -% Keychains are not supported. Please define chains manually. - -% The commands below can either be in English or localized for your locale. - -% File -gtk-new = cn -gtk-open = co -Open Recent... = cao -Reload = cO -gtk-save = cs -gtk-save-as = cS -gtk-close = cw -Close All = cW -Load Session... = -Save Session... = -gtk-quit = cq - -% Edit -gtk-undo = cz -gtk-redo = cZ -gtk-cut = cx -gtk-copy = cc -gtk-paste = cv -Duplicate Line = cd -gtk-delete = del -gtk-select-all = ca -Match Brace = mm -Complete Word = mesc -Delete Word = mdel -Highlight Word = cH -Toggle Block Comment = c/ -Transpose Characters = mt -Join Lines = mj -% Select -Select to Matching Brace = mM -Select between XML Tags = c< -Select in XML Tag = c> -Select in Double Quotes = c" -Select in Single Quotes = c' -Select in Parentheses = c( -Select in Brackets = c[ -Select in Braces = c{ -Select Word = cD -Select Line = cL -Select Paragraph = cP -Select Indented Block = cI -Select Style = cY -% Selection -Upper Case Selection = mu -Lower Case Selection = mU -Enclose as XML Tags = m< -Enclose as Single XML Tag = m> -Enclose in Single Quotes = m' -Enclose in Double Quotes = m" -Enclose in Parentheses = m( -Enclose in Brackets = m[ -Enclose in Braces = m{ -Grow Selection = c+ -Shrink Selection = c_ -Move Selected Lines Up = msdown -Move Selected Lines Down = msup - -% Search -gtk-find = cf -Find Next = cg -Find Previous = cG -Replace = cr -Replace All = cR -Find Incremental = caf -Find in Files = cF -Goto Next File Found = cag -Goto Previous File Found = caG -gtk-jump-to = cj - -% Tools -Command Entry = ce -Select Command = cE -Run = mr -Compile = mR -Filter Through = c| -% Adeptsense -Complete Symbol = aesc -Show Documentation = mh -% Snippets -Insert Snippet... = a\t -Expand Snippet/Next Placeholder = \t -Previous Snippet Placeholder = s\t -Cancel Snippet = as\t -% Bookmark -Toggle Bookmark = cf2 -Clear Bookmarks = csf2 -Next Bookmark = f2 -Previous Bookmark = sf2 -Goto Bookmark... = af2 -% Snapopen -Snapopen User Home = cu -Snapopen Textadept Home = -Snapopen Current Directory = caO -Show Style = ci - -% Buffer -Next Buffer = m` -Previous Buffer = m~ -Switch to Buffer... = cb -% Indentation -Tab width: 2 = -Tab width: 3 = -Tab width: 4 = -Tab width: 8 = -Toggle Use Tabs = mT -Convert Indentation = mi -% EOL Mode -CRLF = -CR = -LF = -% Encoding -UTF-8 Encoding = -ASCII Encoding = -ISO-8859-1 Encoding = -MacRoman Encoding = -UTF-16 Encoding = -Select Lexer... = cal -Refresh Syntax Highlighting = f5 - -% View -Next View = m\t -Previous View = ms\t -Split View Vertical = mS -Split View Horizontal = ms -Unsplit View = mw -Unsplit All Views = mW -Grow View = m+ m= -Shrink View = m- -Toggle Current Fold = -Toggle View EOL = m\n -Toggle Wrap Mode = m\\ -Toggle Show Indent Guides = mI -Toggle View Whitespace = m\s -Toggle Virtual Space = mV -Zoom In = c= -Zoom Out = c- -Reset Zoom = c0 - -% Help -Show Manual = f1 -Show LuaDoc = sf1 -gtk-about = diff --git a/modules/textadept/menu.lua b/modules/textadept/menu.lua index d9841946..efe18e1f 100644 --- a/modules/textadept/menu.lua +++ b/modules/textadept/menu.lua @@ -2,83 +2,30 @@ -- Contributions from Robert Gieseke. local L = locale.localize -local events = events local gui = gui --- -- Provides dynamic menus for Textadept. --- It also loads key commands from _USERHOME/keys.conf, --- _HOME/modules/textadept/keys.conf, _USERHOME/keys.osx.conf, or --- _HOME/modules/textadept/keys.osx.conf depending on the platform. --- This module, like _m.textadept.keys, should be 'require'ed last. +-- This module should be 'require'ed last, after _m.textadept.keys since it +-- looks up defined key commands to show them in menus. module('_m.textadept.menu', package.seeall) local _buffer, _view = buffer, view local m_textadept, m_editing = _m.textadept, _m.textadept.editing -local SEPARATOR = { 'separator' } +local c, SEPARATOR = _SCINTILLA.constants, { 'separator' } +local utils = _m.textadept.keys.utils --- Load menu key commands. -local K = {} -local escapes = { - ['\\b'] = '\b', ['\\n'] = '\n', ['\\r'] = '\r', ['\\t'] = '\t', - ['\\\\'] = '\\', ['\\s'] = ' ' -} -local conf = 'keys'..(OSX and '.osx' or '')..'.conf' -local f = io.open(_USERHOME..'/'..conf) -if not f then f = io.open(_HOME..'/modules/textadept/'..conf) end -for line in f:lines() do - if not line:find('^%s*%%') then - local id, keys = line:match('^(.-)%s*=%s*(.+)$') - if id and keys then - K[id] = {} - for key in keys:gmatch('%S+') do - K[id][#K[id] + 1] = key:gsub('\\[bnrt\\s]', escapes) - end - end +-- Get a string uniquely identifying a key command. +-- This is used to match menu items with key commands to show the key shortcut. +-- @param f A value in the `keys` table. +local function get_id(f) + local id = '' + if type(f) == 'function' then + id = tostring(f) + elseif type(f) == 'table' then + for _, v in ipairs(f) do id = id..tostring(v) end end -end -f:close() - -local function set_encoding(encoding) - buffer:set_encoding(encoding) - events.emit(events.UPDATE_UI) -- for updating statusbar -end -local function toggle_setting(setting, i) - local state = buffer[setting] - if type(state) == 'boolean' then - buffer[setting] = not state - elseif type(state) == 'number' then - buffer[setting] = buffer[setting] == 0 and (i or 1) or 0 - end - events.emit(events.UPDATE_UI) -- for updating statusbar -end -local function set_indentation(i) - buffer.indent, buffer.tab_width = i, i - events.emit(events.UPDATE_UI) -- for updating statusbar -end -local function set_eol_mode(mode) - buffer.eol_mode = mode - buffer:convert_eo_ls(mode) - events.emit(events.UPDATE_UI) -- for updating statusbar -end -local function open_webpage(url) - local cmd - if WIN32 then - cmd = string.format('start "" "%s"', url) - local p = io.popen(cmd) - if not p then error(L('Error loading webpage:')..url) end - else - cmd = string.format(OSX and 'open "file://%s"' or 'xdg-open "%s" &', url) - if os.execute(cmd) ~= 0 then error(L('Error loading webpage:')..url) end - end -end - --- Creates a menuitem readable by read_menu_table(). --- @param label The label that will be localized. --- @param f The function or table. -local function menuitem(label, f) - return { L(label), f, - K[L(label):gsub('_([^_])', '%1')] or K[label:gsub('_([^_])', '%1')] } + return id end --- @@ -87,242 +34,183 @@ end -- @name menubar menubar = { { title = L('File'), - menuitem('gtk-new', new_buffer), - menuitem('gtk-open', io.open_file), - menuitem('Open Recent...', io.open_recent_file), - menuitem('Reload', _buffer.reload), - menuitem('gtk-save', _buffer.save), - menuitem('gtk-save-as', _buffer.save_as), + { L('gtk-new'), new_buffer }, + { L('gtk-open'), io.open_file }, + { L('Open Recent...'), io.open_recent_file }, + { L('Reload'), _buffer.reload }, + { L('gtk-save'), _buffer.save }, + { L('gtk-save-as'), _buffer.save_as }, SEPARATOR, - menuitem('gtk-close', _buffer.close), - menuitem('Close All', io.close_all), + { L('gtk-close'), _buffer.close }, + { L('Close All'), io.close_all }, SEPARATOR, - menuitem('Load Session...', function() - local session_file = _SESSIONFILE or '' - local utf8_filename = gui.dialog('fileselect', - '--title', L('Load Session'), - '--with-directory', - session_file:match('.+[/\\]') or '', - '--with-file', - session_file:match('[^/\\]+$') or '', - '--no-newline') - if #utf8_filename > 0 then - _m.textadept.session.load(utf8_filename:iconv(_CHARSET, 'UTF-8')) - end - end), - menuitem('Save Session...', function() - local session_file = _SESSIONFILE or '' - local utf8_filename = gui.dialog('filesave', - '--title', L('Save Session'), - '--with-directory', - session_file:match('.+[/\\]') or '', - '--with-file', - session_file:match('[^/\\]+$') or '', - '--no-newline') - if #utf8_filename > 0 then - _m.textadept.session.save(utf8_filename:iconv(_CHARSET, 'UTF-8')) - end - end), + { L('Load Session...'), m_textadept.session.prompt_load }, + { L('Save Session...'), m_textadept.session.prompt_save }, SEPARATOR, - menuitem('gtk-quit', quit), + { L('gtk-quit'), quit }, }, { title = L('Edit'), - menuitem('gtk-undo', _buffer.undo), - menuitem('gtk-redo', _buffer.redo), + { L('gtk-undo'), _buffer.undo }, + { L('gtk-redo'), _buffer.redo }, SEPARATOR, - menuitem('gtk-cut', _buffer.cut), - menuitem('gtk-copy', _buffer.copy), - menuitem('gtk-paste', _buffer.paste), - menuitem('Duplicate Line', _buffer.line_duplicate), - menuitem('gtk-delete', _buffer.clear), - menuitem('gtk-select-all', _buffer.select_all), + { L('gtk-cut'), _buffer.cut }, + { L('gtk-copy'), _buffer.copy }, + { L('gtk-paste'), _buffer.paste }, + { L('Duplicate Line'), _buffer.line_duplicate }, + { L('gtk-delete'), _buffer.clear }, + { L('gtk-select-all'), _buffer.select_all }, SEPARATOR, - menuitem('Match Brace', m_editing.match_brace), - menuitem('Complete Word', { m_editing.autocomplete_word, '%w_' }), - menuitem('Delete Word', { m_editing.current_word, 'delete' }), - menuitem('Highlight Word', m_editing.highlight_word), - menuitem('Toggle Block Comment', m_editing.block_comment), - menuitem('Transpose Characters', m_editing.transpose_chars), - menuitem('Join Lines', m_editing.join_lines), + { L('Match Brace'), m_editing.match_brace }, + { L('Complete Word'), { m_editing.autocomplete_word, '%w_' } }, + { L('Delete Word'), { m_editing.current_word, 'delete' } }, + { L('Highlight Word'), m_editing.highlight_word }, + { L('Toggle Block Comment'), m_editing.block_comment }, + { L('Transpose Characters'), m_editing.transpose_chars }, + { L('Join Lines'), m_editing.join_lines }, { title = L('Select'), - menuitem('Select to Matching Brace', { m_editing.match_brace, 'select' }), - menuitem('Select between XML Tags', - { m_editing.select_enclosed, '>', '<' }), - menuitem('Select in XML Tag', { m_editing.select_enclosed, '<', '>' }), - menuitem('Select in Double Quotes', - { m_editing.select_enclosed, '"', '"' }), - menuitem('Select in Single Quotes', - { m_editing.select_enclosed, "'", "'" }), - menuitem('Select in Parentheses', - { m_editing.select_enclosed, '(', ')' }), - menuitem('Select in Brackets', { m_editing.select_enclosed, '[', ']' }), - menuitem('Select in Braces', { m_editing.select_enclosed, '{', '}' }), - menuitem('Select Word', { m_editing.current_word, 'select' }), - menuitem('Select Line', m_editing.select_line), - menuitem('Select Paragraph', m_editing.select_paragraph), - menuitem('Select Indented Block', m_editing.select_indented_block), - menuitem('Select Style', m_editing.select_style), + { L('Select to Matching Brace'), { m_editing.match_brace, 'select' } }, + { L('Select between XML Tags'), { m_editing.select_enclosed, '>', '<' } }, + { L('Select in XML Tag'), { m_editing.select_enclosed, '<', '>' } }, + { L('Select in Single Quotes'), { m_editing.select_enclosed, "'", "'" } }, + { L('Select in Double Quotes'), { m_editing.select_enclosed, '"', '"' } }, + { L('Select in Parentheses'), { m_editing.select_enclosed, '(', ')' } }, + { L('Select in Brackets'), { m_editing.select_enclosed, '[', ']' } }, + { L('Select in Braces'), { m_editing.select_enclosed, '{', '}' } }, + { L('Select Word'), { m_editing.current_word, 'select' } }, + { L('Select Line'), m_editing.select_line }, + { L('Select Paragraph'), m_editing.select_paragraph }, + { L('Select Indented Block'), m_editing.select_indented_block }, + { L('Select Style'), m_editing.select_style }, }, { title = L('Selection'), - menuitem('Upper Case Selection', _buffer.upper_case), - menuitem('Lower Case Selection', _buffer.lower_case), + { L('Upper Case Selection'), _buffer.upper_case }, + { L('Lower Case Selection'), _buffer.lower_case }, SEPARATOR, - menuitem('Enclose as XML Tags', function() - m_editing.enclose('<', '>') - local buffer = buffer - local pos = buffer.current_pos - while buffer.char_at[pos - 1] ~= 60 do pos = pos - 1 end -- '<' - buffer:insert_text(-1, '</'..buffer:text_range(pos, buffer.current_pos)) - end), - menuitem('Enclose as Single XML Tag', { m_editing.enclose, '<', ' />' }), - menuitem('Enclose in Single Quotes', { m_editing.enclose, "'", "'" }), - menuitem('Enclose in Double Quotes', { m_editing.enclose, '"', '"' }), - menuitem('Enclose in Parentheses', { m_editing.enclose, '(', ')' }), - menuitem('Enclose in Brackets', { m_editing.enclose, '[', ']' }), - menuitem('Enclose in Braces', { m_editing.enclose, '{', '}' }), + { L('Enclose as XML Tags'), utils.enclose_as_xml_tags }, + { L('Enclose as Single XML Tag'), { m_editing.enclose, '<', ' />' } }, + { L('Enclose in Single Quotes'), { m_editing.enclose, "'", "'" } }, + { L('Enclose in Double Quotes'), { m_editing.enclose, '"', '"' } }, + { L('Enclose in Parentheses'), { m_editing.enclose, '(', ')' } }, + { L('Enclose in Brackets'), { m_editing.enclose, '[', ']' } }, + { L('Enclose in Braces'), { m_editing.enclose, '{', '}' } }, SEPARATOR, - menuitem('Grow Selection', { m_editing.grow_selection, 1 }), - menuitem('Shrink Selection', { m_editing.grow_selection, -1 }), + { L('Grow Selection'), { m_editing.grow_selection, 1 } }, + { L('Shrink Selection'), { m_editing.grow_selection, -1 } }, SEPARATOR, - menuitem('Move Selected Lines Up', _buffer.move_selected_lines_up), - menuitem('Move Selected Lines Down', _buffer.move_selected_lines_down), + { L('Move Selected Lines Up'), _buffer.move_selected_lines_up }, + { L('Move Selected Lines Down'), _buffer.move_selected_lines_down }, }, }, { title = L('Search'), - menuitem('gtk-find', gui.find.focus), - menuitem('Find Next', gui.find.find_next), - menuitem('Find Previous', gui.find.find_prev), - menuitem('Replace', gui.find.replace), - menuitem('Replace All', gui.find.replace_all), - menuitem('Find Incremental', gui.find.find_incremental), + { L('gtk-find'), gui.find.focus }, + { L('Find Next'), gui.find.find_next }, + { L('Find Previous'), gui.find.find_prev }, + { L('Replace'), gui.find.replace }, + { L('Replace All'), gui.find.replace_all }, + { L('Find Incremental'), gui.find.find_incremental }, SEPARATOR, - menuitem('Find in Files', function() - gui.find.in_files = true - gui.find.focus() - end), - menuitem('Goto Next File Found', { gui.find.goto_file_in_list, true }), - menuitem('Goto Previous File Found', { gui.find.goto_file_in_list, false }), + { L('Find in Files'), utils.find_in_files }, + { L('Goto Next File Found'), { gui.find.goto_file_in_list, true } }, + { L('Goto Previous File Found'), { gui.find.goto_file_in_list, false } }, SEPARATOR, - menuitem('gtk-jump-to', m_editing.goto_line), + { L('gtk-jump-to'), m_editing.goto_line }, }, { title = L('Tools'), - menuitem('Command Entry', gui.command_entry.focus), - menuitem('Select Command', function() _M.select_command() end), + { L('Command Entry'), gui.command_entry.focus }, + { L('Select Command'), utils.select_command }, SEPARATOR, - menuitem('Run', m_textadept.run.run), - menuitem('Compile', m_textadept.run.compile), - menuitem('Filter Through', _m.textadept.filter_through.filter_through), + { L('Run'), m_textadept.run.run }, + { L('Compile'), m_textadept.run.compile }, + { L('Filter Through'), _m.textadept.filter_through.filter_through }, SEPARATOR, { title = L('Adeptsense'), - menuitem('Complete Symbol', function() - local m = _m[buffer:get_lexer()] - if m and m.sense then m.sense:complete() end - end), - menuitem('Show Documentation', function() - local m = _m[buffer:get_lexer()] - if m and m.sense then m.sense:show_apidoc() end - end), + { L('Complete Symbol'), m_textadept.adeptsense.complete_symbol }, + { L('Show Documentation'), m_textadept.adeptsense.show_documentation }, }, { title = L('Snippets'), - menuitem('Insert Snippet...', m_textadept.snippets._select), - menuitem('Expand Snippet/Next Placeholder', m_textadept.snippets._insert), - menuitem('Previous Snippet Placeholder', m_textadept.snippets._previous), - menuitem('Cancel Snippet', m_textadept.snippets._cancel_current), + { L('Insert Snippet...'), m_textadept.snippets._select }, + { L('Expand Snippet/Next Placeholder'), m_textadept.snippets._insert }, + { L('Previous Snippet Placeholder'), m_textadept.snippets._previous }, + { L('Cancel Snippet'), m_textadept.snippets._cancel_current }, }, { title = L('Bookmark'), - menuitem('Toggle Bookmark', m_textadept.bookmarks.toggle), - menuitem('Clear Bookmarks', m_textadept.bookmarks.clear), - menuitem('Next Bookmark', m_textadept.bookmarks.goto_next), - menuitem('Previous Bookmark', m_textadept.bookmarks.goto_prev), - menuitem('Goto Bookmark...', m_textadept.bookmarks.goto), + { L('Toggle Bookmark'), m_textadept.bookmarks.toggle }, + { L('Clear Bookmarks'), m_textadept.bookmarks.clear }, + { L('Next Bookmark'), m_textadept.bookmarks.goto_next }, + { L('Previous Bookmark'), m_textadept.bookmarks.goto_prev }, + { L('Goto Bookmark...'), m_textadept.bookmarks.goto }, }, { title = L('Snapopen'), - menuitem('Snapopen User Home', { m_textadept.snapopen.open, _USERHOME }), - menuitem('Snapopen Textadept Home', { m_textadept.snapopen.open, _HOME }), - menuitem('Snapopen Current Directory', function() - if buffer.filename then - m_textadept.snapopen.open(buffer.filename:match('^(.+)[/\]')) - end - end), + { L('Snapopen User Home'), { m_textadept.snapopen.open, _USERHOME } }, + { L('Snapopen Textadept Home'), { m_textadept.snapopen.open, _HOME } }, + { L('Snapopen Current Directory'), utils.snapopen_filedir }, }, SEPARATOR, - menuitem('Show Style', function() - local buffer = buffer - local style = buffer.style_at[buffer.current_pos] - local text = string.format("%s %s\n%s %s (%d)", L('Lexer'), - buffer:get_lexer(), L('Style'), - buffer:get_style_name(style), style) - buffer:call_tip_show(buffer.current_pos, text) - end), + { L('Show Style'), utils.show_style }, }, { title = L('Buffer'), - menuitem('Next Buffer', { _view.goto_buffer, _view, 1, false }), - menuitem('Previous Buffer', { _view.goto_buffer, _view, -1, false }), - menuitem('Switch to Buffer...', gui.switch_buffer), + { L('Next Buffer'), { _view.goto_buffer, _view, 1, false } }, + { L('Previous Buffer'), { _view.goto_buffer, _view, -1, false } }, + { L('Switch to Buffer...'), gui.switch_buffer }, SEPARATOR, { title = L('Indentation'), - menuitem('Tab width: 2', { set_indentation, 2 }), - menuitem('Tab width: 3', { set_indentation, 3 }), - menuitem('Tab width: 4', { set_indentation, 4 }), - menuitem('Tab width: 8', { set_indentation, 8 }), + { L('Tab width: 2'), { utils.set_indentation, 2 } }, + { L('Tab width: 3'), { utils.set_indentation, 3 } }, + { L('Tab width: 4'), { utils.set_indentation, 4 } }, + { L('Tab width: 8'), { utils.set_indentation, 8 } }, SEPARATOR, - menuitem('Toggle Use Tabs', { toggle_setting, 'use_tabs' }), - menuitem('Convert Indentation', m_editing.convert_indentation), + { L('Toggle Use Tabs'), { utils.toggle_property, 'use_tabs' } }, + { L('Convert Indentation'), m_editing.convert_indentation }, }, { title = L('EOL Mode'), - menuitem('CRLF', { set_eol_mode, 0 }), - menuitem('CR', { set_eol_mode, 1 }), - menuitem('LF', { set_eol_mode, 2 }), + { L('CRLF'), { utils.set_eol_mode, c.SC_EOL_CRLF } }, + { L('CR'), { utils.set_eol_mode, c.SC_EOL_CR } }, + { L('LF'), { utils.set_eol_mode, c.SC_EOL_LF } }, }, { title = L('Encoding'), - menuitem('UTF-8 Encoding', { set_encoding, 'UTF-8' }), - menuitem('ASCII Encoding', { set_encoding, 'ASCII' }), - menuitem('ISO-8859-1 Encoding', { set_encoding, 'ISO-8859-1' }), - menuitem('MacRoman Encoding', { set_encoding, 'MacRoman' }), - menuitem('UTF-16 Encoding', { set_encoding, 'UTF-16LE' }), + { L('UTF-8 Encoding'), { utils.set_encoding, 'UTF-8' } }, + { L('ASCII Encoding'), { utils.set_encoding, 'ASCII' } }, + { L('ISO-8859-1 Encoding'), { utils.set_encoding, 'ISO-8859-1' } }, + { L('MacRoman Encoding'), { utils.set_encoding, 'MacRoman' } }, + { L('UTF-16 Encoding'), { utils.set_encoding, 'UTF-16LE' } }, }, SEPARATOR, - menuitem('Select Lexer...', m_textadept.mime_types.select_lexer), - menuitem('Refresh Syntax Highlighting', - { _buffer.colourise, _buffer, 0, -1 }), + { L('Select Lexer...'), m_textadept.mime_types.select_lexer }, + { L('Refresh Syntax Highlighting'), { _buffer.colourise, _buffer, 0, -1 } }, }, { title = L('View'), - menuitem('Next View', { gui.goto_view, 1, false }), - menuitem('Previous View', { gui.goto_view, -1, false }), + { L('Next View'), { gui.goto_view, 1, false } }, + { L('Previous View'), { gui.goto_view, -1, false } }, SEPARATOR, - menuitem('Split View Vertical', { _view.split, _view }), - menuitem('Split View Horizontal', { _view.split, _view, false }), - menuitem('Unsplit View', function() view:unsplit() end), - menuitem('Unsplit All Views', function() while view:unsplit() do end end), - menuitem('Grow View', - function() if view.size then view.size = view.size + 10 end end), - menuitem('Shrink View', - function() if view.size then view.size = view.size - 10 end end), + { L('Split View Vertical'), { _view.split, _view } }, + { L('Split View Horizontal'), { _view.split, _view, false } }, + { L('Unsplit View'), { _view.unsplit, _view } }, + { L('Unsplit All Views'), utils.unsplit_all }, + { L('Grow View'), { utils.grow, 10 } }, + { L('Shrink View'), { utils.shrink, 10 } }, SEPARATOR, - menuitem('Toggle Current Fold', function() - local buffer = buffer - buffer:toggle_fold(buffer:line_from_position(buffer.current_pos)) - end), + { L('Toggle Current Fold'), utils.toggle_current_fold }, SEPARATOR, - menuitem('Toggle View EOL', { toggle_setting, 'view_eol' }), - menuitem('Toggle Wrap Mode', { toggle_setting, 'wrap_mode' }), - menuitem('Toggle Show Indent Guides', - { toggle_setting, 'indentation_guides' }), - menuitem('Toggle View Whitespace', { toggle_setting, 'view_ws' }), - menuitem('Toggle Virtual Space', - { toggle_setting, 'virtual_space_options', 2 }), + { L('Toggle View EOL'), { utils.toggle_property, 'view_eol' } }, + { L('Toggle Wrap Mode'), { utils.toggle_property, 'wrap_mode' } }, + { L('Toggle Show Indent Guides'), + { utils.toggle_property, 'indentation_guides' } }, + { L('Toggle View Whitespace'), { utils.toggle_property, 'view_ws' } }, + { L('Toggle Virtual Space'), + { utils.toggle_property, 'virtual_space_options', + c.SCVS_USERACCESSIBLE } }, SEPARATOR, - menuitem('Zoom In', _buffer.zoom_in), - menuitem('Zoom Out', _buffer.zoom_out), - menuitem('Reset Zoom', function() buffer.zoom = 0 end), + { L('Zoom In'), _buffer.zoom_in }, + { L('Zoom Out'), _buffer.zoom_out }, + { L('Reset Zoom'), utils.reset_zoom }, }, { title = L('Help'), - menuitem('Show Manual', - { open_webpage, _HOME..'/doc/manual/1_Introduction.html' }), - menuitem('Show LuaDoc', { open_webpage, _HOME..'/doc/index.html' }), + { L('Show Manual'), + { utils.open_webpage, _HOME..'/doc/manual/1_Introduction.html' } }, + { L('Show LuaDoc'), { utils.open_webpage, _HOME..'/doc/index.html' } }, SEPARATOR, - menuitem('gtk-about', { gui.dialog, 'ok-msgbox', '--title', 'Textadept', - '--informative-text', _RELEASE, '--no-cancel' }), + { L('gtk-about'), { gui.dialog, 'ok-msgbox', '--title', 'Textadept', + '--informative-text', _RELEASE, '--no-cancel' } }, }, } @@ -342,6 +230,7 @@ context_menu = { { L('gtk-select-all'), _buffer.select_all } } +local key_shortcuts = {} local menu_actions = {} local contextmenu_actions = {} @@ -356,14 +245,11 @@ local function read_menu_table(menu) if menuitem.title then gtkmenu[#gtkmenu + 1] = read_menu_table(menuitem) else - local label, f, k = menuitem[1], menuitem[2], menuitem[3] + local label, f = menuitem[1], menuitem[2] local menu_id = #menu_actions + 1 - local key, mods = keys.get_gdk_key(k and k[1]) + local key, mods = keys.get_gdk_key(key_shortcuts[get_id(f)]) gtkmenu[#gtkmenu + 1] = { label, menu_id, key, mods } - if f then - menu_actions[menu_id] = f - if k then for _, key in ipairs(k) do keys[key] = f end end - end + if f then menu_actions[menu_id] = f end end end return gtkmenu @@ -381,6 +267,8 @@ end -- separator is created and no action table is required. -- @see keys.get_gdk_key function set_menubar(menubar) + key_shortcuts = {} + for key, f in pairs(keys) do key_shortcuts[get_id(f)] = key end menu_actions = {} local _menubar = {} for i = 1, #menubar do @@ -434,10 +322,10 @@ local function build_command_tables(menu, title, items, commands) if menuitem.title then build_command_tables(menuitem, menuitem.title, items, commands) elseif menuitem[1] ~= 'separator' then - local label, f, key_commands = menuitem[1], menuitem[2], menuitem[3] + local label, f = menuitem[1], menuitem[2] if title then label = title..': '..label end items[#items + 1] = label:gsub('_([^_])', '%1'):gsub('^gtk%-', '') - items[#items + 1] = key_commands and key_commands[1] or '' + items[#items + 1] = key_shortcuts[get_id(f)] or '' commands[#commands + 1] = f end end diff --git a/modules/textadept/session.lua b/modules/textadept/session.lua index f22307d6..bc697f2f 100644 --- a/modules/textadept/session.lua +++ b/modules/textadept/session.lua @@ -166,6 +166,34 @@ function save(filename) end end +--- +-- Prompts the user for a Textadept session to load. +function prompt_load() + local session_file = _SESSIONFILE or '' + local utf8_filename = gui.dialog('fileselect', + '--title', L('Load Session'), + '--with-directory', + session_file:match('.+[/\\]') or '', + '--with-file', + session_file:match('[^/\\]+$') or '', + '--no-newline') + if #utf8_filename > 0 then load(utf8_filename:iconv(_CHARSET, 'UTF-8')) end +end + +--- +-- Prompts the user to save the current Textadept session to a file. +function prompt_save() + local session_file = _SESSIONFILE or '' + local utf8_filename = gui.dialog('filesave', + '--title', L('Save Session'), + '--with-directory', + session_file:match('.+[/\\]') or '', + '--with-file', + session_file:match('[^/\\]+$') or '', + '--no-newline') + if #utf8_filename > 0 then save(utf8_filename:iconv(_CHARSET, 'UTF-8')) end +end + events.connect(events.QUIT, function() if SAVE_ON_QUIT then save() end end, 1) local function no_session() SAVE_ON_QUIT = false end |