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 __proto__: dom_generator.prototype,
36 key_binding : function (str, parent) {
37 var node = this.element("span", "class", "key-binding");
40 parent.appendChild(node);
44 source_code_reference : function (ref, parent) {
45 var f = this.document.createDocumentFragment();
46 var module_name = ref.module_name;
47 var buffer = this.buffer;
48 //f.appendChild(this.text(module_name != null ? "module " : "file "));
49 var x = this.element("a",
50 "class", "source-code-reference",
51 "href", "javascript:");
52 x.addEventListener("click", function (event) {
55 yield ref.open_in_editor();
57 handle_interactive_error(buffer.window, e);
59 event.preventDefault();
60 event.stopPropagation();
61 }, false /* capture */);
62 x.textContent = (module_name != null ? module_name : ref.file_name);
65 parent.appendChild(f);
69 command_name : function (name, parent) {
70 var node = this.element("span", "class", "command");
71 this.text(name, node);
73 parent.appendChild(node);
77 command_reference : function (name, parent) {
78 var node = this.element("a",
80 "href", "javascript:");
81 var buffer = this.buffer;
82 node.addEventListener("click", function (event) {
83 /* FIXME: don't hardcode browse target */
84 describe_command(buffer, name, OPEN_NEW_BUFFER);
85 event.preventDefault();
86 event.stopPropagation();
87 }, false /* capture */);
88 this.text(name, node);
90 parent.appendChild(node);
94 variable_reference : function (name, parent) {
95 var node = this.element("a", "class", "variable", "href", "#");
96 /* FIXME: make this work */
97 this.text(name, node);
99 parent.appendChild(node);
103 help_text : function (str, parent) {
104 var paras = str.split("\n");
105 var f = this.document.createDocumentFragment();
106 for (var i = 0; i < paras.length; ++i) {
108 if (para.length == 0)
111 var p = this.element("p", f);
113 var regexp = /`([a-zA-Z0-9_\-$]+)\'/g;
117 while ((match = regexp.exec(para)) != null) {
118 this.text(para.substring(last_index, match.index), p);
119 var command = match[1];
120 /* FIXME: check if it is a valid command */
121 this.command_reference(command, p);
122 last_index = regexp.lastIndex;
124 if (last_index < para.length)
125 this.text(para.substring(last_index), p);
128 parent.appendChild(f);
132 add_help_stylesheet : function () {
133 this.add_stylesheet("chrome://conkeror-gui/content/help.css");
138 function help_buffer_modality (buffer, element) {
139 buffer.keymaps.push(help_buffer_keymap);
146 define_keywords("$binding_list");
147 function describe_bindings_buffer (window, element) {
148 this.constructor_begin();
150 special_buffer.call(this, window, element, forward_keywords(arguments));
151 this.binding_list = arguments.$binding_list;
152 this.modalities.push(help_buffer_modality);
153 this.constructor_end();
156 describe_bindings_buffer.prototype = {
157 title : "Key bindings",
159 description : "*bindings*",
161 generate : function () {
162 var d = this.document;
163 var list = this.binding_list;
164 delete this.binding_list;
166 var list_by_keymap = {};
167 var keymap_list = [];
168 for each (let x in list) {
169 let name = x.bound_in || "";
171 if (name in list_by_keymap)
172 km = list_by_keymap[name];
174 km = list_by_keymap[name] = {list_by_category: {}, category_list: [], name: name};
175 keymap_list.push(km);
177 let catname = x.category || "";
179 if (catname in km.list_by_category)
180 cat = km.list_by_category[catname];
182 cat = km.list_by_category[catname] = [];
185 km.category_list.unshift(cat);
187 km.category_list.push(cat);
192 var g = new help_document_generator(d, this);
193 g.add_help_stylesheet();
195 d.body.setAttribute("class", "help-list");
197 for each (let km in keymap_list) {
198 g.text(km.name, g.element("h1", d.body));
199 for each (let cat in km.category_list) {
201 g.text(cat.name, g.element("h2", d.body));
203 let table = g.element("table", d.body);
204 for (var i = 0; i < cat.length; ++i) {
206 let tr = g.element("tr", table, "class", (i % 2 == 0) ? "even" : "odd");
207 let seq_td = g.element("td", tr, "class", "key-binding");
208 g.text(bind.seq, seq_td);
209 let command_td = g.element("td", tr, "class", "command");
211 if (bind.command != null) {
212 if (typeof(bind.command) == "function") {
213 g.text("[function]", command_td);
215 g.text(bind.command, command_td);
216 let cmd = interactive_commands.get(bind.command);
218 help_str = cmd.shortdoc;
220 } else if (bind.fallthrough)
221 g.text("[pass through]", command_td);
222 let help_td = g.element("td", tr, "class", "help");
223 g.text(help_str || "", help_td);
229 __proto__: special_buffer.prototype
233 function describe_bindings (buffer, target, keymaps, prefix) {
236 keymaps = get_current_keymaps(buffer.window);
238 prefix = format_binding_sequence(
239 prefix.map(function (x) { return {key:x}; }))+" ";
242 for_each_key_binding(keymaps, function (binding_stack) {
243 var last = binding_stack[binding_stack.length - 1];
244 if (last.command == null && !last.fallthrough)
248 for (let i = binding_stack.length - 1; i >= 0; --i) {
249 bound_in = binding_stack[i].bound_in;
251 if (bound_in.name && !bound_in.anonymous)
253 bound_in = bound_in.bound_in;
256 var bind = {seq: prefix+format_binding_sequence(binding_stack),
257 fallthrough: last.fallthrough,
258 command: last.command,
259 bound_in: bound_in.name,
260 category: last.category
264 create_buffer(buffer.window, buffer_creator(describe_bindings_buffer,
266 $binding_list = list),
269 function describe_bindings_new_buffer (I) {
270 describe_bindings(I.buffer, OPEN_NEW_BUFFER);
272 function describe_bindings_new_window (I) {
273 describe_bindings(I.buffer, OPEN_NEW_WINDOW);
275 interactive("describe-bindings",
276 "Show a help buffer describing the bindings in the context keymaps, "+
277 "meaning the top-level keymaps according to the focus context in the "+
279 alternates(describe_bindings_new_buffer,
280 describe_bindings_new_window));
282 function describe_active_bindings_new_buffer (I) {
283 describe_bindings(I.buffer, OPEN_NEW_BUFFER,
284 I.keymaps || get_current_keymaps(I.buffer.window),
285 I.key_sequence.slice(0, -1));
287 function describe_active_bindings_new_window (I) {
288 describe_bindings(I.buffer, OPEN_NEW_WINDOW,
289 I.keymaps || get_current_keymaps(I.buffer.window),
290 I.key_sequence.slice(0, -1));
292 interactive("describe-active-bindings",
293 "Show a help buffer describing the bindings in the active keymaps, "+
294 "meaning the keymaps in the middle of an ongoing key sequence. This "+
295 "command is intended to be called via `sequence_help_keymap'. For "+
296 "that reason, `describe-active-bindings' does not consume and prefix "+
297 "commands like `universal-argument', as doing so would lead to "+
298 "ambiguities with respect to the intent of the user.",
299 describe_active_bindings_new_buffer);
306 define_keywords("$command_list");
307 function apropos_command_buffer (window, element) {
308 this.constructor_begin();
310 special_buffer.call(this, window, element, forward_keywords(arguments));
311 this.command_list = arguments.$command_list;
312 this.modalities.push(help_buffer_modality);
313 this.constructor_end();
316 apropos_command_buffer.prototype = {
317 title : "Apropos commands",
319 description : "*Apropos*",
321 generate : function () {
322 var d = this.document;
323 var list = this.command_list;
324 delete this.command_list;
326 var g = new help_document_generator(d, this);
327 g.add_help_stylesheet();
329 d.body.setAttribute("class", "help-list");
331 var table = d.createElementNS(XHTML_NS, "table");
332 for (var i = 0; i < list.length; ++i) {
333 var binding = list[i];
334 var tr = d.createElementNS(XHTML_NS, "tr");
335 tr.setAttribute("class", (i % 2 == 0) ? "even" : "odd");
337 var command_td = d.createElementNS(XHTML_NS,"td");
338 g.command_reference(binding.name, command_td);
341 if (binding.cmd.shortdoc != null)
342 shortdoc = binding.cmd.shortdoc;
343 tr.appendChild(command_td);
345 var shortdoc_td = d.createElementNS(XHTML_NS, "td");
346 shortdoc_td.setAttribute("class", "help");
347 shortdoc_td.textContent = shortdoc;
348 tr.appendChild(shortdoc_td);
350 table.appendChild(tr);
352 d.body.appendChild(table);
355 __proto__: special_buffer.prototype
359 /* TODO: support regexps/etc. */
360 function apropos_command (buffer, substring, target) {
362 interactive_commands.for_each(function (name, cmd) {
363 if (name.indexOf(substring) != -1) {
364 var binding = {name: name, cmd: cmd};
368 list.sort(function (a,b) {
375 create_buffer(buffer.window, buffer_creator(apropos_command_buffer,
377 $command_list = list),
381 function apropos_command_new_buffer (I) {
382 apropos_command(I.buffer,
383 (yield I.minibuffer.read($prompt = "Apropos command:",
384 $history = "apropos")),
387 function apropos_command_new_window (I) {
388 apropos_command(I.buffer,
389 (yield I.minibuffer.read($prompt = "Apropos command:",
390 $history = "apropos")),
393 interactive("apropos-command", "List commands whose names contain a given substring.",
394 alternates(apropos_command_new_buffer, apropos_command_new_window));
402 define_keywords("$command", "$bindings");
403 function describe_command_buffer (window, element) {
404 this.constructor_begin();
406 special_buffer.call(this, window, element, forward_keywords(arguments));
407 this.bindings = arguments.$bindings;
408 this.command = arguments.$command;
409 this.cmd = interactive_commands.get(this.command);
410 this.source_code_reference = this.cmd.source_code_reference;
411 this.modalities.push(help_buffer_modality);
412 this.constructor_end();
415 describe_command_buffer.prototype = {
416 get title() { return "Command help: " + this.command; },
418 description : "*help*",
420 generate : function () {
421 var d = this.document;
423 var g = new help_document_generator(d, this);
425 g.add_help_stylesheet();
426 d.body.setAttribute("class", "describe-command");
430 p = g.element("p", d.body);
431 g.command_reference(this.command, p);
432 var cmd = interactive_commands.get(this.command);
433 if (cmd.source_code_reference) {
434 g.text(" is an interactive command in ", p);
435 g.source_code_reference(cmd.source_code_reference, p);
438 g.text(" is an interactive command.", p);
441 if (this.bindings.length > 0) {
442 p = g.element("p", d.body);
443 g.text("It is bound to ", p);
444 for (var i = 0; i < this.bindings.length; ++i) {
447 g.key_binding(this.bindings[i], p);
453 g.help_text(cmd.doc, d.body);
456 __proto__: special_buffer.prototype
460 function describe_command (buffer, command, target) {
461 var keymaps = get_current_keymaps(buffer.window);
462 var bindings = keymap_lookup_command(keymaps, command);
463 create_buffer(buffer.window,
464 buffer_creator(describe_command_buffer,
467 $bindings = bindings),
470 function describe_command_new_buffer (I) {
471 describe_command(I.buffer, (yield I.minibuffer.read_command($prompt = "Describe command:")),
474 function describe_command_new_window (I) {
475 describe_command(I.buffer, (yield I.minibuffer.read_command($prompt = "Describe command:")),
478 interactive("describe-command", null,
479 alternates(describe_command_new_buffer, describe_command_new_window));
483 function view_referenced_source_code (buffer) {
484 if (buffer.source_code_reference == null)
485 throw interactive_error("Command not valid in current buffer.");
486 yield buffer.source_code_reference.open_in_editor();
488 interactive("view-referenced-source-code", null,
489 function (I) {yield view_referenced_source_code(I.buffer);});
496 define_keywords("$binding", "$other_bindings", "$key_sequence");
497 function describe_key_buffer (window, element) {
498 this.constructor_begin();
500 special_buffer.call(this, window, element, forward_keywords(arguments));
501 this.key_sequence = arguments.$key_sequence;
502 this.bindings = arguments.$other_bindings;
503 this.bind = arguments.$binding;
504 this.source_code_reference = this.bind.source_code_reference;
505 this.modalities.push(help_buffer_modality);
506 this.constructor_end();
509 describe_key_buffer.prototype = {
510 get title() { return "Key help: " + this.key_sequence; },
512 description : "*help*",
514 generate : function () {
515 var d = this.document;
517 var g = new help_document_generator(d, this);
519 g.add_help_stylesheet();
520 d.body.setAttribute("class", "describe-key");
524 p = g.element("p", d.body);
525 g.key_binding(this.key_sequence, p);
526 g.text(" is bound to the command ", p);
527 var command = this.bind.command;
529 g.command_name("[pass through]", p);
531 g.command_reference(command, p);
532 if (this.bind.browser_object != null) {
533 g.text(" with the browser object, ", p);
534 if (this.bind.browser_object instanceof Function) {
535 g.text("<anonymous browser-object function>", p);
536 } else if (this.bind.browser_object instanceof browser_object_class) {
537 g.text(this.bind.browser_object.name, p);
538 } else if (typeof(this.bind.browser_object) == "string") {
539 g.text('"'+this.bind.browser_object+'"', p);
541 g.text(this.bind.browser_object, p);
544 if (this.source_code_reference) {
546 g.source_code_reference(this.source_code_reference, p);
550 if (command != null) {
551 p = g.element("p", d.body);
552 g.command_reference(command, p);
553 var cmd = interactive_commands.get(command);
554 if (cmd.source_code_reference) {
555 g.text(" is an interactive command in ", p);
556 g.source_code_reference(cmd.source_code_reference, p);
559 g.text(" is an interactive command.", p);
562 if (this.bindings.length > 0) {
563 p = g.element("p", d.body);
564 g.text("It is bound to ", p);
565 for (var i = 0; i < this.bindings.length; ++i) {
568 g.key_binding(this.bindings[i], p);
574 g.help_text(cmd.doc, d.body);
578 __proto__: special_buffer.prototype
582 function describe_key (buffer, key_info, target) {
584 var seq = key_info[0];
585 var bind = key_info[1];
586 var keymaps = get_current_keymaps(buffer.window);
588 bindings = keymap_lookup_command(keymaps, bind.command);
591 create_buffer(buffer.window,
592 buffer_creator(describe_key_buffer,
594 $key_sequence = seq.join(" "),
595 $other_bindings = bindings,
599 function describe_key_new_buffer (I) {
600 describe_key(I.buffer,
601 (yield I.minibuffer.read_key_binding($prompt = "Describe key:", $buffer = I.buffer)),
604 function describe_key_new_window (I) {
605 describe_key(I.buffer,
606 (yield I.minibuffer.read_key_binding($prompt = "Describe key:", $buffer = I.buffer)),
610 function describe_key_briefly (buffer, key_info) {
612 var seq = key_info[0];
613 var bind = key_info[1];
614 var browser_object = "";
615 if (bind.browser_object != null) {
616 browser_object += " on the browser object, ";
617 if (bind.browser_object instanceof Function) {
618 browser_object += "<anonymous browser-object function>";
619 } else if (bind.browser_object instanceof browser_object_class) {
620 browser_object += bind.browser_object.name;
621 } else if (typeof(bind.browser_object) == "string") {
622 browser_object += '"'+bind.browser_object+'"';
624 browser_object += bind.browser_object;
627 buffer.window.minibuffer.message(seq.join(" ") + " runs the command " + bind.command + browser_object);
630 interactive("describe-key", null,
631 alternates(describe_key_new_buffer, describe_key_new_window));
633 interactive("describe-key-briefly", null,
635 describe_key_briefly(
637 (yield I.minibuffer.read_key_binding($prompt = "Describe key:", $buffer = I.buffer)));
646 define_keywords("$variable");
647 function describe_variable_buffer (window, element) {
648 this.constructor_begin();
650 special_buffer.call(this, window, element, forward_keywords(arguments));
651 this.variable = arguments.$variable;
652 this.cmd = user_variables[this.variable];
653 this.source_code_reference = this.cmd.source_code_reference;
654 this.modalities.push(help_buffer_modality);
655 this.constructor_end();
658 describe_variable_buffer.prototype = {
659 get title() { return "Variable help: " + this.variable; },
661 description : "*help*",
663 generate : function () {
664 var d = this.document;
666 var g = new help_document_generator(d, this);
668 g.add_help_stylesheet();
669 d.body.setAttribute("class", "describe-variable");
673 p = g.element("p", d.body);
674 g.variable_reference(this.variable, p);
675 var uvar = user_variables[this.variable];
676 if (uvar.source_code_reference) {
677 g.text(" is a user variable in ", p);
678 g.source_code_reference(uvar.source_code_reference, p);
681 g.text(" is a user variable.", p);
684 p = g.element("p", d.body);
685 g.text("Its value is: ", p);
686 let value = conkeror[this.variable];
688 let s = pretty_print_value(value);
689 let pre = g.element("pre", p);
693 if (uvar.doc != null)
694 g.help_text(uvar.doc, d.body);
696 if (uvar.default_value !== undefined &&
697 (uvar.default_value !== value ||
698 (typeof(uvar.default_value) != "object"))) {
699 p = g.element("p", d.body);
700 g.text("Its default value is: ", p);
702 let s = pretty_print_value(uvar.default_value);
703 let pre = g.element("pre", p);
709 __proto__: special_buffer.prototype
713 function describe_variable (buffer, variable, target) {
714 create_buffer(buffer.window,
715 buffer_creator(describe_variable_buffer,
717 $variable = variable),
720 function describe_variable_new_buffer (I) {
721 describe_variable(I.buffer,
722 (yield I.minibuffer.read_user_variable($prompt = "Describe variable:")),
725 function describe_variable_new_window (I) {
726 describe_variable(I.buffer,
727 (yield I.minibuffer.read_user_variable($prompt = "Describe variable:")),
730 interactive("describe-variable", null,
731 alternates(describe_variable_new_buffer, describe_variable_new_window));
736 * Describe Preference
739 function describe_preference (buffer, preference, target) {
740 let key = preference.charAt(0).toUpperCase() + preference.substring(1);
741 let url = "http://kb.mozillazine.org/" + key;
742 browser_object_follow(buffer, target, url);
744 function describe_preference_new_buffer (I) {
745 describe_preference(I.buffer, (yield I.minibuffer.read_preference($prompt = "Describe preference:")),
748 function describe_preference_new_window (I) {
749 describe_preference(I.buffer, (yield I.minibuffer.read_preference($prompt = "Describe preference:")),
752 interactive("describe-preference", null,
753 alternates(describe_preference_new_buffer, describe_preference_new_window));