www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - automatic code examples in documentation

reply Gerrit Wichert <gwichert yahoo.com> writes:
Content-Type: text/plain; charset=ISO-8859-15
Content-Transfer-Encoding: 7bit

This is a post i made deep in the 'improving the join function' thread.
Steven asked me to open a new thread on it, so here it comes.
 

Am 13.10.2010 22:07, schrieb Andrei Alexandrescu:

 Good point. On the other hand, an overly simplified documentation
 might hinder a good deal of legit uses for advanced users. I wonder
 how to please everyone.


Maybe it's possible to have a special unit-test block named such as 'example'. The compiler can completely ignore such sections or just syntax check them, or ... . For doc generation they are just taken as they are and put into (or linked to) the documentation. It may be even possible for the doc generator to compile and run these samples, so they become some kind of unit test and their possible output can be part of the documentation. Just an idea that comes to my mind This was the original post, now some more explanation: How can the syntax look like ? ... library code documentation( code) { //hello world example code import std.stdio; void main() { writeln( "Hello, world!"); } } more library code ... This is how a simple example code section may look like. The documentation annotation tells the compiler not to generate any code from the section. The minimal thing it has to do is generate a string from the section content and forward it to the document-generator. This should be sufficient for easily getting code examples into the documentation. I think its a good idea if the compiler executes a syntax check on the example section, like it does on the already implemented syntax checked strings. Then the compiler part is done with this. This are code examples, not unit tests. They are not run with the other unit tests. I think code examples are most usefull if they are self-contained little programs. Part of the document generation may be to compile and run the extracted examples. When a example does't compile it can be marked as defunct or excluded from the documentation. The doc-generator can catch the output of the running examples and add it to the documentation as result. Shurely there are some problems when trying to compile the examples like wich libs to link with, or what about GUI-code, or just code snippets, but for me this is all secondary, maybe we can make that work later. The most important part is to define the transfer mechanics to get the example code into the documentation. So what do you think? Gerrit
Oct 15 2010
next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 10/15/2010 10:18 AM, Gerrit Wichert wrote:
 This is a post i made deep in the 'improving the join function' thread.
 Steven asked me to open a new thread on it, so here it comes.


 Am 13.10.2010 22:07, schrieb Andrei Alexandrescu:

  Good point. On the other hand, an overly simplified documentation
  might hinder a good deal of legit uses for advanced users. I wonder
  how to please everyone.


Maybe it's possible to have a special unit-test block named such as 'example'. The compiler can completely ignore such sections or just syntax check them, or ... . For doc generation they are just taken as they are and put into (or linked to) the documentation. It may be even possible for the doc generator to compile and run these samples, so they become some kind of unit test and their possible output can be part of the documentation. Just an idea that comes to my mind This was the original post, now some more explanation: How can the syntax look like ? ... library code documentation( code) { //hello world example code import std.stdio; void main() { writeln( "Hello, world!"); } } more library code ...

 So what do you think?

I'm happy with a much more modest change that doesn't add anything to the syntax. Simply prefixing a unittest with a documentation comment makes it an example: /** The example below illustrates how D gets a basic arithmetic operation totally right. */ unittest { assert(1 + 1 == 2); } The documentation generator simply plops the comment and then formats the code as an example code. Andrei
Oct 15 2010
next sibling parent reply Lutger <lutger.blijdestijn gmail.com> writes:
Andrei Alexandrescu wrote:

 On 10/15/2010 10:18 AM, Gerrit Wichert wrote:
 This is a post i made deep in the 'improving the join function' thread.
 Steven asked me to open a new thread on it, so here it comes.


 Am 13.10.2010 22:07, schrieb Andrei Alexandrescu:

  Good point. On the other hand, an overly simplified documentation
  might hinder a good deal of legit uses for advanced users. I wonder
  how to please everyone.


