Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: Add support for virtual chapters #592

Closed
wants to merge 18 commits into from
Closed

WIP: Add support for virtual chapters #592

wants to merge 18 commits into from

Conversation

teiesti
Copy link

@teiesti teiesti commented Jan 27, 2018

Beware: This is currently WIP.

This pull request introduces virtual chapters which do not correspondent to a file on disk. Virtual chapters can be used for namespacing purposes since they have an entry in the SUMMARY.md and the rendered navigation on the left side.

Work to be done:

  • Add a variant to BookItem and SummaryItem.
  • Modify the parser.
    • Syntax A: - [Some virtual chapter]()
    • Syntax B: - Some virtual chapter if desired (Design decision pending!)
  • Modify the renderer.
  • Add test cases.
  • Adjust the crate documentation.
  • Write a passage for the user guide.

Closes #483.
Closes #576.

src/book/book.rs Outdated
/// The chapter's name.
pub name: String,
/// The chapter's contents.
pub content: String,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there any content in a virtual chapter?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't believe so. A virtual chapter just has a name, maybe a number, and a bunch of sub-items.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there any content in a virtual chapter?

This was actually a question somewhere else in a // TODO. I will remove the field.

src/book/book.rs Outdated
Ok(ch)
/* TODO Open question: Do we really want to return a `Result<_>` here?
* Pro: Function would have almost signature as load_chapter()
* Con: We do not use it!
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But we do use it. When you call load_summary_item recursively on the sub-items you return an error if any of the sub-items error.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right. I overlooked the ?-operator in line 284. Thanks!

///
/// This is roughly the equivalent of `- Some virtual section`.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct VirtualLink {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What would the difference be between a normal link and a virtual one? My understanding is you'd write a normal link as - [Some Chapter](./chapter.md) and a virtual link as - [Some Chapter]() (i.e. virtual chapters have no link in SUMMARY.md).

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think, that is an important design decision to make!

I propose that a user can write

  • - [Some Chapter](./chapter.md) for a link and
  • both - Some virtual chapter and - [Some virtual chapter]() for a virtual link.

Maybe, we could add a lint for - [Some virtual chapter]() but I would rather not do that.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the moment, I've only implemented parsing - [Some virtual chapter](). - Some virtual chapter seems to be more tricky, therefore I would like to have a decision before going on... ;-)

@teiesti
Copy link
Author

teiesti commented Jan 28, 2018

The parser seems to work -- at least for syntax A. Unfortunately, I have no idea where to start with the renderer. Can someone mentor me through?

last_link.nested_items = sub_items;
},
SummaryItem::Separator => unreachable!(),
};
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't really like this match because of the code duplication. Some ideas on how to make it nicer?

link.number = Some(number);
},
SummaryItem::Separator => panic!(),
}
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another match. Same problem.

@Michael-F-Bryan
Copy link
Contributor

You mentioned there are a lot of redundant match statements where we only care about dealing with a SummaryItem::VirtualLink or a SummaryItem::Link... What are your thoughts on removing the SummaryItem::VirtualLink variant and converting the path on a link to Option<PathBuf>. As far as I can see, the only difference between a virtual link and a normal one is the presence of a concrete path.

@Michael-F-Bryan
Copy link
Contributor

I believe the main thing we need to do on the renderer side is update the toc and next/previous handlebars helpers. When the toc helper encounters a virtual chapter it should just emit a normal <li> with no link and just the chapter's name as the tag body (maybe also adding a CSS class?).

I think the navigation helpers should skip over a virtual chapter when you hit next/previous, although I'd have to have another look at the code to see how you are meant to do that.

@teiesti
Copy link
Author

teiesti commented Jan 28, 2018

What are your thoughts on removing the SummaryItem::VirtualLink variant and converting the path on a link to Option. As far as I can see, the only difference between a virtual link and a normal one is the presence of a concrete path.

My thoughts are:

  1. If we go for an Option<PathBuf>, we can also go for a PathBuf allowed to be empty. (This is, as I understand, the old solution.)
  2. There is currently a correspondence between VirtualLink and VirtualChapter and Link and Chapter. I think, the solution you propose is less intuitive in this regard.

I am really unsure which solution is better. @azerupi What do you think?

@azerupi
Copy link
Contributor

azerupi commented Feb 2, 2018

Yeah I think we should definitely have a separate item in the enum to represent those empty chapters. I am not up to date with the summary loading code however, so I don't have an opinion there.

@@ -128,16 +128,24 @@ where
pub enum BookItem {
/// A nested chapter.
Chapter(Chapter),
/// A nested virtual chapter.
VirtualChapter(VirtualChapter),
/// A section separator.
Separator,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While we're making breaking changes, it may be a good idea to add a variant:

// To make sure clients have a `_ =>` case
#[doc(hidden)]
__NonExhaustive,

to ensure client libraries can handle new enum variants.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good idea. I will do that!

@Michael-F-Bryan Michael-F-Bryan changed the title Add support for virtual chapters WIP: Add support for virtual chapters Feb 18, 2018
@Michael-F-Bryan
Copy link
Contributor

Hey @teiesti, were you wanting any help with the rendering side of things?

@teiesti
Copy link
Author

teiesti commented Feb 18, 2018

@Michael-F-Bryan: Yes.

By the way: Sorry for being inactive. I was busy learning for some university exams.

@suhr
Copy link

suhr commented Apr 2, 2018

Status of this PR?

(If it stays inactive, I'll try to complete it myself. This feature is important)

@teiesti
Copy link
Author

teiesti commented Apr 2, 2018

I am still waiting for help on the rendering side. If you want to give it a try, don't hesitate. Your work is appreciated... :-)

@Michael-F-Bryan
Copy link
Contributor

@teiesti, what do you think you'll need help with from the rendering side? I believe it should be enough to update the make_data() function in src/renderer/html_handlebars/hbs_renderer.rs (creates the global context for template rendering) and then change the TOC helper so it'll render a virtual chapter correctly.

You'll probably want to write a test or two in either the TOC helper or as part of the integration test suite to make sure virtual chapters get added to the TOC but don't contain links to anywhere.

@teiesti
Copy link
Author

teiesti commented Apr 2, 2018

I was thinking about this pull request again, and it seems that I won't have time for it soon. So, if anyone wants to take over, your welcome. Otherwise, I'll keep this open and try to complete my work when time is less rare. But this won't be in near future.

Sorry...

@steveklabnik
Copy link
Member

(If it stays inactive, I'll try to complete it myself. This feature is important)

Agreed, though I'm also busy. @suhr do you want to give it a shot, as per @teiesti's last comment?

@teiesti thanks so much for all your work on this, I really appreciate it

@suhr
Copy link

suhr commented Apr 4, 2018

Hm. It finds out, I have to deal with that 100 lines long toc.rs function. Which should have been fixed by #467, but still isn't.

How unfortunate.

teiesti added 11 commits April 14, 2018 00:28
This includes:
    - Use 'Self' instead of '<type name>' where possible.
    - Match enums in order of their variants.
This includes
    - a modification to parse_a_link()
      since parse_link() was modified and renamed to parse_item(),
    - a new test parse_a_virtual_link() and
    - the removal of an_empty_link_location_is_an_error()
      since an empty link location is parsed into a 'VirtualLink' from now on.
parse_nested_item() had to be adapted.
@azerupi
Copy link
Contributor

azerupi commented Apr 13, 2018

I rebased this PR onto the latest master. The renderer actually still had support for "virtual chapters" from before. So I just added virtual chapters as chapters without a path when making the context for handlebars and it just worked as before.

I am sure it can be improved to make it more robust, but I propose that we merge this as is and improve it in further PRs.

Edit: I found a bug testing this. If the first chapter is a virtual chapter, the index.html will not be created. We should probably create an empty landing page only containing the TOC.

@azerupi
Copy link
Contributor

azerupi commented Apr 14, 2018

I fixed the two remaining bugs, I think it is working correctly now.

image

@azerupi
Copy link
Contributor

azerupi commented May 19, 2018

@Michael-F-Bryan could you review this when you have some time?
It should be mostly done for a first PR, there is maybe some polishing left to do. But I would like to merge this functionality soon, because one of my personal projetcs could use this. :)

Copy link
Contributor

@Michael-F-Bryan Michael-F-Bryan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking at the changes, I believe we've covered everything necessary to add virtual chapters. @azerupi have you tried using this branch for your project?

When I get time I might come back and tidy up the parsing. The initial recursive descent approach gets the job done, but it can lead to some pretty confusing code and lots of redundant match statements. Since then I've gotten a lot better at parsing and we can probably feed the output from pulldown-cmark into lalrpop to make it all a lot nicer.

BookItem::Separator => {
chapter.insert("spacer".to_owned(), json!("_spacer_"));
}
_ => unimplemented!(),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of a catch-all _ we should probably change it to __NonExhaustive => unreachable!(), that way we can't accidentally forget to update this match statement when the BookItem enum gets updated.


link.number = Some(number);
},
SummaryItem::Separator => panic!(),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For consistency with the previous SummaryItem::Separator (line 438) , this should probably be changed to unreachable!().

.filter_map(|(i, item)| item.maybe_link_mut().map(|l| (i, l)))
.filter(|&(_, ref item)| {
match **item {
SummaryItem::Link(_) => true,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can actually be condensed a bit.

SummaryItem::Link(_) | SummaryItem::VirtualLink(_) => true,
SummaryItem::Separator => false,


let href = match parser.stream.next() {
Some(Event::Start(Tag::Link(href, _))) => href,
other => panic!("Unreachable, {:?}", other),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you mean unreachable!()?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suppose it has been written like this to offer more context in case the test fails.

@azerupi
Copy link
Contributor

azerupi commented May 20, 2018

Thanks for the review!
I will try address the comments later today.

have you tried using this branch for your project?

I have, it works great on the books I tried it on :)

@Dylan-DPC-zz
Copy link

@teiesti do you want to continue with this? else it will be a good idea to close it and open a new PR when you feel like doing it again. Thanks for contributing

@teiesti
Copy link
Author

teiesti commented May 9, 2019

@Dylan-DPC I think, it was way overdue to close this PR, cause it was laying around bitrotting. I still want to finish this work but in fact I won't have time anytime soon.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Allow for non-link summary items mdBook forbids "empty chapters" since 0.1.0
7 participants