1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
|
-- Copyright 2007-2020 Mitchell mitchell.att.foicica.com. See LICENSE.
local M = {}
--[[ This comment is for LuaDoc.
---
-- Processes command line arguments for Textadept.
-- @field _G.events.ARG_NONE (string)
-- Emitted when no command line arguments are passed to Textadept on startup.
module('args')]]
events.ARG_NONE = 'arg_none'
-- Contains registered command line switches.
-- @class table
-- @name switches
local switches = {}
---
-- Registers a command line switch with short and long versions *short* and
-- *long*, respectively. *narg* is the number of arguments the switch accepts,
-- *f* is the function called when the switch is tripped, and *description* is
-- the switch's description when displaying help.
-- @param short The string short version of the switch.
-- @param long The string long version of the switch.
-- @param narg The number of expected parameters for the switch.
-- @param f The Lua function to run when the switch is tripped. It is passed
-- *narg* string arguments.
-- @param description The string description of the switch for command line
-- help.
-- @name register
function M.register(short, long, narg, f, description)
local switch = {
narg = assert_type(narg, 'number', 3), f = assert_type(f, 'function', 4),
description = assert_type(description, 'string', 5)
}
switches[assert_type(short, 'string', 1)] = switch
switches[assert_type(long, 'string', 2)] = switch
end
-- Processes command line argument table *arg*, handling switches previously
-- defined using `args.register()` and treating unrecognized arguments as
-- filenames to open.
-- Emits an `ARG_NONE` event when no arguments are present unless
-- *no_emit_arg_none* is `true`.
-- @param arg Argument table.
-- @see register
-- @see _G.events
local function process(arg, no_emit_arg_none)
local no_args = true
local i = 1
while i <= #arg do
local switch = switches[arg[i]]
if switch then
switch.f(table.unpack(arg, i + 1, i + switch.narg))
i = i + switch.narg
else
io.open_file(lfs.abspath(arg[i], arg[-1]))
no_args = false
end
i = i + 1
end
if no_args and not no_emit_arg_none then events.emit(events.ARG_NONE) end
end
events.connect(events.INITIALIZED, function() if arg then process(arg) end end)
-- Undocumented, single-instance event handler for forwarding arguments.
events.connect('command_line', function(arg) process(arg, true) end)
if not CURSES then
-- Shows all registered command line switches on the command line.
M.register('-h', '--help', 0, function()
print('Usage: textadept [args] [filenames]')
local list = {}
for name in pairs(switches) do list[#list + 1] = name end
table.sort(
list, function(a, b) return a:match('[^-]+') < b:match('[^-]+') end)
for _, name in ipairs(list) do
local switch = switches[name]
print(string.format(
' %s [%d args]: %s', name, switch.narg, switch.description))
end
os.exit()
end, 'Shows this')
-- Shows Textadept version and copyright on the command line.
M.register('-v', '--version', 0, function()
print(_RELEASE .. '\n' .. _COPYRIGHT)
quit()
end, 'Prints Textadept version and copyright')
-- After Textadept finishes initializing and processes arguments, remove the
-- help and version switches in order to prevent another instance from sending
-- '-h', '--help', '-v', and '--version' to the first instance, killing the
-- latter.
events.connect(events.INITIALIZED, function()
switches['-h'], switches['--help'] = nil, nil
switches['-v'], switches['--version'] = nil, nil
end)
end
-- Set `_G._USERHOME`.
-- This needs to be set as soon as possible since the processing of arguments is
-- positional.
_USERHOME = os.getenv(not WIN32 and 'HOME' or 'USERPROFILE') .. '/.textadept'
for i, switch in ipairs(arg) do
if (switch == '-u' or switch == '--userhome') and arg[i + 1] then
_USERHOME = arg[i + 1]
break
end
end
local mode = lfs.attributes(_USERHOME, 'mode')
assert(not mode or mode == 'directory', '"%s" is not a directory', _USERHOME)
if not mode then
assert(lfs.mkdir(_USERHOME), 'cannot create "%s"', _USERHOME)
end
local user_init = _USERHOME .. '/init.lua'
mode = lfs.attributes(user_init, 'mode')
assert(not mode or mode == 'file', '"%s" is not a file (%s)', user_init, mode)
if not mode then
assert(io.open(user_init, 'w'), 'unable to create "%s"', user_init):close()
end
-- Placeholders.
M.register('-u', '--userhome', 1, function() end, 'Sets alternate _USERHOME')
M.register('-f', '--force', 0, function() end, 'Forces unique instance')
-- Run unit tests.
-- Note: have them run after the last `events.INITIALIZED` handler so everything
-- is completely initialized (e.g. menus, macro module, etc.).
M.register('-t', '--test', 1, function(patterns)
events.connect(events.INITIALIZED, function()
local arg = {}
for patt in (patterns or ''):gmatch('[^,]+') do arg[#arg + 1] = patt end
local env = setmetatable({arg = arg}, {__index = _G})
assert(loadfile(_HOME..'/test/test.lua', 't', env))()
end)
end, 'Runs unit tests indicated by comma-separated list of patterns (or all)')
return M
|