diff options
author | 2010-11-23 19:09:21 -0500 | |
---|---|---|
committer | 2010-11-23 19:09:21 -0500 | |
commit | 5f29cb8190dbee6bb10b4eb904c8c39750de352d (patch) | |
tree | ef9d178b4834087d829702d4894ec3b9330f3c0b /modules | |
parent | 1f3bbef0357b89f82a65d1e67caa1980eee3f40b (diff) | |
download | textadept-5f29cb8190dbee6bb10b4eb904c8c39750de352d.tar.gz textadept-5f29cb8190dbee6bb10b4eb904c8c39750de352d.zip |
Code cleanup.
Also modified the editing module's enclose() and select_enclosed() functions.
Diffstat (limited to 'modules')
-rw-r--r-- | modules/lua/commands.lua | 20 | ||||
-rw-r--r-- | modules/textadept/bookmarks.lua | 5 | ||||
-rw-r--r-- | modules/textadept/command_entry.lua | 20 | ||||
-rw-r--r-- | modules/textadept/editing.lua | 159 | ||||
-rw-r--r-- | modules/textadept/find.lua | 50 | ||||
-rw-r--r-- | modules/textadept/keys.lua | 241 | ||||
-rw-r--r-- | modules/textadept/menu.lua | 50 | ||||
-rw-r--r-- | modules/textadept/mime_types.lua | 1 | ||||
-rw-r--r--[-rwxr-xr-x] | modules/textadept/run.lua | 28 | ||||
-rw-r--r--[-rwxr-xr-x] | modules/textadept/session.lua | 12 | ||||
-rw-r--r--[-rwxr-xr-x] | modules/textadept/snapopen.lua | 6 | ||||
-rw-r--r--[-rwxr-xr-x] | modules/textadept/snippets.lua | 3 |
12 files changed, 263 insertions, 332 deletions
diff --git a/modules/lua/commands.lua b/modules/lua/commands.lua index e8c11773..4857126c 100644 --- a/modules/lua/commands.lua +++ b/modules/lua/commands.lua @@ -124,7 +124,6 @@ function goto_required() end if not file then return end file = file:sub(2, -2):gsub('%.', '/') - local lfs = require 'lfs' for path in package.path:gmatch('[^;]+') do path = path:gsub('?', file) if lfs.attributes(path) then @@ -137,6 +136,7 @@ end events.connect('file_before_save', function() -- show syntax errors as annotations if buffer:get_lexer() == 'lua' then + local buffer = buffer buffer:annotation_clear_all() local text = buffer:get_text() local _, err = loadstring(text) @@ -169,7 +169,7 @@ local api_files = { ['view'] = _HOME..'/core/.view.luadoc', } -- Add API for loaded textadept modules. -for p, _ in pairs(package.loaded) do +for p in pairs(package.loaded) do if p:find('^_m%.textadept%.') then api_files[p] = _HOME..'/modules/textadept/'..p:match('[^%.]+$')..'.lua' end @@ -181,11 +181,9 @@ for _, m in ipairs(lua) do end api_files[''] = _HOME..'/modules/lua/api/_G.luadoc' -local lfs = require 'lfs' - -- Load API. local apis = {} -local current_doc = '' +local current_doc = {} local f_args = {} for word, api_file in pairs(api_files) do if lfs.attributes(api_file) then @@ -204,16 +202,17 @@ for word, api_file in pairs(api_files) do local funcs = apis[word].funcs funcs[#funcs + 1] = n..'?1' if f and #current_doc > 0 then - f = f..current_doc - current_doc = '' + table.insert(current_doc, 1, f) + f = table.concat(current_doc, '\n') + current_doc = {} end local c = line:find(':') and ':' or '.' if word == '' then c = '' end f_args[word..c..n] = f elseif line:match('^%-%-%-? (.+)$') then - current_doc = current_doc..'\n'..line:match('^%-%-%-? (.+)$') + current_doc[#current_doc + 1] = line:match('^%-%-%-? (.+)$') elseif #current_doc > 0 then - current_doc = '' + current_doc = {} end end table.sort(apis[word].fields) @@ -229,6 +228,7 @@ local v_xpm = '/* XPM */\nstatic char *field[] = {\n/* columns rows colors chars -- @param pos Optional position to start from. -- @return word. local function prev_word(patt, pos) + local buffer = buffer local e = pos or buffer.current_pos - 1 local s = e - 1 while s >= 0 and string.char(buffer.char_at[s]):find(patt) do s = s - 1 end @@ -240,6 +240,7 @@ end -- @param completions Table of completions. -- @see buffer:auto_c_show. local function auto_c_show(len, completions) + local buffer = buffer buffer:clear_registered_images() buffer:register_image(1, f_xpm) buffer:register_image(2, v_xpm) @@ -267,6 +268,7 @@ if type(keys) == 'table' then }, ['\n'] = { try_to_autocomplete_end }, [not OSX and 'c\n' or 'esc'] = { function() -- complete API + local buffer = buffer local part = prev_word('[%w_]', buffer.current_pos) local pos = buffer.current_pos - #part - 1 if pos > 0 then diff --git a/modules/textadept/bookmarks.lua b/modules/textadept/bookmarks.lua index 581390d4..bc5eac9b 100644 --- a/modules/textadept/bookmarks.lua +++ b/modules/textadept/bookmarks.lua @@ -49,10 +49,7 @@ end --- -- Clears all bookmarks in the current buffer. -function clear() - local buffer = buffer - buffer:marker_delete_all(MARK_BOOKMARK) -end +function clear() buffer:marker_delete_all(MARK_BOOKMARK) end --- -- Goes to the next bookmark in the current buffer. diff --git a/modules/textadept/command_entry.lua b/modules/textadept/command_entry.lua index c18550e9..d8a48616 100644 --- a/modules/textadept/command_entry.lua +++ b/modules/textadept/command_entry.lua @@ -3,6 +3,9 @@ local locale = _G.locale +-- Environment for abbreviated commands. +-- @class table +-- @name env local env = setmetatable({}, { __index = function(t, k) local f = buffer[k] @@ -46,37 +49,38 @@ events.connect('command_entry_keypress', local path, o, prefix = substring:match('^([%w_.:]-)([.:]?)([%w_]*)$') local f, err = loadstring('return ('..path..')') if type(f) == "function" then setfenv(f, env) end - local ret, tbl = pcall(f) + local ok, tbl = pcall(f) local cmpls = {} - if not ret then -- shorthand notation + prefix = '^'..prefix + if not ok then -- shorthand notation for _, t in ipairs{ buffer, view, gui, _G } do for k in pairs(t) do - if type(k) == 'string' and k:find('^'..prefix) then + if type(k) == 'string' and k:find(prefix) then cmpls[#cmpls + 1] = k end end end for f in pairs(_SCINTILLA.functions) do - if f:find('^'..prefix) then cmpls[#cmpls + 1] = f end + if f:find(prefix) then cmpls[#cmpls + 1] = f end end for p in pairs(_SCINTILLA.properties) do - if p:find('^'..prefix) then cmpls[#cmpls + 1] = p end + if p:find(prefix) then cmpls[#cmpls + 1] = p end end else if type(tbl) ~= 'table' then return end for k in pairs(tbl) do - if type(k) == 'string' and k:find('^'..prefix) then + if type(k) == 'string' and k:find(prefix) then cmpls[#cmpls + 1] = k end end if path == 'buffer' then if o == ':' then for f in pairs(_SCINTILLA.functions) do - if f:find('^'..prefix) then cmpls[#cmpls + 1] = f end + if f:find(prefix) then cmpls[#cmpls + 1] = f end end else for p in pairs(_SCINTILLA.properties) do - if p:find('^'..prefix) then cmpls[#cmpls + 1] = p end + if p:find(prefix) then cmpls[#cmpls + 1] = p end end end end diff --git a/modules/textadept/editing.lua b/modules/textadept/editing.lua index c2b06ba0..12c72d05 100644 --- a/modules/textadept/editing.lua +++ b/modules/textadept/editing.lua @@ -52,28 +52,10 @@ INDIC_HIGHLIGHT_ALPHA = 100 -- @see block_comment comment_string = {} ---- --- Enclosures for enclosing or selecting ranges of text. --- Note chars and tag enclosures are generated at runtime. --- You can add entries to the table in language-specific modules and use the --- 'enclose' function in key commands. --- @class table --- @name enclosure --- @see enclose -enclosure = { - dbl_quotes = { left = '"', right = '"' }, - sng_quotes = { left = "'", right = "'" }, - parens = { left = '(', right = ')' }, - brackets = { left = '[', right = ']' }, - braces = { left = '{', right = '}' }, - chars = { left = ' ', right = ' ' }, - tags = { left = '>', right = '<' }, - tag = { left = ' ', right = ' ' }, - single_tag = { left = '<', right = ' />' } -} - -- Character matching. -- Used for auto-matching parentheses, brackets, braces, and quotes. +-- @class table +-- @name char_matches local char_matches = { [40] = ')', [91] = ']', [123] = '}', [39] = "'", [34] = '"' @@ -81,6 +63,8 @@ local char_matches = { -- Brace characters. -- Used for going to matching brace positions. +-- @class table +-- @name braces local braces = { -- () [] {} <> [40] = 1, [91] = 1, [123] = 1, [60] = 1, [41] = 1, [93] = 1, [125] = 1, [62] = 1, @@ -88,6 +72,8 @@ local braces = { -- () [] {} <> -- The current call tip. -- Used for displaying call tips. +-- @class table +-- @name current_call_tip local current_call_tip = {} events.connect('char_added', @@ -99,21 +85,20 @@ events.connect('char_added', events.connect('keypress', function(code, shift, control, alt) -- removes matched chars on backspace - if AUTOPAIR and code == 0xff08 and buffer.selections == 1 then - local buffer = buffer - local current_pos = buffer.current_pos - local c = buffer.char_at[current_pos - 1] - if char_matches[c] and - buffer.char_at[current_pos] == string.byte(char_matches[c]) then - buffer:clear() - end + if not AUTOPAIR or code ~= 0xff08 or buffer.selections ~= 1 then return end + local buffer = buffer + local current_pos = buffer.current_pos + local c = buffer.char_at[current_pos - 1] + if char_matches[c] and + buffer.char_at[current_pos] == string.byte(char_matches[c]) then + buffer:clear() end end) events.connect('update_ui', function() -- highlights matching braces - local buffer = buffer if not HIGHLIGHT_BRACES then return end + local buffer = buffer local current_pos = buffer.current_pos if braces[buffer.char_at[current_pos]] and buffer:get_style_name(buffer.style_at[current_pos]) == 'operator' then @@ -133,16 +118,14 @@ events.connect('char_added', if not AUTOINDENT or char ~= 10 then return end local buffer = buffer local anchor, caret = buffer.anchor, buffer.current_pos - local curr_line = buffer:line_from_position(caret) - local last_line = curr_line - 1 - while last_line >= 0 and #buffer:get_line(last_line) == 1 do - last_line = last_line - 1 - end - if last_line >= 0 then - local indentation = buffer.line_indentation[last_line] - local s = buffer.line_indent_position[curr_line] - buffer.line_indentation[curr_line] = indentation - local e = buffer.line_indent_position[curr_line] + local line = buffer:line_from_position(caret) + local pline = line - 1 + while pline >= 0 and #buffer:get_line(pline) == 1 do pline = pline - 1 end + if pline >= 0 then + local indentation = buffer.line_indentation[pline] + local s = buffer.line_indent_position[line] + buffer.line_indentation[line] = indentation + local e = buffer.line_indent_position[line] local diff = e - s if e > s then -- move selection on if anchor >= s then anchor = anchor + diff end @@ -179,6 +162,7 @@ end -- Pops up an autocompletion list for the current word based on other words in -- the document. -- @param word_chars String of chars considered to be part of words. +-- @return true if there were completions to show; false otherwise. function autocomplete_word(word_chars) local buffer = buffer local caret, length = buffer.current_pos, buffer.length @@ -241,7 +225,6 @@ end -- Goes to the requested line. -- @param line Optional line number to go to. function goto_line(line) - local buffer = buffer if not line then line = gui.dialog('standard-inputbox', '--title', L('Go To'), @@ -259,8 +242,8 @@ end -- Strips trailing whitespace off of every line, ensures an ending newline, and -- converts non-consistent EOLs. function prepare_for_save() - local buffer = buffer if not SAVE_STRIPS_WS then return end + local buffer = buffer buffer:begin_undo_action() -- Strip trailing whitespace. local lines = buffer.line_count @@ -304,27 +287,21 @@ end --- -- Transposes characters intelligently. --- If the caret is at the end of the current word, the two characters before --- the caret are transposed. Otherwise the characters to the left and right of --- the caret are transposed. +-- If the caret is at the end of a line, the two characters before the caret are +-- transposed. Otherwise, the characters to the left and right are. function transpose_chars() local buffer = buffer buffer:begin_undo_action() - local caret = buffer.current_pos - local char = buffer.char_at[caret - 1] + local pos = buffer.current_pos + local c1, c2 = buffer.char_at[pos - 1], buffer.char_at[pos] buffer:delete_back() - if caret > buffer.length or buffer.char_at[caret - 1] == 32 then - buffer:char_left() - else - buffer:char_right() - end - buffer:insert_text(-1, string.char(char)) + buffer:insert_text((c2 == 10 or c2 == 13) and pos - 2 or pos, string.char(c1)) buffer:end_undo_action() - buffer:goto_pos(caret) + buffer:goto_pos(pos) end --- --- Joins the current line with the line below, eliminating whitespace. +-- Joins the current line with the line below. function join_lines() local buffer = buffer buffer:line_end() @@ -334,77 +311,33 @@ function join_lines() buffer:lines_join() end --- Returns the number to the left of the caret. --- This is used for the enclose function. --- @see enclose -local function get_preceding_number() - local buffer = buffer - local caret = buffer.current_pos - local char = buffer.char_at[caret - 1] - local txt = '' - while tonumber(string.char(char)) do - txt = txt..string.char(char) - caret = caret - 1 - char = buffer.char_at[caret - 1] - end - return tonumber(txt) or 1, #txt -end - --- --- Encloses text in an enclosure set. +-- Encloses text within a given pair of strings. -- If text is selected, it is enclosed. Otherwise, the previous word is --- enclosed. The n previous words can be enclosed by appending n (a number) to --- the end of the last word. When enclosing with a character, append the --- character to the end of the word(s). To enclose previous word(s) with n --- characters, append n (a number) to the end of character set. --- Examples: --- enclose this2 -> 'enclose this' (enclose in sng_quotes) --- enclose this2**2 -> **enclose this** --- @param str The enclosure type in enclosure. --- @see enclosure --- @see get_preceding_number -function enclose(str) +-- enclosed. +-- @param left The left part of the enclosure. +-- @param right The right part of the enclosure. +function enclose(left, right) local buffer = buffer buffer:begin_undo_action() local txt = buffer:get_sel_text() - if txt == '' then - if str == 'chars' then - local num_chars, len_num_chars = get_preceding_number() - for i = 1, len_num_chars do buffer:delete_back() end - for i = 1, num_chars do buffer:char_left_extend() end - enclosure[str].left = buffer:get_sel_text() - enclosure[str].right = enclosure[str].left - buffer:delete_back() - end - local num_words, len_num_chars = get_preceding_number() - for i = 1, len_num_chars do buffer:delete_back() end - for i = 1, num_words do buffer:word_left_extend() end + if #txt == 0 then + buffer:word_left_extend() txt = buffer:get_sel_text() end - local len = 0 - if str == 'tag' then - enclosure[str].left = '<'..txt..'>' - enclosure[str].right = '</'..txt..'>' - len = #txt + 3 - txt = '' - end - local left = enclosure[str].left - local right = enclosure[str].right buffer:replace_sel(left..txt..right) - if str == 'tag' then buffer:goto_pos(buffer.current_pos - len) end buffer:end_undo_action() end --- --- Selects text in a specified enclosure. --- @param str The enclosure type in enclosure. --- @see enclosure -function select_enclosed(str) - if not str then return end +-- Selects text between a given pair of strings. +-- @param left The left part of the enclosure. +-- @param right The right part of the enclosure. +function select_enclosed(left, right) local buffer = buffer buffer:search_anchor() - local s = buffer:search_prev(0, enclosure[str].left) - local e = buffer:search_next(0, enclosure[str].right) + local s = buffer:search_prev(0, left) + local e = buffer:search_next(0, right) if s and e then buffer:set_sel(s + 1, e) end end @@ -424,7 +357,6 @@ end --- -- Selects the current line. function select_line() - local buffer = buffer buffer:home() buffer:line_end_extend() end @@ -433,7 +365,6 @@ end -- Selects the current paragraph. -- Paragraphs are delimited by two or more consecutive newlines. function select_paragraph() - local buffer = buffer buffer:para_up() buffer:para_down_extend() end @@ -467,7 +398,7 @@ function select_indented_block() end --- --- Selects all text with the same scope/style as under the caret. +-- Selects all text with the same style as under the caret. function select_scope() local buffer = buffer local start_pos = buffer.current_pos diff --git a/modules/textadept/find.lua b/modules/textadept/find.lua index db34358c..202f7b49 100644 --- a/modules/textadept/find.lua +++ b/modules/textadept/find.lua @@ -4,19 +4,19 @@ local L = _G.locale.localize local events = _G.events local find = gui.find -local lfs = require 'lfs' - local MARK_FIND = 0 local MARK_FIND_COLOR = 0x4D9999 local previous_view -- Text escape sequences with their associated characters. +-- @class table +-- @name escapes local escapes = { ['\\a'] = '\a', ['\\b'] = '\b', ['\\f'] = '\f', ['\\n'] = '\n', ['\\r'] = '\r', ['\\t'] = '\t', ['\\v'] = '\v', ['\\\\'] = '\\' } --- LuaDoc is in core/.find.luadoc +-- LuaDoc is in core/.find.luadoc. function find.find_in_files(utf8_dir) if not utf8_dir then utf8_dir = gui.dialog('fileselect', @@ -26,13 +26,12 @@ function find.find_in_files(utf8_dir) (buffer.filename or ''):match('^.+[/\\]') or '', '--no-newline') end - local text = find.find_entry_text if #utf8_dir > 0 then + local text = find.find_entry_text if not find.lua then text = text:gsub('([().*+?^$%%[%]-])', '%%%1') end if not find.match_case then text = text:lower() end if find.whole_word then text = '[^%W_]'..text..'[^%W_]' end - local match_case = find.match_case - local whole_word = find.whole_word + local match_case, whole_word = find.match_case, find.whole_word local string_find, format = string.find, string.format local matches = { 'Find: '..text } function search_file(file) @@ -48,11 +47,12 @@ function find.find_in_files(utf8_dir) line_num = line_num + 1 end end + local lfs_dir, lfs_attributes = lfs.dir, lfs.attributes function search_dir(directory) - for file in lfs.dir(directory) do + for file in lfs_dir(directory) do if not file:find('^%.%.?$') then -- ignore . and .. local path = directory..'/'..file - local type = lfs.attributes(path).mode + local type = lfs_attributes(path).mode if type == 'directory' then search_dir(path) elseif type == 'file' then @@ -108,11 +108,7 @@ local function find_(text, next, flags, nowrap, wrapped) if flags < 8 then buffer:goto_pos(buffer[next and 'current_pos' or 'anchor'] + increment) buffer:search_anchor() - if next then - result = buffer:search_next(flags, text) - else - result = buffer:search_prev(flags, text) - end + result = buffer['search_'..(next and 'next' or 'prev')](buffer, flags, text) if result ~= -1 then buffer:scroll_caret() end elseif flags < 16 then -- lua pattern search (forward search only) @@ -128,17 +124,13 @@ local function find_(text, next, flags, nowrap, wrapped) end else -- find in files - find_in_files() + find.find_in_files() return end if result == -1 and not nowrap and not wrapped then -- wrap the search local anchor, pos = buffer.anchor, buffer.current_pos - if next or flags >= 8 then - buffer:goto_pos(0) - else - buffer:goto_pos(buffer.length) - end + buffer:goto_pos((next or flags >= 8) and 0 or buffer.length) gui.statusbar_text = L('Search wrapped') result = find_(text, next, flags, true, true) if result == -1 then @@ -162,12 +154,11 @@ events.connect('find', find_) local function find_incremental(text) local c = _SCINTILLA.constants local flags = find.match_case and c.SCFIND_MATCHCASE or 0 - --if find.lua then flags = flags + 8 end buffer:goto_pos(find.incremental_start or 0) find_(text, true, flags) end --- LuaDoc is in core/.find.lua. +-- LuaDoc is in core/.find.luadoc. function find.find_incremental() find.incremental = true find.incremental_start = buffer.current_pos @@ -220,10 +211,10 @@ local function replace(rtext) rtext = rtext:gsub('%%'..i, v) end end - local ret, rtext = pcall(rtext.gsub, rtext, '%%(%b())', + local ok, rtext = pcall(rtext.gsub, rtext, '%%(%b())', function(code) - local ret, val = pcall(loadstring('return '..code)) - if not ret then + local ok, val = pcall(loadstring('return '..code)) + if not ok then gui.dialog('ok-msgbox', '--title', L('Error'), '--text', L('An error occured:'), @@ -233,7 +224,7 @@ local function replace(rtext) end return val end) - if ret then + if ok then rtext = rtext:gsub('\\037', '%%') -- unescape '%' buffer:replace_target(rtext:gsub('\\[abfnrtv\\]', escapes)) buffer:goto_pos(buffer.target_end) -- 'find' text after this replacement @@ -287,8 +278,7 @@ local function replace_all(ftext, rtext, flags) buffer:set_sel(anchor, current_pos) buffer:marker_delete_handle(end_marker) end - gui.statusbar_text = string.format("%d %s", tostring(count), - L('replacement(s) made')) + gui.statusbar_text = string.format("%d %s", count, L('replacement(s) made')) buffer:end_undo_action() end events.connect('replace_all', replace_all) @@ -312,8 +302,8 @@ local function goto_file(pos, line_num) local clicked_view = view if previous_view then previous_view:focus() end if buffer._type == L('[Files Found Buffer]') then - -- there are at least two find in files views; find one of those views - -- that the file was not selected from and focus it + -- There are at least two find in files views; find one of those views + -- that the file was not selected from and focus it. for _, v in ipairs(_VIEWS) do if v ~= clicked_view then previous_view = v @@ -331,7 +321,7 @@ local function goto_file(pos, line_num) end events.connect('double_click', goto_file) --- LuaDoc is in core/.find.lua. +-- LuaDoc is in core/.find.luadoc. function find.goto_file_in_list(next) local orig_view = view for _, buffer in ipairs(_BUFFERS) do diff --git a/modules/textadept/keys.lua b/modules/textadept/keys.lua index 67512b51..ad4ed760 100644 --- a/modules/textadept/keys.lua +++ b/modules/textadept/keys.lua @@ -87,18 +87,18 @@ module('_m.textadept.keys', package.seeall) -- ['ctrl+b'] = { 'char_left', 'buffer' }, -- lua = { -- ['ctrl+c'] = { 'add_text', 'buffer', '-- ' }, --- whitespace = { --- ['ctrl+f'] = { function() print('whitespace') end } +-- comment = { +-- ['ctrl+f'] = { function() print('comment') end } -- } -- } -- } -- -- The first two key commands are global and call `buffer:char_right()` and -- `buffer:char_left()` respectively. The last two commands apply only in the --- Lua lexer with the very last one only being available in Lua's `whitespace` --- style. If `ctrl+f` is pressed when the current style is `whitespace` in the +-- Lua lexer with the very last one only being available in Lua's `comment` +-- style. If `ctrl+f` is pressed when the current style is `comment` in the -- `lua` lexer, the global key command with the same shortcut is overridden and --- `whitespace` is printed to standard out. +-- `comment` is printed to standard out. -- -- ## Problems -- @@ -114,6 +114,20 @@ module('_m.textadept.keys', package.seeall) -- Windows and Linux key commands are listed in the first block. -- Mac OSX key commands are listed in the second block. +-- +-- ## Events +-- +-- The following is a list of all key events generated in +-- `event_name(arguments)` format: +-- +-- * **keypress** (code, shift, control, alt)<br /> +-- Called when a key is pressed. +-- - code: the key code (according to `<gdk/gdkkeysyms.h>`). +-- - shift: flag indicating whether or not the Shift key is pressed. +-- - control: flag indicating whether or not the Control key is pressed. +-- - alt: flag indicating whether or not the Alt/Apple key is pressed. +-- <br /> +-- Note: The Alt-Option key in Mac OSX is not available. -- settings local SCOPES_ENABLED = true @@ -127,6 +141,46 @@ local keys = _M local b, v = 'buffer', 'view' local gui = gui +-- Utility functions used by both layouts. +local function enclose_in_tag() + 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 +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) + local state = buffer[setting] + if type(state) == 'boolean' then + buffer[setting] = not state + elseif type(state) == 'number' then + buffer[setting] = buffer[setting] == 0 and 1 or 0 + end + events.emit('update_ui') -- for updating statusbar +end +local RECENT_FILES = 1 +events.connect('user_list_selection', + function(type, text) + if type == RECENT_FILES then io.open_file(text) end + end) +local function show_recent_file_list() + local buffer = buffer + local files = {} + for _, filename in ipairs(io.recent_files) do + table.insert(files, 1, filename) + end + local sep = buffer.auto_c_separator + buffer.auto_c_separator = ('|'):byte() + buffer:user_list_show(RECENT_FILES, table.concat(files, '|')) + buffer.auto_c_separator = sep +end + -- CTRL = 'c' -- SHIFT = 's' -- ALT = 'a' @@ -172,38 +226,39 @@ if not OSX then keys.cv = { 'paste', b } -- Delete is delete. keys.ca = { 'select_all', b } - keys.ce = { m_editing.match_brace } - keys.cE = { m_editing.match_brace, 'select' } - keys['c\n'] = { m_editing.autocomplete_word, '%w_' } + 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 } + 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 = { m_editing.enclose, 'tag' }, - T = { m_editing.enclose, 'single_tag' }, - ['"'] = { m_editing.enclose, 'dbl_quotes' }, - ["'"] = { m_editing.enclose, 'sng_quotes' }, - ['('] = { m_editing.enclose, 'parens' }, - ['['] = { m_editing.enclose, 'brackets' }, - ['{'] = { m_editing.enclose, 'braces' }, - c = { m_editing.enclose, 'chars' }, + 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, 'tags' }, - ['"'] = { m_editing.select_enclosed, 'dbl_quotes' }, - ["'"] = { m_editing.select_enclosed, 'sng_quotes' }, - ['('] = { m_editing.select_enclosed, 'parens' }, - ['['] = { m_editing.select_enclosed, 'brackets' }, - ['{'] = { m_editing.select_enclosed, 'braces' }, - 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 }, + 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 @@ -236,15 +291,6 @@ if not OSX then keys.cb = { gui.switch_buffer } keys['c\t'] = { 'goto_buffer', v, 1, false } keys['cs\t'] = { 'goto_buffer', v, -1, false } - local function toggle_setting(setting) - local state = buffer[setting] - if type(state) == 'boolean' then - buffer[setting] = not state - elseif type(state) == 'number' then - buffer[setting] = buffer[setting] == 0 and 1 or 0 - end - events.emit('update_ui') -- for updating statusbar - end keys.ct.v = { e = { toggle_setting, 'view_eol' }, w = { toggle_setting, 'wrap_mode' }, @@ -269,25 +315,7 @@ if not OSX then keys.c0 = { function() buffer.zoom = 0 end } -- Miscellaneous not in standard menu. - -- Recent files. - local RECENT_FILES = 1 - events.connect('user_list_selection', - function(type, text) - if type == RECENT_FILES then io.open_file(text) end - end) - keys.ao = { - function() - local buffer = buffer - local files = {} - for _, filename in ipairs(io.recent_files) do - table.insert(files, 1, filename) - end - local sep = buffer.auto_c_separator - buffer.auto_c_separator = ('|'):byte() - buffer:user_list_show(RECENT_FILES, table.concat(files, '|')) - buffer.auto_c_separator = sep - end - } + keys.ao = { show_recent_file_list } else -- Mac OSX key commands @@ -336,36 +364,36 @@ else keys.ct = { m_editing.transpose_chars } -- TODO: { m_editing.convert_indentation } keys.cc = { -- enClose in... - t = { m_editing.enclose, 'tag' }, - T = { m_editing.enclose, 'single_tag' }, - ['"'] = { m_editing.enclose, 'dbl_quotes' }, - ["'"] = { m_editing.enclose, 'sng_quotes' }, - ['('] = { m_editing.enclose, 'parens' }, - ['['] = { m_editing.enclose, 'brackets' }, - ['{'] = { m_editing.enclose, 'braces' }, - c = { m_editing.enclose, 'chars' }, + 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... - e = { m_editing.select_enclosed }, - t = { m_editing.select_enclosed, 'tags' }, - ['"'] = { m_editing.select_enclosed, 'dbl_quotes' }, - ["'"] = { m_editing.select_enclosed, 'sng_quotes' }, - ['('] = { m_editing.select_enclosed, 'parens' }, - ['['] = { m_editing.select_enclosed, 'brackets' }, - ['{'] = { m_editing.select_enclosed, 'braces' }, - 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 }, + 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.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() @@ -375,7 +403,7 @@ else } keys.cag = { gui.find.goto_file_in_list, true } keys.caG = { gui.find.goto_file_in_list, false } - keys.cg = { m_editing.goto_line } + keys.cg = { m_editing.goto_line } -- Tools keys['f2'] = { gui.command_entry.focus } @@ -395,15 +423,6 @@ else keys.ab = { gui.switch_buffer } keys['c\t'] = { 'goto_buffer', v, 1, false } keys['cs\t'] = { 'goto_buffer', v, -1, false } - local function toggle_setting(setting) - local state = buffer[setting] - if type(state) == 'boolean' then - buffer[setting] = not state - elseif type(state) == 'number' then - buffer[setting] = buffer[setting] == 0 and 1 or 0 - end - events.emit('update_ui') -- for updating statusbar - end keys.at.v = { e = { toggle_setting, 'view_eol' }, w = { toggle_setting, 'wrap_mode' }, @@ -428,25 +447,7 @@ else keys.c0 = { function() buffer.zoom = 0 end } -- Miscellaneous not in standard menu. - -- Recent files. - local RECENT_FILES = 1 - events.connect('user_list_selection', - function(type, text) - if type == RECENT_FILES then io.open_file(text) end - end) - keys.co = { - function() - local buffer = buffer - local files = {} - for _, filename in ipairs(io.recent_files) do - table.insert(files, 1, filename) - end - local sep = buffer.auto_c_separator - buffer.auto_c_separator = ('|'):byte() - buffer:user_list_show(RECENT_FILES, table.concat(files, '|')) - buffer.auto_c_separator = sep - end - } + keys.co = { show_recent_file_list } -- Movement/selection commands keys.cf = { 'char_right', b } @@ -469,7 +470,7 @@ else keys.cah = { 'del_word_left', b } keys.cd = { 'clear', b } keys.cad = { 'del_word_right', b } - keys.ck = { + keys.ck = { function() buffer:line_end_extend() buffer:cut() @@ -568,9 +569,7 @@ local function run_key_command(lexer, scope) end end - if type(f) ~= 'function' then - error(L('Unknown command:')..tostring(f)) - end + if type(f) ~= 'function' then error(L('Unknown command:')..tostring(f)) end return f(unpack(args)) == false and PROPAGATE or HALT end @@ -594,19 +593,9 @@ local function keypress(code, shift, control, alt) if code < 256 then key = string_char(code) shift = false -- for printable characters, key is upper case - if OSX and not shift and not control and not alt then - local ch = string_char(code) - -- work around native GTK-OSX's handling of Alt key - if ch:find('[%p%d]') and #keychain == 0 then - if buffer.anchor ~= buffer.current_pos then buffer:delete_back() end - buffer:add_text(ch) - events.emit('char_added', code) - return true - end - end else - if not KEYSYMS[code] then return end key = KEYSYMS[code] + if not key then return end end control = control and CTRL or '' shift = shift and SHIFT or '' diff --git a/modules/textadept/menu.lua b/modules/textadept/menu.lua index 196d685a..436f86bb 100644 --- a/modules/textadept/menu.lua +++ b/modules/textadept/menu.lua @@ -10,6 +10,20 @@ local gui = _G.gui -- This module, like _m.textadept.keys, should be 'require'ed last. module('_m.textadept.menu', package.seeall) +-- Markdown: +-- +-- ## Events +-- +-- The following is a list of all menu events generated in +-- `event_name(arguments)` format: +-- +-- * **menu\_clicked** (menu\_id)<br /> +-- Called when a menu item is selected. +-- - menu\_id: the numeric ID of the menu item set in +-- [`gui.gtkmenu()`][gui_gtkmenu]. +-- +-- [gui_gtkmenu]: ../modules/gui.html#gtkmenu + local SEPARATOR = 'separator' local b, v = 'buffer', 'view' local m_snippets = _m.textadept.snippets @@ -121,24 +135,32 @@ menubar = { { L('Convert _Indentation'), { m_editing.convert_indentation } }, { title = L('S_election'), { title = L('_Enclose in...'), - { L('_HTML Tags'), { m_editing.enclose, 'tag' } }, - { L('HTML Single _Tag'), { m_editing.enclose, 'single_tag' } }, - { L('_Double Quotes'), { m_editing.enclose, 'dbl_quotes' } }, - { L('_Single Quotes'), { m_editing.enclose, 'sng_quotes' } }, - { L('_Parentheses'), { m_editing.enclose, 'parens' } }, - { L('_Brackets'), { m_editing.enclose, 'brackets' } }, - { L('B_races'), { m_editing.enclose, 'braces' } }, - { L('_Character Sequence'), { m_editing.enclose, 'chars' } }, + { L('_HTML 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 + } }, + { 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('B_races'), { m_editing.enclose, '{', '}' } }, }, { L('_Grow'), { m_editing.grow_selection, 1 } }, }, { title = L('Select i_n...'), - { L('_HTML Tag'), { m_editing.select_enclosed, 'tags' } }, - { L('_Double Quote'), { m_editing.select_enclosed, 'dbl_quotes' } }, - { L('_Single Quote'), { m_editing.select_enclosed, 'sng_quotes' } }, - { L('_Parenthesis'), { m_editing.select_enclosed, 'parens' } }, - { L('_Bracket'), { m_editing.select_enclosed, 'brackets' } }, - { L('B_race'), { m_editing.select_enclosed, 'braces' } }, + { 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('B_race'), { m_editing.select_enclosed, '{', '}' } }, { L('_Word'), { m_editing.current_word, 'select' } }, { L('_Line'), { m_editing.select_line } }, { L('Para_graph'), { m_editing.select_paragraph } }, diff --git a/modules/textadept/mime_types.lua b/modules/textadept/mime_types.lua index f066e46b..e6023995 100644 --- a/modules/textadept/mime_types.lua +++ b/modules/textadept/mime_types.lua @@ -114,7 +114,6 @@ lexers = {} -- Generate lexer list local lexers_found = {} -local lfs = require 'lfs' for lexer in lfs.dir(_HOME..'/lexers/') do if lexer:find('%.lua$') and lexer ~= 'lexer.lua' then lexers_found[lexer:match('^(.+)%.lua$')] = true diff --git a/modules/textadept/run.lua b/modules/textadept/run.lua index 3cae9e86..2203b129 100755..100644 --- a/modules/textadept/run.lua +++ b/modules/textadept/run.lua @@ -19,11 +19,9 @@ module('_m.textadept.run', package.seeall) -- * %(filename_noext) The name of the file excluding extension. function execute(command) local filepath = buffer.filename:iconv(_CHARSET, 'UTF-8') - local filedir, filename + local filedir, filename = '', filepath if filepath:find('[/\\]') then filedir, filename = filepath:match('^(.+[/\\])([^/\\]+)$') - else - filedir, filename = '', filepath end local filename_noext = filename:match('^(.+)%.') command = command:gsub('%%%b()', { @@ -42,6 +40,15 @@ function execute(command) buffer:goto_pos(buffer.length) end +-- Executes a compile or run command. +-- @param cmd_table Either compile_command or run_command +local function command(cmd_table) + if not buffer.filename then return end + buffer:save() + local action = cmd_table[buffer.filename:match('[^.]+$')] + if action then execute(type(action) == 'function' and action() or action) end +end + --- -- File extensions and their associated 'compile' actions. -- Each key is a file extension whose value is a either a command line string to @@ -55,12 +62,7 @@ compile_command = {} -- Compiles the file as specified by its extension in the compile_command -- table. -- @see compile_command -function compile() - if not buffer.filename then return end - buffer:save() - local action = compile_command[buffer.filename:match('[^.]+$')] - if action then execute(type(action) == 'function' and action() or action) end -end +function compile() command(compile_command) end --- -- File extensions and their associated 'go' actions. @@ -75,12 +77,7 @@ run_command = {} -- Runs/executes the file as specified by its extension in the run_command -- table. -- @see run_command -function run() - if not buffer.filename then return end - buffer:save() - local action = run_command[buffer.filename:match('[^.]+$')] - if action then execute(type(action) == 'function' and action() or action) end -end +function run() command(run_command) end --- -- A table of error string details. @@ -112,7 +109,6 @@ function goto_error(pos, line_num) for _, error_detail in pairs(error_detail) do local captures = { line:match(error_detail.pattern) } if #captures > 0 then - local lfs = require 'lfs' local utf8_filename = captures[error_detail.filename] local filename = utf8_filename:iconv(_CHARSET, 'UTF-8') if lfs.attributes(filename) then diff --git a/modules/textadept/session.lua b/modules/textadept/session.lua index c8fc1d90..745638cc 100755..100644 --- a/modules/textadept/session.lua +++ b/modules/textadept/session.lua @@ -19,8 +19,6 @@ DEFAULT_SESSION = _USERHOME..'/session' SAVE_ON_QUIT = true -- end settings -local lfs = require 'lfs' - --- -- Loads a Textadept session file. -- Textadept restores split views, opened buffers, cursor information, and @@ -33,16 +31,17 @@ function load(filename) local not_found = {} local f = io.open(filename or DEFAULT_SESSION, 'rb') if not f then - if not io.close_all() then return false end + io.close_all() + return false end - if not f then return false end local current_view, splits = 1, { [0] = {} } + local lfs_attributes = lfs.attributes for line in f:lines() do if line:find('^buffer:') then local anchor, current_pos, first_visible_line, filename = line:match('^buffer: (%d+) (%d+) (%d+) (.+)$') if not filename:find('^%[.+%]$') then - if lfs.attributes(filename) then + if lfs_attributes(filename) then io.open_file(filename) else not_found[#not_found + 1] = filename @@ -77,8 +76,7 @@ function load(filename) elseif line:find('^current_view:') then local view_idx = line:match('^current_view: (%d+)') current_view = tonumber(view_idx) or 1 - end - if line:find('^size:') then + elseif line:find('^size:') then local width, height = line:match('^size: (%d+) (%d+)$') if width and height then gui.size = { width, height } end end diff --git a/modules/textadept/snapopen.lua b/modules/textadept/snapopen.lua index f472b744..10690c75 100755..100644 --- a/modules/textadept/snapopen.lua +++ b/modules/textadept/snapopen.lua @@ -36,7 +36,7 @@ DEFAULT_DEPTH = 4 MAX = 1000
-- end settings
-local lfs = require 'lfs'
+local lfs_dir, lfs_attributes = lfs.dir, lfs.attributes
local DEPTH = DEFAULT_DEPTH
-- Determines whether or not the given file matches the given filter.
@@ -64,10 +64,10 @@ end -- @param filter The filter table.
local function add_directory(dir, list, depth, filter)
local string_match, string_gsub, MAX = string.match, string.gsub, MAX
- for file in lfs.dir(dir) do
+ for file in lfs_dir(dir) do
if not string_match(file, '^%.%.?$') then
file = dir..(not WIN32 and '/' or '\\')..file
- if lfs.attributes(file).mode == 'directory' then
+ if lfs_attributes(file).mode == 'directory' then
if not exclude(file, filter.folders) and depth < DEPTH then
add_directory(file, list, depth + 1, filter)
end
diff --git a/modules/textadept/snippets.lua b/modules/textadept/snippets.lua index 4d426532..95a80558 100755..100644 --- a/modules/textadept/snippets.lua +++ b/modules/textadept/snippets.lua @@ -34,6 +34,9 @@ module('_m.textadept.snippets', package.seeall) -- and `lua`. Style names are different lexer styles, most of which are in -- `lexers/lexer.lua`; examples are `whitespace`, `comment`, and `string`. -- +-- Snippet text should contain spaces instead of tabs since Textadept +-- automatically converts spaces to tabs depending on the current settings. +-- -- ## Snippet Precedence -- -- When searching for a snippet to expand in the `snippets` table, snippets in |