1 # This file defines VimFx’s config file API.
3 defaults = require('./defaults')
4 prefs = require('./prefs')
5 utils = require('./utils')
8 counter = new utils.Counter({start: 10000, step: 100})
10 createConfigAPI = (vimfx, {allowDeprecated = true} = {}) -> {
12 pref = alias(inputPref, allowDeprecated)
14 try return prefs.get(inputPref)
16 when pref of defaults.parsed_options
18 when pref of defaults.all_prefs or pref?.startsWith('custom.')
21 throw new Error("VimFx: Unknown option: #{pref}")
23 getDefault: (inputPref) ->
24 pref = alias(inputPref, allowDeprecated)
26 try return prefs.default.get(inputPref)
28 when pref of defaults.parsed_options or pref?.startsWith('custom.')
29 throw new Error("VimFx: No default for option: #{pref}")
30 when pref of defaults.all_prefs
31 defaults.all_prefs[pref]
33 throw new Error("VimFx: Unknown option: #{pref}")
35 set: (inputPref, value) ->
36 pref = alias(inputPref, allowDeprecated)
38 when pref of defaults.parsed_options
39 previousValue = vimfx.options[pref]
40 vimfx.options[pref] = value
41 onShutdown(vimfx, -> vimfx.options[pref] = previousValue)
42 when pref of defaults.all_prefs or pref?.startsWith('custom.')
43 previousValue = if prefs.has(pref) then prefs.get(pref) else null
44 prefs.set(pref, value)
45 onShutdown(vimfx, -> prefs.set(pref, previousValue))
47 throw new Error("VimFx: Unknown option: #{pref}")
49 addCommand: ({name, description, mode, category, order} = {}, fn) ->
51 category ?= if mode == 'normal' then 'misc' else ''
52 order ?= counter.tick()
54 unless typeof name == 'string'
56 "VimFx: A command name as a string is required. Got: #{name}"
58 unless /^[a-z_]+$/.test(name)
60 "VimFx: Command names should only consist of a-z (lowercase) and
61 underscores. Got: #{name}"
63 unless typeof description == 'string' and description != ''
65 "VimFx: Commands must have a non-empty description. Got: #{description}"
67 unless utils.has(vimfx.modes, mode)
68 modes = Object.keys(vimfx.modes).join(', ')
70 "VimFx: Unknown mode. Available modes are: #{modes}. Got: #{mode}"
72 unless utils.has(vimfx.options.categories, category)
73 categories = Object.keys(vimfx.options.categories).join(', ')
75 "VimFx: Unknown category. Available categories are: #{categories}.
78 unless typeof order == 'number'
79 throw new Error("VimFx: Command order must be a number. Got: #{order}")
80 unless typeof fn == 'function'
81 throw new Error("VimFx: Commands need a function to run. Got: #{fn}")
83 pref = "#{defaults.BRANCH}custom.mode.#{mode}.#{name}"
84 prefs.root.default.set(pref, '')
85 vimfx.modes[mode].commands[name] = {
86 pref, category, order, run: fn, description
88 onShutdown(vimfx, -> delete vimfx.modes[mode].commands[name])
90 addOptionOverrides: (rules...) ->
91 validateRules(rules, (override) ->
92 unless Object::toString.call(override) == '[object Object]'
97 unless vimfx.optionOverrides
98 vimfx.optionOverrides = []
99 vimfx.options = new Proxy(vimfx.options, {
100 get: (options, pref) ->
101 location = utils.getCurrentLocation()
102 return options[pref] unless location
103 overrides = getOverrides(vimfx.optionOverrides ? [], location)
104 return overrides?[pref] ? options[pref]
106 onShutdown(vimfx, -> vimfx.optionOverrides = null)
108 vimfx.optionOverrides.push(rules...)
110 addKeyOverrides: (rules...) ->
111 validateRules(rules, (override) ->
112 unless Array.isArray(override) and
113 override.every((item) -> typeof item == 'string')
114 return 'an array of strings'
118 unless vimfx.keyOverrides
119 vimfx.keyOverrides = []
120 vimfx.options.keyValidator = (keyStr, mode) ->
121 return true unless mode == 'normal'
122 location = utils.getCurrentLocation()
123 return true unless location
124 overrides = getOverrides(vimfx.keyOverrides ? [], location)
125 return keyStr not in (overrides ? [])
126 onShutdown(vimfx, -> vimfx.keyOverrides = null)
128 vimfx.keyOverrides.push(rules...)
130 send: (vim, message, data = null, callback = null) ->
131 unless vim instanceof Vim
133 "VimFx: The first argument must be a vim object. Got: #{vim}"
135 unless typeof message == 'string'
137 "VimFx: The second argument must be a message string. Got: #{message}"
139 if typeof data == 'function'
141 "VimFx: The third argument must not be a function. Got: #{data}"
144 unless typeof callback == 'function' or callback == null
146 "VimFx: If provided, `callback` must be a function. Got: #{callback}"
148 vim._send(message, data, callback, {prefix: 'config:'})
150 on: (event, listener) ->
151 validateEventListener(event, listener)
152 vimfx.on(event, listener)
153 onShutdown(vimfx, -> vimfx.off(event, listener))
155 off: (event, listener) ->
156 validateEventListener(event, listener)
157 vimfx.off(event, listener)
162 # Don’t crash the users’s entire config file on startup if they happen to try to
163 # set a renamed pref (only warn), but do throw an error if they reload the
164 # config file; then they could update while editing the file anyway.
165 alias = (pref, allowDeprecated) ->
166 if pref of renamedPrefs
167 newPref = renamedPrefs[pref]
168 message = "VimFx: `#{pref}` has been renamed to `#{newPref}`."
170 console.warn(message)
173 throw new Error(message)
178 'hint_chars': 'hints.chars'
179 'hints_sleep': 'hints.sleep'
180 'hints_timeout': 'hints.matched_timeout'
181 'hints_peek_through': 'hints.peek_through'
182 'hints_toggle_in_tab': 'hints.toggle_in_tab'
183 'hints_toggle_in_background': 'hints.toggle_in_background'
184 'mode.hints.delete_hint_char': 'mode.hints.delete_char'
187 getOverrides = (rules, args...) ->
188 for [matcher, override] in rules
189 return override if matcher(args...)
192 validateRules = (rules, overrideValidator) ->
194 unless Array.isArray(rule)
196 "VimFx: An override rule must be an array. Got: #{rule}"
198 unless rule.length == 2
200 "VimFx: An override rule array must be of length 2. Got: #{rule.length}"
202 [matcher, override] = rule
203 unless typeof matcher == 'function'
205 "VimFx: The first item of an override rule array must be a function.
208 overrideValidationMessage = overrideValidator(override)
209 if overrideValidationMessage
211 "VimFx: The second item of an override rule array must be
212 #{overrideValidationMessage}. Got: #{override}"
216 validateEventListener = (event, listener) ->
217 unless typeof event == 'string'
219 "VimFx: The first argument must be a string. Got: #{event}"
221 unless typeof listener == 'function'
223 "VimFx: The second argument must be a function. Got: #{listener}"
227 onShutdown = (vimfx, handler) ->
230 vimfx.off('shutdown', fn)
231 vimfx.on('shutdown', fn)
233 module.exports = createConfigAPI