init.lua 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407
  1. ---- keyboard shortcuts
  2. -- switch between last used tabs
  3. local last_buffer
  4. events.connect(events.BUFFER_BEFORE_SWITCH, function()
  5. last_buffer = buffer
  6. end)
  7. keys['ctrl+\t'] = function() -- ctrl + tab
  8. if (last_buffer ~= nil) then
  9. view.goto_buffer(_VIEWS[1], last_buffer)
  10. end
  11. end
  12. keys['ctrl+pgup'] = function() -- ctrl + page down
  13. view.goto_buffer(_VIEWS[1], -1)
  14. end
  15. keys['ctrl+pgdn'] = function() -- ctrl + page down
  16. view.goto_buffer(_VIEWS[1], 1)
  17. end
  18. keys.cT = function() io.open_recent_file() end
  19. keys['end'] = function() -- end - go to visible end of line (esp, when wrapped)
  20. buffer.line_end_display(buffer)
  21. end
  22. keys['home'] = function() -- home - go to visible beginning of line (esp, when wrapped)
  23. buffer.home_display(buffer)
  24. end
  25. keys['send'] = function() -- shift + end - select till the visible end of line (esp, when wrapped)
  26. buffer.line_end_display_extend(buffer)
  27. end
  28. keys['ctrl+p'] = function() -- ctrl + p
  29. textadept.menu.select_command()
  30. end
  31. keys['ctrl+g'] = textadept.editing.goto_line -- ctrl + g
  32. ---- theme
  33. if not CURSES then
  34. view:set_theme("base16-custom-dark", {
  35. -- font = "Inconsolata", -- you can set it, if you want
  36. fontsize = 14
  37. })
  38. end
  39. ---- convenient settings:
  40. events.connect(events.BUFFER_NEW, function(str)
  41. -- use tabs, always
  42. buffer.use_tabs = true
  43. buffer.tab_width = 4
  44. -- show whitespace, always
  45. buffer.view_ws = buffer.WS_VISIBLEALWAYS
  46. -- wrap long lines, always
  47. buffer.wrap_mode = buffer.WRAP_WHITESPACE
  48. -- paste on multiple cursor places
  49. buffer.multi_paste = buffer.MULTIPASTE_EACH
  50. end)
  51. ui.silent_print = true
  52. --[[
  53. Braces like Atom.
  54. Typing brace, e.g. { and pressing enter creates a new line with a tab between the brace.
  55. ]]
  56. keys['\n'] = function() -- enter
  57. local caret_pos = buffer.current_pos
  58. local match = buffer:brace_match(caret_pos, 0)
  59. local current_line_number = buffer:line_from_position(caret_pos)
  60. local current_line = buffer.get_line(buffer, current_line_number)
  61. buffer.new_line(buffer) -- handle normal enter/return
  62. if string.match(current_line, "%s%*%s") ~= nil and string.match(current_line, "%s%*%s") ~= '' then
  63. buffer:add_text( '* ' )
  64. elseif string.match(current_line, "((.*)/%*%*)") ~= nil and string.match(current_line, "((.*)/%*%*)") ~= '' then
  65. -- typed /** and pressed enter, creates new line with *
  66. buffer:add_text( ' * ' )
  67. end
  68. if match ~= -1 then -- if braces (), [], {} are found then...
  69. buffer.goto_pos(buffer, caret_pos)
  70. buffer.new_line(buffer)
  71. buffer:add_text('\t')
  72. end
  73. end
  74. --[[
  75. Javadoc/phpdoc implementation.
  76. place cursor on previous line than function line,
  77. then press ctrl+j
  78. ]]
  79. keys['ctrl+j'] = function() -- ctrl + j
  80. local lexer_type = buffer:get_lexer(true)
  81. if lexer_type == 'php' then
  82. local current_line_number = buffer:line_from_position(buffer.current_pos)
  83. local function_line = buffer.get_line(buffer, current_line_number + 1)
  84. buffer.new_line(buffer)
  85. buffer.move_selected_lines_up(buffer)
  86. if string.find(function_line, "function") ~= nil then
  87. local comment = '/**\n * %1(description)\n *'
  88. local placeholder_num = 2
  89. for w in string.gmatch(function_line, "%$[%a_]+[%a%d_]*") do
  90. comment = comment..'\n * @param %'..placeholder_num..'(type) '..w..' %'..(placeholder_num + 1)..'(description)'
  91. placeholder_num = placeholder_num + 2
  92. end
  93. comment = comment..'\n * return %'..placeholder_num..'(type)\n */'
  94. textadept.snippets._insert(comment)
  95. end
  96. end
  97. end
  98. -- delete the line with line number
  99. -- esp. for delete line
  100. function delete_line(line_number)
  101. buffer.goto_line(buffer, line_number)
  102. buffer.line_end_extend(buffer)
  103. buffer.clear(buffer)
  104. buffer.clear(buffer) -- delete line ending
  105. end
  106. -- only keep unique items on the table
  107. -- esp. for delete line
  108. function unique_table(mytable)
  109. -- from: https://stackoverflow.com/a/20067270
  110. local hash = {}
  111. local res = {}
  112. for _,v in ipairs(mytable) do
  113. if (not hash[v]) then
  114. res[#res+1] = v
  115. hash[v] = true
  116. end
  117. end
  118. return res
  119. end
  120. -- for sorting a table
  121. -- esp. for delete line
  122. -- from: https://stackoverflow.com/a/15706820
  123. function spairs(t, order)
  124. -- collect the keys
  125. local keys = {}
  126. for k in pairs(t) do keys[#keys+1] = k end
  127. -- if order function given, sort by it by passing the table and keys a, b,
  128. -- otherwise just sort the keys
  129. if order then
  130. table.sort(keys, function(a,b) return order(t, a, b) end)
  131. else
  132. table.sort(keys)
  133. end
  134. -- return the iterator function
  135. local i = 0
  136. return function()
  137. i = i + 1
  138. if keys[i] then
  139. return keys[i], t[keys[i]]
  140. end
  141. end
  142. end
  143. -- for getting line numbers that are selected or have a (multi)cursor on them
  144. function get_selected_or_focus_lines()
  145. local lines = {}
  146. -- first we determine which lines we want to delete
  147. for i = 1, buffer.selections do
  148. local s, e = buffer.selection_n_start[i], buffer.selection_n_end[i]
  149. -- not a selection, just a cursor placed (supports multi cursor)
  150. if s == e then
  151. local line_num = buffer:line_from_position(s)
  152. table.insert(lines, line_num)
  153. -- selection (supports multi selection)
  154. else
  155. local line_num_start = buffer:line_from_position(s)
  156. local line_num_end = buffer:line_from_position(e)
  157. for j = line_num_start, line_num_end do
  158. table.insert(lines, j)
  159. end
  160. end
  161. end
  162. -- we list one line only once
  163. -- this is a fix, if there are two selections in the same line
  164. lines = unique_table(lines)
  165. local kkey = 1
  166. local lines_ = {}
  167. for k,v in spairs(lines, function(t,a,b) return t[b] > t[a] end) do
  168. table.insert(lines_, v)
  169. kkey = kkey + 1
  170. end
  171. lines = lines_
  172. return lines
  173. end
  174. -- delete line(s)
  175. -- supports multi selection, multi cursor or both
  176. keys['ctrl+K'] = function() -- ctrl + shift + k
  177. buffer:begin_undo_action()
  178. local lines = get_selected_or_focus_lines()
  179. local lines_delete_offset = 0
  180. for key, line in ipairs(lines) do
  181. delete_line(line + lines_delete_offset)
  182. lines_delete_offset = lines_delete_offset - 1
  183. end
  184. buffer:end_undo_action()
  185. end
  186. -- pair multiline selections together in table
  187. -- it will help us to process selections differently
  188. function pair_line_numbers(lines)
  189. local paired_lines = {}
  190. local selection_open = false
  191. local last_begin = 0
  192. local last_end = 0
  193. for key, line in ipairs(lines) do
  194. if lines[key + 1] ~= nil and lines[key + 1] == lines[key] + 1 then
  195. if selection_open == false then
  196. last_begin = line
  197. selection_open = true
  198. end
  199. else
  200. if selection_open == true then
  201. last_end = line
  202. selection_open = false
  203. local data = {
  204. ['beginning'] = last_begin,
  205. ['ending'] = last_end
  206. }
  207. table.insert(paired_lines, data)
  208. else
  209. table.insert(paired_lines, line) -- normal line, not selection
  210. end
  211. end
  212. end
  213. return paired_lines
  214. end
  215. -- duplicate the lines passed
  216. -- supports multi selections + multi cursors
  217. function duplicate_line(line_number, offset)
  218. if (type(line_number) == "table") then
  219. local lines_queue = {}
  220. for i = line_number['beginning']+offset, line_number['ending']+offset do
  221. local line_contents = buffer.get_line(buffer, i)
  222. table.insert( lines_queue, line_contents )
  223. end
  224. buffer.goto_line(buffer, line_number['ending']+offset)
  225. for key, linetext in pairs(lines_queue) do
  226. buffer.line_end(buffer)
  227. buffer.new_line(buffer)
  228. local cursor_line = buffer:line_from_position( buffer.current_pos )
  229. textadept.editing.goto_line(cursor_line)
  230. buffer.line_end_display_extend(buffer)
  231. buffer.replace_sel(buffer, '')
  232. buffer.insert_text(buffer, -1, linetext)
  233. buffer.line_end(buffer)
  234. buffer.clear(buffer)
  235. end
  236. else
  237. buffer.goto_line(buffer, line_number + offset)
  238. buffer.line_duplicate()
  239. end
  240. end
  241. -- duplicate line(s)
  242. -- supports multi selection, multi cursor or both
  243. keys['ctrl+d'] = function() -- ctrl + d
  244. buffer:begin_undo_action()
  245. local lines = get_selected_or_focus_lines()
  246. local paired_lines = pair_line_numbers(lines)
  247. local lines_change_offset = 0
  248. for key, line in pairs(paired_lines) do
  249. if (type(line) == "table") then
  250. duplicate_line(line, lines_change_offset)
  251. lines_change_offset = lines_change_offset + (line['ending'] - line['beginning']) + 1
  252. else
  253. duplicate_line(line, lines_change_offset)
  254. lines_change_offset = lines_change_offset + 1
  255. end
  256. end
  257. buffer:end_undo_action()
  258. end
  259. -- highlight trailing whitespace
  260. -- from: https://foicica.com/wiki/highlight-trailing-whitespace
  261. local tw_indicator = _SCINTILLA.next_indic_number()
  262. buffer.indic_style[tw_indicator] = buffer.INDIC_ROUNDBOX
  263. buffer.indic_fore[tw_indicator] = 0x0000FF
  264. events.connect(events.UPDATE_UI, function(updated)
  265. if updated ~= buffer.UPDATE_CONTENT then return end
  266. buffer.target_start = 0
  267. buffer.search_flags = buffer.FIND_REGEXP
  268. buffer.indicator_current = tw_indicator
  269. buffer:indicator_clear_range(0, buffer.length)
  270. while true do
  271. buffer.target_end = buffer.length
  272. if buffer:search_in_target('[ \t]+$') == -1 then break end
  273. buffer:indicator_fill_range(
  274. buffer.target_start, buffer.target_end - buffer.target_start)
  275. buffer.target_start = buffer.target_end
  276. end
  277. end)
  278. -- from https://github.com/rgieseke/textadept/blob/d184ca77570bdc72a97b9dee02ee8cea9844c9a6/modules/textadept/editing.lua#L208
  279. function trim_whitespace(buffer)
  280. buffer:begin_undo_action()
  281. -- Strip trailing whitespace.
  282. for line = 0, buffer.line_count - 1 do
  283. local s, e = buffer:position_from_line(line), buffer.line_end_position[line]
  284. local i, byte = e - 1, buffer.char_at[e - 1]
  285. while i >= s and (byte == 9 or byte == 32) do
  286. i, byte = i - 1, buffer.char_at[i - 1]
  287. end
  288. if i < e - 1 then buffer:delete_range(i + 1, e - i - 1) end
  289. end
  290. -- Ensure ending newline.
  291. local e = buffer:position_from_line(buffer.line_count)
  292. if buffer.line_count == 1 or
  293. e > buffer:position_from_line(buffer.line_count - 1) then
  294. buffer:insert_text(e, '\n')
  295. end
  296. -- Convert non-consistent EOLs
  297. buffer:convert_eols(buffer.eol_mode)
  298. buffer:end_undo_action()
  299. end
  300. -- selects the line with the line_number given
  301. function select_line(line_number)
  302. local line_beginning = buffer:position_from_line(line_number)
  303. local line_ending = buffer.line_end_position[line_number]
  304. buffer.add_selection(line_ending, line_beginning)
  305. end
  306. keys['ctrl+l'] = function() -- ctrl + l
  307. local lines = get_selected_or_focus_lines()
  308. for key, line in ipairs(lines) do
  309. select_line(line)
  310. end
  311. end
  312. -- close tab on middle click
  313. -- patching source is required to work
  314. if events.TAB_MIDDLE_CLICK then
  315. events.connect(events.TAB_MIDDLE_CLICK, function()
  316. io.close_buffer()
  317. end)
  318. end
  319. -- new tab on middle click on blank space
  320. -- patching source is required to work
  321. if events.TAB_MIDDLE_CLICK_BLANK then
  322. events.connect(events.TAB_MIDDLE_CLICK_BLANK, buffer.new)
  323. end
  324. -- new tab on double click on blank space
  325. -- patching source is required to work
  326. if events.TAB_DOUBLE_CLICK_BLANK then
  327. events.connect(events.TAB_DOUBLE_CLICK_BLANK, buffer.new)
  328. end
  329. -- shows total number of selected characters on statusbar
  330. events.connect(events.UPDATE_UI, function()
  331. if buffer.UPDATE_SELECTION ~= 0 then
  332. local selcount = 0
  333. for i = 1, buffer.selections do
  334. selcount = selcount + (buffer.selection_n_end[i] - buffer.selection_n_start[i])
  335. end
  336. ui.statusbar_text = 'Sel: '..selcount
  337. end
  338. end)
  339. ---- context menu
  340. local lua_menu = {
  341. title = 'Extra',
  342. {'Copy current filename', function() buffer.copy_text(_G.buffer, _G.buffer.filename) end},
  343. {'Trim leading whitespace', function() trim_whitespace(_G.buffer) end},
  344. {'Set Filetype', keys['ctrl+L']},
  345. }
  346. local options_menu = textadept.menu.menubar[_L['Tools']]
  347. options_menu[#options_menu + 1] = lua_menu
  348. textadept.menu.context_menu[#textadept.menu.context_menu + 1] = lua_menu
  349. -- various other language related mods
  350. _M.mods = require 'mods'
  351. _M.mods = require 'hotexit'