code-examples. Maybe it's possible to have a special unit-test block named such as 'example'. The compiler can completely ignore such sections or just syntax check them, or ... . For doc generation they are just taken as they are and put into (or linked to) the documentation. It may be even possible for the doc generator to compile and run these samples, so they become some kind of unit test and their possible output can be part of the documentation. Just an idea that comes to my mind This was the original post, now some more explanation: How can the syntax look like ? ... library code documentation( code) { //hello world example code import std.stdio; void main() { writeln( "Hello, world!"); } } more library code ...

 So what do you think?

I'm happy with a much more modest change that doesn't add anything to the syntax. Simply prefixing a unittest with a documentation comment makes it an example: /** The example below illustrates how D gets a basic arithmetic operation totally right. */ unittest { assert(1 + 1 == 2); } The documentation generator simply plops the comment and then formats the code as an example code. Andrei

for reference: http://d.puremagic.com/issues/show_bug.cgi?id=2630 Tomasz Sowiński raises the point that each unittest should test the preceding declaration. I think that's a little inflexible, instead the following could work: - unittests marked with 'ditto' will document the preceding declaration - unittest not marked with ditto will be put in a hardwired macro like BODY is, so that you have control where it gets put in the generated documentation.
Oct 15 2010
parent reply Tomek =?UTF-8?B?U293acWEc2tp?= <just ask.me> writes:
Lutger napisał:

 for reference: http://d.puremagic.com/issues/show_bug.cgi?id=2630
 
 Tomasz Sowiński raises the point that each unittest should test the
 preceding declaration. I think that's a little inflexible, instead the
 following could work:
 
 - unittests marked with 'ditto' will document the preceding declaration

Well, a unittest making a trial run of the preceding declaration is a convention, natural and widely adopted. That well-trodden path deserves to be acknowledged by the doc generator. Good thing about this idea is that *nothing* changes, no extra gimmicks around unittest blocks, the code's natural flow is intact.
 - unittest not marked with ditto will be put in a hardwired macro like
 BODY is, so that you have control where it gets put in the generated
 documentation.

Hm.. would the hardwired macro name be same for all unittests? If so, the notion of implicit ownership by the preceding declaration is necessary so that the names wouldn't mix up. -- Tomek
Oct 15 2010
parent Lutger <lutger.blijdestijn gmail.com> writes:
Tomek Sowiński wrote:

 Lutger napisał:
 
 for reference: http://d.puremagic.com/issues/show_bug.cgi?id=2630
 
 Tomasz Sowiński raises the point that each unittest should test the
 preceding declaration. I think that's a little inflexible, instead the
 following could work:
 
 - unittests marked with 'ditto' will document the preceding declaration

Well, a unittest making a trial run of the preceding declaration is a convention, natural and widely adopted. That well-trodden path deserves to be acknowledged by the doc generator.

That's why I suggested ditto. Any comments can be put as regular code comments inside the unittest itself, that will be marked up nicely by ddoc.
 Good thing about this idea is that *nothing* changes, no extra gimmicks
 around unittest blocks, the code's natural flow is intact.
 
 - unittest not marked with ditto will be put in a hardwired macro like
 BODY is, so that you have control where it gets put in the generated
 documentation.

Hm.. would the hardwired macro name be same for all unittests? If so, the notion of implicit ownership by the preceding declaration is necessary so that the names wouldn't mix up.

Not necessarily. It could just be a container. With this macro: DDOC_UNITTEST_MEMBERS = <h2>unittests</h2> $0 /** one plus one*/ unittest { assert(1+1==2); } /** one minus one */ unittest { assert(1-1==0); } the above could expand to something like: <h2>unittests</h2> $(DDOC_SUMMARY one plus one) $(D_CODE unittest { assert(1+1==2); } ) $(DDOC_SUMMARY one minus one) $(D_CODE unittest { assert(1-1==0); } )
Oct 16 2010
prev sibling parent reply Tomek =?UTF-8?B?U293acWEc2tp?= <just ask.me> writes:
Andrei Alexandrescu napisał:

 I'm happy with a much more modest change that doesn't add anything to
 the syntax. Simply prefixing a unittest with a documentation comment
 makes it an example:
 
 /**
 The example below illustrates how D gets a basic arithmetic operation
 totally right.
 */
 unittest
 {
 assert(1 + 1 == 2);
 }
 
 The documentation generator simply plops the comment and then formats
 the code as an example code.

