2 * (C) Copyright 2008 Jeremy Maitin-Shepard
3 * (C) Copyright 2008 Nelson Elhage
4 * (C) Copyright 2008 David Glasser
5 * (C) Copyright 2009 John J. Foerch
7 * Use, modification, and distribution are subject to the terms specified in the
11 require("special-buffer.js");
12 require("interactive.js");
14 function where_is_command (buffer, command) {
15 var keymaps = get_current_keymaps(buffer.window);
16 var list = keymap_lookup_command(keymaps, command);
19 msg = command + " is not on any key";
21 msg = command + " is on " + list.join(", ");
22 buffer.window.minibuffer.message(msg);
24 interactive("where-is", null, function (I) {
25 where_is_command(I.buffer,
26 (yield I.minibuffer.read_command($prompt = "Where is command:")));
29 function help_document_generator (document, buffer) {
30 dom_generator.call(this, document, XHTML_NS);
33 help_document_generator.prototype = {
34 constructor: help_document_generator,
35 __proto__: dom_generator.prototype,
37 key_binding: function (str, parent) {
38 var node = this.element("span", "class", "key-binding");
41 parent.appendChild(node);
45 source_code_reference: function (ref, parent) {
46 var f = this.document.createDocumentFragment();
47 var module_name = ref.module_name;
48 var buffer = this.buffer;
49 //f.appendChild(this.text(module_name != null ? "module " : "file "));
50 var x = this.element("a",
51 "class", "source-code-reference",
52 "href", "javascript:");
53 x.addEventListener("click", function (event) {
56 yield ref.open_in_editor();
58 handle_interactive_error(buffer.window, e);
60 event.preventDefault();
61 event.stopPropagation();
62 }, false /* capture */);
63 x.textContent = (module_name != null ? module_name : ref.file_name);
66 parent.appendChild(f);
70 command_name: function (name, parent) {
71 var node = this.element("span", "class", "command");
72 this.text(name, node);
74 parent.appendChild(node);
78 command_reference: function (name, parent) {
79 var node = this.element("a",
81 "href", "javascript:");
82 var buffer = this.buffer;
83 node.addEventListener("click", function (event) {
84 /* FIXME: don't hardcode browse target */
85 describe_command(buffer, name, OPEN_NEW_BUFFER);
86 event.preventDefault();
87 event.stopPropagation();
88 }, false /* capture */);
89 this.text(name, node);
91 parent.appendChild(node);
95 variable_reference: function (name, parent) {
96 var node = this.element("a", "class", "variable", "href", "#");
97 /* FIXME: make this work */
98 this.text(name, node);
100 parent.appendChild(node);
104 help_text: function (str, parent) {
105 var paras = str.split("\n");
106 var f = this.document.createDocumentFragment();
107 for (var i = 0; i < paras.length; ++i) {
109 if (para.length == 0)
112 var p = this.element("p", f);
114 var regexp = /`([a-zA-Z0-9_\-$]+)\'/g;
118 while ((match = regexp.exec(para)) != null) {
119 this.text(para.substring(last_index, match.index), p);
120 var command = match[1];
121 /* FIXME: check if it is a valid command */
122 this.command_reference(command, p);
123 last_index = regexp.lastIndex;
125 if (last_index < para.length)
126 this.text(para.substring(last_index), p);
129 parent.appendChild(f);
133 add_help_stylesheet: function () {
134 this.add_stylesheet("chrome://conkeror-gui/content/help.css");
139 function help_buffer_modality (buffer, element) {
140 buffer.keymaps.push(help_buffer_keymap);
147 define_keywords("$binding_list");
148 function describe_bindings_buffer (window) {
149 this.constructor_begin();
151 special_buffer.call(this, window, forward_keywords(arguments));
152 this.binding_list = arguments.$binding_list;
153 this.modalities.push(help_buffer_modality);
154 this.constructor_end();
156 describe_bindings_buffer.prototype = {
157 constructor: describe_bindings_buffer,
158 toString: function () "#<describe_bindings_buffer>",
160 title: "Key bindings",
162 description: "*bindings*",
164 generate: function () {
165 var d = this.document;
166 var list = this.binding_list;
167 delete this.binding_list;
169 var list_by_keymap = {};
170 var keymap_list = [];
171 for each (let x in list) {
172 let name = x.bound_in || "";
174 if (name in list_by_keymap)
175 km = list_by_keymap[name];
177 km = list_by_keymap[name] = {list_by_category: {}, category_list: [], name: name};
178 keymap_list.push(km);
180 let catname = x.category || "";
182 if (catname in km.list_by_category)
183 cat = km.list_by_category[catname];
185 cat = km.list_by_category[catname] = [];
188 km.category_list.unshift(cat);
190 km.category_list.push(cat);
195 var g = new help_document_generator(d, this);
196 g.add_help_stylesheet();
198 d.body.setAttribute("class", "help-list");
200 for each (let km in keymap_list) {
201 g.text(km.name, g.element("h1", d.body));
202 for each (let cat in km.category_list) {
204 g.text(cat.name, g.element("h2", d.body));
206 let table = g.element("table", d.body);
207 for (var i = 0; i < cat.length; ++i) {
209 let tr = g.element("tr", table, "class", (i % 2 == 0) ? "even" : "odd");
210 let seq_td = g.element("td", tr, "class", "key-binding");
211 g.text(bind.seq, seq_td);
212 let command_td = g.element("td", tr, "class", "command");
214 if (bind.command != null) {
215 if (typeof(bind.command) == "function") {
216 g.text("[function]", command_td);
218 let cmd = interactive_commands[bind.command];
220 g.command_reference(cmd.name, command_td);
221 help_str = cmd.shortdoc;
223 g.text(bind.command, command_td);
226 } else if (bind.keymap != null) {
227 g.text("["+bind.keymap+"]", command_td);
228 } else if (bind.fallthrough)
229 g.text("[pass through]", command_td);
230 let help_td = g.element("td", tr, "class", "help");
231 g.text(help_str || "", help_td);
237 __proto__: special_buffer.prototype
241 function describe_bindings (buffer, target, keymaps, prefix) {
244 keymaps = get_current_keymaps(buffer.window);
246 prefix = format_binding_sequence(
247 prefix.map(function (x) { return {key:x}; }))+" ";
250 for_each_key_binding(keymaps, function (binding_stack) {
251 var last = binding_stack[binding_stack.length - 1];
252 //we don't care about auto-generated keymap bindings.
253 if (last.keymap && last.keymap.anonymous)
257 for (let i = binding_stack.length - 1; i >= 0; --i) {
258 bound_in = binding_stack[i].bound_in;
260 if (bound_in.name && !bound_in.anonymous)
262 bound_in = bound_in.bound_in;
266 if (last.keymap && ! last.keymap.anonymous)
267 keymap = last.keymap.name;
268 var bind = {seq: prefix+format_binding_sequence(binding_stack),
269 fallthrough: last.fallthrough,
270 command: last.command,
272 bound_in: bound_in.name,
273 category: last.category
277 create_buffer(buffer.window, buffer_creator(describe_bindings_buffer,
279 $binding_list = list),
282 function describe_bindings_new_buffer (I) {
283 describe_bindings(I.buffer, OPEN_NEW_BUFFER);
285 function describe_bindings_new_window (I) {
286 describe_bindings(I.buffer, OPEN_NEW_WINDOW);
288 interactive("describe-bindings",
289 "Show a help buffer describing the bindings in the context keymaps, "+
290 "meaning the top-level keymaps according to the focus context in the "+
292 alternates(describe_bindings_new_buffer,
293 describe_bindings_new_window));
295 function describe_active_bindings_new_buffer (I) {
296 describe_bindings(I.buffer, OPEN_NEW_BUFFER,
297 I.keymaps || get_current_keymaps(I.buffer.window),
298 I.key_sequence.slice(0, -1));
300 function describe_active_bindings_new_window (I) {
301 describe_bindings(I.buffer, OPEN_NEW_WINDOW,
302 I.keymaps || get_current_keymaps(I.buffer.window),
303 I.key_sequence.slice(0, -1));
305 interactive("describe-active-bindings",
306 "Show a help buffer describing the bindings in the active keymaps, "+
307 "meaning the keymaps in the middle of an ongoing key sequence. This "+
308 "command is intended to be called via `sequence_help_keymap'. For "+
309 "that reason, `describe-active-bindings' does not consume and prefix "+
310 "commands like `universal-argument', as doing so would lead to "+
311 "ambiguities with respect to the intent of the user.",
312 describe_active_bindings_new_buffer);
319 define_keywords("$command_list");
320 function apropos_command_buffer (window) {
321 this.constructor_begin();
323 special_buffer.call(this, window, forward_keywords(arguments));
324 this.command_list = arguments.$command_list;
325 this.modalities.push(help_buffer_modality);
326 this.constructor_end();
328 apropos_command_buffer.prototype = {
329 constructor: apropos_command_buffer,
330 toString: function () "#<apropos_command_buffer>",
332 title: "Apropos commands",
334 description: "*Apropos*",
336 generate: function () {
337 var d = this.document;
338 var list = this.command_list;
339 delete this.command_list;
341 var g = new help_document_generator(d, this);
342 g.add_help_stylesheet();
344 d.body.setAttribute("class", "help-list");
346 var table = d.createElementNS(XHTML_NS, "table");
347 for (var i = 0; i < list.length; ++i) {
348 var binding = list[i];
349 var tr = d.createElementNS(XHTML_NS, "tr");
350 tr.setAttribute("class", (i % 2 == 0) ? "even" : "odd");
352 var command_td = d.createElementNS(XHTML_NS,"td");
353 g.command_reference(binding.name, command_td);
356 if (binding.cmd.shortdoc != null)
357 shortdoc = binding.cmd.shortdoc;
358 tr.appendChild(command_td);
360 var shortdoc_td = d.createElementNS(XHTML_NS, "td");
361 shortdoc_td.setAttribute("class", "help");
362 shortdoc_td.textContent = shortdoc;
363 tr.appendChild(shortdoc_td);
365 table.appendChild(tr);
367 d.body.appendChild(table);
370 __proto__: special_buffer.prototype
374 /* TODO: support regexps/etc. */
375 function apropos_command (buffer, substring, target) {
377 for (let [name, cmd] in Iterator(interactive_commands)) {
378 if (name.indexOf(substring) != -1) {
379 var binding = {name: name, cmd: cmd};
383 list.sort(function (a,b) {
390 create_buffer(buffer.window, buffer_creator(apropos_command_buffer,
392 $command_list = list),
396 function apropos_command_new_buffer (I) {
397 apropos_command(I.buffer,
398 (yield I.minibuffer.read($prompt = "Apropos command:",
399 $history = "apropos")),
402 function apropos_command_new_window (I) {
403 apropos_command(I.buffer,
404 (yield I.minibuffer.read($prompt = "Apropos command:",
405 $history = "apropos")),
408 interactive("apropos-command", "List commands whose names contain a given substring.",
409 alternates(apropos_command_new_buffer, apropos_command_new_window));
417 define_keywords("$command", "$bindings");
418 function describe_command_buffer (window) {
419 this.constructor_begin();
421 special_buffer.call(this, window, forward_keywords(arguments));
422 this.bindings = arguments.$bindings;
423 this.command = arguments.$command;
424 this.cmd = interactive_commands[this.command];
425 this.source_code_reference = this.cmd.source_code_reference;
426 this.modalities.push(help_buffer_modality);
427 this.constructor_end();
429 describe_command_buffer.prototype = {
430 constructor: describe_command_buffer,
431 toString: function () "#<describe_command_buffer>",
433 get title () { return "Command help: " + this.command; },
435 description: "*help*",
437 generate: function () {
438 var d = this.document;
440 var g = new help_document_generator(d, this);
442 g.add_help_stylesheet();
443 d.body.setAttribute("class", "describe-command");
447 p = g.element("p", d.body);
448 g.command_reference(this.command, p);
449 var cmd = interactive_commands[this.command];
450 if (cmd.source_code_reference) {
451 g.text(" is an interactive command in ", p);
452 g.source_code_reference(cmd.source_code_reference, p);
455 g.text(" is an interactive command.", p);
458 if (this.bindings.length > 0) {
459 p = g.element("p", d.body);
460 g.text("It is bound to ", p);
461 for (var i = 0; i < this.bindings.length; ++i) {
464 g.key_binding(this.bindings[i], p);
470 g.help_text(cmd.doc, d.body);
473 __proto__: special_buffer.prototype
477 function describe_command (buffer, command, target) {
478 var keymaps = get_current_keymaps(buffer.window);
479 var bindings = keymap_lookup_command(keymaps, command);
480 create_buffer(buffer.window,
481 buffer_creator(describe_command_buffer,
484 $bindings = bindings),
487 function describe_command_new_buffer (I) {
488 describe_command(I.buffer, (yield I.minibuffer.read_command($prompt = "Describe command:")),
491 function describe_command_new_window (I) {
492 describe_command(I.buffer, (yield I.minibuffer.read_command($prompt = "Describe command:")),
495 interactive("describe-command", null,
496 alternates(describe_command_new_buffer, describe_command_new_window));
500 function view_referenced_source_code (buffer) {
501 if (buffer.source_code_reference == null)
502 throw interactive_error("Command not valid in current buffer.");
503 yield buffer.source_code_reference.open_in_editor();
505 interactive("view-referenced-source-code", null,
506 function (I) {yield view_referenced_source_code(I.buffer);});
513 define_keywords("$binding", "$other_bindings", "$key_sequence");
514 function describe_key_buffer (window) {
515 this.constructor_begin();
517 special_buffer.call(this, window, forward_keywords(arguments));
518 this.key_sequence = arguments.$key_sequence;
519 this.bindings = arguments.$other_bindings;
520 this.bind = arguments.$binding;
521 this.source_code_reference = this.bind.source_code_reference;
522 this.modalities.push(help_buffer_modality);
523 this.constructor_end();
525 describe_key_buffer.prototype = {
526 constructor: describe_key_buffer,
527 toString: function () "#<describe_key_buffer>",
529 get title () { return "Key help: " + this.key_sequence; },
531 description: "*help*",
533 generate: function () {
534 var d = this.document;
536 var g = new help_document_generator(d, this);
538 g.add_help_stylesheet();
539 d.body.setAttribute("class", "describe-key");
543 p = g.element("p", d.body);
544 g.key_binding(this.key_sequence, p);
545 g.text(" is bound to the command ", p);
546 var command = this.bind.command;
548 g.command_name("[pass through]", p);
550 g.command_reference(command, p);
551 if (this.bind.browser_object != null) {
552 g.text(" with the browser object, ", p);
553 if (this.bind.browser_object instanceof Function) {
554 g.text("<anonymous browser-object function>", p);
555 } else if (this.bind.browser_object instanceof browser_object_class) {
556 g.text(this.bind.browser_object.name, p);
557 } else if (typeof(this.bind.browser_object) == "string") {
558 g.text('"'+this.bind.browser_object+'"', p);
560 g.text(this.bind.browser_object, p);
563 if (this.source_code_reference) {
565 g.source_code_reference(this.source_code_reference, p);
569 if (command != null) {
570 p = g.element("p", d.body);
571 g.command_reference(command, p);
572 var cmd = interactive_commands[command];
573 if (cmd.source_code_reference) {
574 g.text(" is an interactive command in ", p);
575 g.source_code_reference(cmd.source_code_reference, p);
578 g.text(" is an interactive command.", p);
581 if (this.bindings.length > 0) {
582 p = g.element("p", d.body);
583 g.text("It is bound to ", p);
584 for (var i = 0; i < this.bindings.length; ++i) {
587 g.key_binding(this.bindings[i], p);
593 g.help_text(cmd.doc, d.body);
597 __proto__: special_buffer.prototype
601 function describe_key (buffer, key_info, target) {
603 var seq = key_info[0];
604 var bind = key_info[1];
605 var keymaps = get_current_keymaps(buffer.window);
607 bindings = keymap_lookup_command(keymaps, bind.command);
610 create_buffer(buffer.window,
611 buffer_creator(describe_key_buffer,
613 $key_sequence = seq.join(" "),
614 $other_bindings = bindings,
618 function describe_key_new_buffer (I) {
619 describe_key(I.buffer,
620 (yield I.minibuffer.read_key_binding($prompt = "Describe key:")),
623 function describe_key_new_window (I) {
624 describe_key(I.buffer,
625 (yield I.minibuffer.read_key_binding($prompt = "Describe key:")),
629 function describe_key_briefly (buffer, key_info) {
631 var seq = key_info[0];
632 var bind = key_info[1];
633 var browser_object = "";
634 if (bind.browser_object != null) {
635 browser_object += " on the browser object, ";
636 if (bind.browser_object instanceof Function) {
637 browser_object += "<anonymous browser-object function>";
638 } else if (bind.browser_object instanceof browser_object_class) {
639 browser_object += bind.browser_object.name;
640 } else if (typeof(bind.browser_object) == "string") {
641 browser_object += '"'+bind.browser_object+'"';
643 browser_object += bind.browser_object;
646 buffer.window.minibuffer.message(seq.join(" ") + " runs the command " + bind.command + browser_object);
649 interactive("describe-key", null,
650 alternates(describe_key_new_buffer, describe_key_new_window));
652 interactive("describe-key-briefly", null,
654 describe_key_briefly(
656 (yield I.minibuffer.read_key_binding($prompt = "Describe key:")));
665 define_keywords("$variable");
666 function describe_variable_buffer (window) {
667 this.constructor_begin();
669 special_buffer.call(this, window, forward_keywords(arguments));
670 this.variable = arguments.$variable;
671 this.cmd = user_variables[this.variable];
672 this.source_code_reference = this.cmd.source_code_reference;
673 this.modalities.push(help_buffer_modality);
674 this.constructor_end();
676 describe_variable_buffer.prototype = {
677 constructor: describe_variable_buffer,
678 toString: function () "#<describe_variable_buffer>",
680 get title () { return "Variable help: " + this.variable; },
682 description: "*help*",
684 generate: function () {
685 var d = this.document;
687 var g = new help_document_generator(d, this);
689 g.add_help_stylesheet();
690 d.body.setAttribute("class", "describe-variable");
694 p = g.element("p", d.body);
695 g.variable_reference(this.variable, p);
696 var uvar = user_variables[this.variable];
697 if (uvar.source_code_reference) {
698 g.text(" is a user variable in ", p);
699 g.source_code_reference(uvar.source_code_reference, p);
702 g.text(" is a user variable.", p);
705 p = g.element("p", d.body);
706 g.text("Its value is: ", p);
707 let value = conkeror[this.variable];
709 let s = pretty_print_value(value);
710 let pre = g.element("pre", p);
714 if (uvar.doc != null)
715 g.help_text(uvar.doc, d.body);
717 if (uvar.default_value !== undefined &&
718 (uvar.default_value !== value ||
719 (typeof(uvar.default_value) != "object"))) {
720 p = g.element("p", d.body);
721 g.text("Its default value is: ", p);
723 let s = pretty_print_value(uvar.default_value);
724 let pre = g.element("pre", p);
730 __proto__: special_buffer.prototype
734 function describe_variable (buffer, variable, target) {
735 create_buffer(buffer.window,
736 buffer_creator(describe_variable_buffer,
738 $variable = variable),
741 function describe_variable_new_buffer (I) {
742 describe_variable(I.buffer,
743 (yield I.minibuffer.read_user_variable($prompt = "Describe variable:")),
746 function describe_variable_new_window (I) {
747 describe_variable(I.buffer,
748 (yield I.minibuffer.read_user_variable($prompt = "Describe variable:")),
751 interactive("describe-variable", null,
752 alternates(describe_variable_new_buffer, describe_variable_new_window));
757 * Describe Preference
760 function describe_preference (buffer, preference, target) {
761 let key = preference.charAt(0).toUpperCase() + preference.substring(1);
762 let url = "http://kb.mozillazine.org/" + key;
763 browser_object_follow(buffer, target, url);
765 function describe_preference_new_buffer (I) {
766 describe_preference(I.buffer, (yield I.minibuffer.read_preference($prompt = "Describe preference:")),
769 function describe_preference_new_window (I) {
770 describe_preference(I.buffer, (yield I.minibuffer.read_preference($prompt = "Describe preference:")),
773 interactive("describe-preference", null,
774 alternates(describe_preference_new_buffer, describe_preference_new_window));