1 require("special-buffer.js");
2 require("interactive.js");
4 function help_buffer(window, element) {
6 conkeror.buffer.call(this, window, element, forward_keywords(arguments));
9 help_buffer.prototype = {
10 constructor: help_buffer,
12 __proto__: special_buffer.prototype
15 function where_is_command(buffer, command) {
16 var list = find_command_in_keymap(buffer, 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", 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 */, false /* allow untrusted */);
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 */, false /* allow untrusted */);
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/content/help.css");
131 define_keywords("$binding_list");
132 function describe_bindings_buffer(window, element) {
133 this.constructor_begin();
135 special_buffer.call(this, window, element, forward_keywords(arguments));
136 this.binding_list = arguments.$binding_list;
137 this.constructor_end();
140 describe_bindings_buffer.prototype = {
143 return help_buffer_keymap;
146 title : "Key bindings",
148 description : "*bindings*",
150 generate : function () {
151 var d = this.document;
152 var list = this.binding_list;
153 delete this.binding_list;
155 var list_by_keymap = {};
156 var keymap_list = [];
157 for each (let x in list) {
158 let name = x.bound_in || "";
160 if (name in list_by_keymap)
161 km = list_by_keymap[name];
163 km = list_by_keymap[name] = {list_by_category: {}, category_list: [], name: name};
164 keymap_list.push(km);
166 let catname = x.category || "";
168 if (catname in km.list_by_category)
169 cat = km.list_by_category[catname];
171 cat = km.list_by_category[catname] = [];
174 km.category_list.unshift(cat);
176 km.category_list.push(cat);
181 var g = new help_document_generator(d, this);
182 g.add_help_stylesheet();
184 d.body.setAttribute("class", "help-list");
186 for each (let km in keymap_list) {
187 g.text(km.name, g.element("h1", d.body));
188 for each (let cat in km.category_list) {
190 g.text(cat.name, g.element("h2", d.body));
192 let table = g.element("table", d.body);
193 for (var i = 0; i < cat.length; ++i) {
195 let tr = g.element("tr", table, "class", (i % 2 == 0) ? "even" : "odd");
196 let seq_td = g.element("td", tr, "class", "key-binding");
197 g.text(bind.seq, seq_td);
198 let command_td = g.element("td", tr, "class", "command");
200 if (bind.command != null) {
201 if (typeof(bind.command) == "function") {
202 g.text("[function]", command_td);
204 g.text(bind.command, command_td);
205 let cmd = interactive_commands.get(bind.command);
207 help_str = cmd.shortdoc;
210 else if (bind.fallthrough)
211 g.text("[pass through]", command_td);
212 let help_td = g.element("td", tr, "class", "help");
213 g.text(help_str || "", help_td);
219 __proto__: special_buffer.prototype
223 function describe_bindings(buffer, target) {
225 for_each_key_binding(buffer, function (binding_stack) {
226 var last = binding_stack[binding_stack.length - 1];
227 if (last.command == null && !last.fallthrough)
231 for (let i = binding_stack.length - 1; i >= 0; --i) {
232 bound_in = binding_stack[i].bound_in;
236 bound_in = bound_in.bound_in;
239 var bind = {seq: format_binding_sequence(binding_stack),
240 fallthrough: last.fallthrough,
241 command: last.command,
242 bound_in: bound_in.name,
243 category: last.category
247 create_buffer(buffer.window, buffer_creator(describe_bindings_buffer,
248 $configuration = buffer.configuration,
249 $binding_list = list),
252 interactive("describe-bindings", function (I) {describe_bindings(I.buffer, I.browse_target("describe-bindings"));});
253 default_browse_targets["describe-bindings"] = "find-url";
256 define_keywords("$command_list");
257 function apropos_command_buffer(window, element) {
258 this.constructor_begin();
260 special_buffer.call(this, window, element, forward_keywords(arguments));
261 this.command_list = arguments.$command_list;
262 this.constructor_end();
265 apropos_command_buffer.prototype = {
268 return help_buffer_keymap;
271 title : "Apropos commands",
273 description : "*Apropos*",
275 generate : function () {
276 var d = this.document;
277 var list = this.command_list;
278 delete this.command_list;
280 var g = new help_document_generator(d, this);
281 g.add_help_stylesheet();
283 d.body.setAttribute("class", "help-list");
285 var table = d.createElementNS(XHTML_NS, "table");
286 for (var i = 0; i < list.length; ++i) {
287 var binding = list[i];
288 var tr = d.createElementNS(XHTML_NS, "tr");
289 tr.setAttribute("class", (i % 2 == 0) ? "even" : "odd");
291 var command_td = d.createElementNS(XHTML_NS,"td");
292 g.command_reference(binding.name, command_td);
295 if (binding.cmd.shortdoc != null)
296 shortdoc = binding.cmd.shortdoc;
297 tr.appendChild(command_td);
299 var shortdoc_td = d.createElementNS(XHTML_NS, "td");
300 shortdoc_td.setAttribute("class", "help");
301 shortdoc_td.textContent = shortdoc;
302 tr.appendChild(shortdoc_td);
304 table.appendChild(tr);
306 d.body.appendChild(table);
309 __proto__: special_buffer.prototype
313 /* TODO: support regexps/etc. */
314 function apropos_command(buffer, substring, target) {
316 interactive_commands.for_each(function (name, cmd) {
317 if (name.indexOf(substring) != -1) {
318 var binding = {name: name, cmd: cmd};
322 list.sort(function (a,b) {
329 create_buffer(buffer.window, buffer_creator(apropos_command_buffer,
330 $configuration = buffer.configuration,
331 $command_list = list),
335 interactive("apropos-command", "List commands whose names contain a given substring.",
337 apropos_command(I.buffer,
338 (yield I.minibuffer.read($prompt = "Apropos command:",
339 $history = "apropos")),
340 I.browse_target("apropos-command"));
342 default_browse_targets["apropos-command"] = "find-url";
346 define_keywords("$command", "$bindings");
347 function describe_command_buffer(window, element) {
348 this.constructor_begin();
350 special_buffer.call(this, window, element, forward_keywords(arguments));
351 this.bindings = arguments.$bindings;
352 this.command = arguments.$command;
353 this.cmd = interactive_commands.get(this.command);
354 this.source_code_reference = this.cmd.source_code_reference;
355 this.constructor_end();
358 describe_command_buffer.prototype = {
361 return help_buffer_keymap;
364 get title() { return "Command help: " + this.command; },
366 description : "*help*",
368 generate : function () {
369 var d = this.document;
371 var g = new help_document_generator(d, this);
373 g.add_help_stylesheet();
374 d.body.setAttribute("class", "describe-command");
378 p = g.element("p", d.body);
379 g.command_reference(this.command, p);
380 var cmd = interactive_commands.get(this.command);
381 if (cmd.source_code_reference) {
382 g.text(" is an interactive command in ", p);
383 g.source_code_reference(cmd.source_code_reference, p);
386 g.text(" is an interactive command.", p);
389 if (this.bindings.length > 0) {
390 p = g.element("p", d.body);
391 g.text("It is bound to ", p);
392 for (var i = 0; i < this.bindings.length; ++i) {
395 g.key_binding(this.bindings[i], p);
401 g.help_text(cmd.doc, d.body);
404 __proto__: special_buffer.prototype
408 function describe_command(buffer, command, target) {
409 var bindings = find_command_in_keymap(buffer, command);
410 create_buffer(buffer.window,
411 buffer_creator(describe_command_buffer,
412 $configuration = buffer.configuration,
414 $bindings = bindings),
417 interactive("describe-command", function (I) {
418 describe_command(I.buffer, (yield I.minibuffer.read_command($prompt = "Describe command:")),
419 I.browse_target("describe-command"));
421 default_browse_targets["describe-command"] = "find-url";
428 function view_referenced_source_code(buffer) {
429 if (buffer.source_code_reference == null)
430 throw interactive_error("Command not valid in current buffer.");
431 yield buffer.source_code_reference.open_in_editor();
433 interactive("view-referenced-source-code", function (I) {yield view_referenced_source_code(I.buffer);});
436 define_keywords("$binding", "$other_bindings", "$key_sequence");
437 function describe_key_buffer(window, element) {
438 this.constructor_begin();
440 special_buffer.call(this, window, element, forward_keywords(arguments));
441 this.key_sequence = arguments.$key_sequence;
442 this.bindings = arguments.$other_bindings;
443 this.bind = arguments.$binding;
444 this.source_code_reference = this.bind.source_code_reference;
445 this.constructor_end();
448 describe_key_buffer.prototype = {
451 return help_buffer_keymap;
454 get title() { return "Key help: " + this.key_sequence; },
456 description : "*help*",
458 generate : function () {
459 var d = this.document;
461 var g = new help_document_generator(d, this);
463 g.add_help_stylesheet();
464 d.body.setAttribute("class", "describe-key");
468 p = g.element("p", d.body);
469 g.key_binding(this.key_sequence, p);
470 g.text(" is bound to the command ", p);
471 var command = this.bind.command;
473 g.command_name("[pass through]", p);
475 g.command_reference(command, p);
476 if (this.source_code_reference) {
478 g.source_code_reference(this.source_code_reference, p);
482 if (command != null) {
483 p = g.element("p", d.body);
484 g.command_reference(command, p);
485 var cmd = interactive_commands.get(command);
486 if (cmd.source_code_reference) {
487 g.text(" is an interactive command in ", p);
488 g.source_code_reference(cmd.source_code_reference, p);
491 g.text(" is an interactive command.", p);
494 if (this.bindings.length > 0) {
495 p = g.element("p", d.body);
496 g.text("It is bound to ", p);
497 for (var i = 0; i < this.bindings.length; ++i) {
500 g.key_binding(this.bindings[i], p);
506 g.help_text(cmd.doc, d.body);
510 __proto__: special_buffer.prototype
514 function describe_key(buffer, key_info, target) {
516 var seq = key_info[0];
517 var bind = key_info[1];
520 bindings = find_command_in_keymap(buffer, bind.command);
524 create_buffer(buffer.window,
525 buffer_creator(describe_key_buffer,
526 $configuration = buffer.configuration,
527 $key_sequence = seq.join(" "),
528 $other_bindings = bindings,
533 function describe_key_briefly(buffer, key_info) {
535 var seq = key_info[0];
536 var bind = key_info[1];
538 buffer.window.minibuffer.message(seq.join(" ") + " runs the command " + bind.command);
541 interactive("describe-key", function (I) {
542 describe_key(I.buffer,
543 (yield I.minibuffer.read_key_binding($prompt = "Describe key:", $buffer = I.buffer)),
544 I.browse_target("describe-key"));
546 interactive("describe-key-briefly", function (I) {
547 describe_key_briefly(I.buffer,
548 (yield I.minibuffer.read_key_binding($prompt = "Describe key:", $buffer = I.buffer)));
550 default_browse_targets["describe-key"] = "find-url";
555 define_keywords("$variable");
556 function describe_variable_buffer(window, element) {
557 this.constructor_begin();
559 special_buffer.call(this, window, element, forward_keywords(arguments));
560 this.bindings = arguments.$bindings;
561 this.variable = arguments.$variable;
562 this.cmd = user_variables.get(this.variable);
563 this.source_code_reference = this.cmd.source_code_reference;
564 this.constructor_end();
567 function pretty_print_value(value) {
568 if (value === undefined)
572 if (typeof(value) == "object")
573 return value.toSource();
574 if (typeof(value) == "function")
575 return value.toString();
576 if (typeof(value) == "string") {
577 let s = value.toSource();
578 // toSource returns: (new String("<blah>"))
579 // we want just: "<blah>"
580 return s.substring(12, s.length - 2);
582 return new String(value);
585 describe_variable_buffer.prototype = {
588 return help_buffer_keymap;
591 get title() { return "Variable help: " + this.variable; },
593 description : "*help*",
595 generate : function () {
596 var d = this.document;
598 var g = new help_document_generator(d, this);
600 g.add_help_stylesheet();
601 d.body.setAttribute("class", "describe-variable");
605 p = g.element("p", d.body);
606 g.variable_reference(this.variable, p);
607 var uvar = user_variables.get(this.variable);
608 if (uvar.source_code_reference) {
609 g.text(" is a user variable in ", p);
610 g.source_code_reference(uvar.source_code_reference, p);
613 g.text(" is a user variable.", p);
616 p = g.element("p", d.body);
617 g.text("Its value is: ", p);
619 let s = pretty_print_value(conkeror[this.variable]);
620 let pre = g.element("pre", p);
624 if (uvar.doc != null)
625 g.help_text(uvar.doc, d.body);
627 p = g.element("p", d.body);
628 g.text("Its default value is: ", p);
630 let s = pretty_print_value(user_variables.get(this.variable).default_value);
631 let pre = g.element("pre", p);
636 __proto__: special_buffer.prototype
640 function describe_variable(buffer, variable, target) {
641 create_buffer(buffer.window,
642 buffer_creator(describe_variable_buffer,
643 $configuration = buffer.configuration,
644 $variable = variable),
647 interactive("describe-variable", function (I) {
648 describe_variable(I.buffer, (yield I.minibuffer.read_user_variable($prompt = "Describe variable:")),
649 I.browse_target("describe-variable"));
651 default_browse_targets["describe-variable"] = "find-url";