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
13 require("special-buffer.js");
14 require("interactive.js");
16 function where_is_command (buffer, command) {
17 var keymaps = get_current_keymaps(buffer.window);
18 var list = keymap_lookup_command(keymaps, command);
21 msg = command + " is not on any key";
23 msg = command + " is on " + list.join(", ");
24 buffer.window.minibuffer.message(msg);
26 interactive("where-is", null, function (I) {
27 where_is_command(I.buffer,
28 (yield I.minibuffer.read_command($prompt = "Where is command:")));
31 function help_document_generator (document, buffer) {
32 dom_generator.call(this, document, XHTML_NS);
35 help_document_generator.prototype = {
36 __proto__: dom_generator.prototype,
38 key_binding: function (str, parent) {
39 var node = this.element("span", "class", "key-binding");
42 parent.appendChild(node);
46 source_code_reference: function (ref, parent) {
47 var f = this.document.createDocumentFragment();
48 var module_name = ref.module_name;
49 var buffer = this.buffer;
50 //f.appendChild(this.text(module_name != null ? "module " : "file "));
51 var x = this.element("a",
52 "class", "source-code-reference",
53 "href", "javascript:");
54 x.addEventListener("click", function (event) {
57 yield ref.open_in_editor();
59 handle_interactive_error(buffer.window, e);
61 event.preventDefault();
62 event.stopPropagation();
63 }, false /* capture */);
64 x.textContent = (module_name != null ? module_name : ref.file_name);
67 parent.appendChild(f);
71 command_name: function (name, parent) {
72 var node = this.element("span", "class", "command");
73 this.text(name, node);
75 parent.appendChild(node);
79 command_reference: function (name, parent) {
80 var node = this.element("a",
82 "href", "javascript:");
83 var buffer = this.buffer;
84 node.addEventListener("click", function (event) {
85 /* FIXME: don't hardcode browse target */
86 describe_command(buffer, name, OPEN_NEW_BUFFER);
87 event.preventDefault();
88 event.stopPropagation();
89 }, false /* capture */);
90 this.text(name, node);
92 parent.appendChild(node);
96 variable_reference: function (name, parent) {
97 var node = this.element("a", "class", "variable", "href", "#");
98 /* FIXME: make this work */
99 this.text(name, node);
101 parent.appendChild(node);
105 help_text: function (str, parent) {
106 var paras = str.split("\n");
107 var f = this.document.createDocumentFragment();
108 for (var i = 0; i < paras.length; ++i) {
110 if (para.length == 0)
113 var p = this.element("p", f);
115 var regexp = /`([a-zA-Z0-9_\-$]+)\'/g;
119 while ((match = regexp.exec(para)) != null) {
120 this.text(para.substring(last_index, match.index), p);
121 var command = match[1];
122 /* FIXME: check if it is a valid command */
123 this.command_reference(command, p);
124 last_index = regexp.lastIndex;
126 if (last_index < para.length)
127 this.text(para.substring(last_index), p);
130 parent.appendChild(f);
134 add_help_stylesheet: function () {
135 this.add_stylesheet("chrome://conkeror-gui/content/help.css");
140 function help_buffer_modality (buffer, element) {
141 buffer.keymaps.push(help_buffer_keymap);
148 define_keywords("$binding_list");
149 function describe_bindings_buffer (window, element) {
150 this.constructor_begin();
152 special_buffer.call(this, window, element, forward_keywords(arguments));
153 this.binding_list = arguments.$binding_list;
154 this.modalities.push(help_buffer_modality);
155 this.constructor_end();
158 describe_bindings_buffer.prototype = {
159 title: "Key bindings",
161 description: "*bindings*",
163 generate: function () {
164 var d = this.document;
165 var list = this.binding_list;
166 delete this.binding_list;
168 var list_by_keymap = {};
169 var keymap_list = [];
170 for each (let x in list) {
171 let name = x.bound_in || "";
173 if (name in list_by_keymap)
174 km = list_by_keymap[name];
176 km = list_by_keymap[name] = {list_by_category: {}, category_list: [], name: name};
177 keymap_list.push(km);
179 let catname = x.category || "";
181 if (catname in km.list_by_category)
182 cat = km.list_by_category[catname];
184 cat = km.list_by_category[catname] = [];
187 km.category_list.unshift(cat);
189 km.category_list.push(cat);
194 var g = new help_document_generator(d, this);
195 g.add_help_stylesheet();
197 d.body.setAttribute("class", "help-list");
199 for each (let km in keymap_list) {
200 g.text(km.name, g.element("h1", d.body));
201 for each (let cat in km.category_list) {
203 g.text(cat.name, g.element("h2", d.body));
205 let table = g.element("table", d.body);
206 for (var i = 0; i < cat.length; ++i) {
208 let tr = g.element("tr", table, "class", (i % 2 == 0) ? "even" : "odd");
209 let seq_td = g.element("td", tr, "class", "key-binding");
210 g.text(bind.seq, seq_td);
211 let command_td = g.element("td", tr, "class", "command");
213 if (bind.command != null) {
214 if (typeof(bind.command) == "function") {
215 g.text("[function]", command_td);
217 let cmd = interactive_commands.get(bind.command);
219 g.command_reference(cmd.name, command_td);
220 help_str = cmd.shortdoc;
222 g.text(bind.command, command_td);
225 } else if (bind.fallthrough)
226 g.text("[pass through]", command_td);
227 let help_td = g.element("td", tr, "class", "help");
228 g.text(help_str || "", help_td);
234 __proto__: special_buffer.prototype
238 function describe_bindings (buffer, target, keymaps, prefix) {
241 keymaps = get_current_keymaps(buffer.window);
243 prefix = format_binding_sequence(
244 prefix.map(function (x) { return {key:x}; }))+" ";
247 for_each_key_binding(keymaps, function (binding_stack) {
248 var last = binding_stack[binding_stack.length - 1];
249 if (last.command == null && !last.fallthrough)
253 for (let i = binding_stack.length - 1; i >= 0; --i) {
254 bound_in = binding_stack[i].bound_in;
256 if (bound_in.name && !bound_in.anonymous)
258 bound_in = bound_in.bound_in;
261 var bind = {seq: prefix+format_binding_sequence(binding_stack),
262 fallthrough: last.fallthrough,
263 command: last.command,
264 bound_in: bound_in.name,
265 category: last.category
269 create_buffer(buffer.window, buffer_creator(describe_bindings_buffer,
271 $binding_list = list),
274 function describe_bindings_new_buffer (I) {
275 describe_bindings(I.buffer, OPEN_NEW_BUFFER);
277 function describe_bindings_new_window (I) {
278 describe_bindings(I.buffer, OPEN_NEW_WINDOW);
280 interactive("describe-bindings",
281 "Show a help buffer describing the bindings in the context keymaps, "+
282 "meaning the top-level keymaps according to the focus context in the "+
284 alternates(describe_bindings_new_buffer,
285 describe_bindings_new_window));
287 function describe_active_bindings_new_buffer (I) {
288 describe_bindings(I.buffer, OPEN_NEW_BUFFER,
289 I.keymaps || get_current_keymaps(I.buffer.window),
290 I.key_sequence.slice(0, -1));
292 function describe_active_bindings_new_window (I) {
293 describe_bindings(I.buffer, OPEN_NEW_WINDOW,
294 I.keymaps || get_current_keymaps(I.buffer.window),
295 I.key_sequence.slice(0, -1));
297 interactive("describe-active-bindings",
298 "Show a help buffer describing the bindings in the active keymaps, "+
299 "meaning the keymaps in the middle of an ongoing key sequence. This "+
300 "command is intended to be called via `sequence_help_keymap'. For "+
301 "that reason, `describe-active-bindings' does not consume and prefix "+
302 "commands like `universal-argument', as doing so would lead to "+
303 "ambiguities with respect to the intent of the user.",
304 describe_active_bindings_new_buffer);
311 define_keywords("$command_list");
312 function apropos_command_buffer (window, element) {
313 this.constructor_begin();
315 special_buffer.call(this, window, element, forward_keywords(arguments));
316 this.command_list = arguments.$command_list;
317 this.modalities.push(help_buffer_modality);
318 this.constructor_end();
321 apropos_command_buffer.prototype = {
322 title: "Apropos commands",
324 description: "*Apropos*",
326 generate: function () {
327 var d = this.document;
328 var list = this.command_list;
329 delete this.command_list;
331 var g = new help_document_generator(d, this);
332 g.add_help_stylesheet();
334 d.body.setAttribute("class", "help-list");
336 var table = d.createElementNS(XHTML_NS, "table");
337 for (var i = 0; i < list.length; ++i) {
338 var binding = list[i];
339 var tr = d.createElementNS(XHTML_NS, "tr");
340 tr.setAttribute("class", (i % 2 == 0) ? "even" : "odd");
342 var command_td = d.createElementNS(XHTML_NS,"td");
343 g.command_reference(binding.name, command_td);
346 if (binding.cmd.shortdoc != null)
347 shortdoc = binding.cmd.shortdoc;
348 tr.appendChild(command_td);
350 var shortdoc_td = d.createElementNS(XHTML_NS, "td");
351 shortdoc_td.setAttribute("class", "help");
352 shortdoc_td.textContent = shortdoc;
353 tr.appendChild(shortdoc_td);
355 table.appendChild(tr);
357 d.body.appendChild(table);
360 __proto__: special_buffer.prototype
364 /* TODO: support regexps/etc. */
365 function apropos_command (buffer, substring, target) {
367 interactive_commands.for_each(function (name, cmd) {
368 if (name.indexOf(substring) != -1) {
369 var binding = {name: name, cmd: cmd};
373 list.sort(function (a,b) {
380 create_buffer(buffer.window, buffer_creator(apropos_command_buffer,
382 $command_list = list),
386 function apropos_command_new_buffer (I) {
387 apropos_command(I.buffer,
388 (yield I.minibuffer.read($prompt = "Apropos command:",
389 $history = "apropos")),
392 function apropos_command_new_window (I) {
393 apropos_command(I.buffer,
394 (yield I.minibuffer.read($prompt = "Apropos command:",
395 $history = "apropos")),
398 interactive("apropos-command", "List commands whose names contain a given substring.",
399 alternates(apropos_command_new_buffer, apropos_command_new_window));
407 define_keywords("$command", "$bindings");
408 function describe_command_buffer (window, element) {
409 this.constructor_begin();
411 special_buffer.call(this, window, element, forward_keywords(arguments));
412 this.bindings = arguments.$bindings;
413 this.command = arguments.$command;
414 this.cmd = interactive_commands.get(this.command);
415 this.source_code_reference = this.cmd.source_code_reference;
416 this.modalities.push(help_buffer_modality);
417 this.constructor_end();
420 describe_command_buffer.prototype = {
421 get title () { return "Command help: " + this.command; },
423 description: "*help*",
425 generate: function () {
426 var d = this.document;
428 var g = new help_document_generator(d, this);
430 g.add_help_stylesheet();
431 d.body.setAttribute("class", "describe-command");
435 p = g.element("p", d.body);
436 g.command_reference(this.command, p);
437 var cmd = interactive_commands.get(this.command);
438 if (cmd.source_code_reference) {
439 g.text(" is an interactive command in ", p);
440 g.source_code_reference(cmd.source_code_reference, p);
443 g.text(" is an interactive command.", p);
446 if (this.bindings.length > 0) {
447 p = g.element("p", d.body);
448 g.text("It is bound to ", p);
449 for (var i = 0; i < this.bindings.length; ++i) {
452 g.key_binding(this.bindings[i], p);
458 g.help_text(cmd.doc, d.body);
461 __proto__: special_buffer.prototype
465 function describe_command (buffer, command, target) {
466 var keymaps = get_current_keymaps(buffer.window);
467 var bindings = keymap_lookup_command(keymaps, command);
468 create_buffer(buffer.window,
469 buffer_creator(describe_command_buffer,
472 $bindings = bindings),
475 function describe_command_new_buffer (I) {
476 describe_command(I.buffer, (yield I.minibuffer.read_command($prompt = "Describe command:")),
479 function describe_command_new_window (I) {
480 describe_command(I.buffer, (yield I.minibuffer.read_command($prompt = "Describe command:")),
483 interactive("describe-command", null,
484 alternates(describe_command_new_buffer, describe_command_new_window));
488 function view_referenced_source_code (buffer) {
489 if (buffer.source_code_reference == null)
490 throw interactive_error("Command not valid in current buffer.");
491 yield buffer.source_code_reference.open_in_editor();
493 interactive("view-referenced-source-code", null,
494 function (I) {yield view_referenced_source_code(I.buffer);});
501 define_keywords("$binding", "$other_bindings", "$key_sequence");
502 function describe_key_buffer (window, element) {
503 this.constructor_begin();
505 special_buffer.call(this, window, element, forward_keywords(arguments));
506 this.key_sequence = arguments.$key_sequence;
507 this.bindings = arguments.$other_bindings;
508 this.bind = arguments.$binding;
509 this.source_code_reference = this.bind.source_code_reference;
510 this.modalities.push(help_buffer_modality);
511 this.constructor_end();
514 describe_key_buffer.prototype = {
515 get title () { return "Key help: " + this.key_sequence; },
517 description: "*help*",
519 generate: function () {
520 var d = this.document;
522 var g = new help_document_generator(d, this);
524 g.add_help_stylesheet();
525 d.body.setAttribute("class", "describe-key");
529 p = g.element("p", d.body);
530 g.key_binding(this.key_sequence, p);
531 g.text(" is bound to the command ", p);
532 var command = this.bind.command;
534 g.command_name("[pass through]", p);
536 g.command_reference(command, p);
537 if (this.bind.browser_object != null) {
538 g.text(" with the browser object, ", p);
539 if (this.bind.browser_object instanceof Function) {
540 g.text("<anonymous browser-object function>", p);
541 } else if (this.bind.browser_object instanceof browser_object_class) {
542 g.text(this.bind.browser_object.name, p);
543 } else if (typeof(this.bind.browser_object) == "string") {
544 g.text('"'+this.bind.browser_object+'"', p);
546 g.text(this.bind.browser_object, p);
549 if (this.source_code_reference) {
551 g.source_code_reference(this.source_code_reference, p);
555 if (command != null) {
556 p = g.element("p", d.body);
557 g.command_reference(command, p);
558 var cmd = interactive_commands.get(command);
559 if (cmd.source_code_reference) {
560 g.text(" is an interactive command in ", p);
561 g.source_code_reference(cmd.source_code_reference, p);
564 g.text(" is an interactive command.", p);
567 if (this.bindings.length > 0) {
568 p = g.element("p", d.body);
569 g.text("It is bound to ", p);
570 for (var i = 0; i < this.bindings.length; ++i) {
573 g.key_binding(this.bindings[i], p);
579 g.help_text(cmd.doc, d.body);
583 __proto__: special_buffer.prototype
587 function describe_key (buffer, key_info, target) {
589 var seq = key_info[0];
590 var bind = key_info[1];
591 var keymaps = get_current_keymaps(buffer.window);
593 bindings = keymap_lookup_command(keymaps, bind.command);
596 create_buffer(buffer.window,
597 buffer_creator(describe_key_buffer,
599 $key_sequence = seq.join(" "),
600 $other_bindings = bindings,
604 function describe_key_new_buffer (I) {
605 describe_key(I.buffer,
606 (yield I.minibuffer.read_key_binding($prompt = "Describe key:", $buffer = I.buffer)),
609 function describe_key_new_window (I) {
610 describe_key(I.buffer,
611 (yield I.minibuffer.read_key_binding($prompt = "Describe key:", $buffer = I.buffer)),
615 function describe_key_briefly (buffer, key_info) {
617 var seq = key_info[0];
618 var bind = key_info[1];
619 var browser_object = "";
620 if (bind.browser_object != null) {
621 browser_object += " on the browser object, ";
622 if (bind.browser_object instanceof Function) {
623 browser_object += "<anonymous browser-object function>";
624 } else if (bind.browser_object instanceof browser_object_class) {
625 browser_object += bind.browser_object.name;
626 } else if (typeof(bind.browser_object) == "string") {
627 browser_object += '"'+bind.browser_object+'"';
629 browser_object += bind.browser_object;
632 buffer.window.minibuffer.message(seq.join(" ") + " runs the command " + bind.command + browser_object);
635 interactive("describe-key", null,
636 alternates(describe_key_new_buffer, describe_key_new_window));
638 interactive("describe-key-briefly", null,
640 describe_key_briefly(
642 (yield I.minibuffer.read_key_binding($prompt = "Describe key:", $buffer = I.buffer)));
651 define_keywords("$variable");
652 function describe_variable_buffer (window, element) {
653 this.constructor_begin();
655 special_buffer.call(this, window, element, forward_keywords(arguments));
656 this.variable = arguments.$variable;
657 this.cmd = user_variables[this.variable];
658 this.source_code_reference = this.cmd.source_code_reference;
659 this.modalities.push(help_buffer_modality);
660 this.constructor_end();
663 describe_variable_buffer.prototype = {
664 get title () { return "Variable help: " + this.variable; },
666 description: "*help*",
668 generate: function () {
669 var d = this.document;
671 var g = new help_document_generator(d, this);
673 g.add_help_stylesheet();
674 d.body.setAttribute("class", "describe-variable");
678 p = g.element("p", d.body);
679 g.variable_reference(this.variable, p);
680 var uvar = user_variables[this.variable];
681 if (uvar.source_code_reference) {
682 g.text(" is a user variable in ", p);
683 g.source_code_reference(uvar.source_code_reference, p);
686 g.text(" is a user variable.", p);
689 p = g.element("p", d.body);
690 g.text("Its value is: ", p);
691 let value = conkeror[this.variable];
693 let s = pretty_print_value(value);
694 let pre = g.element("pre", p);
698 if (uvar.doc != null)
699 g.help_text(uvar.doc, d.body);
701 if (uvar.default_value !== undefined &&
702 (uvar.default_value !== value ||
703 (typeof(uvar.default_value) != "object"))) {
704 p = g.element("p", d.body);
705 g.text("Its default value is: ", p);
707 let s = pretty_print_value(uvar.default_value);
708 let pre = g.element("pre", p);
714 __proto__: special_buffer.prototype
718 function describe_variable (buffer, variable, target) {
719 create_buffer(buffer.window,
720 buffer_creator(describe_variable_buffer,
722 $variable = variable),
725 function describe_variable_new_buffer (I) {
726 describe_variable(I.buffer,
727 (yield I.minibuffer.read_user_variable($prompt = "Describe variable:")),
730 function describe_variable_new_window (I) {
731 describe_variable(I.buffer,
732 (yield I.minibuffer.read_user_variable($prompt = "Describe variable:")),
735 interactive("describe-variable", null,
736 alternates(describe_variable_new_buffer, describe_variable_new_window));
741 * Describe Preference
744 function describe_preference (buffer, preference, target) {
745 let key = preference.charAt(0).toUpperCase() + preference.substring(1);
746 let url = "http://kb.mozillazine.org/" + key;
747 browser_object_follow(buffer, target, url);
749 function describe_preference_new_buffer (I) {
750 describe_preference(I.buffer, (yield I.minibuffer.read_preference($prompt = "Describe preference:")),
753 function describe_preference_new_window (I) {
754 describe_preference(I.buffer, (yield I.minibuffer.read_preference($prompt = "Describe preference:")),
757 interactive("describe-preference", null,
758 alternates(describe_preference_new_buffer, describe_preference_new_window));