Let's drill down on this: /// The ultimate foo. void foo(); /// The test. unittest { ... } What would be the <dl><dt><dd> outline of the above snippet? -- Tomek
Oct 15 2010
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 10/15/10 18:00 CDT, Tomek Sowiński wrote:
 Andrei Alexandrescu napisał:

 I'm happy with a much more modest change that doesn't add anything to
 the syntax. Simply prefixing a unittest with a documentation comment
 makes it an example:

 /**
 The example below illustrates how D gets a basic arithmetic operation
 totally right.
 */
 unittest
 {
 assert(1 + 1 == 2);
 }

 The documentation generator simply plops the comment and then formats
 the code as an example code.

Let's drill down on this: /// The ultimate foo. void foo(); /// The test. unittest { ... } What would be the<dl><dt><dd> outline of the above snippet?

From a ddoc perspective this should be the same as: /** The ultimate foo. The test. ---- ... ---- */ void foo(); Andrei
Oct 15 2010
parent reply Tomek =?UTF-8?B?U293acWEc2tp?= <just ask.me> writes:
Andrei Alexandrescu napisał:

 Let's drill down on this:

 /// The ultimate foo.
 void foo();

 /// The test.
 unittest { ... }

 What would be the<dl><dt><dd>  outline of the above snippet?

From a ddoc perspective this should be the same as: /** The ultimate foo. The test. ---- ... ---- */ void foo();

I see. That means unittests summarizing the whole of module's goodies would have to be at the top, but that's passable. Otherwise it's a superb idea. As I mentioned in bugzilla, it opens the opportunity to kill the unittest naming problem with the same stone: void foo(); unittest(owner) { Log.info("Testing " ~ owner.stringof ~ "..."); scope(exit) Log.info("Testing " ~ owner.stringof ~ " complete"); } The syntax takes after the out(result) contract. 'owner' is an alias to the preceding symbol. The overall ROI looks positive, eh? -- Tomek
Oct 15 2010
parent Tomek =?UTF-8?B?U293acWEc2tp?= <just ask.me> writes:
Tomek Sowiński napisał:

 As I mentioned in bugzilla, it opens the opportunity to kill the unittest
 naming problem with the same stone:
 
 void foo();
 
 unittest(owner) {
 Log.info("Testing " ~ owner.stringof ~ "...");
 scope(exit) Log.info("Testing " ~ owner.stringof ~ " complete");
 }
 
 The syntax takes after the out(result) contract. 'owner' is an alias to
 the preceding symbol. The overall ROI looks positive, eh?

Nevermind. It's little better than: unittest { alias foo owner; Log.info("Testing " ~ owner.stringof ~ "..."); scope(exit) Log.info("Testing " ~ owner.stringof ~ " complete"); } There must be a way to make it all work elegantly with, say, an IDE's unittest runner, but I won't find it posting at 2:28 a.m. -- Tomek
Oct 15 2010
prev sibling next sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Friday, October 15, 2010 16:49:06 Tomek Sowi=C5=84ski wrote:
 Andrei Alexandrescu napisa=C5=82:
 Let's drill down on this:
=20
 /// The ultimate foo.
 void foo();
=20
 /// The test.
 unittest { ... }
=20
 What would be the<dl><dt><dd>  outline of the above snippet?

From a ddoc perspective this should be the same as: =20 /** The ultimate foo. =20 The test. ---- ... ---- */ void foo();

I see. That means unittests summarizing the whole of module's goodies wou=

 have to be at the top, but that's passable. Otherwise it's a superb idea.
=20
 As I mentioned in bugzilla, it opens the opportunity to kill the unittest
 naming problem with the same stone:
=20
 void foo();
=20
 unittest(owner) {
   Log.info("Testing " ~ owner.stringof ~ "...");
   scope(exit) Log.info("Testing " ~ owner.stringof ~ " complete");
 }
