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 title: "Key bindings",
160 description: "*bindings*",
162 generate: function () {
163 var d = this.document;
164 var list = this.binding_list;
165 delete this.binding_list;
167 var list_by_keymap = {};
168 var keymap_list = [];
169 for each (let x in list) {
170 let name = x.bound_in || "";
172 if (name in list_by_keymap)
173 km = list_by_keymap[name];
175 km = list_by_keymap[name] = {list_by_category: {}, category_list: [], name: name};
176 keymap_list.push(km);
178 let catname = x.category || "";
180 if (catname in km.list_by_category)
181 cat = km.list_by_category[catname];
183 cat = km.list_by_category[catname] = [];
186 km.category_list.unshift(cat);
188 km.category_list.push(cat);
193 var g = new help_document_generator(d, this);
194 g.add_help_stylesheet();
196 d.body.setAttribute("class", "help-list");
198 for each (let km in keymap_list) {
199 g.text(km.name, g.element("h1", d.body));
200 for each (let cat in km.category_list) {
202 g.text(cat.name, g.element("h2", d.body));
204 let table = g.element("table", d.body);
205 for (var i = 0; i < cat.length; ++i) {
207 let tr = g.element("tr", table, "class", (i % 2 == 0) ? "even" : "odd");
208 let seq_td = g.element("td", tr, "class", "key-binding");
209 g.text(bind.seq, seq_td);
210 let command_td = g.element("td", tr, "class", "command");
212 if (bind.command != null) {
213 if (typeof(bind.command) == "function") {
214 g.text("[function]", command_td);
216 let cmd = interactive_commands.get(bind.command);
218 g.command_reference(cmd.name, command_td);
219 help_str = cmd.shortdoc;
221 g.text(bind.command, command_td);
224 } else if (bind.keymap != null) {
225 g.text("["+bind.keymap+"]", command_td);
226 } else if (bind.fallthrough)
227 g.text("[pass through]", command_td);
228 let help_td = g.element("td", tr, "class", "help");
229 g.text(help_str || "", help_td);
235 __proto__: special_buffer.prototype
239 function describe_bindings (buffer, target, keymaps, prefix) {
242 keymaps = get_current_keymaps(buffer.window);
244 prefix = format_binding_sequence(
245 prefix.map(function (x) { return {key:x}; }))+" ";
248 for_each_key_binding(keymaps, function (binding_stack) {
249 var last = binding_stack[binding_stack.length - 1];
250 //we don't care about auto-generated keymap bindings.
251 if (last.keymap && last.keymap.anonymous)
255 for (let i = binding_stack.length - 1; i >= 0; --i) {
256 bound_in = binding_stack[i].bound_in;
258 if (bound_in.name && !bound_in.anonymous)
260 bound_in = bound_in.bound_in;
264 if (last.keymap && ! last.keymap.anonymous)
265 keymap = last.keymap.name;
266 var bind = {seq: prefix+format_binding_sequence(binding_stack),
267 fallthrough: last.fallthrough,
268 command: last.command,
270 bound_in: bound_in.name,
271 category: last.category
275 create_buffer(buffer.window, buffer_creator(describe_bindings_buffer,
277 $binding_list = list),
280 function describe_bindings_new_buffer (I) {
281 describe_bindings(I.buffer, OPEN_NEW_BUFFER);
283 function describe_bindings_new_window (I) {
284 describe_bindings(I.buffer, OPEN_NEW_WINDOW);
286 interactive("describe-bindings",
287 "Show a help buffer describing the bindings in the context keymaps, "+
288 "meaning the top-level keymaps according to the focus context in the "+
290 alternates(describe_bindings_new_buffer,
291 describe_bindings_new_window));
293 function describe_active_bindings_new_buffer (I) {
294 describe_bindings(I.buffer, OPEN_NEW_BUFFER,
295 I.keymaps || get_current_keymaps(I.buffer.window),
296 I.key_sequence.slice(0, -1));
298 function describe_active_bindings_new_window (I) {
299 describe_bindings(I.buffer, OPEN_NEW_WINDOW,
300 I.keymaps || get_current_keymaps(I.buffer.window),
301 I.key_sequence.slice(0, -1));
303 interactive("describe-active-bindings",
304 "Show a help buffer describing the bindings in the active keymaps, "+
305 "meaning the keymaps in the middle of an ongoing key sequence. This "+
306 "command is intended to be called via `sequence_help_keymap'. For "+
307 "that reason, `describe-active-bindings' does not consume and prefix "+
308 "commands like `universal-argument', as doing so would lead to "+
309 "ambiguities with respect to the intent of the user.",
310 describe_active_bindings_new_buffer);
317 define_keywords("$command_list");
318 function apropos_command_buffer (window) {
319 this.constructor_begin();
321 special_buffer.call(this, window, forward_keywords(arguments));
322 this.command_list = arguments.$command_list;
323 this.modalities.push(help_buffer_modality);
324 this.constructor_end();
326 apropos_command_buffer.prototype = {
327 constructor: apropos_command_buffer,
328 title: "Apropos commands",
330 description: "*Apropos*",
332 generate: function () {
333 var d = this.document;
334 var list = this.command_list;
335 delete this.command_list;
337 var g = new help_document_generator(d, this);
338 g.add_help_stylesheet();
340 d.body.setAttribute("class", "help-list");
342 var table = d.createElementNS(XHTML_NS, "table");
343 for (var i = 0; i < list.length; ++i) {
344 var binding = list[i];
345 var tr = d.createElementNS(XHTML_NS, "tr");
346 tr.setAttribute("class", (i % 2 == 0) ? "even" : "odd");
348 var command_td = d.createElementNS(XHTML_NS,"td");
349 g.command_reference(binding.name, command_td);
352 if (binding.cmd.shortdoc != null)
353 shortdoc = binding.cmd.shortdoc;
354 tr.appendChild(command_td);
356 var shortdoc_td = d.createElementNS(XHTML_NS, "td");
357 shortdoc_td.setAttribute("class", "help");
358 shortdoc_td.textContent = shortdoc;
359 tr.appendChild(shortdoc_td);
361 table.appendChild(tr);
363 d.body.appendChild(table);
366 __proto__: special_buffer.prototype
370 /* TODO: support regexps/etc. */
371 function apropos_command (buffer, substring, target) {
373 interactive_commands.for_each(function (name, cmd) {
374 if (name.indexOf(substring) != -1) {
375 var binding = {name: name, cmd: cmd};
379 list.sort(function (a,b) {
386 create_buffer(buffer.window, buffer_creator(apropos_command_buffer,
388 $command_list = list),
392 function apropos_command_new_buffer (I) {
393 apropos_command(I.buffer,
394 (yield I.minibuffer.read($prompt = "Apropos command:",
395 $history = "apropos")),
398 function apropos_command_new_window (I) {
399 apropos_command(I.buffer,
400 (yield I.minibuffer.read($prompt = "Apropos command:",
401 $history = "apropos")),
404 interactive("apropos-command", "List commands whose names contain a given substring.",
405 alternates(apropos_command_new_buffer, apropos_command_new_window));
413 define_keywords("$command", "$bindings");
414 function describe_command_buffer (window) {
415 this.constructor_begin();
417 special_buffer.call(this, window, forward_keywords(arguments));
418 this.bindings = arguments.$bindings;
419 this.command = arguments.$command;
420 this.cmd = interactive_commands.get(this.command);
421 this.source_code_reference = this.cmd.source_code_reference;
422 this.modalities.push(help_buffer_modality);
423 this.constructor_end();
425 describe_command_buffer.prototype = {
426 constructor: describe_command_buffer,
427 get title () { return "Command help: " + this.command; },
429 description: "*help*",
431 generate: function () {
432 var d = this.document;
434 var g = new help_document_generator(d, this);
436 g.add_help_stylesheet();
437 d.body.setAttribute("class", "describe-command");
441 p = g.element("p", d.body);
442 g.command_reference(this.command, p);
443 var cmd = interactive_commands.get(this.command);
444 if (cmd.source_code_reference) {
445 g.text(" is an interactive command in ", p);
446 g.source_code_reference(cmd.source_code_reference, p);
449 g.text(" is an interactive command.", p);
452 if (this.bindings.length > 0) {
453 p = g.element("p", d.body);
454 g.text("It is bound to ", p);
455 for (var i = 0; i < this.bindings.length; ++i) {
458 g.key_binding(this.bindings[i], p);
464 g.help_text(cmd.doc, d.body);
467 __proto__: special_buffer.prototype
471 function describe_command (buffer, command, target) {
472 var keymaps = get_current_keymaps(buffer.window);
473 var bindings = keymap_lookup_command(keymaps, command);
474 create_buffer(buffer.window,
475 buffer_creator(describe_command_buffer,
478 $bindings = bindings),
481 function describe_command_new_buffer (I) {
482 describe_command(I.buffer, (yield I.minibuffer.read_command($prompt = "Describe command:")),
485 function describe_command_new_window (I) {
486 describe_command(I.buffer, (yield I.minibuffer.read_command($prompt = "Describe command:")),
489 interactive("describe-command", null,
490 alternates(describe_command_new_buffer, describe_command_new_window));
494 function view_referenced_source_code (buffer) {
495 if (buffer.source_code_reference == null)
496 throw interactive_error("Command not valid in current buffer.");
497 yield buffer.source_code_reference.open_in_editor();
499 interactive("view-referenced-source-code", null,
500 function (I) {yield view_referenced_source_code(I.buffer);});
507 define_keywords("$binding", "$other_bindings", "$key_sequence");
508 function describe_key_buffer (window) {
509 this.constructor_begin();
511 special_buffer.call(this, window, forward_keywords(arguments));
512 this.key_sequence = arguments.$key_sequence;
513 this.bindings = arguments.$other_bindings;
514 this.bind = arguments.$binding;
515 this.source_code_reference = this.bind.source_code_reference;
516 this.modalities.push(help_buffer_modality);
517 this.constructor_end();
519 describe_key_buffer.prototype = {
520 constructor: describe_key_buffer,
521 get title () { return "Key help: " + this.key_sequence; },
523 description: "*help*",
525 generate: function () {
526 var d = this.document;
528 var g = new help_document_generator(d, this);
530 g.add_help_stylesheet();
531 d.body.setAttribute("class", "describe-key");
535 p = g.element("p", d.body);
536 g.key_binding(this.key_sequence, p);
537 g.text(" is bound to the command ", p);
538 var command = this.bind.command;
540 g.command_name("[pass through]", p);
542 g.command_reference(command, p);
543 if (this.bind.browser_object != null) {
544 g.text(" with the browser object, ", p);
545 if (this.bind.browser_object instanceof Function) {
546 g.text("<anonymous browser-object function>", p);
547 } else if (this.bind.browser_object instanceof browser_object_class) {
548 g.text(this.bind.browser_object.name, p);
549 } else if (typeof(this.bind.browser_object) == "string") {
550 g.text('"'+this.bind.browser_object+'"', p);
552 g.text(this.bind.browser_object, p);
555 if (this.source_code_reference) {
557 g.source_code_reference(this.source_code_reference, p);
561 if (command != null) {
562 p = g.element("p", d.body);
563 g.command_reference(command, p);
564 var cmd = interactive_commands.get(command);
565 if (cmd.source_code_reference) {
566 g.text(" is an interactive command in ", p);
567 g.source_code_reference(cmd.source_code_reference, p);
570 g.text(" is an interactive command.", p);
573 if (this.bindings.length > 0) {
574 p = g.element("p", d.body);
575 g.text("It is bound to ", p);
576 for (var i = 0; i < this.bindings.length; ++i) {
579 g.key_binding(this.bindings[i], p);
585 g.help_text(cmd.doc, d.body);
589 __proto__: special_buffer.prototype
593 function describe_key (buffer, key_info, target) {
595 var seq = key_info[0];
596 var bind = key_info[1];
597 var keymaps = get_current_keymaps(buffer.window);
599 bindings = keymap_lookup_command(keymaps, bind.command);
602 create_buffer(buffer.window,
603 buffer_creator(describe_key_buffer,
605 $key_sequence = seq.join(" "),
606 $other_bindings = bindings,
610 function describe_key_new_buffer (I) {
611 describe_key(I.buffer,
612 (yield I.minibuffer.read_key_binding($prompt = "Describe key:")),
615 function describe_key_new_window (I) {
616 describe_key(I.buffer,
617 (yield I.minibuffer.read_key_binding($prompt = "Describe key:")),
621 function describe_key_briefly (buffer, key_info) {
623 var seq = key_info[0];
624 var bind = key_info[1];
625 var browser_object = "";
626 if (bind.browser_object != null) {
627 browser_object += " on the browser object, ";
628 if (bind.browser_object instanceof Function) {
629 browser_object += "<anonymous browser-object function>";
630 } else if (bind.browser_object instanceof browser_object_class) {
631 browser_object += bind.browser_object.name;
632 } else if (typeof(bind.browser_object) == "string") {
633 browser_object += '"'+bind.browser_object+'"';
635 browser_object += bind.browser_object;
638 buffer.window.minibuffer.message(seq.join(" ") + " runs the command " + bind.command + browser_object);
641 interactive("describe-key", null,
642 alternates(describe_key_new_buffer, describe_key_new_window));
644 interactive("describe-key-briefly", null,
646 describe_key_briefly(
648 (yield I.minibuffer.read_key_binding($prompt = "Describe key:")));
657 define_keywords("$variable");
658 function describe_variable_buffer (window) {
659 this.constructor_begin();
661 special_buffer.call(this, window, forward_keywords(arguments));
662 this.variable = arguments.$variable;
663 this.cmd = user_variables[this.variable];
664 this.source_code_reference = this.cmd.source_code_reference;
665 this.modalities.push(help_buffer_modality);
666 this.constructor_end();
668 describe_variable_buffer.prototype = {
669 constructor: describe_variable_buffer,
670 get title () { return "Variable help: " + this.variable; },
672 description: "*help*",
674 generate: function () {
675 var d = this.document;
677 var g = new help_document_generator(d, this);
679 g.add_help_stylesheet();
680 d.body.setAttribute("class", "describe-variable");
684 p = g.element("p", d.body);
685 g.variable_reference(this.variable, p);
686 var uvar = user_variables[this.variable];
687 if (uvar.source_code_reference) {
688 g.text(" is a user variable in ", p);
689 g.source_code_reference(uvar.source_code_reference, p);
692 g.text(" is a user variable.", p);
695 p = g.element("p", d.body);
696 g.text("Its value is: ", p);
697 let value = conkeror[this.variable];
699 let s = pretty_print_value(value);
700 let pre = g.element("pre", p);
704 if (uvar.doc != null)
705 g.help_text(uvar.doc, d.body);
707 if (uvar.default_value !== undefined &&
708 (uvar.default_value !== value ||
709 (typeof(uvar.default_value) != "object"))) {
710 p = g.element("p", d.body);
711 g.text("Its default value is: ", p);
713 let s = pretty_print_value(uvar.default_value);
714 let pre = g.element("pre", p);
720 __proto__: special_buffer.prototype
724 function describe_variable (buffer, variable, target) {
725 create_buffer(buffer.window,
726 buffer_creator(describe_variable_buffer,
728 $variable = variable),
731 function describe_variable_new_buffer (I) {
732 describe_variable(I.buffer,
733 (yield I.minibuffer.read_user_variable($prompt = "Describe variable:")),
736 function describe_variable_new_window (I) {
737 describe_variable(I.buffer,
738 (yield I.minibuffer.read_user_variable($prompt = "Describe variable:")),
741 interactive("describe-variable", null,
742 alternates(describe_variable_new_buffer, describe_variable_new_window));
747 * Describe Preference
750 function describe_preference (buffer, preference, target) {
751 let key = preference.charAt(0).toUpperCase() + preference.substring(1);
752 let url = "http://kb.mozillazine.org/" + key;
753 browser_object_follow(buffer, target, url);
755 function describe_preference_new_buffer (I) {
756 describe_preference(I.buffer, (yield I.minibuffer.read_preference($prompt = "Describe preference:")),
759 function describe_preference_new_window (I) {
760 describe_preference(I.buffer, (yield I.minibuffer.read_preference($prompt = "Describe preference:")),
763 interactive("describe-preference", null,
764 alternates(describe_preference_new_buffer, describe_preference_new_window));