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 //f.appendChild(this.text(module_name != null ? "module " : "file "));
48 var x = this.element("a",
49 "class", "source-code-reference",
50 "href", "javascript:");
51 x.addEventListener("click", function (event) {
52 co_call(ref.open_in_editor());
53 event.preventDefault();
54 event.stopPropagation();
55 }, false /* capture */);
56 x.textContent = (module_name != null ? module_name : ref.file_name);
59 parent.appendChild(f);
63 command_name : function (name, parent) {
64 var node = this.element("span", "class", "command");
65 this.text(name, node);
67 parent.appendChild(node);
71 command_reference : function (name, parent) {
72 var node = this.element("a",
74 "href", "javascript:");
75 var buffer = this.buffer;
76 node.addEventListener("click", function (event) {
77 /* FIXME: don't hardcode browse target */
78 describe_command(buffer, name, OPEN_NEW_BUFFER);
79 event.preventDefault();
80 event.stopPropagation();
81 }, false /* capture */);
82 this.text(name, node);
84 parent.appendChild(node);
88 variable_reference : function (name, parent) {
89 var node = this.element("a", "class", "variable", "href", "#");
90 /* FIXME: make this work */
91 this.text(name, node);
93 parent.appendChild(node);
97 help_text : function (str, parent) {
98 var paras = str.split("\n");
99 var f = this.document.createDocumentFragment();
100 for (var i = 0; i < paras.length; ++i) {
102 if (para.length == 0)
105 var p = this.element("p", f);
107 var regexp = /`([a-zA-Z0-9_\-$]+)\'/g;
111 while ((match = regexp.exec(para)) != null) {
112 this.text(para.substring(last_index, match.index), p);
113 var command = match[1];
114 /* FIXME: check if it is a valid command */
115 this.command_reference(command, p);
116 last_index = regexp.lastIndex;
118 if (last_index < para.length)
119 this.text(para.substring(last_index), p);
122 parent.appendChild(f);
126 add_help_stylesheet : function () {
127 this.add_stylesheet("chrome://conkeror-gui/content/help.css");
132 function help_buffer_modality (buffer, element) {
133 buffer.keymaps.push(help_buffer_keymap);
140 define_keywords("$binding_list");
141 function describe_bindings_buffer (window, element) {
142 this.constructor_begin();
144 special_buffer.call(this, window, element, forward_keywords(arguments));
145 this.binding_list = arguments.$binding_list;
146 this.modalities.push(help_buffer_modality);
147 this.constructor_end();
150 describe_bindings_buffer.prototype = {
151 title : "Key bindings",
153 description : "*bindings*",
155 generate : function () {
156 var d = this.document;
157 var list = this.binding_list;
158 delete this.binding_list;
160 var list_by_keymap = {};
161 var keymap_list = [];
162 for each (let x in list) {
163 let name = x.bound_in || "";
165 if (name in list_by_keymap)
166 km = list_by_keymap[name];
168 km = list_by_keymap[name] = {list_by_category: {}, category_list: [], name: name};
169 keymap_list.push(km);
171 let catname = x.category || "";
173 if (catname in km.list_by_category)
174 cat = km.list_by_category[catname];
176 cat = km.list_by_category[catname] = [];
179 km.category_list.unshift(cat);
181 km.category_list.push(cat);
186 var g = new help_document_generator(d, this);
187 g.add_help_stylesheet();
189 d.body.setAttribute("class", "help-list");
191 for each (let km in keymap_list) {
192 g.text(km.name, g.element("h1", d.body));
193 for each (let cat in km.category_list) {
195 g.text(cat.name, g.element("h2", d.body));
197 let table = g.element("table", d.body);
198 for (var i = 0; i < cat.length; ++i) {
200 let tr = g.element("tr", table, "class", (i % 2 == 0) ? "even" : "odd");
201 let seq_td = g.element("td", tr, "class", "key-binding");
202 g.text(bind.seq, seq_td);
203 let command_td = g.element("td", tr, "class", "command");
205 if (bind.command != null) {
206 if (typeof(bind.command) == "function") {
207 g.text("[function]", command_td);
209 g.text(bind.command, command_td);
210 let cmd = interactive_commands.get(bind.command);
212 help_str = cmd.shortdoc;
214 } else if (bind.fallthrough)
215 g.text("[pass through]", command_td);
216 let help_td = g.element("td", tr, "class", "help");
217 g.text(help_str || "", help_td);
223 __proto__: special_buffer.prototype
227 function describe_bindings (buffer, target) {
229 var keymaps = get_current_keymaps(buffer.window);
230 for_each_key_binding(keymaps, function (binding_stack) {
231 var last = binding_stack[binding_stack.length - 1];
232 if (last.command == null && !last.fallthrough)
236 for (let i = binding_stack.length - 1; i >= 0; --i) {
237 bound_in = binding_stack[i].bound_in;
239 if (bound_in.name && !bound_in.anonymous)
241 bound_in = bound_in.bound_in;
244 var bind = {seq: format_binding_sequence(binding_stack),
245 fallthrough: last.fallthrough,
246 command: last.command,
247 bound_in: bound_in.name,
248 category: last.category
252 create_buffer(buffer.window, buffer_creator(describe_bindings_buffer,
254 $binding_list = list),
257 function describe_bindings_new_buffer (I) {
258 describe_bindings(I.buffer, OPEN_NEW_BUFFER);
260 function describe_bindings_new_window (I) {
261 describe_bindings(I.buffer, OPEN_NEW_WINDOW);
263 interactive("describe-bindings", null,
264 alternates(describe_bindings_new_buffer, describe_bindings_new_window));
272 define_keywords("$command_list");
273 function apropos_command_buffer (window, element) {
274 this.constructor_begin();
276 special_buffer.call(this, window, element, forward_keywords(arguments));
277 this.command_list = arguments.$command_list;
278 this.modalities.push(help_buffer_modality);
279 this.constructor_end();
282 apropos_command_buffer.prototype = {
283 title : "Apropos commands",
285 description : "*Apropos*",
287 generate : function () {
288 var d = this.document;
289 var list = this.command_list;
290 delete this.command_list;
292 var g = new help_document_generator(d, this);
293 g.add_help_stylesheet();
295 d.body.setAttribute("class", "help-list");
297 var table = d.createElementNS(XHTML_NS, "table");
298 for (var i = 0; i < list.length; ++i) {
299 var binding = list[i];
300 var tr = d.createElementNS(XHTML_NS, "tr");
301 tr.setAttribute("class", (i % 2 == 0) ? "even" : "odd");
303 var command_td = d.createElementNS(XHTML_NS,"td");
304 g.command_reference(binding.name, command_td);
307 if (binding.cmd.shortdoc != null)
308 shortdoc = binding.cmd.shortdoc;
309 tr.appendChild(command_td);
311 var shortdoc_td = d.createElementNS(XHTML_NS, "td");
312 shortdoc_td.setAttribute("class", "help");
313 shortdoc_td.textContent = shortdoc;
314 tr.appendChild(shortdoc_td);
316 table.appendChild(tr);
318 d.body.appendChild(table);
321 __proto__: special_buffer.prototype
325 /* TODO: support regexps/etc. */
326 function apropos_command (buffer, substring, target) {
328 interactive_commands.for_each(function (name, cmd) {
329 if (name.indexOf(substring) != -1) {
330 var binding = {name: name, cmd: cmd};
334 list.sort(function (a,b) {
341 create_buffer(buffer.window, buffer_creator(apropos_command_buffer,
343 $command_list = list),
347 function apropos_command_new_buffer (I) {
348 apropos_command(I.buffer,
349 (yield I.minibuffer.read($prompt = "Apropos command:",
350 $history = "apropos")),
353 function apropos_command_new_window (I) {
354 apropos_command(I.buffer,
355 (yield I.minibuffer.read($prompt = "Apropos command:",
356 $history = "apropos")),
359 interactive("apropos-command", "List commands whose names contain a given substring.",
360 alternates(apropos_command_new_buffer, apropos_command_new_window));
368 define_keywords("$command", "$bindings");
369 function describe_command_buffer (window, element) {
370 this.constructor_begin();
372 special_buffer.call(this, window, element, forward_keywords(arguments));
373 this.bindings = arguments.$bindings;
374 this.command = arguments.$command;
375 this.cmd = interactive_commands.get(this.command);
376 this.source_code_reference = this.cmd.source_code_reference;
377 this.modalities.push(help_buffer_modality);
378 this.constructor_end();
381 describe_command_buffer.prototype = {
382 get title() { return "Command help: " + this.command; },
384 description : "*help*",
386 generate : function () {
387 var d = this.document;
389 var g = new help_document_generator(d, this);
391 g.add_help_stylesheet();
392 d.body.setAttribute("class", "describe-command");
396 p = g.element("p", d.body);
397 g.command_reference(this.command, p);
398 var cmd = interactive_commands.get(this.command);
399 if (cmd.source_code_reference) {
400 g.text(" is an interactive command in ", p);
401 g.source_code_reference(cmd.source_code_reference, p);
404 g.text(" is an interactive command.", p);
407 if (this.bindings.length > 0) {
408 p = g.element("p", d.body);
409 g.text("It is bound to ", p);
410 for (var i = 0; i < this.bindings.length; ++i) {
413 g.key_binding(this.bindings[i], p);
419 g.help_text(cmd.doc, d.body);
422 __proto__: special_buffer.prototype
426 function describe_command (buffer, command, target) {
427 var keymaps = get_current_keymaps(buffer.window);
428 var bindings = keymap_lookup_command(keymaps, command);
429 create_buffer(buffer.window,
430 buffer_creator(describe_command_buffer,
433 $bindings = bindings),
436 function describe_command_new_buffer (I) {
437 describe_command(I.buffer, (yield I.minibuffer.read_command($prompt = "Describe command:")),
440 function describe_command_new_window (I) {
441 describe_command(I.buffer, (yield I.minibuffer.read_command($prompt = "Describe command:")),
444 interactive("describe-command", null,
445 alternates(describe_command_new_buffer, describe_command_new_window));
449 function view_referenced_source_code (buffer) {
450 if (buffer.source_code_reference == null)
451 throw interactive_error("Command not valid in current buffer.");
452 yield buffer.source_code_reference.open_in_editor();
454 interactive("view-referenced-source-code", null,
455 function (I) {yield view_referenced_source_code(I.buffer);});
462 define_keywords("$binding", "$other_bindings", "$key_sequence");
463 function describe_key_buffer (window, element) {
464 this.constructor_begin();
466 special_buffer.call(this, window, element, forward_keywords(arguments));
467 this.key_sequence = arguments.$key_sequence;
468 this.bindings = arguments.$other_bindings;
469 this.bind = arguments.$binding;
470 this.source_code_reference = this.bind.source_code_reference;
471 this.modalities.push(help_buffer_modality);
472 this.constructor_end();
475 describe_key_buffer.prototype = {
476 get title() { return "Key help: " + this.key_sequence; },
478 description : "*help*",
480 generate : function () {
481 var d = this.document;
483 var g = new help_document_generator(d, this);
485 g.add_help_stylesheet();
486 d.body.setAttribute("class", "describe-key");
490 p = g.element("p", d.body);
491 g.key_binding(this.key_sequence, p);
492 g.text(" is bound to the command ", p);
493 var command = this.bind.command;
495 g.command_name("[pass through]", p);
497 g.command_reference(command, p);
498 if (this.bind.browser_object != null) {
499 g.text(" with the browser object, ", p);
500 if (this.bind.browser_object instanceof Function) {
501 g.text("<anonymous browser-object function>", p);
502 } else if (this.bind.browser_object instanceof browser_object_class) {
503 g.text(this.bind.browser_object.name, p);
504 } else if (typeof(this.bind.browser_object) == "string") {
505 g.text('"'+this.bind.browser_object+'"', p);
507 g.text(this.bind.browser_object, p);
510 if (this.source_code_reference) {
512 g.source_code_reference(this.source_code_reference, p);
516 if (command != null) {
517 p = g.element("p", d.body);
518 g.command_reference(command, p);
519 var cmd = interactive_commands.get(command);
520 if (cmd.source_code_reference) {
521 g.text(" is an interactive command in ", p);
522 g.source_code_reference(cmd.source_code_reference, p);
525 g.text(" is an interactive command.", p);
528 if (this.bindings.length > 0) {
529 p = g.element("p", d.body);
530 g.text("It is bound to ", p);
531 for (var i = 0; i < this.bindings.length; ++i) {
534 g.key_binding(this.bindings[i], p);
540 g.help_text(cmd.doc, d.body);
544 __proto__: special_buffer.prototype
548 function describe_key (buffer, key_info, target) {
550 var seq = key_info[0];
551 var bind = key_info[1];
552 var keymaps = get_current_keymaps(buffer.window);
554 bindings = keymap_lookup_command(keymaps, bind.command);
557 create_buffer(buffer.window,
558 buffer_creator(describe_key_buffer,
560 $key_sequence = seq.join(" "),
561 $other_bindings = bindings,
565 function describe_key_new_buffer (I) {
566 describe_key(I.buffer,
567 (yield I.minibuffer.read_key_binding($prompt = "Describe key:", $buffer = I.buffer)),
570 function describe_key_new_window (I) {
571 describe_key(I.buffer,
572 (yield I.minibuffer.read_key_binding($prompt = "Describe key:", $buffer = I.buffer)),
576 function describe_key_briefly (buffer, key_info) {
578 var seq = key_info[0];
579 var bind = key_info[1];
580 var browser_object = "";
581 if (bind.browser_object != null) {
582 browser_object += " on the browser object, ";
583 if (bind.browser_object instanceof Function) {
584 browser_object += "<anonymous browser-object function>";
585 } else if (bind.browser_object instanceof browser_object_class) {
586 browser_object += bind.browser_object.name;
587 } else if (typeof(bind.browser_object) == "string") {
588 browser_object += '"'+bind.browser_object+'"';
590 browser_object += bind.browser_object;
593 buffer.window.minibuffer.message(seq.join(" ") + " runs the command " + bind.command + browser_object);
596 interactive("describe-key", null,
597 alternates(describe_key_new_buffer, describe_key_new_window));
599 interactive("describe-key-briefly", null,
601 describe_key_briefly(
603 (yield I.minibuffer.read_key_binding($prompt = "Describe key:", $buffer = I.buffer)));
612 define_keywords("$variable");
613 function describe_variable_buffer (window, element) {
614 this.constructor_begin();
616 special_buffer.call(this, window, element, forward_keywords(arguments));
617 this.variable = arguments.$variable;
618 this.cmd = user_variables[this.variable];
619 this.source_code_reference = this.cmd.source_code_reference;
620 this.modalities.push(help_buffer_modality);
621 this.constructor_end();
624 function pretty_print_value (value) {
625 if (value === undefined)
629 if (typeof(value) == "object")
630 return value.toSource();
631 if (typeof(value) == "function")
632 return value.toString();
633 if (typeof(value) == "string") {
634 let s = value.toSource();
635 // toSource returns: (new String("<blah>"))
636 // we want just: "<blah>"
637 return s.substring(12, s.length - 2);
639 return new String(value);
642 describe_variable_buffer.prototype = {
643 get title() { return "Variable help: " + this.variable; },
645 description : "*help*",
647 generate : function () {
648 var d = this.document;
650 var g = new help_document_generator(d, this);
652 g.add_help_stylesheet();
653 d.body.setAttribute("class", "describe-variable");
657 p = g.element("p", d.body);
658 g.variable_reference(this.variable, p);
659 var uvar = user_variables[this.variable];
660 if (uvar.source_code_reference) {
661 g.text(" is a user variable in ", p);
662 g.source_code_reference(uvar.source_code_reference, p);
665 g.text(" is a user variable.", p);
668 p = g.element("p", d.body);
669 g.text("Its value is: ", p);
670 let value = conkeror[this.variable];
672 let s = pretty_print_value(value);
673 let pre = g.element("pre", p);
677 if (uvar.doc != null)
678 g.help_text(uvar.doc, d.body);
680 if (uvar.default_value !== undefined &&
681 (uvar.default_value !== value ||
682 (typeof(uvar.default_value) != "object"))) {
683 p = g.element("p", d.body);
684 g.text("Its default value is: ", p);
686 let s = pretty_print_value(uvar.default_value);
687 let pre = g.element("pre", p);
693 __proto__: special_buffer.prototype
697 function describe_variable (buffer, variable, target) {
698 create_buffer(buffer.window,
699 buffer_creator(describe_variable_buffer,
701 $variable = variable),
704 function describe_variable_new_buffer (I) {
705 describe_variable(I.buffer,
706 (yield I.minibuffer.read_user_variable($prompt = "Describe variable:")),
709 function describe_variable_new_window (I) {
710 describe_variable(I.buffer,
711 (yield I.minibuffer.read_user_variable($prompt = "Describe variable:")),
714 interactive("describe-variable", null,
715 alternates(describe_variable_new_buffer, describe_variable_new_window));
720 * Describe Preference
723 function describe_preference (buffer, preference, target) {
724 let key = preference.charAt(0).toUpperCase() + preference.substring(1);
725 let url = "http://kb.mozillazine.org/" + key;
726 browser_object_follow(buffer, target, url);
728 function describe_preference_new_buffer (I) {
729 describe_preference(I.buffer, (yield I.minibuffer.read_preference($prompt = "Describe preference:")),
732 function describe_preference_new_window (I) {
733 describe_preference(I.buffer, (yield I.minibuffer.read_preference($prompt = "Describe preference:")),
736 interactive("describe-preference", null,
737 alternates(describe_preference_new_buffer, describe_preference_new_window));