=20
 The syntax takes after the out(result) contract. 'owner' is an alias to t=

 preceding symbol. The overall ROI looks positive, eh?

I'd actually consider that to be a bad idea. 1. We should be able to name unit tests independently of any function or=20 functions that they're testing. 2. It should be fully possible to have more than one unit test for a single= =20 function or have one unit test for multiple functions and have names on tho= se=20 unittest blocks. Only a small portion of unit test code necessarily makes sense as an exampl= e.=20 So, while having syntax that indicates that a test goes with a particular=20 function as its example is a good idea, remember that there are going to be= =20 plenty of other unittest blocks that are _not_ intended as examples. I'm no= t=20 sure that we want to mix up named unit tests and unit tests which become=20 examples in ddoc comments. I fully support the syntax of unittest(testname) { } but I don't like the idea of unittest(owner) { } I think that it would make far more sense to do it in another way, perhaps example unittest { } or to have an example unit test with a name example unittest(testname) { } Or we could simple use a ddoc marker on it as Andrei suggested: /// unittest(testname) { } The one issue is templates. Putting a unittest in a block in a template doe= sn't=20 make much sense for examples. For an example, you'd need to be dealing with= a=20 concrete template instantiation, while a unittest block within a template t= ype=20 would be for all instantiations of that template. So, in that case, being a= ble=20 to indicate an owner would certainly be useful. But I'm loathe to do it in = away=20 that makes it harder to add proper unit test names. Maybe what we do is mak= e it=20 so that unit test names which match a function name within that module are= =20 associated with that function as its example and the test function is given= the=20 same name as the function with _example tacked on or something similar. So, void func() {} unittest(mytest) { } unittest(func) { } would result in a named unit test mytest which was not an example, and a na= med=20 unit test func_example which was the example for func() and was put in its = ddoc. =2D Jonathan M Davis
Oct 15 2010
prev sibling parent Austin Hastings <ah08010-d yahoo.com> writes:
Interspersed


On 10/15/2010 11:18 AM, Gerrit Wichert wrote:
 This is a post i made deep in the 'improving the join function' thread.
 Steven asked me to open a new thread on it, so here it comes.

[ ... ]
 I think its a good idea if the compiler executes a syntax check on the example
section,
 like it does on the already implemented syntax checked strings.

The perl and parrot guys do this with POD. The pod syntax is simple enough that it is trivial to write code that extracts the examples and tries to compile them. I think development of this started because some readers complained about book examples not compiling. The obvious (in perl) solution was to filter out the examples and verify them as part of a full build.
 Then the compiler part is done with this. This are code examples, not unit
tests.
 They are not run with the other unit tests.
 I think code examples are most usefull if they are self-contained little
programs.

 Part of the document generation may be to compile and run the extracted
examples.
 When a example does't compile it can be marked as defunct or excluded from the
 documentation. The doc-generator can catch the output of the running examples
 and add it to the documentation as result.

 Shurely there are some problems when trying to compile the examples like wich
 libs to link with, or what about GUI-code, or just code snippets, but for me
 this is all secondary, maybe we can make that work later. The most important
part
 is to define the transfer mechanics to get the example code into the
documentation.

 So what do you think?

Doxygen already supports something like this. Quoting the manual: http://www.stack.nl/~dimitri/doxygen/commands.html#cmdexample
 \example <file-name>

 Indicates that a comment block contains documentation for a
 source code example. The name of the source file is <file-name>.
 The text of this file will be included in the documentation, just
 after the documentation contained in the comment block. All examples
 are placed in a list. The source code is scanned for documented
 members and classes. If any are found, the names are cross-referenced
 with the documentation.

The usage example looks like: /** \example example_test.cpp * This is an example of how to use the Test class. * More details about the example. */ Using this approach would have the benefit of nicely solving the "how do I compile the examples" question, since the examples become stand-alone files with the "usual" compilation semantics. (Doxygen supports a search path for this kind of thing, so putting the files in an examples/ subdirectory is trivial.) =Austin
Oct 16 2010