diff options
Diffstat (limited to 'modules/textadept')
-rw-r--r-- | modules/textadept/command_entry.lua | 5 | ||||
-rw-r--r-- | modules/textadept/editing.lua | 6 | ||||
-rw-r--r-- | modules/textadept/filter_through.lua | 2 | ||||
-rw-r--r-- | modules/textadept/find.lua | 7 | ||||
-rw-r--r-- | modules/textadept/keys.conf | 149 | ||||
-rw-r--r-- | modules/textadept/keys.lua | 346 | ||||
-rw-r--r-- | modules/textadept/keys.osx.conf | 149 | ||||
-rw-r--r-- | modules/textadept/menu.lua | 325 |
8 files changed, 538 insertions, 451 deletions
diff --git a/modules/textadept/command_entry.lua b/modules/textadept/command_entry.lua index f0c771ed..b2eab2c1 100644 --- a/modules/textadept/command_entry.lua +++ b/modules/textadept/command_entry.lua @@ -40,11 +40,10 @@ end) events.connect(events.COMMAND_ENTRY_KEYPRESS, function(code) local ce = gui.command_entry - local KEYSYMS = keys.KEYSYMS - if KEYSYMS[code] == 'esc' then + if keys.KEYSYMS[code] == 'esc' then ce.focus() -- toggle focus to hide return true - elseif KEYSYMS[code] == '\t' then + elseif keys.KEYSYMS[code] == '\t' then local substring = ce.entry_text:match('[%w_.:]+$') or '' local path, o, prefix = substring:match('^([%w_.:]-)([.:]?)([%w_]*)$') local f, err = loadstring('return ('..path..')') diff --git a/modules/textadept/editing.lua b/modules/textadept/editing.lua index 16ead02b..885449a4 100644 --- a/modules/textadept/editing.lua +++ b/modules/textadept/editing.lua @@ -87,7 +87,7 @@ events.connect(events.CHAR_ADDED, function(c) end) -- Removes matched chars on backspace. -events.connect(events.KEYPRESS, function(code, shift, control, alt) +events.connect(events.KEYPRESS, function(code) if not AUTOPAIR or K[code] ~= '\b' or buffer.selections ~= 1 then return end local buffer = buffer local pos = buffer.current_pos @@ -398,7 +398,7 @@ end --- -- Selects all text with the same style as under the caret. -function select_scope() +function select_style() local buffer = buffer local start_pos, length = buffer.current_pos, buffer.length local base_style, style_at = buffer.style_at[start_pos], buffer.style_at @@ -446,7 +446,7 @@ local function clear_highlighted_words() buffer:indicator_clear_range(0, buffer.length) end events.connect(events.KEYPRESS, - function(c) if K[c] == 'esc' then clear_highlighted_words() end end) + function(code) if K[code] == 'esc' then clear_highlighted_words() end end) --- -- Highlights all occurances of the word under the caret and adds markers to the diff --git a/modules/textadept/filter_through.lua b/modules/textadept/filter_through.lua index a0cc0072..35123194 100644 --- a/modules/textadept/filter_through.lua +++ b/modules/textadept/filter_through.lua @@ -28,7 +28,7 @@ function filter_through() end events.connect(events.COMMAND_ENTRY_KEYPRESS, function(code) - if filter_through_active and code == 0xff1b then -- escape + if filter_through_active and keys.KEYSYMS[code] == 'esc' then filter_through_active = false end end, 1) -- place before command_entry.lua's handler (if necessary) diff --git a/modules/textadept/find.lua b/modules/textadept/find.lua index 4d80ae39..a0f6e299 100644 --- a/modules/textadept/find.lua +++ b/modules/textadept/find.lua @@ -176,13 +176,12 @@ function find.find_incremental() end events.connect(events.COMMAND_ENTRY_KEYPRESS, function(code) - local K = keys.KEYSYMS if find.incremental then - if K[code] == 'esc' then + if keys.KEYSYMS[code] == 'esc' then find.incremental = nil - elseif code < 256 or K[code] == '\b' then + elseif code < 256 or keys.KEYSYMS[code] == '\b' then local text = gui.command_entry.entry_text - if K[code] == '\b' then + if keys.KEYSYMS[code] == '\b' then find_incremental(text:sub(1, -2)) else find_incremental(text..string.char(code)) diff --git a/modules/textadept/keys.conf b/modules/textadept/keys.conf new file mode 100644 index 00000000..c2951fac --- /dev/null +++ b/modules/textadept/keys.conf @@ -0,0 +1,149 @@ +% 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 D E N p qQ T ~ V X Y ) ] } * \n +% a: aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpP QrRsStTuUvVwWxXyYzZ_ ) ] } *+-/=~~\n\s +% ca: aAbBcCdDeE F hHi jJkK LmMnN pPqQ t Uv xXyYzZ_"'()[]{}<>* / + +% 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' + +% 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 = cd +gtk-delete = +gtk-select-all = ca +Match Brace = cm +Select to Brace = cM +Complete Word = c\n +Delete Word = +Highlight Word = cH +Complete Symbol = c\s +Show Documentation = ch +Toggle Block Comment = c/ +Transpose Characters = ct +Join Lines = cJ +Convert Indentation = +% Selection +% Enclose In... +HTML Tags = a< +HTML Single Tag = a> +Double Quotes = a" +Single Quotes = a' +Parentheses = a( +Brackets = a[ +Braces = a{ +Grow Selection = c+ +Shrink Selection = c_ +% Select In... +Between Tags = c< +HTML Tag = c> +Double Quote = c" +Single Quote = c' +Parenthesis = c( +Bracket = c[ +Brace = c{ +Word = +Line = cL +Paragraph = cP +Indented Block = cI +Style = + +% 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 +Run = car +Compile = caR +Filter Through = c| +% Snippets +Expand = \t +Insert... = ck +Previous Placeholder = s\t +Cancel = cK +% Bookmark +Toggle on Current Line = cf2 +Clear All = csf2 +Next = f2 +Previous = sf2 +Goto Bookmark... = af2 +% Snapopen +User Home = cu +Textadept Home = cau +Current Directory = caO +Show Style = ci + +% Buffer +Next Buffer = c\t cpgdn +Previous Buffer = cs\t cpgup +Switch Buffer = cb +Toggle View EOL = ca\n ca\n\r +Toggle Wrap Mode = ca\\ +Toggle Show Indent Guides = caI +Toggle Use Tabs = caT +Toggle View Whitespace = ca\s +Toggle Virtual Space = caV +% EOL Mode +CRLF = +CR = +LF = +% Encoding +UTF-8 = +ASCII = +ISO-8859-1 = +MacRoman = +UTF-16 = +Select Lexer... = cal +Refresh Syntax Highlighting = f5 + +% View +Next View = ca\t +Previous View = cas\t +Split Vertical = caS +Split Horizontal = cas +Unsplit = caw +Unsplit All = caW +Grow View = ca+ ca= +Shrink View = ca- +Zoom In = c= +Zoom Out = c- +Reset Zoom = c0 + +% Help +Manual = f1 +LuaDoc = sf1 +gtk-about = diff --git a/modules/textadept/keys.lua b/modules/textadept/keys.lua index f51e378a..e39fa796 100644 --- a/modules/textadept/keys.lua +++ b/modules/textadept/keys.lua @@ -3,329 +3,39 @@ local L = locale.localize --- --- Defines key commands for Textadept. --- This set of key commands is pretty standard among other text editors. +-- 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. module('_m.textadept.keys', package.seeall) local keys = keys -local _buffer, _view = buffer, view -local gui, m_textadept = gui, _m.textadept --- Utility functions used by both layouts. -local function enclose_in_tag() - m_textadept.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 -local function any_char_mt(f) - return setmetatable({['\0'] = {}}, { - __index = function(t, k) - if #k == 1 then return { f, k, k } end - end }) -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 show_style() - 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 - --- CTRL = 'c' --- SHIFT = 's' --- ALT = 'a' --- ADD = '' --- Control, Shift, Alt, and 'a' = 'caA' --- Control, Shift, Alt, and '\t' = 'csa\t' - -if not OSX then - -- Windows and Linux key commands. - - --[[ - C: D J K M T U - A: A B D E F G H J K L M N P T U V W X Y Z - CS: A C D G I J K L M N O Q T U V X Y Z - SA: A B C D E F G H I J K L M N O P Q R S T U V W X Y Z - CA: A B C D E F G H J K L M N O Q R S T U W X Y Z - CSA: A B C D E F G H J K L M N O P Q R S T U V W X Y Z - ]]-- - - keys.clear_sequence = 'esc' - - -- File - keys.cn = new_buffer - keys.co = io.open_file - -- TODO: _buffer.reload - keys.cs = _buffer.save - keys.cS = _buffer.save_as - keys.cw = _buffer.close - keys.cW = io.close_all - -- TODO: m_textadept.session.load after prompting with open dialog - -- TODO: m_textadept.session.save after prompting with save dialog - keys.aq = quit - - -- Edit - local m_editing = m_textadept.editing - keys.cz = _buffer.undo - keys.cy = _buffer.redo - keys.cx = _buffer.cut - keys.cc = _buffer.copy - keys.cv = _buffer.paste - -- Delete is delete. - keys.ca = _buffer.select_all - keys.ce = m_editing.match_brace - keys.cE = { m_editing.match_brace, 'select' } - keys['c\n'] = { m_editing.autocomplete_word, '%w_' } - keys['c\n\r'] = { m_editing.autocomplete_word, '%w_' } -- win32 - keys.cq = m_editing.block_comment - -- TODO: { m_editing.current_word, 'delete' } - keys.cH = m_editing.highlight_word - -- TODO: m_editing.transpose_chars - -- TODO: m_editing.convert_indentation - keys.ac = { -- enClose in... - t = enclose_in_tag, - T = { m_editing.enclose, '<', ' />' }, - ['"'] = { m_editing.enclose, '"', '"' }, - ["'"] = { m_editing.enclose, "'", "'" }, - ['('] = { m_editing.enclose, '(', ')' }, - ['['] = { m_editing.enclose, '[', ']' }, - ['{'] = { m_editing.enclose, '{', '}' }, - c = any_char_mt(m_editing.enclose), - } - keys.as = { -- select in... - t = { m_editing.select_enclosed, '>', '<' }, - ['"'] = { m_editing.select_enclosed, '"', '"' }, - ["'"] = { m_editing.select_enclosed, "'", "'" }, - ['('] = { m_editing.select_enclosed, '(', ')' }, - ['['] = { m_editing.select_enclosed, '[', ']' }, - ['{'] = { m_editing.select_enclosed, '{', '}' }, - w = { m_editing.current_word, 'select' }, - l = m_editing.select_line, - p = m_editing.select_paragraph, - b = m_editing.select_indented_block, - s = m_editing.select_scope, - g = { m_editing.grow_selection, 1 }, - c = any_char_mt(m_editing.select_enclosed), - } - - -- Search - keys.cf = gui.find.focus -- find/replace - keys['f3'] = gui.find.find_next - -- Find Next is an when find pane is focused. - -- Find Prev is ap when find pane is focused. - -- Replace is ar when find pane is focused. - keys.cF = gui.find.find_incremental - -- Find in Files is ai when find pane is focused. - -- TODO: { gui.find.goto_file_in_list, true } - -- TODO: { gui.find.goto_file_in_list, false } - keys.cg = m_editing.goto_line - - -- Tools - keys['f2'] = gui.command_entry.focus - -- Run - keys.cr = m_textadept.run.run - keys.cR = m_textadept.run.compile - keys.ar = m_textadept.filter_through.filter_through - -- Snippets - keys['\t'] = m_textadept.snippets._insert - keys['s\t'] = m_textadept.snippets._previous - keys.cai = m_textadept.snippets._cancel_current - keys.ai = m_textadept.snippets._select - - -- Buffers - keys.cb = gui.switch_buffer - keys['c\t'] = { _view.goto_buffer, _view, 1, false } - keys['cs\t'] = { _view.goto_buffer, _view, -1, false } - keys.cB = { - e = { toggle_setting, 'view_eol' }, - w = { toggle_setting, 'wrap_mode' }, - i = { toggle_setting, 'indentation_guides' }, - ['\t'] = { toggle_setting, 'use_tabs' }, - [' '] = { toggle_setting, 'view_ws' }, - v = { toggle_setting, 'virtual_space_options', 2 }, - } - keys.cl = m_textadept.mime_types.select_lexer - keys['f5'] = { _buffer.colourise, _buffer, 0, -1 } - - -- Views - keys.cav = { - n = { gui.goto_view, 1, false }, - p = { gui.goto_view, -1, false }, - S = { _view.split, _view }, -- vertical - s = { _view.split, _view, false }, -- horizontal - w = function() view:unsplit() return true end, - W = function() while view:unsplit() do end end, - -- TODO: function() view.size = view.size + 10 end - -- TODO: function() view.size = view.size - 10 end - } - keys.c0 = function() buffer.zoom = 0 end - - -- Miscellaneous not in standard menu. - keys.ao = io.open_recent_file - keys.caI = show_style - -else - -- Mac OSX key commands - - --[[ - C: J M U W X Z - A: D E H J K L T U Y - CS: C D G H I J K L M O Q S T U V W X Y Z - SA: A C D H I J K L M N O Q R T U V X Y - CA: A C E J K L M N O Q S U V W X Y Z - CSA: A C D E H J K L M N O P Q R S T U V W X Y Z - ]]-- - - keys.clear_sequence = 'aesc' - - -- File - keys.an = new_buffer - keys.ao = io.open_file - -- TODO: _buffer.reload - keys.as = _buffer.save - keys.aS = _buffer.save_as - keys.aw = _buffer.close - keys.aW = { io.close_all } - -- TODO: m_textadept.session.load after prompting with open dialog - -- TODO: m_textadept.session.save after prompting with save dialog - keys.aq = quit - - -- Edit - local m_editing = m_textadept.editing - keys.az = _buffer.undo - keys.aZ = _buffer.redo - keys.ax = _buffer.cut - keys.ac = _buffer.copy - keys.av = _buffer.paste - -- Delete is delete. - keys.aa = _buffer.select_all - keys.cm = m_editing.match_brace - keys.aE = { m_editing.match_brace, 'select' } - keys.esc = { m_editing.autocomplete_word, '%w_' } - keys.cq = m_editing.block_comment - -- TODO: { m_editing.current_word, 'delete' } - keys.cat = m_editing.highlight_word - keys.ct = m_editing.transpose_chars - -- TODO: m_editing.convert_indentation - keys.cc = { -- enClose in... - t = enclose_in_tag, - T = { m_editing.enclose, '<', ' />' }, - ['"'] = { m_editing.enclose, '"', '"' }, - ["'"] = { m_editing.enclose, "'", "'" }, - ['('] = { m_editing.enclose, '(', ')' }, - ['['] = { m_editing.enclose, '[', ']' }, - ['{'] = { m_editing.enclose, '{', '}' }, - c = any_char_mt(m_editing.enclose), - } - keys.cs = { -- select in... - t = { m_editing.select_enclosed, '>', '<' }, - ['"'] = { m_editing.select_enclosed, '"', '"' }, - ["'"] = { m_editing.select_enclosed, "'", "'" }, - ['('] = { m_editing.select_enclosed, '(', ')' }, - ['['] = { m_editing.select_enclosed, '[', ']' }, - ['{'] = { m_editing.select_enclosed, '{', '}' }, - w = { m_editing.current_word, 'select' }, - l = m_editing.select_line, - p = m_editing.select_paragraph, - b = m_editing.select_indented_block, - s = m_editing.select_scope, - g = { m_editing.grow_selection, 1 }, - c = any_char_mt(m_editing.select_enclosed), - } - - -- Search - keys.af = gui.find.focus -- find/replace - keys.ag = gui.find.find_next - keys.aG = gui.find.find_prev - keys.ar = gui.find.replace - keys.ai = gui.find.find_incremental - keys.aF = function() - gui.find.in_files = true - gui.find.focus() - end - keys.cag = { gui.find.goto_file_in_list, true } - keys.caG = { gui.find.goto_file_in_list, false } - keys.cg = m_editing.goto_line - - -- Tools - keys['f2'] = gui.command_entry.focus - -- Run - keys.cr = { m_textadept.run.run } - keys.cR = { m_textadept.run.compile } - keys.car = { m_textadept.filter_through.filter_through } - -- Snippets - keys['\t'] = m_textadept.snippets._insert - keys['s\t'] = m_textadept.snippets._previous - keys.cai = m_textadept.snippets._cancel_current - keys.ci = m_textadept.snippets._select - - -- Buffers - keys.ab = gui.switch_buffer - keys['c\t'] = { _view.goto_buffer, _view, 1, false } - keys['cs\t'] = { _view.goto_buffer, _view, -1, false } - keys.aB = { - e = { toggle_setting, 'view_eol' }, - w = { toggle_setting, 'wrap_mode' }, - i = { toggle_setting, 'indentation_guides' }, - ['\t'] = { toggle_setting, 'use_tabs' }, - [' '] = { toggle_setting, 'view_ws' }, - v = { toggle_setting, 'virtual_space_options', 2 }, - } - keys.cl = m_textadept.mime_types.select_lexer - keys['f5'] = { _buffer.colourise, _buffer, 0, -1 } - - -- Views - keys.cv = { - n = { gui.goto_view, 1, false }, - p = { gui.goto_view, -1, false }, - S = { _view.split, _view }, -- vertical - s = { _view.split, _view, false }, -- horizontal - w = function() view:unsplit() return true end, - W = function() while view:unsplit() do end end, - -- TODO: function() view.size = view.size + 10 end - -- TODO: function() view.size = view.size - 10 end - } - keys.c0 = function() buffer.zoom = 0 end - - -- Miscellaneous not in standard menu. - keys.co = io.open_recent_file - keys.caI = show_style - - -- Movement/selection commands - keys.cf = _buffer.char_right - keys.cF = _buffer.char_right_extend - keys.caf = _buffer.word_right - keys.caF = _buffer.word_right_extend - keys.cb = _buffer.char_left - keys.cB = _buffer.char_left_extend - keys.cab = _buffer.word_left - keys.caB = _buffer.word_left_extend - keys.cn = _buffer.line_down - keys.cN = _buffer.line_down_extend - keys.cp = _buffer.line_up - keys.cP = _buffer.line_up_extend - keys.ca = _buffer.vc_home - keys.cA = _buffer.home_extend - keys.ce = _buffer.line_end - keys.cE = _buffer.line_end_extend - keys.cah = _buffer.del_word_left - keys.cd = _buffer.clear - keys.cad = _buffer.del_word_right - keys.ck = function() +if OSX then + -- See keys.osx.conf for unassigned keys. + keys.mk = function() buffer:line_end_extend() buffer:cut() end - keys.cy = _buffer.paste + 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 end diff --git a/modules/textadept/keys.osx.conf b/modules/textadept/keys.osx.conf new file mode 100644 index 00000000..22877373 --- /dev/null +++ b/modules/textadept/keys.osx.conf @@ -0,0 +1,149 @@ +% 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 D E ~ JkK ~M N p ~ tT U V XyY ) ] } * ~~\n~~ +% ca: aAbBcC~DeE F ~HiIjJkK L~MnN pPq~rRsStT UvVwWxXyYzZ_"'()[]{}<>*+-/= \n~~ +% m: cC D gG Hi J K L oO qQ uUv 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 +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 = cd +gtk-delete = +gtk-select-all = ca +Match Brace = mm +Select to Brace = mM +Complete Word = mesc +Delete Word = +Highlight Word = cH +Complete Symbol = aesc +Show Documentation = mh +Toggle Block Comment = c/ +Transpose Characters = mt +Join Lines = mj +Convert Indentation = +% Selection +% Enclose In... +HTML Tags = m< +HTML Single Tag = m> +Double Quotes = m" +Single Quotes = m' +Parentheses = m( +Brackets = m[ +Braces = m{ +Grow Selection = c+ +Shrink Selection = c_ +% Select In... +Between Tags = c< +HTML Tag = c> +Double Quote = c" +Single Quote = c' +Parenthesis = c( +Bracket = c[ +Brace = c{ +Word = +Line = cL +Paragraph = cP +Indented Block = cI +Style = + +% 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 +Run = mr +Compile = mR +Filter Through = c| +% Snippets +Expand = \t +Insert... = a\t +Previous Placeholder = s\t +Cancel = as\t +% Bookmark +Toggle on Current Line = cf2 +Clear All = csf2 +Next = f2 +Previous = sf2 +Goto Bookmark... = af2 +% Snapopen +User Home = cu +Textadept Home = cau +Current Directory = caO +Show Style = ci + +% Buffer +Next Buffer = m` +Previous Buffer = m~ +Switch Buffer = cb +Toggle View EOL = m\n +Toggle Wrap Mode = m\\ +Toggle Show Indent Guides = mI +Toggle Use Tabs = mT +Toggle View Whitespace = m\s +Toggle Virtual Space = mV +% EOL Mode +CRLF = +CR = +LF = +% Encoding +UTF-8 = +ASCII = +ISO-8859-1 = +MacRoman = +UTF-16 = +Select Lexer... = cal +Refresh Syntax Highlighting = f5 + +% View +Next View = m\t +Previous View = ms\t +Split Vertical = mS +Split Horizontal = ms +Unsplit = mw +Unsplit All = mW +Grow View = m+ m= +Shrink View = m- +Zoom In = c= +Zoom Out = c- +Reset Zoom = c0 + +% Help +Manual = f1 +LuaDoc = sf1 +gtk-about = diff --git a/modules/textadept/menu.lua b/modules/textadept/menu.lua index 03277491..38fb8e9e 100644 --- a/modules/textadept/menu.lua +++ b/modules/textadept/menu.lua @@ -1,5 +1,5 @@ -- Copyright 2007-2011 Mitchell mitchell<att>caladbolg.net. See LICENSE. --- Modified by Robert Gieseke. +-- Contributions from Robert Gieseke. local L = locale.localize local events = events @@ -7,6 +7,9 @@ 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. module('_m.textadept.menu', package.seeall) @@ -14,6 +17,28 @@ local _buffer, _view = buffer, view local m_textadept, m_editing = _m.textadept, _m.textadept.editing local SEPARATOR = { 'separator' } +-- 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 + end +end +f:close() + local function set_encoding(encoding) buffer:set_encoding(encoding) events.emit(events.UPDATE_UI) -- for updating statusbar @@ -59,15 +84,15 @@ end -- @name menubar menubar = { { title = L('File'), - { 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 }, + { L('gtk-new'), new_buffer, K['gtk-new'] }, + { L('gtk-open'), io.open_file, K['gtk-open'] }, + { L('Open Recent...'), io.open_recent_file, K['Open Recent...'] }, + { L('Reload'), _buffer.reload, K['Reload'] }, + { L('gtk-save'), _buffer.save, K['gtk-save'] }, + { L('gtk-save-as'), _buffer.save_as, K['gtk-save-as'] }, SEPARATOR, - { L('gtk-close'), _buffer.close }, - { L('Close All'), io.close_all }, + { L('gtk-close'), _buffer.close, K['gtk-close'] }, + { L('Close All'), io.close_all, K['Close All'] }, SEPARATOR, { L('Load Session...'), function() local session_file = _SESSIONFILE or '' @@ -81,7 +106,7 @@ menubar = { if #utf8_filename > 0 then _m.textadept.session.load(utf8_filename:iconv(_CHARSET, 'UTF-8')) end - end }, + end, K['Load Session...'] }, { L('Save Session...'), function() local session_file = _SESSIONFILE or '' local utf8_filename = gui.dialog('filesave', @@ -94,38 +119,44 @@ menubar = { if #utf8_filename > 0 then _m.textadept.session.save(utf8_filename:iconv(_CHARSET, 'UTF-8')) end - end }, + end, K['Save Session...'] }, SEPARATOR, - { L('gtk-quit'), quit }, + { L('gtk-quit'), quit, K['gtk-quit'] }, }, { title = L('Edit'), - { L('gtk-undo'), _buffer.undo }, - { L('gtk-redo'), _buffer.redo }, + { L('gtk-undo'), _buffer.undo, K['gtk-undo'] }, + { L('gtk-redo'), _buffer.redo, K['gtk-redo'] }, SEPARATOR, - { L('gtk-cut'), _buffer.cut }, - { L('gtk-copy'), _buffer.copy }, - { L('gtk-paste'), _buffer.paste }, - { L('Duplicate'), _buffer.line_duplicate }, - { L('gtk-delete'), _buffer.clear }, - { L('gtk-select-all'), _buffer.select_all }, + { L('gtk-cut'), _buffer.cut, K['gtk-cut'] }, + { L('gtk-copy'), _buffer.copy, K['gtk-copy'] }, + { L('gtk-paste'), _buffer.paste, K['gtk-paste'] }, + { L('Duplicate'), _buffer.line_duplicate, K['Duplicate'] }, + { L('gtk-delete'), _buffer.clear, K['gtk-delete'] }, + { L('gtk-select-all'), _buffer.select_all, K['gtk-select-all'] }, SEPARATOR, - { L('Match Brace'), m_editing.match_brace }, - { L('Select to Brace'), { m_editing.match_brace, 'select' } }, - { L('Complete Word'), { m_editing.autocomplete_word, '%w_' } }, - { L('Delete Word'), { m_editing.current_word, 'delete' } }, - { L('Highlight Word'), m_editing.highlight_word }, + { L('Match Brace'), m_editing.match_brace, K['Match Brace'] }, + { L('Select to Brace'), { m_editing.match_brace, 'select' }, + K['Select to Brace'] }, + { L('Complete Word'), { m_editing.autocomplete_word, '%w_' }, + K['Complete Word'] }, + { L('Delete Word'), { m_editing.current_word, 'delete' }, + K['Delete Word'] }, + { L('Highlight Word'), m_editing.highlight_word, K['Highlight Word'] }, { L('Complete Symbol'), function() local m = _m[buffer:get_lexer()] - if m and m.adeptsense then m.adeptsense.sense:complete() end - end }, + if m and m.sense then m.sense:complete() end + end, K['Complete Symbol'] }, { L('Show Documentation'), function() local m = _m[buffer:get_lexer()] - if m and m.adeptsense then m.adeptsense.sense:show_apidoc() end - end }, - { L('Toggle Block Comment'), m_editing.block_comment }, - { L('Transpose Characters'), m_editing.transpose_chars }, - { L('Join Lines'), m_editing.join_lines }, - { L('Convert Indentation'), m_editing.convert_indentation }, + if m and m.sense then m.sense:show_apidoc() end + end, K['Show Documentation'] }, + { L('Toggle Block Comment'), m_editing.block_comment, + K['Toggle Block Comment'] }, + { L('Transpose Characters'), m_editing.transpose_chars, + K['Transpose Characters'] }, + { L('Join Lines'), m_editing.join_lines, K['Join Lines'] }, + { L('Convert Indentation'), m_editing.convert_indentation, + K['Convert Indentation'] }, { title = L('Selection'), { title = L('Enclose in...'), { L('HTML Tags'), function() @@ -135,90 +166,128 @@ menubar = { 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('HTML Single Tag'), { m_editing.enclose, '<', ' />' } }, - { L('Double Quotes'), { m_editing.enclose, '"', '"' } }, - { L('Single Quotes'), { m_editing.enclose, "'", "'" } }, - { L('Parentheses'), { m_editing.enclose, '(', ')' } }, - { L('Brackets'), { m_editing.enclose, '[', ']' } }, - { L('Braces'), { m_editing.enclose, '{', '}' } }, + end, K['HTML Tags'] }, + { L('HTML Single Tag'), { m_editing.enclose, '<', ' />' }, + K['HTML Single Tag'] }, + { L('Double Quotes'), { m_editing.enclose, '"', '"' }, + K['Double Quotes'] }, + { L('Single Quotes'), { m_editing.enclose, "'", "'" }, + K['Single Quotes'] }, + { L('Parentheses'), { m_editing.enclose, '(', ')' }, K['Parentheses'] }, + { L('Brackets'), { m_editing.enclose, '[', ']' }, K['Brackets'] }, + { L('Braces'), { m_editing.enclose, '{', '}' }, K['Braces'] }, }, - { L('Grow'), { m_editing.grow_selection, 1 } }, + { L('Grow Selection'), { m_editing.grow_selection, 1 }, + K['Grow Selection'] }, + { L('Shrink Selection'), { m_editing.grow_selection, -1 }, + K['Shrink Selection'] }, }, { title = L('Select in...'), - { L('HTML Tag'), { m_editing.select_enclosed, '>', '<' } }, - { L('Double Quote'), { m_editing.select_enclosed, '"', '"' } }, - { L('Single Quote'), { m_editing.select_enclosed, "'", "'" } }, - { L('Parenthesis'), { m_editing.select_enclosed, '(', ')' } }, - { L('Bracket'), { m_editing.select_enclosed, '[', ']' } }, - { L('Brace'), { m_editing.select_enclosed, '{', '}' } }, - { L('Word'), { m_editing.current_word, 'select' } }, - { L('Line'), m_editing.select_line }, - { L('Paragraph'), m_editing.select_paragraph }, - { L('Indented Block'), m_editing.select_indented_block }, - { L('Scope'), m_editing.select_scope }, + { L('Between Tags'), { m_editing.select_enclosed, '>', '<' }, + K['Between Tags'] }, + { L('HTML Tag'), { m_editing.select_enclosed, '<', '>' }, + K['HTML Tag'] }, + { L('Double Quote'), { m_editing.select_enclosed, '"', '"' }, + K['Double Quote'] }, + { L('Single Quote'), { m_editing.select_enclosed, "'", "'" }, + K['Single Quote'] }, + { L('Parenthesis'), { m_editing.select_enclosed, '(', ')' }, + K['Parenthesis'] }, + { L('Bracket'), { m_editing.select_enclosed, '[', ']' }, K['Bracket'] }, + { L('Brace'), { m_editing.select_enclosed, '{', '}' }, K['Brace'] }, + { L('Word'), { m_editing.current_word, 'select' }, K['Word'] }, + { L('Line'), m_editing.select_line, K['Line'] }, + { L('Paragraph'), m_editing.select_paragraph, K['Paragraph'] }, + { L('Indented Block'), m_editing.select_indented_block, + K['Indented Block'] }, + { L('Style'), m_editing.select_style, K['Style'] }, }, }, { title = L('Search'), - { L('gtk-find'), gui.find.focus }, - { L('Find Next'), gui.find.call_find_next }, - { L('Find Previous'), gui.find.call_find_prev }, - { L('Replace'), gui.find.call_replace }, - { L('Replace All'), gui.find.call_replace_all }, - { L('Find Incremental'), gui.find.find_incremental }, + { L('gtk-find'), gui.find.focus, K['gtk-find'] }, + { L('Find Next'), gui.find.find_next, K['Find Next'] }, + { L('Find Previous'), gui.find.find_prev, K['Find Previous'] }, + { L('Replace'), gui.find.replace, K['Replace'] }, + { L('Replace All'), gui.find.replace_all, K['Replace All'] }, + { L('Find Incremental'), gui.find.find_incremental, K['Find Incremental'] }, SEPARATOR, { L('Find in Files'), function() gui.find.in_files = true gui.find.focus() - end }, - { L('Goto Next File Found'), { gui.find.goto_file_in_list, true } }, - { L('Goto Previous File Found'), { gui.find.goto_file_in_list, false } }, + end, K['Find in Files'] }, + { L('Goto Next File Found'), { gui.find.goto_file_in_list, true }, + K['Goto Next File Found'] }, + { L('Goto Previous File Found'), { gui.find.goto_file_in_list, false }, + K['Goto Previous File Found'] }, SEPARATOR, - { L('gtk-jump-to'), m_editing.goto_line }, + { L('gtk-jump-to'), m_editing.goto_line, K['gtk-jump-to'] }, }, { title = L('Tools'), - { L('Command Entry'), gui.command_entry.focus }, + { L('Command Entry'), gui.command_entry.focus, K['Command Entry'] }, SEPARATOR, - { L('Run'), m_textadept.run.run }, - { L('Compile'), m_textadept.run.compile }, - { L('Filter Through'), _m.textadept.filter_through.filter_through }, + { L('Run'), m_textadept.run.run, K['Run'] }, + { L('Compile'), m_textadept.run.compile, K['Compile'] }, + { L('Filter Through'), _m.textadept.filter_through.filter_through, + K['Filter Through'] }, SEPARATOR, { title = L('Snippets'), - { L('Expand'), m_textadept.snippets._insert }, - { L('Insert...'), m_textadept.snippets._select }, - { L('Previous Placeholder'), m_textadept.snippets._previous }, - { L('Cancel'), m_textadept.snippets._cancel_current }, + { L('Expand'), m_textadept.snippets._insert, K['Expand'] }, + { L('Insert...'), m_textadept.snippets._select, K['Insert...'] }, + { L('Previous Placeholder'), m_textadept.snippets._previous, + K['Previous Placeholder'] }, + { L('Cancel'), m_textadept.snippets._cancel_current, K['Cancel'] }, }, { title = L('Bookmark'), - { L('Toggle on Current Line'), m_textadept.bookmarks.toggle }, - { L('Clear All'), m_textadept.bookmarks.clear }, - { L('Next'), m_textadept.bookmarks.goto_next }, - { L('Previous'), m_textadept.bookmarks.goto_prev }, - { L('Goto Bookmark...'), m_textadept.bookmarks.goto }, + { L('Toggle on Current Line'), m_textadept.bookmarks.toggle, + K['Toggle on Current Line'] }, + { L('Clear All'), m_textadept.bookmarks.clear, K['Clear All'] }, + { L('Next'), m_textadept.bookmarks.goto_next, K['Next'] }, + { L('Previous'), m_textadept.bookmarks.goto_prev, K['Previous'] }, + { L('Goto Bookmark...'), m_textadept.bookmarks.goto, + K['Goto Bookmark...'] }, }, { title = L('Snapopen'), - { L('User Home'), { m_textadept.snapopen.open, _USERHOME } }, - { L('Textadept Home'), { m_textadept.snapopen.open, _HOME } }, + { L('User Home'), { m_textadept.snapopen.open, _USERHOME }, + K['User Home'] }, + { L('Textadept Home'), { m_textadept.snapopen.open, _HOME }, + K['Textadept Home'] }, { L('Current Directory'), function() if buffer.filename then m_textadept.snapopen.open(buffer.filename:match('^(.+)[/\\]')) end - end }, + end, K['Current Directory'] }, }, + SEPARATOR, + { L('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 , K['Show Style'] }, }, { title = L('Buffer'), - { L('Next Buffer'), { _view.goto_buffer, _view, 1, false } }, - { L('Previous Buffer'), { _view.goto_buffer, _view, -1, false } }, - { L('Switch Buffer'), gui.switch_buffer }, + { L('Next Buffer'), { _view.goto_buffer, _view, 1, false }, + K['Next Buffer'] }, + { L('Previous Buffer'), { _view.goto_buffer, _view, -1, false }, + K['Previous Buffer'] }, + { L('Switch Buffer'), gui.switch_buffer, K['Switch Buffer'] }, SEPARATOR, - { L('Toggle View EOL'), { toggle_setting, 'view_eol' } }, - { L('Toggle Wrap Mode'), { toggle_setting, 'wrap_mode' } }, - { L('Toggle Show Indentation Guides'), - { toggle_setting, 'indentation_guides' } }, - { L('Toggle Use Tabs'), { toggle_setting, 'use_tabs' } }, - { L('Toggle View Whitespace'), { toggle_setting, 'view_ws' } }, + { L('Toggle View EOL'), { toggle_setting, 'view_eol' }, + K['Toggle View EOL'] }, + { L('Toggle Wrap Mode'), { toggle_setting, 'wrap_mode' }, + K['Toggle Wrap Mode'] }, + { L('Toggle Show Indent Guides'), + { toggle_setting, 'indentation_guides' }, + K['Toggle Show Indent Guides'] }, + { L('Toggle Use Tabs'), { toggle_setting, 'use_tabs' }, + K['Toggle Use Tabs'] }, + { L('Toggle View Whitespace'), { toggle_setting, 'view_ws' }, + K['Toggle View Whitespace'] }, { L('Toggle Virtual Space'), - { toggle_setting, 'virtual_space_options', 2} }, + { toggle_setting, 'virtual_space_options', 2 }, + K['Toggle Virtual Space'] }, SEPARATOR, { title = L('Indentation'), { '2', { set_indentation, 2 } }, @@ -227,56 +296,59 @@ menubar = { { '8', { set_indentation, 8 } }, }, { title = L('EOL Mode'), - { L('CRLF'), { set_eol_mode, 0 } }, - { L('CR'), { set_eol_mode, 1 } }, - { L('LF'), { set_eol_mode, 2 } }, + { L('CRLF'), { set_eol_mode, 0 }, K['CRLF'] }, + { L('CR'), { set_eol_mode, 1 }, K['CR'] }, + { L('LF'), { set_eol_mode, 2 }, K['LF'] }, }, { title = L('Encoding'), - { L('UTF-8'), { set_encoding, 'UTF-8' } }, - { L('ASCII'), { set_encoding, 'ASCII' } }, - { L('ISO-8859-1'), { set_encoding, 'ISO-8859-1' } }, - { L('MacRoman'), { set_encoding, 'MacRoman' } }, - { L('UTF-16'), { set_encoding, 'UTF-16LE' } }, + { L('UTF-8'), { set_encoding, 'UTF-8' }, K['UTF-8'] }, + { L('ASCII'), { set_encoding, 'ASCII' }, K['ASCII'] }, + { L('ISO-8859-1'), { set_encoding, 'ISO-8859-1' }, K['ISO-8859-1'] }, + { L('MacRoman'), { set_encoding, 'MacRoman' }, K['MacRoman'] }, + { L('UTF-16'), { set_encoding, 'UTF-16LE' }, K['UTF-16'] }, }, SEPARATOR, + { L('Select Lexer...'), m_textadept.mime_types.select_lexer, + K['Select Lexer...'] }, { L('Refresh Syntax Highlighting'), - { _buffer.colourise, _buffer, 0, -1 } }, + { _buffer.colourise, _buffer, 0, -1 }, K['Refresh Syntax Highlighting'] }, }, { title = L('View'), - { L('Next View'), { gui.goto_view, 1, false } }, - { L('Previous View'), { gui.goto_view, -1, false } }, + { L('Next View'), { gui.goto_view, 1, false }, K['Next View'] }, + { L('Previous View'), { gui.goto_view, -1, false }, K['Previous View'] }, SEPARATOR, - { L('Split Vertical'), { _view.split, _view } }, - { L('Split Horizontal'), { _view.split, _view, false } }, - { L('Unsplit'), function() view:unsplit() end }, - { L('Unsplit All'), function() while view:unsplit() do end end }, + { L('Split Vertical'), { _view.split, _view }, K['Split Vertical'] }, + { L('Split Horizontal'), { _view.split, _view, false }, + K['Split Horizontal'] }, + { L('Unsplit'), function() view:unsplit() end, K['Unsplit'] }, + { L('Unsplit All'), function() while view:unsplit() do end end, + K['Unsplit All'] }, SEPARATOR, - { L('Grow'), - function() if view.size then view.size = view.size + 10 end end - }, - { L('Shrink'), - function() if view.size then view.size = view.size - 10 end end - }, + { L('Grow View'), + function() if view.size then view.size = view.size + 10 end end, + K['Grow View'] }, + { L('Shrink View'), + function() if view.size then view.size = view.size - 10 end end, + K['Shrink View'] }, SEPARATOR, - { L('Zoom In'), function() buffer.zoom = buffer.zoom + 1 end }, - { L('Zoom Out'), function() buffer.zoom = buffer.zoom - 1 end }, - { L('Reset Zoom'), function() buffer.zoom = 0 end }, + { L('Zoom In'), _buffer.zoom_in, K['Zoom In'] }, + { L('Zoom Out'), _buffer.zoom_out, K['Zoom Out'] }, + { L('Reset Zoom'), function() buffer.zoom = 0 end, K['Reset Zoom'] }, }, -- Lexer menu inserted here { title = L('Help'), { L('Manual'), - { open_webpage, _HOME..'/doc/manual/1_Introduction.html' } }, - { L('LuaDoc'), { open_webpage, _HOME..'/doc/index.html' } }, + { open_webpage, _HOME..'/doc/manual/1_Introduction.html' }, K['Manual'] }, + { L('LuaDoc'), { open_webpage, _HOME..'/doc/index.html' }, K['LuaDoc'] }, SEPARATOR, { L('gtk-about'), { gui.dialog, 'ok-msgbox', '--title', 'Textadept', '--informative-text', - _RELEASE, '--no-cancel' } - }, + _RELEASE, '--no-cancel' }, K['gtk-about'] }, }, } local lexer_menu = { title = L('Lexers') } for _, lexer in ipairs(_m.textadept.mime_types.lexers) do - lexer_menu[#lexer_menu + 1] = { lexer:gsub('_', '__'), { set_lexer, lexer} } + lexer_menu[#lexer_menu + 1] = { lexer:gsub('_', '__'), { set_lexer, lexer } } end table.insert(menubar, #menubar, lexer_menu) -- before 'Help' @@ -300,6 +372,7 @@ local menu_actions = {} local contextmenu_actions = {} -- Creates a menu suitable for gui.gtkmenu from the menu table format. +-- Also assigns key commands. -- @param menu The menu to create a gtkmenu from. -- @return gtkmenu that can be passed to gui.gtkmenu. local function read_menu_table(menu) @@ -309,9 +382,14 @@ 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 menu_id = #menu_actions + 1 - gtkmenu[#gtkmenu + 1] = { menuitem[1], menu_id } - if menuitem[2] then menu_actions[menu_id] = menuitem[2] end + local key, mods = keys.get_gdk_key(k and k[1]) + 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 end end return gtkmenu @@ -323,8 +401,11 @@ end -- entry is another table that corresponds to a particular menu. A menu can -- have a 'title' key with string value. Each menu item is either a submenu -- (another menu table) or a table consisting of two items: string menu text --- and an action table just like `keys`'s action table. If the menu text is --- 'separator', a menu separator is created and no action table is required. +-- and a function or action table just like in `keys`. The table can +-- optionally contain 2 more number values: a GDK keycode and modifier mask +-- for setting a menu accelerator. If the menu text is 'separator', a menu +-- separator is created and no action table is required. +-- @see keys.get_gdk_key function set_menubar(menubar) menu_actions = {} local _menubar = {} @@ -337,7 +418,7 @@ end --- -- Sets gui.context_menu from the given menu table. -- @param menu_table The menu table to create the context menu from. Each table --- entry is either a submenu or menu text and an action table. +-- entry is either a submenu or menu text and a function or action table. -- @see set_menubar function set_contextmenu(menu_table) context_actions = {} |