1if arg[1] == '--help' then
2  print('Usage: genoptions.lua src/nvim options_file')
3  os.exit(0)
4end
5
6local nvimsrcdir = arg[1]
7local options_file = arg[2]
8
9package.path = nvimsrcdir .. '/?.lua;' .. package.path
10
11local opt_fd = io.open(options_file, 'w')
12
13local w = function(s)
14  if s:match('^    %.') then
15    opt_fd:write(s .. ',\n')
16  else
17    opt_fd:write(s .. '\n')
18  end
19end
20
21local options = require('options')
22
23local cstr = options.cstr
24
25local type_flags={
26  bool='P_BOOL',
27  number='P_NUM',
28  string='P_STRING',
29}
30
31local redraw_flags={
32  statuslines='P_RSTAT',
33  current_window='P_RWIN',
34  current_window_only='P_RWINONLY',
35  current_buffer='P_RBUF',
36  all_windows='P_RALL',
37  everything='P_RCLR',
38  curswant='P_CURSWANT',
39  ui_option='P_UI_OPTION',
40}
41
42local list_flags={
43  comma='P_COMMA',
44  onecomma='P_ONECOMMA',
45  flags='P_FLAGLIST',
46  flagscomma='P_COMMA|P_FLAGLIST',
47}
48
49local get_flags = function(o)
50  local ret = {type_flags[o.type]}
51  local add_flag = function(f)
52    ret[1] = ret[1] .. '|' .. f
53  end
54  if o.list then
55    add_flag(list_flags[o.list])
56  end
57  if o.redraw then
58    for _, r_flag in ipairs(o.redraw) do
59      add_flag(redraw_flags[r_flag])
60    end
61  end
62  if o.expand then
63    add_flag('P_EXPAND')
64    if o.expand == 'nodefault' then
65      add_flag('P_NO_DEF_EXP')
66    end
67  end
68  for _, flag_desc in ipairs({
69    {'alloced'},
70    {'nodefault'},
71    {'no_mkrc'},
72    {'secure'},
73    {'gettext'},
74    {'noglob'},
75    {'normal_fname_chars', 'P_NFNAME'},
76    {'normal_dname_chars', 'P_NDNAME'},
77    {'pri_mkrc'},
78    {'deny_in_modelines', 'P_NO_ML'},
79    {'deny_duplicates', 'P_NODUP'},
80    {'modelineexpr', 'P_MLE'},
81  }) do
82    local key_name = flag_desc[1]
83    local def_name = flag_desc[2] or ('P_' .. key_name:upper())
84    if o[key_name] then
85      add_flag(def_name)
86    end
87  end
88  return ret[1]
89end
90
91local get_cond
92get_cond = function(c, base_string)
93  local cond_string = base_string or '#if '
94  if type(c) == 'table' then
95    cond_string = cond_string .. get_cond(c[1], '')
96    for i, subc in ipairs(c) do
97      if i > 1 then
98        cond_string = cond_string .. ' && ' .. get_cond(subc, '')
99      end
100    end
101  elseif c:sub(1, 1) == '!' then
102    cond_string = cond_string .. '!defined(' .. c:sub(2) .. ')'
103  else
104    cond_string = cond_string .. 'defined(' .. c .. ')'
105  end
106  return cond_string
107end
108
109local value_dumpers = {
110  ['function']=function(v) return v() end,
111  string=cstr,
112  boolean=function(v) return v and 'true' or 'false' end,
113  number=function(v) return ('%iL'):format(v) end,
114  ['nil']=function(_) return '0L' end,
115}
116
117local get_value = function(v)
118  return '(char_u *) ' .. value_dumpers[type(v)](v)
119end
120
121local get_defaults = function(d,n)
122  if d == nil then
123    error("option '"..n.."' should have a default value")
124  end
125  return get_value(d)
126end
127
128local defines = {}
129
130local dump_option = function(i, o)
131  w('  [' .. ('%u'):format(i - 1) .. ']={')
132  w('    .fullname=' .. cstr(o.full_name))
133  if o.abbreviation then
134    w('    .shortname=' .. cstr(o.abbreviation))
135  end
136  w('    .flags=' .. get_flags(o))
137  if o.enable_if then
138    w(get_cond(o.enable_if))
139  end
140  if o.varname then
141    w('    .var=(char_u *)&' .. o.varname)
142  elseif #o.scope == 1 and o.scope[1] == 'window' then
143    w('    .var=VAR_WIN')
144  end
145  if #o.scope == 1 and o.scope[1] == 'global' then
146    w('    .indir=PV_NONE')
147  else
148    assert (#o.scope == 1 or #o.scope == 2)
149    assert (#o.scope == 1 or o.scope[1] == 'global')
150    local min_scope = o.scope[#o.scope]
151    local varname = o.pv_name or o.varname or (
152      'p_' .. (o.abbreviation or o.full_name))
153    local pv_name = (
154      'OPT_' .. min_scope:sub(1, 3):upper() .. '(' .. (
155        min_scope:sub(1, 1):upper() .. 'V_' .. varname:sub(3):upper()
156      ) .. ')'
157    )
158    if #o.scope == 2 then
159      pv_name = 'OPT_BOTH(' .. pv_name .. ')'
160    end
161    defines['PV_' .. varname:sub(3):upper()] = pv_name
162    w('    .indir=' .. pv_name)
163  end
164  if o.enable_if then
165    w('#else')
166    w('    .var=NULL')
167    w('    .indir=PV_NONE')
168    w('#endif')
169  end
170  if o.defaults then
171    if o.defaults.condition then
172      w(get_cond(o.defaults.condition))
173    end
174    w('    .def_val=' .. get_defaults(o.defaults.if_true, o.full_name))
175    if o.defaults.condition then
176      if o.defaults.if_false then
177        w('#else')
178        w('    .def_val=' .. get_defaults(o.defaults.if_false, o.full_name))
179      end
180      w('#endif')
181    end
182  end
183  w('  },')
184end
185
186w('static vimoption_T options[] = {')
187for i, o in ipairs(options.options) do
188  dump_option(i, o)
189end
190w('  [' .. ('%u'):format(#options.options) .. ']={.fullname=NULL}')
191w('};')
192w('')
193
194for k, v in pairs(defines) do
195  w('#define ' .. k .. ' ' .. v)
196end
197opt_fd:close()
198