aboutsummaryrefslogtreecommitdiff
path: root/modules
diff options
context:
space:
mode:
Diffstat (limited to 'modules')
-rw-r--r--modules/lua/commands.lua20
-rw-r--r--modules/textadept/bookmarks.lua5
-rw-r--r--modules/textadept/command_entry.lua20
-rw-r--r--modules/textadept/editing.lua159
-rw-r--r--modules/textadept/find.lua50
-rw-r--r--modules/textadept/keys.lua241
-rw-r--r--modules/textadept/menu.lua50
-rw-r--r--modules/textadept/mime_types.lua1
-rw-r--r--[-rwxr-xr-x]modules/textadept/run.lua28
-rw-r--r--[-rwxr-xr-x]modules/textadept/session.lua12
-rw-r--r--[-rwxr-xr-x]modules/textadept/snapopen.lua6
-rw-r--r--[-rwxr-xr-x]modules/textadept/snippets.lua3
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