3 function interactive_variable(name, value)
9 function interactive_variable_reference(name)
14 function _get_interactive_variable_setter(name) {
15 return function (value) { return new interactive_variable(name, value); }
18 function _get_interactive_variable_getter(name) {
19 return function () { return new interactive_variable_reference(name); }
23 function define_interactive_variable_keywords()
25 for (var i = 0; i < arguments.length; ++i)
27 var name = arguments[i];
28 this.__defineSetter__(name, _get_interactive_variable_setter(name));
29 this.__defineGetter__(name, _get_interactive_variable_getter(name));
33 define_interactive_variable_keywords("$$", "$$1", "$$2", "$$3", "$$4", "$$5", "$$6", "$$7", "$$8", "$$9");
35 var interactive_commands = new string_hashmap();
37 function interactive_spec(args, sync_handler, async_handler)
40 this.sync_handler = sync_handler;
41 this.async_handler = async_handler;
44 define_keywords("$doc", "$sync", "$async");
45 function interactive_method()
47 keywords(arguments, $doc = null, $sync = null, $async = null);
48 var sync = arguments.$sync;
49 var async = arguments.$async;
50 if ((sync == null) == (async == null))
51 throw new Error("Invalid arguments.");
53 return new interactive_spec(arguments, sync, async);
55 i.is_interactive_method = true;
61 // Special interactive methods
62 function interactive_bind_spec(args)
64 this.handler = args[0];
65 this.args = Array.prototype.slice.call(args, 1);
68 I.bind = function () {
69 return new interactive_bind_spec(arguments);
73 * First argument is the command name.
75 * This is optionally followed by a documentation string.
77 * This is followed by the command function.
79 * Remaining arguments specify interactive arguments.
81 function interactive(name)
86 if (typeof(arguments[1]) == "string")
91 handler = arguments[offset++];
92 var args = Array.prototype.slice.call(arguments, offset);
95 var idx = doc.indexOf("\n");
97 shortdoc = doc.substring(0,idx);
101 var cmd = { name: name,
106 source_code_reference: get_caller_source_code_reference() };
108 interactive_commands.put(name, cmd);
111 function interactive_error(str) {
112 var e = new Error(str);
113 e.is_interactive_error = true;
117 function join_argument_lists(args1, args2)
119 if (args1.length == 0)
121 var positional_args = [];
122 var keyword_args = [];
123 var keywords_seen = new Object();
125 // First process the given arguments (args1)
126 for (var i = 0; i < args1.length; ++i)
129 if (arg instanceof keyword_argument)
131 keywords_seen[arg.name] = keyword_args.length;
132 keyword_args.push(arg);
134 positional_args[i] = arg;
137 // Process the argument list specified in the command definition (args2)
138 for (var i = 0; i < args2.length; ++i)
141 var actual_arg = arg;
142 if (arg instanceof interactive_variable)
143 actual_arg = arg.value;
144 if (actual_arg instanceof keyword_argument)
146 if (actual_arg.name in keywords_seen)
148 if (arg != actual_arg)
150 var j = keywords_seen[actual_arg.name];
151 keyword_args[j] = new interactive_variable(arg.name, keyword_args[j]);
154 keyword_args.push(arg);
157 if (positional_args[i] === undefined)
158 positional_args[i] = arg;
159 else if (actual_arg != arg)
160 positional_args[i] = new interactive_variable(arg.name, positional_args[i]);
163 return positional_args.concat(keyword_args);
167 // Any additional arguments specify "given" arguments to the function.
168 function call_interactively(ctx, command)
173 if (ctx.buffer == null)
174 ctx.buffer = ctx.window.buffers.current;
175 else if (ctx.window == null)
176 ctx.window = ctx.buffer.window;
178 var window = ctx.window;
180 var explicit_args = Array.prototype.slice.call(arguments, 2);
182 if (typeof(cmd) == "function") {
184 top_args = explicit_args;
186 var cmd = interactive_commands.get(command);
189 window.minibuffer.message("Invalid command: " + command);
192 ctx.command = command;
193 top_args = join_argument_lists(explicit_args, cmd.arguments);
194 handler = cmd.handler;
197 var variable_values = new Object();
199 var state = [{args: top_args, out_args: [], handler: handler}];
200 var next_variable = null;
202 function process_next()
206 var top = state[state.length - 1];
208 dumpln("at level " + state.length +", args.length: " + top.args.length
209 +", out_args.length: " + top.out_args.length);
212 // Check if we are done with this level
213 if (top.args.length == top.out_args.length)
216 next_variable = top.variable;
217 if (top.async_handler)
219 top.async_handler.apply(null, [ctx,cont].concat(top.out_args));
223 if (top.sync_handler)
224 result = top.sync_handler.apply(null, [ctx].concat(top.out_args));
225 else if (top.handler)
227 result = top.handler.apply(null, top.out_args);
228 if (state.length == 0)
235 // Not done: we need to process the next argument at this level
236 var arg = top.args[top.out_args.length];
238 if (arg instanceof interactive_variable)
244 if (arg instanceof keyword_argument)
246 if (spec instanceof interactive_bind_spec)
248 state.push({args: spec.args, out_args: [], handler: spec.handler, variable: variable});
251 // Expand an interactive_method
252 if (typeof(spec) == "function" && spec.is_interactive_method)
254 if (spec instanceof interactive_spec) {
255 state.push({args: spec.args, out_args: [],
256 sync_handler: spec.sync_handler,
257 async_handler: spec.async_handler,
258 variable: variable});
260 if (spec instanceof interactive_variable_reference)
262 if (!(spec.name in variable_values))
263 throw new Error("Invalid interactive variable reference: " + spec.name);
264 spec = variable_values[spec.name];
267 // Just a normal value
268 if (arg instanceof keyword_argument)
269 arg = new keyword_argument(arg.name, spec);
272 top.out_args.push(arg);
274 variable_values[variable] = spec;
278 if (e.is_interactive_error) {
279 window.minibuffer.message("" + e);
281 window.minibuffer.message("call_interactively: " + e);
287 function push_arg(out_arg)
289 var top = state[state.length - 1];
290 var arg = top.args[top.out_args.length];
291 if (arg instanceof keyword_argument)
292 out_arg = new keyword_argument(arg.name, out_arg);
293 top.out_args.push(out_arg);
295 variable_values[next_variable] = out_arg;
298 function cont(out_arg)
307 I.p = interactive_method(
308 $doc = "Prefix argument converted to a number",
309 $sync = function (ctx, default_value) {
310 return univ_arg_to_number(ctx.prefix_argument, default_value);
313 I.P = interactive_method(
314 $doc = "Raw prefix argument",
315 $sync = function (ctx) {
316 return ctx.prefix_argument;
319 I.current_window = interactive_method(
320 $doc = "Current window",
321 $sync = function (ctx) {
325 I.current_command = interactive_method(
326 $doc = "Current command",
327 $sync = function (ctx) {
331 I.e = interactive_method(
332 $doc = "Most recent keyboard event",
333 $sync = function (ctx) {
337 I.s = interactive_method(
338 $doc = "Read a string from the minibuffer",
339 $async = function (ctx, cont) {
341 ctx.window.minibuffer.read($prompt = "String:", $history = "string",
342 forward_keywords(arguments),
346 I.n = interactive_method(
347 $doc = "Read a number from the minibuffer",
348 $async = function (ctx, cont) {
349 keywords(arguments, $prompt = "Number:", $history = "number");
350 ctx.window.minibuffer.read($prompt = "Number:", $history = "number",
354 I.pref = interactive_method(
355 $sync = function (pref) {
356 var type = preferences.getPrefType (pref);
358 case preferences.PREF_BOOL:
359 return preferences.getBoolPref (pref);
360 case preferences.PREF_INT:
361 return preferences.getIntPref (pref);
362 case preferences.PREF_STRING:
363 return preferences.getCharPref (pref);
369 I.C = interactive_method(
370 $doc = "Name of a command",
371 $async = function (ctx, cont) {
372 minibuffer_read_command(ctx.window.minibuffer,
373 forward_keywords(arguments),
377 I.f = interactive_method(
378 $doc = "Existing file",
379 $async = function (ctx, cont) {
380 keywords(arguments, $prompt = "File:", $initial_value = default_directory.path,
382 ctx.window.minibuffer.read(
383 $prompt = arguments.$prompt,
384 $initial_value = arguments.$initial_value,
385 $history = arguments.$history,
386 $callback = function(s) {
387 var f = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile);
393 // FIXME: eventually they will differ, when completion for files is added