spawn-process: Better error information in case of timeout
[conkeror.git] / modules / interactive.js
blob6249801c3eedc7af07096d6a7cc2932d6bf8a1d3
1 /**
2  * (C) Copyright 2007-2009 John J. Foerch
3  * (C) Copyright 2007-2008 Jeremy Maitin-Shepard
4  *
5  * Use, modification, and distribution are subject to the terms specified in the
6  * COPYING file.
7 **/
9 require("utils.js");
11 var interactive_commands = {};
13 /**
14  * name: string name of the command.
15  *
16  * doc: Documentation string, may be null.
17  *
18  * handler: A function to handle the command.
19  *
20  * The $prefix keyword, when true, means that the command
21  * is a prefix-command.
22  */
23 define_keywords("$prefix", "$browser_object", "$prompt");
24 function interactive (name, doc, handler) {
25     keywords(arguments);
26     var cmd = {
27         name: name,
28         handler: handler,
29         browser_object: arguments.$browser_object,
30         prefix: arguments.$prefix,
31         doc: doc,
32         shortdoc: get_shortdoc_string(doc),
33         prompt: arguments.$prompt,
34         source_code_reference: get_caller_source_code_reference() };
35     interactive_commands[name] = cmd;
36     return name;
39 function interactive_error (str) {
40     var e = new Error(str);
41     e.__proto__ = interactive_error.prototype;
42     return e;
44 interactive_error.prototype.__proto__ = Error.prototype;
47 function interactive_context (buffer) {
48     this.local = conkeror;
49     this.buffer = buffer;
50     if (buffer) {
51         this.window = this.buffer.window;
52         if (buffer.page) {
53             this.local = buffer.page.local;
54         } else {
55             this.local = buffer.local;
56         }
57     }
59 interactive_context.prototype = {
60     constructor: interactive_context,
61     toString: function () "#<interactive_context>",
62     get P () this.prefix_argument,
63     get p () univ_arg_to_number(this.prefix_argument),
64     set p (default_value) univ_arg_to_number(this.prefix_argument, default_value),
65     get minibuffer () this.window.minibuffer,
66     prefix_argument: null,
67     repeat: null
71 function handle_interactive_error (window, e) {
72     if (! window)
73         throw e;
74     if (e instanceof interactive_error) {
75         window.minibuffer.message(e.message);
76     } else if (e instanceof abort) {
77         window.minibuffer.message("Quit");
78     } else {
79         dump_error(e);
80         window.minibuffer.message("call interactively: " + e);
81     }
84 // Coroutine that runs an interactive command synchronously.
85 // Returns true if prefix command, returns false otherwise.
86 // Exceptions are allowed to pass through.
87 function run_interactively (I, command) {
88     var handler;
89     var window = I.window;
91     if (typeof command == "function") {
92         // Special interactive command
93         yield command(I);
94         yield co_return(false);
95     }
97     var cmd = interactive_commands[command];
98     if (!cmd)
99         throw interactive_error("Invalid command: " + command);
101     I.command = cmd;
102     handler = cmd.handler;
104     while (typeof handler == "string") {
105         let parent = interactive_commands[handler];
106         handler = parent.handler;
107         if (handler == command) {
108             throw interactive_error("circular command alias: " + command);
109         }
110     }
112     yield handler(I);
114     yield co_return(cmd.prefix ? true : false);
118  * Runs the interactive command asynchronously.
120  * Returns a Promise which will be resolved (with `undefined') when
121  * the command completes.
122  **/
123 function call_interactively (I, command) {
124     return spawn(function () {
125         try {
126             yield run_interactively(I, command);
127         } catch (e) {
128             handle_interactive_error(I.window, e);
129         }
130     }());
133 function alternates () {
134     let alts = Array.prototype.slice.call(arguments, 0);
135     return function (I) {
136         var index = 0;
137         if (array_p(I.prefix_argument)) {
138             let num = I.prefix_argument = I.prefix_argument[0];
139             while (num >= 4 && index + 1 < alts.length) {
140                 num = num / 4;
141                 index++;
142             }
143         }
144         yield alts[index](I);
145     };
150  * Utility functions for use in the rc to alter the behavior
151  * of interactive commands.
152  */
153 function set_handler (name, handler) {
154     var cmd = interactive_commands[name];
155     cmd.handler = handler;
158 function set_default_browser_object (name, browser_object) {
159     var cmd = interactive_commands[name];
160     cmd.browser_object = browser_object;
163 provide("interactive");