2 * (C) Copyright 2008 Jeremy Maitin-Shepard
3 * (C) Copyright 2008 Nelson Elhage
4 * (C) Copyright 2008 David Glasser
6 * Use, modification, and distribution are subject to the terms specified in the
10 require("special-buffer.js");
11 require("interactive.js");
13 function where_is_command (buffer, command) {
14 var list = find_command_in_keymap(buffer, command);
17 msg = command + " is not on any key";
19 msg = command + " is on " + list.join(", ");
20 buffer.window.minibuffer.message(msg);
22 interactive("where-is", null, function (I) {
23 where_is_command(I.buffer,
24 (yield I.minibuffer.read_command($prompt = "Where is command:")));
27 function help_document_generator (document, buffer) {
28 dom_generator.call(this, document, XHTML_NS);
31 help_document_generator.prototype = {
32 __proto__: dom_generator.prototype,
34 key_binding : function (str, parent) {
35 var node = this.element("span", "class", "key-binding");
38 parent.appendChild(node);
42 source_code_reference : function (ref, parent) {
43 var f = this.document.createDocumentFragment();
44 var module_name = ref.module_name;
45 //f.appendChild(this.text(module_name != null ? "module " : "file "));
46 var x = this.element("a",
47 "class", "source-code-reference",
48 "href", "javascript:");
49 x.addEventListener("click", function (event) {
50 co_call(ref.open_in_editor());
51 event.preventDefault();
52 event.stopPropagation();
53 }, false /* capture */, false /* allow untrusted */);
54 x.textContent = (module_name != null ? module_name : ref.file_name);
57 parent.appendChild(f);
61 command_name : function (name, parent) {
62 var node = this.element("span", "class", "command");
63 this.text(name, node);
65 parent.appendChild(node);
69 command_reference : function (name, parent) {
70 var node = this.element("a",
72 "href", "javascript:");
73 var buffer = this.buffer;
74 node.addEventListener("click", function (event) {
75 /* FIXME: don't hardcode browse target */
76 describe_command(buffer, name, OPEN_NEW_BUFFER);
77 event.preventDefault();
78 event.stopPropagation();
79 }, false /* capture */, false /* allow untrusted */);
80 this.text(name, node);
82 parent.appendChild(node);
86 variable_reference : function (name, parent) {
87 var node = this.element("a", "class", "variable", "href", "#");
88 /* FIXME: make this work */
89 this.text(name, node);
91 parent.appendChild(node);
95 help_text : function (str, parent) {
96 var paras = str.split("\n");
97 var f = this.document.createDocumentFragment();
98 for (var i = 0; i < paras.length; ++i) {
100 if (para.length == 0)
103 var p = this.element("p", f);
105 var regexp = /`([a-zA-Z0-9_\-$]+)\'/g;
109 while ((match = regexp.exec(para)) != null) {
110 this.text(para.substring(last_index, match.index), p);
111 var command = match[1];
112 /* FIXME: check if it is a valid command */
113 this.command_reference(command, p);
114 last_index = regexp.lastIndex;
116 if (last_index < para.length)
117 this.text(para.substring(last_index), p);
120 parent.appendChild(f);
124 add_help_stylesheet : function () {
125 this.add_stylesheet("chrome://conkeror-gui/content/help.css");
129 define_keywords("$binding_list");
130 function describe_bindings_buffer (window, element) {
131 this.constructor_begin();
133 special_buffer.call(this, window, element, forward_keywords(arguments));
134 this.binding_list = arguments.$binding_list;
135 this.constructor_end();
138 describe_bindings_buffer.prototype = {
141 return help_buffer_keymap;
144 title : "Key bindings",
146 description : "*bindings*",
148 generate : function () {
149 var d = this.document;
150 var list = this.binding_list;
151 delete this.binding_list;
153 var list_by_keymap = {};
154 var keymap_list = [];
155 for each (let x in list) {
156 let name = x.bound_in || "";
158 if (name in list_by_keymap)
159 km = list_by_keymap[name];
161 km = list_by_keymap[name] = {list_by_category: {}, category_list: [], name: name};
162 keymap_list.push(km);
164 let catname = x.category || "";
166 if (catname in km.list_by_category)
167 cat = km.list_by_category[catname];
169 cat = km.list_by_category[catname] = [];
172 km.category_list.unshift(cat);
174 km.category_list.push(cat);
179 var g = new help_document_generator(d, this);
180 g.add_help_stylesheet();
182 d.body.setAttribute("class", "help-list");
184 for each (let km in keymap_list) {
185 g.text(km.name, g.element("h1", d.body));
186 for each (let cat in km.category_list) {
188 g.text(cat.name, g.element("h2", d.body));
190 let table = g.element("table", d.body);
191 for (var i = 0; i < cat.length; ++i) {
193 let tr = g.element("tr", table, "class", (i % 2 == 0) ? "even" : "odd");
194 let seq_td = g.element("td", tr, "class", "key-binding");
195 g.text(bind.seq, seq_td);
196 let command_td = g.element("td", tr, "class", "command");
198 if (bind.command != null) {
199 if (typeof(bind.command) == "function") {
200 g.text("[function]", command_td);
202 g.text(bind.command, command_td);
203 let cmd = interactive_commands.get(bind.command);
205 help_str = cmd.shortdoc;
207 } else if (bind.fallthrough)
208 g.text("[pass through]", command_td);
209 let help_td = g.element("td", tr, "class", "help");
210 g.text(help_str || "", help_td);
216 __proto__: special_buffer.prototype
220 function describe_bindings (buffer, target) {
222 for_each_key_binding(buffer, function (binding_stack) {
223 var last = binding_stack[binding_stack.length - 1];
224 if (last.command == null && !last.fallthrough)
228 for (let i = binding_stack.length - 1; i >= 0; --i) {
229 bound_in = binding_stack[i].bound_in;
231 if (bound_in.name && !bound_in.anonymous)
233 bound_in = bound_in.bound_in;
236 var bind = {seq: format_binding_sequence(binding_stack),
237 fallthrough: last.fallthrough,
238 command: last.command,
239 bound_in: bound_in.name,
240 category: last.category
244 create_buffer(buffer.window, buffer_creator(describe_bindings_buffer,
246 $binding_list = list),
249 function describe_bindings_new_buffer (I) {
250 describe_bindings(I.buffer, OPEN_NEW_BUFFER);
252 function describe_bindings_new_window (I) {
253 describe_bindings(I.buffer, OPEN_NEW_WINDOW);
255 interactive("describe-bindings", null,
256 alternates(describe_bindings_new_buffer, describe_bindings_new_window));
259 define_keywords("$command_list");
260 function apropos_command_buffer (window, element) {
261 this.constructor_begin();
263 special_buffer.call(this, window, element, forward_keywords(arguments));
264 this.command_list = arguments.$command_list;
265 this.constructor_end();
268 apropos_command_buffer.prototype = {
271 return help_buffer_keymap;
274 title : "Apropos commands",
276 description : "*Apropos*",
278 generate : function () {
279 var d = this.document;
280 var list = this.command_list;
281 delete this.command_list;
283 var g = new help_document_generator(d, this);
284 g.add_help_stylesheet();
286 d.body.setAttribute("class", "help-list");
288 var table = d.createElementNS(XHTML_NS, "table");
289 for (var i = 0; i < list.length; ++i) {
290 var binding = list[i];
291 var tr = d.createElementNS(XHTML_NS, "tr");
292 tr.setAttribute("class", (i % 2 == 0) ? "even" : "odd");
294 var command_td = d.createElementNS(XHTML_NS,"td");
295 g.command_reference(binding.name, command_td);
298 if (binding.cmd.shortdoc != null)
299 shortdoc = binding.cmd.shortdoc;
300 tr.appendChild(command_td);
302 var shortdoc_td = d.createElementNS(XHTML_NS, "td");
303 shortdoc_td.setAttribute("class", "help");
304 shortdoc_td.textContent = shortdoc;
305 tr.appendChild(shortdoc_td);
307 table.appendChild(tr);
309 d.body.appendChild(table);
312 __proto__: special_buffer.prototype
316 /* TODO: support regexps/etc. */
317 function apropos_command (buffer, substring, target) {
319 interactive_commands.for_each(function (name, cmd) {
320 if (name.indexOf(substring) != -1) {
321 var binding = {name: name, cmd: cmd};
325 list.sort(function (a,b) {
332 create_buffer(buffer.window, buffer_creator(apropos_command_buffer,
334 $command_list = list),
338 function apropos_command_new_buffer (I) {
339 apropos_command(I.buffer,
340 (yield I.minibuffer.read($prompt = "Apropos command:",
341 $history = "apropos")),
344 function apropos_command_new_window (I) {
345 apropos_command(I.buffer,
346 (yield I.minibuffer.read($prompt = "Apropos command:",
347 $history = "apropos")),
350 interactive("apropos-command", "List commands whose names contain a given substring.",
351 alternates(apropos_command_new_buffer, apropos_command_new_window));
355 define_keywords("$command", "$bindings");
356 function describe_command_buffer (window, element) {
357 this.constructor_begin();
359 special_buffer.call(this, window, element, forward_keywords(arguments));
360 this.bindings = arguments.$bindings;
361 this.command = arguments.$command;
362 this.cmd = interactive_commands.get(this.command);
363 this.source_code_reference = this.cmd.source_code_reference;
364 this.constructor_end();
367 describe_command_buffer.prototype = {
370 return help_buffer_keymap;
373 get title() { return "Command help: " + this.command; },
375 description : "*help*",
377 generate : function () {
378 var d = this.document;
380 var g = new help_document_generator(d, this);
382 g.add_help_stylesheet();
383 d.body.setAttribute("class", "describe-command");
387 p = g.element("p", d.body);
388 g.command_reference(this.command, p);
389 var cmd = interactive_commands.get(this.command);
390 if (cmd.source_code_reference) {
391 g.text(" is an interactive command in ", p);
392 g.source_code_reference(cmd.source_code_reference, p);
395 g.text(" is an interactive command.", p);
398 if (this.bindings.length > 0) {
399 p = g.element("p", d.body);
400 g.text("It is bound to ", p);
401 for (var i = 0; i < this.bindings.length; ++i) {
404 g.key_binding(this.bindings[i], p);
410 g.help_text(cmd.doc, d.body);
413 __proto__: special_buffer.prototype
417 function describe_command (buffer, command, target) {
418 var bindings = find_command_in_keymap(buffer, command);
419 create_buffer(buffer.window,
420 buffer_creator(describe_command_buffer,
423 $bindings = bindings),
426 function describe_command_new_buffer (I) {
427 describe_command(I.buffer, (yield I.minibuffer.read_command($prompt = "Describe command:")),
430 function describe_command_new_window (I) {
431 describe_command(I.buffer, (yield I.minibuffer.read_command($prompt = "Describe command:")),
434 interactive("describe-command", null,
435 alternates(describe_command_new_buffer, describe_command_new_window));
439 function view_referenced_source_code (buffer) {
440 if (buffer.source_code_reference == null)
441 throw interactive_error("Command not valid in current buffer.");
442 yield buffer.source_code_reference.open_in_editor();
444 interactive("view-referenced-source-code", null,
445 function (I) {yield view_referenced_source_code(I.buffer);});
448 define_keywords("$binding", "$other_bindings", "$key_sequence");
449 function describe_key_buffer (window, element) {
450 this.constructor_begin();
452 special_buffer.call(this, window, element, forward_keywords(arguments));
453 this.key_sequence = arguments.$key_sequence;
454 this.bindings = arguments.$other_bindings;
455 this.bind = arguments.$binding;
456 this.source_code_reference = this.bind.source_code_reference;
457 this.constructor_end();
460 describe_key_buffer.prototype = {
463 return help_buffer_keymap;
466 get title() { return "Key help: " + this.key_sequence; },
468 description : "*help*",
470 generate : function () {
471 var d = this.document;
473 var g = new help_document_generator(d, this);
475 g.add_help_stylesheet();
476 d.body.setAttribute("class", "describe-key");
480 p = g.element("p", d.body);
481 g.key_binding(this.key_sequence, p);
482 g.text(" is bound to the command ", p);
483 var command = this.bind.command;
485 g.command_name("[pass through]", p);
487 g.command_reference(command, p);
488 if (this.bind.browser_object != null) {
489 g.text(" with the browser object, ", p);
490 if (this.bind.browser_object instanceof Function) {
491 g.text("<anonymous browser-object function>", p);
492 } else if (this.bind.browser_object instanceof browser_object_class) {
493 g.text(this.bind.browser_object.name, p);
494 } else if (typeof(this.bind.browser_object) == "string") {
495 g.text('"'+this.bind.browser_object+'"', p);
497 g.text(this.bind.browser_object, p);
500 if (this.source_code_reference) {
502 g.source_code_reference(this.source_code_reference, p);
506 if (command != null) {
507 p = g.element("p", d.body);
508 g.command_reference(command, p);
509 var cmd = interactive_commands.get(command);
510 if (cmd.source_code_reference) {
511 g.text(" is an interactive command in ", p);
512 g.source_code_reference(cmd.source_code_reference, p);
515 g.text(" is an interactive command.", p);
518 if (this.bindings.length > 0) {
519 p = g.element("p", d.body);
520 g.text("It is bound to ", p);
521 for (var i = 0; i < this.bindings.length; ++i) {
524 g.key_binding(this.bindings[i], p);
530 g.help_text(cmd.doc, d.body);
534 __proto__: special_buffer.prototype
538 function describe_key (buffer, key_info, target) {
540 var seq = key_info[0];
541 var bind = key_info[1];
544 bindings = find_command_in_keymap(buffer, bind.command);
548 create_buffer(buffer.window,
549 buffer_creator(describe_key_buffer,
551 $key_sequence = seq.join(" "),
552 $other_bindings = bindings,
556 function describe_key_new_buffer (I) {
557 describe_key(I.buffer,
558 (yield I.minibuffer.read_key_binding($prompt = "Describe key:", $buffer = I.buffer)),
561 function describe_key_new_window (I) {
562 describe_key(I.buffer,
563 (yield I.minibuffer.read_key_binding($prompt = "Describe key:", $buffer = I.buffer)),
567 function describe_key_briefly (buffer, key_info) {
569 var seq = key_info[0];
570 var bind = key_info[1];
571 var browser_object = "";
572 if (bind.browser_object != null) {
573 browser_object += " on the browser object, ";
574 if (bind.browser_object instanceof Function) {
575 browser_object += "<anonymous browser-object function>";
576 } else if (bind.browser_object instanceof browser_object_class) {
577 browser_object += bind.browser_object.name;
578 } else if (typeof(bind.browser_object) == "string") {
579 browser_object += '"'+bind.browser_object+'"';
581 browser_object += bind.browser_object;
584 buffer.window.minibuffer.message(seq.join(" ") + " runs the command " + bind.command + browser_object);
587 interactive("describe-key", null,
588 alternates(describe_key_new_buffer, describe_key_new_window));
590 interactive("describe-key-briefly", null,
592 describe_key_briefly(
594 (yield I.minibuffer.read_key_binding($prompt = "Describe key:", $buffer = I.buffer)));
600 define_keywords("$variable");
601 function describe_variable_buffer (window, element) {
602 this.constructor_begin();
604 special_buffer.call(this, window, element, forward_keywords(arguments));
605 this.variable = arguments.$variable;
606 this.cmd = user_variables[this.variable];
607 this.source_code_reference = this.cmd.source_code_reference;
608 this.constructor_end();
611 function pretty_print_value (value) {
612 if (value === undefined)
616 if (typeof(value) == "object")
617 return value.toSource();
618 if (typeof(value) == "function")
619 return value.toString();
620 if (typeof(value) == "string") {
621 let s = value.toSource();
622 // toSource returns: (new String("<blah>"))
623 // we want just: "<blah>"
624 return s.substring(12, s.length - 2);
626 return new String(value);
629 describe_variable_buffer.prototype = {
632 return help_buffer_keymap;
635 get title() { return "Variable help: " + this.variable; },
637 description : "*help*",
639 generate : function () {
640 var d = this.document;
642 var g = new help_document_generator(d, this);
644 g.add_help_stylesheet();
645 d.body.setAttribute("class", "describe-variable");
649 p = g.element("p", d.body);
650 g.variable_reference(this.variable, p);
651 var uvar = user_variables[this.variable];
652 if (uvar.source_code_reference) {
653 g.text(" is a user variable in ", p);
654 g.source_code_reference(uvar.source_code_reference, p);
657 g.text(" is a user variable.", p);
660 p = g.element("p", d.body);
661 g.text("Its value is: ", p);
662 let value = conkeror[this.variable];
664 let s = pretty_print_value(value);
665 let pre = g.element("pre", p);
669 if (uvar.doc != null)
670 g.help_text(uvar.doc, d.body);
672 if (uvar.default_value !== undefined &&
673 (uvar.default_value !== value ||
674 (typeof(uvar.default_value) != "object"))) {
675 p = g.element("p", d.body);
676 g.text("Its default value is: ", p);
678 let s = pretty_print_value(uvar.default_value);
679 let pre = g.element("pre", p);
685 __proto__: special_buffer.prototype
689 function describe_variable (buffer, variable, target) {
690 create_buffer(buffer.window,
691 buffer_creator(describe_variable_buffer,
693 $variable = variable),
696 function describe_variable_new_buffer (I) {
697 describe_variable(I.buffer,
698 (yield I.minibuffer.read_user_variable($prompt = "Describe variable:")),
701 function describe_variable_new_window (I) {
702 describe_variable(I.buffer,
703 (yield I.minibuffer.read_user_variable($prompt = "Describe variable:")),
706 interactive("describe-variable", null,
707 alternates(describe_variable_new_buffer, describe_variable_new_window));
711 function describe_preference (buffer, preference, target) {
712 let key = preference.charAt(0).toUpperCase() + preference.substring(1);
713 let url = "http://kb.mozillazine.org/" + key;
714 browser_object_follow(buffer, target, url);
716 function describe_preference_new_buffer (I) {
717 describe_preference(I.buffer, (yield I.minibuffer.read_preference($prompt = "Describe preference:")),
720 function describe_preference_new_window (I) {
721 describe_preference(I.buffer, (yield I.minibuffer.read_preference($prompt = "Describe preference:")),
724 interactive("describe-preference", null,
725 alternates(describe_preference_new_buffer, describe_preference_new_window));