diff options
Diffstat (limited to 'modules/textadept')
-rw-r--r-- | modules/textadept/command_entry.lua | 40 | ||||
-rw-r--r-- | modules/textadept/init.lua | 2 | ||||
-rw-r--r-- | modules/textadept/keys.lua | 459 | ||||
-rw-r--r-- | modules/textadept/menu.lua | 394 |
4 files changed, 467 insertions, 428 deletions
diff --git a/modules/textadept/command_entry.lua b/modules/textadept/command_entry.lua index fb8726cd..e511527a 100644 --- a/modules/textadept/command_entry.lua +++ b/modules/textadept/command_entry.lua @@ -18,10 +18,10 @@ local M = ui.command_entry -- -- local function complete_lua() ... end -- local function run_lua() ... end --- keys['ce'] = {ui.command_entry.enter_mode, 'lua_command'} +-- keys['ce'] = function() ui.command_entry.enter_mode('lua_command') end -- keys.lua_command = { -- ['\t'] = complete_lua, --- ['\n'] = {ui.command_entry.finish_mode, run_lua} +-- ['\n'] = function() return ui.command_entry.finish_mode(run_lua) end -- } -- -- In this case, `Ctrl+E` opens the command entry and enters "lua_command" key @@ -43,18 +43,21 @@ module('ui.command_entry')]] -- @class table -- @name editing_keys M.editing_keys = {__index = { - [not OSX and 'cx' or 'mx'] = {buffer.cut, M}, - [not OSX and 'cc' or 'mc'] = {buffer.copy, M}, - [not OSX and 'cv' or 'mv'] = {buffer.paste, M}, - [not OSX and not CURSES and 'ca' or 'ma'] = {buffer.select_all, M}, - [not OSX and 'cz' or 'mz'] = {buffer.undo, M}, - [not OSX and 'cZ' or 'mZ'] = {buffer.redo, M}, cy = {buffer.redo, M}, + -- Note: cannot use `M.cut`, `M.copy`, etc. since M is never considered the + -- global buffer. + [not OSX and 'cx' or 'mx'] = function() M:cut() end, + [not OSX and 'cc' or 'mc'] = function() M:copy() end, + [not OSX and 'cv' or 'mv'] = function() M:paste() end, + [not OSX and not CURSES and 'ca' or 'ma'] = function() M:select_all() end, + [not OSX and 'cz' or 'mz'] = function() M:undo() end, + [not OSX and 'cZ' or 'mZ'] = function() M:redo() end, + [not OSX and 'cy' or '\0'] = function() M:redo() end, -- Movement keys. - [(OSX or CURSES) and 'cf' or '\0'] = {buffer.char_right, M}, - [(OSX or CURSES) and 'cb' or '\0'] = {buffer.char_left, M}, - [(OSX or CURSES) and 'ca' or '\0'] = {buffer.vc_home, M}, - [(OSX or CURSES) and 'ce' or '\0'] = {buffer.line_end, M}, - [(OSX or CURSES) and 'cd' or '\0'] = {buffer.clear, M} + [(OSX or CURSES) and 'cf' or '\0'] = function() M:char_right() end, + [(OSX or CURSES) and 'cb' or '\0'] = function() M:char_left() end, + [(OSX or CURSES) and 'ca' or '\0'] = function() M:vc_home() end, + [(OSX or CURSES) and 'ce' or '\0'] = function() M:line_end() end, + [(OSX or CURSES) and 'cd' or '\0'] = function() M:clear() end }} --- @@ -69,7 +72,8 @@ M.editing_keys = {__index = { -- default value is `'text'`. -- @param height Optional number of lines to display in the command entry. The -- default value is `1`. --- @usage keys['ce'] = {ui.command_entry.enter_mode, 'command_entry'} +-- @usage keys['ce'] = +-- function() ui.command_entry.enter_mode('command_entry') end -- @see _G.keys.MODE -- @name enter_mode function M.enter_mode(mode, lexer, height) @@ -93,7 +97,8 @@ end -- action with the entered text. -- @param f Optional function to call. It should accept the command entry text -- as an argument. --- @usage keys['\n'] = {ui.command_entry.finish_mode, ui.print} +-- @usage keys['\n'] = +-- function() return ui.command_entry.finish_mode(ui.print) end -- @name finish_mode function M.finish_mode(f) if M:auto_c_active() then return false end -- allow Enter to autocomplete @@ -174,7 +179,10 @@ local function complete_lua() end -- Define key mode for entering Lua commands. -keys.lua_command = {['\t'] = complete_lua, ['\n'] = {M.finish_mode, run_lua}} +keys.lua_command = { + ['\t'] = complete_lua, + ['\n'] = function() return M.finish_mode(run_lua) end +} -- Configure the command entry's default properties. events.connect(events.INITIALIZED, function() diff --git a/modules/textadept/init.lua b/modules/textadept/init.lua index d6db4cca..457cfa4e 100644 --- a/modules/textadept/init.lua +++ b/modules/textadept/init.lua @@ -19,7 +19,7 @@ M.session = require('textadept.session') M.snippets = require('textadept.snippets') -- These need to be loaded last. -M.keys = require('textadept.keys') M.menu = require('textadept.menu') +M.keys = require('textadept.keys') return M diff --git a/modules/textadept/keys.lua b/modules/textadept/keys.lua index 6e77911a..c392bc7e 100644 --- a/modules/textadept/keys.lua +++ b/modules/textadept/keys.lua @@ -5,9 +5,7 @@ local M = {} --[[ This comment is for LuaDoc. --- -- Defines key commands for Textadept. --- This set of key commands is pretty standard among other text editors. If --- applicable, load this module second to last in your *~/.textadept/init.lua*, --- before `textadept.menu`. +-- This set of key commands is pretty standard among other text editors. -- -- ## Key Bindings -- @@ -219,101 +217,6 @@ local M = {} -- ‡: Ctrl+Enter in Win32 curses. module('textadept.keys')]] --- Utility functions. -M.utils = { - delete_word = function() - textadept.editing.select_word() - buffer:delete_back() - end, - autocomplete_symbol = function() - textadept.editing.autocomplete(buffer:get_lexer(true)) - end, - enclose_as_xml_tags = function() - textadept.editing.enclose('<', '>') - 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 = function(in_files) - ui.find.in_files = in_files - ui.find.focus() - end, - select_command = function() textadept.menu.select_command() end, - snapopen_filedir = function() - if buffer.filename then io.snapopen(buffer.filename:match('^(.+)[/\\]')) end - end, - show_style = function() - local pos = buffer.current_pos - local char = buffer:text_range(pos, buffer:position_after(pos)) - local code = utf8.codepoint(char) - local bytes = string.rep(' 0x%X', #char):format(char:byte(1, #char)) - local style = buffer.style_at[pos] - local text = string.format("'%s' (U+%04X:%s)\n%s %s\n%s %s (%d)", char, - code, bytes, _L['Lexer'], buffer:get_lexer(true), - _L['Style'], buffer.style_name[style], style) - buffer:call_tip_show(buffer.current_pos, text) - end, - set_indentation = function(i) - buffer.tab_width = 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] = state == 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_eols(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 + buffer:text_height(0) end - end, - shrink = function() - if view.size then view.size = view.size - buffer:text_height(0) end - end, - toggle_current_fold = function() - buffer:toggle_fold(buffer:line_from_position(buffer.current_pos)) - end, - reset_zoom = function() buffer.zoom = 0 end, - open_webpage = function(url) - local cmd = 'xdg-open "%s"' - if WIN32 then - cmd = 'start "" "%s"' - elseif OSX then - cmd = 'open "file://%s"' - end - spawn(cmd:format(url)) - end, - cut_to_eol = function() - buffer:line_end_extend() - buffer:cut() - end -} - -local keys, buffer, view = keys, buffer, view -local editing, utils = textadept.editing, M.utils -local OSX, CURSES = OSX, CURSES - --- 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' -} -for _, f in ipairs(menu_buffer_functions) do buffer[f] = buffer[f] end - -- Windows and Linux key bindings. -- -- Unassigned keys (~ denotes keys reserved by the operating system): @@ -370,218 +273,228 @@ for _, f in ipairs(menu_buffer_functions) do buffer[f] = buffer[f] end -- ADD = '' -- Control, Meta, and 'a' = 'cma' +local keys, OSX, GUI, CURSES, _L = keys, OSX, not CURSES, CURSES, _L + -- File. -keys[not OSX and (not CURSES and 'cn' or 'cmn') or 'mn'] = buffer.new +keys[not OSX and (GUI and 'cn' or 'cmn') or 'mn'] = buffer.new keys[not OSX and 'co' or 'mo'] = io.open_file -keys[not OSX and not CURSES and 'cao' or 'cmo'] = io.open_recent_file -keys[not OSX and (not CURSES and 'cO' or 'mo') or 'mO'] = io.reload_file +keys[not OSX and GUI and 'cao' or 'cmo'] = io.open_recent_file +keys[not OSX and (GUI and 'cO' or 'mo') or 'mO'] = io.reload_file keys[not OSX and 'cs' or 'ms'] = io.save_file -keys[not OSX and (not CURSES and 'cS' or 'cms') or 'mS'] = io.save_file_as +keys[not OSX and (GUI and 'cS' or 'cms') or 'mS'] = io.save_file_as -- TODO: io.save_all_files keys[not OSX and 'cw' or 'mw'] = io.close_buffer -keys[not OSX and (not CURSES and 'cW' or 'cmw') or 'mW'] = io.close_all_buffers +keys[not OSX and (GUI and 'cW' or 'cmw') or 'mW'] = io.close_all_buffers -- TODO: textadept.sessions.load -- TODO: textadept.sessions.save keys[not OSX and 'cq' or 'mq'] = quit -- Edit. +local m_edit = textadept.menu.menubar[_L['_Edit']] keys[not OSX and 'cz' or 'mz'] = buffer.undo if CURSES then keys.mz = keys.cz end -- ^Z suspends in some terminals if not OSX then keys.cy = buffer.redo end -keys[not OSX and not CURSES and 'cZ' or 'mZ'] = buffer.redo +keys[not OSX and GUI and 'cZ' or 'mZ'] = buffer.redo keys[not OSX and 'cx' or 'mx'] = buffer.cut keys[not OSX and 'cc' or 'mc'] = buffer.copy keys[not OSX and 'cv' or 'mv'] = buffer.paste -if not CURSES then keys[not OSX and 'cd' or 'md'] = buffer.line_duplicate end +if GUI then keys[not OSX and 'cd' or 'md'] = buffer.line_duplicate end keys.del = buffer.clear -keys[not OSX and (not CURSES and 'adel' or 'mdel') - or 'cdel'] = utils.delete_word -keys[not OSX and not CURSES and 'ca' or 'ma'] = buffer.select_all -keys[not CURSES and 'cm' or 'mm'] = editing.match_brace -keys[not OSX and (not CURSES and 'c\n' or 'cmj') - or 'cesc'] = {editing.autocomplete, 'word'} -if CURSES and WIN32 then keys['c\n'] = keys['cmj'] end -if not CURSES then - keys[not OSX and 'caH' or 'mH'] = editing.highlight_word +keys[not OSX and (GUI and 'adel' or 'mdel') + or 'cdel'] = m_edit[_L['D_elete Word']][2] +keys[not OSX and GUI and 'ca' or 'ma'] = buffer.select_all +keys[GUI and 'cm' or 'mm'] = textadept.editing.match_brace +keys[not OSX and ((GUI or WIN32) and 'c\n' or 'cmj') + or 'cesc'] = m_edit[_L['Complete _Word']][2] +if GUI then + keys[not OSX and 'caH' or 'mH'] = textadept.editing.highlight_word end -keys[not OSX and not CURSES and 'c/' or 'm/'] = editing.block_comment -keys.ct = editing.transpose_chars -keys[not OSX and (not CURSES and 'cJ' or 'mj') or 'cj'] = editing.join_lines -keys[not OSX and (not CURSES and 'c|' or 'c\\') - or 'm|'] = {ui.command_entry.enter_mode, 'filter_through', 'bash'} +keys[not OSX and GUI and 'c/' or 'm/'] = textadept.editing.block_comment +keys.ct = textadept.editing.transpose_chars +keys[not OSX and (GUI and 'cJ' or 'mj') or 'cj'] = textadept.editing.join_lines +keys[not OSX and (GUI and 'c|' or 'c\\') + or 'm|'] = m_edit[_L['_Filter Through']][2] -- Select. -keys[not CURSES and 'cM' or 'mM'] = {editing.match_brace, 'select'} -keys[not OSX and not CURSES and 'c<' - or 'm<'] = {editing.select_enclosed, '>', '<'} -if not CURSES then - keys[not OSX and 'c>' or 'm>'] = {editing.select_enclosed, '<', '>'} +local m_sel = m_edit[_L['_Select']] +keys[GUI and 'cM' or 'mM'] = m_sel[_L['Select to _Matching Brace']][2] +keys[not OSX and GUI and 'c<' + or 'm<'] = m_sel[_L['Select between _XML Tags']][2] +if GUI then + keys[not OSX and 'c>' or 'm>'] = m_sel[_L['Select in XML _Tag']][2] end -keys[not OSX and not CURSES and "c'" - or "m'"] = {editing.select_enclosed, "'", "'"} -keys[not OSX and not CURSES and 'c"' - or 'm"'] = {editing.select_enclosed, '"', '"'} -keys[not OSX and not CURSES and 'c(' - or 'm('] = {editing.select_enclosed, '(', ')'} -keys[not OSX and not CURSES and 'c[' - or 'm['] = {editing.select_enclosed, '[', ']'} -keys[not OSX and not CURSES and 'c{' - or 'm{'] = {editing.select_enclosed, '{', '}'} -keys[not OSX and (not CURSES and 'cD' or 'mW') or 'mD'] = editing.select_word -keys[not OSX and not CURSES and 'cN' or 'mN'] = editing.select_line -keys[not OSX and not CURSES and 'cP' or 'mP'] = editing.select_paragraph +keys[not OSX and GUI and "c'" + or "m'"] = m_sel[_L['Select in _Single Quotes']][2] +keys[not OSX and GUI and 'c"' + or 'm"'] = m_sel[_L['Select in _Double Quotes']][2] +keys[not OSX and GUI and 'c(' or 'm('] = m_sel[_L['Select in _Parentheses']][2] +keys[not OSX and GUI and 'c[' or 'm['] = m_sel[_L['Select in _Brackets']][2] +keys[not OSX and GUI and 'c{' or 'm{'] = m_sel[_L['Select in B_races']][2] +keys[not OSX and (GUI and 'cD' or 'mW') or 'mD'] = textadept.editing.select_word +keys[not OSX and GUI and 'cN' or 'mN'] = textadept.editing.select_line +keys[not OSX and GUI and 'cP' or 'mP'] = textadept.editing.select_paragraph -- Selection. -keys[not OSX and (not CURSES and 'cau' or 'cmu') or 'cu'] = buffer.upper_case -keys[not OSX and (not CURSES and 'caU' or 'cml') or 'cU'] = buffer.lower_case -keys[not OSX and (not CURSES and 'a<' or 'm>') - or 'c<'] = utils.enclose_as_xml_tags -if not CURSES then - keys[not OSX and 'a>' or 'c>'] = {editing.enclose, '<', ' />'} - keys[not OSX and "a'" or "c'"] = {editing.enclose, "'", "'"} - keys[not OSX and 'a"' or 'c"'] = {editing.enclose, '"', '"'} +m_sel = m_edit[_L['Selectio_n']] +keys[not OSX and (GUI and 'cau' or 'cmu') or 'cu'] = buffer.upper_case +keys[not OSX and (GUI and 'caU' or 'cml') or 'cU'] = buffer.lower_case +keys[not OSX and (GUI and 'a<' or 'm>') + or 'c<'] = m_sel[_L['Enclose as _XML Tags']][2] +if GUI then + keys[not OSX and 'a>' or 'c>'] = m_sel[_L['Enclose as Single XML _Tag']][2] + keys[not OSX and "a'" or "c'"] = m_sel[_L['Enclose in Single _Quotes']][2] + keys[not OSX and 'a"' or 'c"'] = m_sel[_L['Enclose in _Double Quotes']][2] end -keys[not OSX and (not CURSES and 'a(' or 'm)') - or 'c('] = {editing.enclose, '(', ')'} -keys[not OSX and (not CURSES and 'a[' or 'm]') - or 'c['] = {editing.enclose, '[', ']'} -keys[not OSX and (not CURSES and 'a{' or 'm}') - or 'c{'] = {editing.enclose, '{', '}'} +keys[not OSX and (GUI and 'a(' or 'm)') + or 'c('] = m_sel[_L['Enclose in _Parentheses']][2] +keys[not OSX and (GUI and 'a[' or 'm]') + or 'c['] = m_sel[_L['Enclose in _Brackets']][2] +keys[not OSX and (GUI and 'a{' or 'm}') + or 'c{'] = m_sel[_L['Enclose in B_races']][2] keys.csup = buffer.move_selected_lines_up keys.csdown = buffer.move_selected_lines_down -- Search. -keys[not OSX and not CURSES and 'cf' or 'mf'] = utils.find -if CURSES then keys.mF = keys.mf end -- in case mf is used by GUI terminals -keys[not OSX and not CURSES and 'cg' or 'mg'] = ui.find.find_next -if not OSX and not CURSES then keys.f3 = keys.cg end -keys[not OSX and not CURSES and 'cG' or 'mG'] = ui.find.find_prev -if not OSX and not CURSES then keys.sf3 = keys.cG end -keys[not OSX and (not CURSES and 'car' or 'mr') or 'cr'] = ui.find.replace -keys[not OSX and (not CURSES and 'caR' or 'mR') or 'cR'] = ui.find.replace_all +local m_search = textadept.menu.menubar[_L['_Search']] +keys[not OSX and GUI and 'cf' or 'mf'] = m_search[_L['_Find']][2] +if CURSES then keys.mF = keys.mf end -- mf is used by some GUI terminals +keys[not OSX and GUI and 'cg' or 'mg'] = ui.find.find_next +if not OSX and GUI then keys.f3 = keys.cg end +keys[not OSX and GUI and 'cG' or 'mG'] = ui.find.find_prev +if not OSX and GUI then keys.sf3 = keys.cG end +keys[not OSX and (GUI and 'car' or 'mr') or 'cr'] = ui.find.replace +keys[not OSX and (GUI and 'caR' or 'mR') or 'cR'] = ui.find.replace_all -- Find Next is an when find pane is focused in GUI. -- Find Prev is ap when find pane is focused in GUI. -- Replace is ar when find pane is focused in GUI. -- Replace All is aa when find pane is focused in GUI. -keys[not OSX and not CURSES and 'caf' or 'cmf'] = ui.find.find_incremental -if not CURSES then keys[not OSX and 'cF' or 'mF'] = {utils.find, true} end +keys[not OSX and GUI and 'caf' or 'cmf'] = ui.find.find_incremental +if GUI then + keys[not OSX and 'cF' or 'mF'] = m_search[_L['Find in Fi_les']][2] +end -- Find in Files is ai when find pane is focused in GUI. -if not CURSES then - keys[not OSX and 'cag' or 'cmg'] = {ui.find.goto_file_found, false, true} - keys[not OSX and 'caG' or 'cmG'] = {ui.find.goto_file_found, false, false} +if GUI then + keys[not OSX and 'cag' or 'cmg'] = m_search[_L['Goto Nex_t File Found']][2] + keys[not OSX and 'caG' + or 'cmG'] = m_search[_L['Goto Previou_s File Found']][2] end -keys[not OSX and 'cj' or 'mj'] = editing.goto_line +keys[not OSX and 'cj' or 'mj'] = textadept.editing.goto_line -- Tools. -keys[not OSX and (not CURSES and 'ce' or 'mc') - or 'me'] = {ui.command_entry.enter_mode, 'lua_command', 'lua'} -keys[not OSX and (not CURSES and 'cE' or 'mC') or 'mE'] = utils.select_command +local m_tools = textadept.menu.menubar[_L['_Tools']] +keys[not OSX and (GUI and 'ce' or 'mc') + or 'me'] = m_tools[_L['Command _Entry']][2] +keys[not OSX and (GUI and 'cE' or 'mC') + or 'mE'] = m_tools[_L['Select Co_mmand']][2] keys[not OSX and 'cr' or 'mr'] = textadept.run.run -keys[not OSX and (not CURSES and 'cR' or 'cmr') or 'mR'] = textadept.run.compile -keys[not OSX and (not CURSES and 'cB' or 'cmb') or 'mB'] = textadept.run.build -keys[not OSX and (not CURSES and 'cX' or 'cmx') or 'mX'] = textadept.run.stop -keys[not OSX and (not CURSES and 'cae' or 'mx') - or 'cme'] = {textadept.run.goto_error, false, true} -keys[not OSX and (not CURSES and 'caE' or 'mX') - or 'cmE'] = {textadept.run.goto_error, false, false} +keys[not OSX and (GUI and 'cR' or 'cmr') or 'mR'] = textadept.run.compile +keys[not OSX and (GUI and 'cB' or 'cmb') or 'mB'] = textadept.run.build +keys[not OSX and (GUI and 'cX' or 'cmx') or 'mX'] = textadept.run.stop +keys[not OSX and (GUI and 'cae' or 'mx') + or 'cme'] = m_tools[_L['_Next Error']][2] +keys[not OSX and (GUI and 'caE' or 'mX') + or 'cmE'] = m_tools[_L['_Previous Error']][2] +-- Bookmark. +local m_bookmark = m_tools[_L['_Bookmark']] +keys[not OSX and (GUI and 'cf2' or 'f1') or 'mf2'] = textadept.bookmarks.toggle +keys[not OSX and (GUI and 'csf2' or 'f6') or 'msf2'] = textadept.bookmarks.clear +keys.f2 = m_bookmark[_L['_Next Bookmark']][2] +keys[GUI and 'sf2' or 'f3'] = m_bookmark[_L['_Previous Bookmark']][2] +keys[GUI and 'af2' or 'f4'] = textadept.bookmarks.goto_mark +-- Snapopen. +local m_snapopen = m_tools[_L['Snap_open']] +keys[not OSX and 'cu' or 'mu'] = m_snapopen[_L['Snapopen _User Home']][2] +-- TODO: m_snapopen[_L['Snapopen _Textadept Home']][2] +keys[not OSX and (GUI and 'caO' or 'mO') + or 'cmO'] = m_snapopen[_L['Snapopen _Current Directory']][2] +keys[not OSX and (GUI and 'caP' or 'cmp') or 'cmP'] = io.snapopen -- Snippets. -keys[not OSX and (not CURSES and 'ck' or 'mk') - or 'a\t'] = textadept.snippets._select +keys[not OSX and (GUI and 'ck' or 'mk') or 'a\t'] = textadept.snippets._select keys['\t'] = textadept.snippets._insert keys['s\t'] = textadept.snippets._previous -keys[not OSX and (not CURSES and 'cK' or 'mK') +keys[not OSX and (GUI and 'cK' or 'mK') or 'as\t'] = textadept.snippets._cancel_current --- Bookmark. -keys[not OSX and (not CURSES and 'cf2' or 'f1') - or 'mf2'] = textadept.bookmarks.toggle -keys[not OSX and (not CURSES and 'csf2' or 'f6') - or 'msf2'] = textadept.bookmarks.clear -keys.f2 = {textadept.bookmarks.goto_mark, true} -keys[not CURSES and 'sf2' or 'f3'] = {textadept.bookmarks.goto_mark, false} -keys[not CURSES and 'af2' or 'f4'] = textadept.bookmarks.goto_mark --- Snapopen. -keys[not OSX and 'cu' or 'mu'] = {io.snapopen, _USERHOME} --- TODO: {io.snapopen, _HOME} -keys[not OSX and (not CURSES and 'caO' or 'mO') - or 'cmO'] = utils.snapopen_filedir -keys[not OSX and (not CURSES and 'caP' or 'cmp') or 'cmP'] = io.snapopen -- Other. -keys[not OSX and ((not CURSES or WIN32) and 'c ' or 'c@') - or 'aesc'] = utils.autocomplete_symbol -keys[not CURSES and 'ch' or 'mh'] = textadept.editing.show_documentation -if CURSES then keys.mH = keys.mh end -- in case mh is used by GUI terminals -keys[not OSX and (not CURSES and 'ci' or 'mI') or 'mi'] = utils.show_style +keys[not OSX and ((GUI or WIN32) and 'c ' or 'c@') + or 'aesc'] = m_tools[_L['_Complete Symbol']][2] +keys[GUI and 'ch' or 'mh'] = textadept.editing.show_documentation +if CURSES then keys.mH = keys.mh end -- mh is used by some GUI terminals +keys[not OSX and (GUI and 'ci' or 'mI') or 'mi'] = m_tools[_L['Show St_yle']][2] -- Buffer. -keys[not CURSES and 'c\t' or 'mn'] = {view.goto_buffer, view, 1, true} -keys[not CURSES and 'cs\t' or 'mp'] = {view.goto_buffer, view, -1, true} -keys[not OSX and not CURSES and 'cb' or 'mb'] = ui.switch_buffer -if CURSES then keys.mB = keys.mb end -- in case mb is used by GUI terminals +local m_buffer = textadept.menu.menubar[_L['_Buffer']] +keys[GUI and 'c\t' or 'mn'] = m_buffer[_L['_Next Buffer']][2] +keys[GUI and 'cs\t' or 'mp'] = m_buffer[_L['_Previous Buffer']][2] +keys[not OSX and GUI and 'cb' or 'mb'] = ui.switch_buffer +if CURSES then keys.mB = keys.mb end -- mb is used by some GUI terminals -- 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 (not CURSES and 'caT' or 'mt') - or 'cT'] = {utils.toggle_property, 'use_tabs'} -if CURSES then keys.mT = keys.mt end -- in case mt is used by GUI terminals -keys[not OSX and (not CURSES and 'cai' or 'mi') - or 'ci'] = editing.convert_indentation +local m_indentation = m_buffer[_L['_Indentation']] +-- TODO: m_indentation[_L['Tab width: _2']][2] +-- TODO: m_indentation[_L['Tab width: _3']][2] +-- TODO: m_indentation[_L['Tab width: _4']][2] +-- TODO: m_indentation[_L['Tab width: _8']][2] +keys[not OSX and (GUI and 'caT' or 'mt') + or 'cT'] = m_indentation[_L['_Toggle Use Tabs']][2] +if CURSES then keys.mT = keys.mt end -- mt is used by some GUI terminals +keys[not OSX and (GUI and 'cai' or 'mi') + or 'ci'] = textadept.editing.convert_indentation -- EOL Mode. --- TODO: {utils.set_eol_mode, buffer.EOL_CRLF} --- TODO: {utils.set_eol_mode, buffer.EOL_CR} --- TODO: {utils.set_eol_mode, buffer.EOL_LF} +-- TODO: m_buffer[_L['_EOL Mode']][_L['CRLF']][2] +-- TODO: m_buffer[_L['_EOL Mode']][_L['CR']][2] +-- TODO: m_buffer[_L['_EOL Mode']][_L['LF']][2] -- 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[not OSX and not CURSES and 'cL' - or 'mL'] = textadept.file_types.select_lexer -keys.f5 = {buffer.colourise, buffer, 0, -1} +-- TODO: m_buffer[_L['E_ncoding']][_L['_UTF-8 Encoding']][2] +-- TODO: m_buffer[_L['E_ncoding']][_L['_ASCII Encoding']][2] +-- TODO: m_buffer[_L['E_ncoding']][_L['_ISO-8859-1 Encoding']][2] +-- TODO: m_buffer[_L['E_ncoding']][_L['_MacRoman Encoding']][2] +-- TODO: m_buffer[_L['E_ncoding']][_L['UTF-1_6 Encoding']][2] +if GUI then + keys[not OSX and 'ca\n' or 'c\n'] = m_buffer[_L['Toggle View _EOL']][2] + keys[not OSX and 'ca\\' or 'c\\'] = m_buffer[_L['Toggle _Wrap Mode']][2] + keys[not OSX and 'caS' or 'cS'] = m_buffer[_L['Toggle View White_space']][2] +end +keys[not OSX and GUI and 'cL' or 'mL'] = textadept.file_types.select_lexer +keys.f5 = m_buffer[_L['_Refresh Syntax Highlighting']][2] if CURSES then keys.cl = keys.f5 end -- View. -local view_next, view_prev = {ui.goto_view, 1, true}, {ui.goto_view, -1, true} -local view_splith, view_splitv = {view.split, view}, {view.split, view, true} -local view_unsplit = {view.unsplit, view} -if not CURSES then - keys[not OSX and 'can' or 'ca\t'] = view_next - keys[not OSX and 'cap' or 'cas\t'] = view_prev - keys[not OSX and 'cas' or 'cs'] = view_splith - if not OSX then keys.cah = view_splith end - keys[not OSX and 'cav' or 'cv'] = view_splitv - keys[not OSX and 'caw' or 'cw'] = view_unsplit - keys[not OSX and 'caW' or 'cW'] = utils.unsplit_all - keys[not OSX and 'ca+' or 'c+'] = utils.grow - keys[not OSX and 'ca=' or 'c='] = utils.grow - keys[not OSX and 'ca-' or 'c-'] = utils.shrink +local m_view = textadept.menu.menubar[_L['_View']] +if GUI then + keys[not OSX and 'can' or 'ca\t'] = m_view[_L['_Next View']][2] + keys[not OSX and 'cap' or 'cas\t'] = m_view[_L['_Previous View']][2] + keys[not OSX and 'cas' or 'cs'] = m_view[_L['Split View _Horizontal']][2] + if not OSX then keys.cah = keys.cas end + keys[not OSX and 'cav' or 'cv'] = m_view[_L['Split View _Vertical']][2] + keys[not OSX and 'caw' or 'cw'] = m_view[_L['_Unsplit View']][2] + keys[not OSX and 'caW' or 'cW'] = m_view[_L['Unsplit _All Views']][2] + keys[not OSX and 'ca+' or 'c+'] = m_view[_L['_Grow View']][2] + keys[not OSX and 'ca=' or 'c='] = keys[not OSX and 'ca+' or 'c+'] + keys[not OSX and 'ca-' or 'c-'] = m_view[_L['Shrin_k View']][2] else keys.cmv = { - n = view_next, p = view_prev, - s = view_splith, v = view_splitv, - w = view_unsplit, W = utils.unsplit_all, - ['+'] = utils.grow, ['='] = utils.grow, ['-'] = utils.shrink + n = m_view[_L['_Next View']][2], + p = m_view[_L['_Previous View']][2], + s = m_view[_L['Split View _Horizontal']][2], + v = m_view[_L['Split View _Vertical']][2], + w = m_view[_L['_Unsplit View']][2], + W = m_view[_L['Unsplit _All Views']][2], + ['+'] = m_view[_L['_Grow View']][2], + ['-'] = m_view[_L['Shrin_k View']][2] } - if not OSX then keys.cmv.h = view_splith end + if not OSX then keys.cmv.h = keys.cmv.s end + keys.cmv['='] = keys.cmv['+'] end -keys[not OSX and not CURSES and 'c*' or 'm*'] = utils.toggle_current_fold -if not CURSES then - keys[not OSX and 'ca\n' or 'c\n'] = {utils.toggle_property, 'view_eol'} - keys[not OSX and 'ca\\' or 'c\\'] = {utils.toggle_property, 'wrap_mode'} - keys[not OSX and 'caI' or 'cI'] = - {utils.toggle_property, 'indentation_guides'} - keys[not OSX and 'caS' or 'cS'] = {utils.toggle_property, 'view_ws'} - keys[not OSX and 'caV' or 'cV'] = - {utils.toggle_property, 'virtual_space_options', buffer.VS_USERACCESSIBLE} +keys[not OSX and GUI and 'c*' or 'm*'] = m_view[_L['Toggle Current _Fold']][2] +if GUI then + keys[not OSX and 'caI' or 'cI'] = m_view[_L['Toggle Show In_dent Guides']][2] + keys[not OSX and 'caV' or 'cV'] = m_view[_L['Toggle _Virtual Space']][2] end -keys[not OSX and not CURSES and 'c=' or 'm='] = buffer.zoom_in -keys[not OSX and not CURSES and 'c-' or 'm-'] = buffer.zoom_out -keys[not OSX and not CURSES and 'c0' or 'm0'] = utils.reset_zoom +keys[not OSX and GUI and 'c=' or 'm='] = buffer.zoom_in +keys[not OSX and GUI and 'c-' or 'm-'] = buffer.zoom_out +keys[not OSX and GUI and 'c0' or 'm0'] = m_view[_L['_Reset Zoom']][2] -- Help. -if not CURSES then - keys.f1 = {utils.open_webpage, _HOME..'/doc/manual.html'} - keys.sf1 = {utils.open_webpage, _HOME..'/doc/api.html'} +if GUI then + keys.f1 = textadept.menu.menubar[_L['_Help']][_L['Show _Manual']][2] + keys.sf1 = textadept.menu.menubar[_L['_Help']][_L['Show _LuaDoc']][2] end -- Movement commands. @@ -596,13 +509,16 @@ if OSX then keys.ce, keys.cE = buffer.line_end, buffer.line_end_extend keys.aright, keys.aleft = buffer.word_right, buffer.word_left keys.cd = buffer.clear - keys.ck = utils.cut_to_eol + keys.ck = function() + buffer:line_end_extend() + buffer:cut() + end keys.cl = buffer.vertical_centre_caret - -- GTKOSX reports Fn-key as a single keycode which confuses Scintilla. Do + -- GTK-OSX reports Fn-key as a single keycode which confuses Scintilla. Do -- not propagate it. keys.fn = function() return true end elseif CURSES then - keys['c^'] = function() _G.buffer.selection_mode = 0 end + keys['c^'] = function() buffer.selection_mode = 0 end keys['c]'] = buffer.swap_main_anchor_caret keys.cf, keys.cb = buffer.char_right, buffer.char_left keys.cn, keys.cp = buffer.line_down, buffer.line_up @@ -610,13 +526,18 @@ elseif CURSES then keys.mA, keys.mE = buffer.vc_home_extend, buffer.line_end_extend keys.mU, keys.mD = buffer.page_up_extend, buffer.page_down_extend keys.cma, keys.cme = buffer.document_start, buffer.document_end - keys.cd, keys.md = buffer.clear, utils.delete_word - keys.ck = utils.cut_to_eol + keys.cd, keys.md = buffer.clear, keys.mdel + keys.ck = function() + buffer:line_end_extend() + buffer:cut() + end end -- Modes. keys.filter_through = { - ['\n'] = {ui.command_entry.finish_mode, editing.filter_through}, + ['\n'] = function() + return ui.command_entry.finish_mode(textadept.editing.filter_through) + end, } keys.find_incremental = { ['\n'] = function() @@ -638,7 +559,7 @@ setmetatable(keys.find_incremental, {__index = function(_, k) ui.find.find_incremental(ui.command_entry:get_text()..k, true) end}) -- Show documentation for symbols in the Lua command entry. -keys.lua_command[not CURSES and 'ch' or 'mh'] = function() +keys.lua_command[GUI and 'ch' or 'mh'] = function() -- Temporarily change _G.buffer since ui.command_entry is the "active" buffer. local orig_buffer = _G.buffer _G.buffer = ui.command_entry @@ -647,10 +568,16 @@ keys.lua_command[not CURSES and 'ch' or 'mh'] = function() end if OSX or CURSES then -- UTF-8 input. - keys.utf8_input = {['\n'] = {ui.command_entry.finish_mode, function(code) - _G.buffer:add_text(utf8.char(tonumber(code, 16))) - end}} - keys[OSX and 'mU' or 'mu'] = {ui.command_entry.enter_mode, 'utf8_input'} + keys.utf8_input = { + ['\n'] = function() + return ui.command_entry.finish_mode(function(code) + buffer:add_text(utf8.char(tonumber(code, 16))) + end) + end + } + keys[OSX and 'mU' or 'mu'] = function() + ui.command_entry.enter_mode('utf8_input') + end end return M diff --git a/modules/textadept/menu.lua b/modules/textadept/menu.lua index f2bd2d17..acec62b3 100644 --- a/modules/textadept/menu.lua +++ b/modules/textadept/menu.lua @@ -10,25 +10,54 @@ local M = {} -- place. A menu item itself is a table whose first element is a menu label and -- whose second element is a menu command to run. Submenus have `title` keys -- assigned to string text. --- If applicable, load this module last in your *~/.textadept/init.lua*, after --- [`textadept.keys`]() since it looks up defined key commands to show them in --- menus. module('textadept.menu')]] -local _L, buffer, view = _L, buffer, view -local editing, utils = textadept.editing, textadept.keys.utils +local _L = _L local SEPARATOR = {''} +-- 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' +} +for i = 1, #menu_buffer_functions do + buffer[menu_buffer_functions[i]] = buffer[menu_buffer_functions[i]] +end + +-- Commonly used functions in menu commands. +local sel_enc = textadept.editing.select_enclosed +local enc = textadept.editing.enclose +local function set_indentation(i) + buffer.tab_width = i + events.emit(events.UPDATE_UI) -- for updating statusbar +end +local function set_eol_mode(mode) + buffer.eol_mode = mode + buffer:convert_eols(mode) + events.emit(events.UPDATE_UI) -- for updating statusbar +end +local function set_encoding(encoding) + buffer:set_encoding(encoding) + events.emit(events.UPDATE_UI) -- for updating statusbar +end +local function open_page(url) + local cmd = (WIN32 and 'start ""') or (OSX and 'open') or 'xdg-open' + spawn(string.format('%s "%s"', cmd, not OSX and url or 'file://'..url)) +end + --- -- The default main menubar. -- Individual menus, submenus, and menu items can be retrieved by name in -- addition to table index number. -- @class table -- @name menubar --- @usage textadept.menubar[_L['_File']]['_New'] -- returns {'_New', buffer.new} --- @usage textadept.menubar[_L['_File']]['_New'][2] = function() ... end +-- @usage textadept.menu.menubar[_L['_File']][_L['_New']] +-- @usage textadept.menu.menubar[_L['_File']][_L['_New']][2] = function() .. end local default_menubar = { - { title = _L['_File'], + { + title = _L['_File'], {_L['_New'], buffer.new}, {_L['_Open'], io.open_file}, {_L['Open _Recent...'], io.open_recent_file}, @@ -43,9 +72,10 @@ local default_menubar = { {_L['Loa_d Session...'], textadept.session.load}, {_L['Sav_e Session...'], textadept.session.save}, SEPARATOR, - {_L['_Quit'], quit}, + {_L['_Quit'], quit} }, - { title = _L['_Edit'], + { + title = _L['_Edit'], {_L['_Undo'], buffer.undo}, {_L['_Redo'], buffer.redo}, SEPARATOR, @@ -54,161 +84,241 @@ local default_menubar = { {_L['_Paste'], buffer.paste}, {_L['Duplicate _Line'], buffer.line_duplicate}, {_L['_Delete'], buffer.clear}, - {_L['D_elete Word'], utils.delete_word}, + {_L['D_elete Word'], function() + textadept.editing.select_word() + buffer:delete_back() + end}, {_L['Select _All'], buffer.select_all}, SEPARATOR, - {_L['_Match Brace'], editing.match_brace}, - {_L['Complete _Word'], {editing.autocomplete, 'word'}}, - {_L['_Highlight Word'], editing.highlight_word}, - {_L['Toggle _Block Comment'], editing.block_comment}, - {_L['T_ranspose Characters'], editing.transpose_chars}, - {_L['_Join Lines'], editing.join_lines}, - {_L['_Filter Through'], - {ui.command_entry.enter_mode, 'filter_through', 'bash'}}, - { title = _L['_Select'], - {_L['Select to _Matching Brace'], {editing.match_brace, 'select'}}, - {_L['Select between _XML Tags'], {editing.select_enclosed, '>', '<'}}, - {_L['Select in XML _Tag'], {editing.select_enclosed, '<', '>'}}, - {_L['Select in _Single Quotes'], {editing.select_enclosed, "'", "'"}}, - {_L['Select in _Double Quotes'], {editing.select_enclosed, '"', '"'}}, - {_L['Select in _Parentheses'], {editing.select_enclosed, '(', ')'}}, - {_L['Select in _Brackets'], {editing.select_enclosed, '[', ']'}}, - {_L['Select in B_races'], {editing.select_enclosed, '{', '}'}}, - {_L['Select _Word'], editing.select_word}, - {_L['Select _Line'], editing.select_line}, - {_L['Select Para_graph'], editing.select_paragraph}, + {_L['_Match Brace'], textadept.editing.match_brace}, + {_L['Complete _Word'], function() + textadept.editing.autocomplete('word') + end}, + {_L['_Highlight Word'], textadept.editing.highlight_word}, + {_L['Toggle _Block Comment'], textadept.editing.block_comment}, + {_L['T_ranspose Characters'], textadept.editing.transpose_chars}, + {_L['_Join Lines'], textadept.editing.join_lines}, + {_L['_Filter Through'], function() + ui.command_entry.enter_mode('filter_through', 'bash') + end}, + { + title = _L['_Select'], + {_L['Select to _Matching Brace'], function() + textadept.editing.match_brace('select') + end}, + {_L['Select between _XML Tags'], function() sel_enc('>', '<') end}, + {_L['Select in XML _Tag'], function() sel_enc('<', '>') end}, + {_L['Select in _Single Quotes'], function() sel_enc("'", "'") end}, + {_L['Select in _Double Quotes'], function() sel_enc('"', '"') end}, + {_L['Select in _Parentheses'], function() sel_enc('(', ')') end}, + {_L['Select in _Brackets'], function() sel_enc('[', ']') end}, + {_L['Select in B_races'], function() sel_enc('{', '}') end}, + {_L['Select _Word'], textadept.editing.select_word}, + {_L['Select _Line'], textadept.editing.select_line}, + {_L['Select Para_graph'], textadept.editing.select_paragraph} }, - { title = _L['Selectio_n'], + { + title = _L['Selectio_n'], {_L['_Upper Case Selection'], buffer.upper_case}, {_L['_Lower Case Selection'], buffer.lower_case}, SEPARATOR, - {_L['Enclose as _XML Tags'], utils.enclose_as_xml_tags}, - {_L['Enclose as Single XML _Tag'], {editing.enclose, '<', ' />'}}, - {_L['Enclose in Single _Quotes'], {editing.enclose, "'", "'"}}, - {_L['Enclose in _Double Quotes'], {editing.enclose, '"', '"'}}, - {_L['Enclose in _Parentheses'], {editing.enclose, '(', ')'}}, - {_L['Enclose in _Brackets'], {editing.enclose, '[', ']'}}, - {_L['Enclose in B_races'], {editing.enclose, '{', '}'}}, + {_L['Enclose as _XML Tags'], function() + enc('<', '>') + 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}, + {_L['Enclose as Single XML _Tag'], function() enc('<', ' />') end}, + {_L['Enclose in Single _Quotes'], function() enc("'", "'") end}, + {_L['Enclose in _Double Quotes'], function() enc('"', '"') end}, + {_L['Enclose in _Parentheses'], function() enc('(', ')') end}, + {_L['Enclose in _Brackets'], function() enc('[', ']') end}, + {_L['Enclose in B_races'], function() enc('{', '}') end}, SEPARATOR, {_L['_Move Selected Lines Up'], buffer.move_selected_lines_up}, - {_L['Move Selected Lines Do_wn'], buffer.move_selected_lines_down}, - }, + {_L['Move Selected Lines Do_wn'], buffer.move_selected_lines_down} + } }, - { title = _L['_Search'], - {_L['_Find'], utils.find}, + { + title = _L['_Search'], + {_L['_Find'], function() + ui.find.in_files = false + ui.find.focus() + end}, {_L['Find _Next'], ui.find.find_next}, {_L['Find _Previous'], ui.find.find_prev}, {_L['_Replace'], ui.find.replace}, {_L['Replace _All'], ui.find.replace_all}, {_L['Find _Incremental'], ui.find.find_incremental}, SEPARATOR, - {_L['Find in Fi_les'], {utils.find, true}}, - {_L['Goto Nex_t File Found'], {ui.find.goto_file_found, false, true}}, - {_L['Goto Previou_s File Found'], {ui.find.goto_file_found, false, false}}, + {_L['Find in Fi_les'], function() + ui.find.in_files = true + ui.find.focus() + end}, + {_L['Goto Nex_t File Found'], function() + ui.find.goto_file_found(false, true) + end}, + {_L['Goto Previou_s File Found'], function() + ui.find.goto_file_found(false, false) + end}, SEPARATOR, - {_L['_Jump to'], editing.goto_line}, + {_L['_Jump to'], textadept.editing.goto_line} }, - { title = _L['_Tools'], - {_L['Command _Entry'], {ui.command_entry.enter_mode, 'lua_command', 'lua'}}, - {_L['Select Co_mmand'], utils.select_command}, + { + title = _L['_Tools'], + {_L['Command _Entry'], function() + ui.command_entry.enter_mode('lua_command', 'lua') + end}, + {_L['Select Co_mmand'], function() M.select_command() end}, SEPARATOR, {_L['_Run'], textadept.run.run}, {_L['_Compile'], textadept.run.compile}, {_L['Buil_d'], textadept.run.build}, {_L['S_top'], textadept.run.stop}, - {_L['_Next Error'], {textadept.run.goto_error, false, true}}, - {_L['_Previous Error'], {textadept.run.goto_error, false, false}}, + {_L['_Next Error'], function() textadept.run.goto_error(false, true) end}, + {_L['_Previous Error'], function() + textadept.run.goto_error(false, false) + end}, SEPARATOR, - { title = _L['_Bookmark'], + { + title = _L['_Bookmark'], {_L['_Toggle Bookmark'], textadept.bookmarks.toggle}, {_L['_Clear Bookmarks'], textadept.bookmarks.clear}, - {_L['_Next Bookmark'], {textadept.bookmarks.goto_mark, true}}, - {_L['_Previous Bookmark'], {textadept.bookmarks.goto_mark, false}}, + {_L['_Next Bookmark'], function() + textadept.bookmarks.goto_mark(true) + end}, + {_L['_Previous Bookmark'], function() + textadept.bookmarks.goto_mark(false) + end}, {_L['_Goto Bookmark...'], textadept.bookmarks.goto_mark}, }, - { title = _L['Snap_open'], - {_L['Snapopen _User Home'], {io.snapopen, _USERHOME}}, - {_L['Snapopen _Textadept Home'], {io.snapopen, _HOME}}, - {_L['Snapopen _Current Directory'], utils.snapopen_filedir}, + { + title = _L['Snap_open'], + {_L['Snapopen _User Home'], function() io.snapopen(_USERHOME) end}, + {_L['Snapopen _Textadept Home'], function() io.snapopen(_HOME) end}, + {_L['Snapopen _Current Directory'], function() + if buffer.filename then + io.snapopen(buffer.filename:match('^(.+)[/\\]')) + end + end}, {_L['Snapopen Current _Project'], io.snapopen}, }, - { title = _L['_Snippets'], + { + title = _L['_Snippets'], {_L['_Insert Snippet...'], textadept.snippets._select}, {_L['_Expand Snippet/Next Placeholder'], textadept.snippets._insert}, {_L['_Previous Snippet Placeholder'], textadept.snippets._previous}, {_L['_Cancel Snippet'], textadept.snippets._cancel_current}, }, SEPARATOR, - {_L['_Complete Symbol'], utils.autocomplete_symbol}, + {_L['_Complete Symbol'], function() + textadept.editing.autocomplete(buffer:get_lexer(true)) + end}, {_L['Show _Documentation'], textadept.editing.show_documentation}, - {_L['Show St_yle'], utils.show_style}, + {_L['Show St_yle'], function() + local char = buffer:text_range(buffer.current_pos, + buffer:position_after(buffer.current_pos)) + local bytes = string.rep(' 0x%X', #char):format(char:byte(1, #char)) + local style = buffer.style_at[buffer.current_pos] + local text = string.format("'%s' (U+%04X:%s)\n%s %s\n%s %s (%d)", char, + utf8.codepoint(char), bytes, _L['Lexer'], + buffer:get_lexer(true), _L['Style'], + buffer.style_name[style], style) + buffer:call_tip_show(buffer.current_pos, text) + end} }, - { title = _L['_Buffer'], - {_L['_Next Buffer'], {view.goto_buffer, view, 1, true}}, - {_L['_Previous Buffer'], {view.goto_buffer, view, -1, true}}, + { + title = _L['_Buffer'], + {_L['_Next Buffer'], function() view:goto_buffer(1, true) end}, + {_L['_Previous Buffer'], function() view:goto_buffer(-1, true) end}, {_L['_Switch to Buffer...'], ui.switch_buffer}, SEPARATOR, - { title = _L['_Indentation'], - {_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}}, + { + title = _L['_Indentation'], + {_L['Tab width: _2'], function() set_indentation(2) end}, + {_L['Tab width: _3'], function() set_indentation(3) end}, + {_L['Tab width: _4'], function() set_indentation(4) end}, + {_L['Tab width: _8'], function() set_indentation(8) end}, SEPARATOR, - {_L['_Toggle Use Tabs'], {utils.toggle_property, 'use_tabs'}}, - {_L['_Convert Indentation'], editing.convert_indentation}, + {_L['_Toggle Use Tabs'], function() + buffer.use_tabs = not buffer.use_tabs + events.emit(events.UPDATE_UI) -- for updating statusbar + end}, + {_L['_Convert Indentation'], textadept.editing.convert_indentation} }, - { title = _L['_EOL Mode'], - {_L['CRLF'], {utils.set_eol_mode, buffer.EOL_CRLF}}, - {_L['CR'], {utils.set_eol_mode, buffer.EOL_CR}}, - {_L['LF'], {utils.set_eol_mode, buffer.EOL_LF}}, + { + title = _L['_EOL Mode'], + {_L['CRLF'], function() set_eol_mode(buffer.EOL_CRLF) end}, + {_L['CR'], function() set_eol_mode(buffer.EOL_CR) end}, + {_L['LF'], function() set_eol_mode(buffer.EOL_LF) end} }, - { title = _L['E_ncoding'], - {_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-1_6 Encoding'], {utils.set_encoding, 'UTF-16LE'}}, + { + title = _L['E_ncoding'], + {_L['_UTF-8 Encoding'], function() set_encoding('UTF-8') end}, + {_L['_ASCII Encoding'], function() set_encoding('ASCII') end}, + {_L['_ISO-8859-1 Encoding'], function() set_encoding('ISO-8859-1') end}, + {_L['_MacRoman Encoding'], function() set_encoding('MacRoman') end}, + {_L['UTF-1_6 Encoding'], function() set_encoding('UTF-16LE') end} }, SEPARATOR, - {_L['Toggle View _EOL'], {utils.toggle_property, 'view_eol'}}, - {_L['Toggle _Wrap Mode'], {utils.toggle_property, 'wrap_mode'}}, - {_L['Toggle View White_space'], {utils.toggle_property, 'view_ws'}}, + {_L['Toggle View _EOL'], function() + buffer.view_eol = not buffer.view_eol + end}, + {_L['Toggle _Wrap Mode'], function() + buffer.wrap_mode = buffer.wrap_mode == 0 and buffer.WRAP_WHITESPACE or 0 + end}, + {_L['Toggle View White_space'], function() + buffer.view_ws = buffer.view_ws == 0 and buffer.WS_VISIBLEALWAYS or 0 + end}, SEPARATOR, {_L['Select _Lexer...'], textadept.file_types.select_lexer}, - {_L['_Refresh Syntax Highlighting'], {buffer.colourise, buffer, 0, -1}}, + {_L['_Refresh Syntax Highlighting'], function() buffer:colourise(0, -1) end} }, - { title = _L['_View'], - {_L['_Next View'], {ui.goto_view, 1, true}}, - {_L['_Previous View'], {ui.goto_view, -1, true}}, + { + title = _L['_View'], + {_L['_Next View'], function() ui.goto_view(1, true) end}, + {_L['_Previous View'], function() ui.goto_view(-1, true) end}, SEPARATOR, - {_L['Split View _Horizontal'], {view.split, view}}, - {_L['Split View _Vertical'], {view.split, view, true}}, - {_L['_Unsplit View'], {view.unsplit, view}}, - {_L['Unsplit _All Views'], utils.unsplit_all}, - {_L['_Grow View'], utils.grow}, - {_L['Shrin_k View'], utils.shrink}, + {_L['Split View _Horizontal'], function() view:split() end}, + {_L['Split View _Vertical'], function() view:split(true) end}, + {_L['_Unsplit View'], function() view:unsplit() end}, + {_L['Unsplit _All Views'], function() while view:unsplit() do end end}, + {_L['_Grow View'], function() + if view.size then view.size = view.size + buffer:text_height(0) end + end}, + {_L['Shrin_k View'], function() + if view.size then view.size = view.size - buffer:text_height(0) end + end}, SEPARATOR, - {_L['Toggle Current _Fold'], utils.toggle_current_fold}, + {_L['Toggle Current _Fold'], function() + buffer:toggle_fold(buffer:line_from_position(buffer.current_pos)) + end}, SEPARATOR, - {_L['Toggle Show In_dent Guides'], - {utils.toggle_property, 'indentation_guides'}}, - {_L['Toggle _Virtual Space'], - {utils.toggle_property, 'virtual_space_options', - buffer.VS_USERACCESSIBLE}}, + {_L['Toggle Show In_dent Guides'], function() + local off = buffer.indentation_guides == 0 + buffer.indentation_guides = off and buffer.IV_LOOKBOTH or 0 + end}, + {_L['Toggle _Virtual Space'], function() + local off = buffer.virtual_space_options == 0 + buffer.virtual_space_options = off and buffer.VS_USERACCESSIBLE or 0 + end}, SEPARATOR, {_L['Zoom _In'], buffer.zoom_in}, {_L['Zoom _Out'], buffer.zoom_out}, - {_L['_Reset Zoom'], utils.reset_zoom}, + {_L['_Reset Zoom'], function() buffer.zoom = 0 end} }, - { title = _L['_Help'], - {_L['Show _Manual'], {utils.open_webpage, _HOME..'/doc/manual.html'}}, - {_L['Show _LuaDoc'], {utils.open_webpage, _HOME..'/doc/api.html'}}, + { + title = _L['_Help'], + {_L['Show _Manual'], function() open_page(_HOME..'/doc/manual.html') end}, + {_L['Show _LuaDoc'], function() open_page(_HOME..'/doc/api.html') end}, SEPARATOR, - {_L['_About'], - {ui.dialogs.msgbox, {title = 'Textadept', text = _RELEASE, - informative_text = _COPYRIGHT, - icon_file = _HOME..'/core/images/ta_64x64.png'}}}, - }, + {_L['_About'], function() + ui.dialogs.msgbox({ + title = 'Textadept', text = _RELEASE, informative_text = _COPYRIGHT, + icon_file = _HOME..'/core/images/ta_64x64.png' + }) + end} + } } --- @@ -311,27 +421,6 @@ local function read_menu_table(menu, contextmenu) return gtkmenu end --- Builds the item and commands tables for the filtered list dialog. --- @param menu The menu to read from. --- @param title The title of the menu. --- @param items The current list of items. --- @param commands The current list of commands. -local function build_command_tables(menu, title, items, commands) - for i = 1, #menu do - if menu[i].title then - build_command_tables(menu[i], menu[i].title, items, commands) - elseif menu[i][1] ~= '' then - local label, f = menu[i][1], menu[i][2] - if title then label = title..': '..label end - items[#items + 1] = label:gsub('_([^_])', '%1') - items[#items + 1] = key_shortcuts[get_id(f)] or '' - commands[#commands + 1] = f - end - end -end - -local items, commands - -- Returns a proxy table for menu table *menu* such that when a menu item is -- changed or added, *update* is called to update the menu in the UI. -- @param menu The menu or table of menus to create a proxy for. @@ -343,7 +432,7 @@ local function proxy_menu(menu, update, menubar) return setmetatable({}, { __index = function(_, k) local v - if type(k) == 'number' then + if type(k) == 'number' or k == 'title' then v = menu[k] elseif type(k) == 'string' then for i = 1, #menu do @@ -378,11 +467,10 @@ local function set_menubar(menubar) _menubar[#_menubar + 1] = ui.menu(read_menu_table(menubar[i])) end ui.menubar = _menubar - items, commands = {}, {} - build_command_tables(menubar, nil, items, commands) proxies.menubar = proxy_menu(menubar, set_menubar) end -set_menubar(default_menubar) +proxies.menubar = proxy_menu(default_menubar, function() end) -- for keys.lua +events.connect(events.INITIALIZED, function() set_menubar(default_menubar) end) -- Sets `ui.context_menu` and `ui.tab_context_menu` from menu item lists -- *buffer_menu* and *tab_menu*, respectively. @@ -408,12 +496,37 @@ local function set_contextmenus(buffer_menu, tab_menu) set_contextmenus(nil, menu) end) end -set_contextmenus() +events.connect(events.INITIALIZED, set_contextmenus) + +-- Performs the appropriate action when clicking a menu item. +events.connect(events.MENU_CLICKED, function(menu_id) + local actions = menu_id < 1000 and menu_actions or contextmenu_actions + local action = actions[menu_id < 1000 and menu_id or menu_id - 1000] + assert(type(action) == 'function' or type(action) == 'table', + _L['Unknown command:']..' '..tostring(action)) + keys.run_command(action, type(action)) +end) --- -- Prompts the user to select a menu command to run. -- @name select_command function M.select_command() + local items, commands = {}, {} + -- Builds the item and commands tables for the filtered list dialog. + -- @param menu The menu to read from. + local function build_command_tables(menu) + for i = 1, #menu do + if menu[i].title then + build_command_tables(menu[i]) + elseif menu[i][1] ~= '' then + local label = menu.title and menu.title..': '..menu[i][1] or menu[i][1] + items[#items + 1] = label:gsub('_([^_])', '%1') + items[#items + 1] = key_shortcuts[get_id(menu[i][2])] or '' + commands[#commands + 1] = menu[i][2] + end + end + end + build_command_tables(getmetatable(M.menubar).menu) local button, i = ui.dialogs.filteredlist{ title = _L['Run Command'], columns = {_L['Command'], _L['Key Command']}, items = items, width = CURSES and ui.size[1] - 2 or nil @@ -422,15 +535,6 @@ function M.select_command() keys.run_command(commands[i], type(commands[i])) end --- Performs the appropriate action when clicking a menu item. -events.connect(events.MENU_CLICKED, function(menu_id) - local actions = menu_id < 1000 and menu_actions or contextmenu_actions - local action = actions[menu_id < 1000 and menu_id or menu_id - 1000] - assert(type(action) == 'function' or type(action) == 'table', - _L['Unknown command:']..' '..tostring(action)) - keys.run_command(action, type(action)) -end) - return setmetatable(M, { __index = function(_, k) return proxies[k] or M[k] end, __newindex = function(_, k, v) |