diff --git a/mk/clean.mk b/mk/clean.mk index bc5961a998131..73813a4b7512b 100644 --- a/mk/clean.mk +++ b/mk/clean.mk @@ -41,7 +41,6 @@ clean-misc: @$(call E, cleaning) $(Q)rm -f $(RUNTIME_OBJS) $(RUNTIME_DEF) $(Q)rm -f $(RUSTLLVM_LIB_OBJS) $(RUSTLLVM_OBJS_OBJS) $(RUSTLLVM_DEF) - $(Q)rm -Rf $(DOCS) $(Q)rm -Rf $(GENERATED) $(Q)rm -Rf tmp/* $(Q)rm -Rf rust-stage0-*.tar.bz2 $(PKG_NAME)-*.tar.gz $(PKG_NAME)-*.exe dist diff --git a/mk/crates.mk b/mk/crates.mk index 45b6ed1a058d0..70560d41b8d4c 100644 --- a/mk/crates.mk +++ b/mk/crates.mk @@ -92,6 +92,7 @@ TOOL_SOURCE_rustc := $(S)src/driver/driver.rs ################################################################################ DOC_CRATES := $(filter-out rustc, $(filter-out syntax, $(CRATES))) +COMPILER_DOC_CRATES := rustc syntax # This macro creates some simple definitions for each crate being built, just # some munging of all of the parameters above. diff --git a/mk/docs.mk b/mk/docs.mk index 7861888482ebe..00832e0821690 100644 --- a/mk/docs.mk +++ b/mk/docs.mk @@ -9,24 +9,96 @@ # except according to those terms. ###################################################################### -# Doc variables and rules +# The various pieces of standalone documentation: guides, tutorial, +# manual etc. +# +# The DOCS variable is their names (with no file extension). +# +# PDF_DOCS lists the targets for which PDF documentation should be +# build. +# +# RUSTDOC_FLAGS_xyz variables are extra arguments to pass to the +# rustdoc invocation for xyz. +# +# RUSTDOC_DEPS_xyz are extra dependencies for the rustdoc invocation +# on xyz. +# +# L10N_LANGS are the languages for which the docs have been +# translated. ###################################################################### +DOCS := index tutorial guide-ffi guide-macros guide-lifetimes \ + guide-tasks guide-container guide-pointers \ + complement-cheatsheet guide-runtime \ + rust rustdoc + +PDF_DOCS := tutorial rust + +RUSTDOC_DEPS_rust := doc/full-toc.inc +RUSTDOC_FLAGS_rust := --markdown-in-header=doc/full-toc.inc -DOCS := -CDOCS := -DOCS_L10N := -HTML_DEPS := doc/ +L10N_LANGS := ja -BASE_DOC_OPTS := --standalone --toc --number-sections -HTML_OPTS = $(BASE_DOC_OPTS) --to=html5 --section-divs --css=rust.css \ - --include-before-body=doc/version_info.html \ - --include-in-header=doc/favicon.inc --include-after-body=doc/footer.inc -TEX_OPTS = $(BASE_DOC_OPTS) --include-before-body=doc/version.md \ - --from=markdown --include-before-body=doc/footer.tex --to=latex -EPUB_OPTS = $(BASE_DOC_OPTS) --to=epub +# Generally no need to edit below here. + +# The options are passed to the documentation generators. +RUSTDOC_HTML_OPTS = --markdown-css rust.css \ + --markdown-before-content=doc/version_info.html \ + --markdown-in-header=doc/favicon.inc --markdown-after-content=doc/footer.inc + +PANDOC_BASE_OPTS := --standalone --toc --number-sections +PANDOC_TEX_OPTS = $(PANDOC_BASE_OPTS) --include-before-body=doc/version.md \ + --from=markdown --include-before-body=doc/footer.tex --to=latex +PANDOC_EPUB_OPTS = $(PANDOC_BASE_OPTS) --to=epub + +# The rustdoc executable... +RUSTDOC_EXE = $(HBIN2_H_$(CFG_BUILD))/rustdoc$(X_$(CFG_BUILD)) +# ...with rpath included in case --disable-rpath was provided to +# ./configure +RUSTDOC = $(RPATH_VAR2_T_$(CFG_BUILD)_H_$(CFG_BUILD)) $(RUSTDOC_EXE) D := $(S)src/doc +DOC_TARGETS := +COMPILER_DOC_TARGETS := +DOC_L10N_TARGETS := + +# If NO_REBUILD is set then break the dependencies on rustdoc so we +# build the documentation without having to rebuild rustdoc. +ifeq ($(NO_REBUILD),) +HTML_DEPS := $(RUSTDOC_EXE) +else +HTML_DEPS := +endif + +# Check for the various external utilities for the EPUB/PDF docs: + +ifeq ($(CFG_PDFLATEX),) + $(info cfg: no pdflatex found, omitting doc/rust.pdf) + NO_PDF_DOCS = 1 +else + ifeq ($(CFG_XETEX),) + $(info cfg: no xetex found, disabling doc/rust.pdf) + NO_PDF_DOCS = 1 + else + ifeq ($(CFG_LUATEX),) + $(info cfg: lacking luatex, disabling pdflatex) + NO_PDF_DOCS = 1 + endif + endif +endif + + +ifeq ($(CFG_PANDOC),) +$(info cfg: no pandoc found, omitting PDF and EPUB docs) +ONLY_HTML_DOCS = 1 +endif + +ifeq ($(CFG_NODE),) +$(info cfg: no node found, omitting PDF and EPUB docs) +ONLY_HTML_DOCS = 1 +endif + + ###################################################################### # Rust version ###################################################################### @@ -46,7 +118,7 @@ doc/version_info.html: $(D)/version_info.html.template $(MKFILE_DEPS) \ GENERATED += doc/version.md doc/version_info.html ###################################################################### -# Docs, from pandoc, rustdoc (which runs pandoc), and node +# Docs, from rustdoc and sometimes pandoc & node ###################################################################### doc/: @@ -75,184 +147,85 @@ doc/footer.tex: $(D)/footer.tex | doc/ @$(call E, cp: $@) $(Q)cp -a $< $@ 2> /dev/null -ifeq ($(CFG_PANDOC),) - $(info cfg: no pandoc found, omitting docs) - NO_DOCS = 1 -endif +# The (english) documentation for each doc item. -ifeq ($(CFG_NODE),) - $(info cfg: no node found, omitting docs) - NO_DOCS = 1 -endif +define DEF_SHOULD_BUILD_PDF_DOC +SHOULD_BUILD_PDF_DOC_$(1) = 1 +endef +$(foreach docname,$(PDF_DOCS),$(eval $(call DEF_SHOULD_BUILD_PDF_DOC,$(docname)))) + +define DEF_DOC + +# HTML (rustdoc) +DOC_TARGETS += doc/$(1).html +doc/$(1).html: $$(D)/$(1).md $$(HTML_DEPS) $$(RUSTDOC_DEPS_$(1)) | doc/ + @$$(call E, rustdoc: $$@) + $$(RUSTDOC) $$(RUSTDOC_HTML_OPTS) $$(RUSTDOC_FLAGS_$(1)) $$< + +ifneq ($(ONLY_HTML_DOCS),1) + +# EPUB (pandoc directly) +DOC_TARGETS += doc/$(1).epub +doc/$(1).epub: $$(D)/$(1).md | doc/ + @$$(call E, pandoc: $$@) + $$(Q)$$(CFG_NODE) $$(D)/prep.js --highlight $$< | \ + $$(CFG_PANDOC) $$(PANDOC_EPUB_OPTS) --output=$$@ + +# PDF (md =(pandoc)=> tex =(pdflatex)=> pdf) +DOC_TARGETS += doc/$(1).tex +doc/$(1).tex: $$(D)/$(1).md doc/footer.tex doc/version.md | doc/ + @$$(call E, pandoc: $$@) + $$(Q)$$(CFG_NODE) $$(D)/prep.js $$< | \ + $$(CFG_PANDOC) $$(PANDOC_TEX_OPTS) --output=$$@ + +ifneq ($(NO_PDF_DOCS),1) +ifeq ($$(SHOULD_BUILD_PDF_DOC_$(1)),1) +DOC_TARGETS += doc/$(1).pdf +doc/$(1).pdf: doc/$(1).tex + @$$(call E, pdflatex: $$@) + $$(Q)$$(CFG_PDFLATEX) \ + -interaction=batchmode \ + -output-directory=doc \ + $$< +endif # SHOULD_BUILD_PDF_DOCS_$(1) +endif # NO_PDF_DOCS + +endif # ONLY_HTML_DOCS + +endef + +$(foreach docname,$(DOCS),$(eval $(call DEF_DOC,$(docname)))) + + +# Localized documentation -ifneq ($(NO_DOCS),1) - -DOCS += doc/rust.html -doc/rust.html: $(D)/rust.md doc/full-toc.inc $(HTML_DEPS) | doc/ - @$(call E, pandoc: $@) - $(Q)$(CFG_NODE) $(D)/prep.js --highlight $< | \ - $(CFG_PANDOC) $(HTML_OPTS) --include-in-header=doc/full-toc.inc --output=$@ - -DOCS += doc/rust.tex -doc/rust.tex: $(D)/rust.md doc/footer.tex doc/version.md | doc/ - @$(call E, pandoc: $@) - $(Q)$(CFG_NODE) $(D)/prep.js $< | \ - $(CFG_PANDOC) $(TEX_OPTS) --output=$@ - -DOCS += doc/rust.epub -doc/rust.epub: $(D)/rust.md | doc/ - @$(call E, pandoc: $@) - $(Q)$(CFG_NODE) $(D)/prep.js --highlight $< | \ - $(CFG_PANDOC) $(EPUB_OPTS) --output=$@ - -DOCS += doc/rustdoc.html -doc/rustdoc.html: $(D)/rustdoc.md $(HTML_DEPS) - @$(call E, pandoc: $@) - $(Q)$(CFG_NODE) $(D)/prep.js --highlight $< | \ - $(CFG_PANDOC) $(HTML_OPTS) --output=$@ - -DOCS += doc/tutorial.html -doc/tutorial.html: $(D)/tutorial.md $(HTML_DEPS) - @$(call E, pandoc: $@) - $(Q)$(CFG_NODE) $(D)/prep.js --highlight $< | \ - $(CFG_PANDOC) $(HTML_OPTS) --output=$@ - -DOCS += doc/tutorial.tex -doc/tutorial.tex: $(D)/tutorial.md doc/footer.tex doc/version.md - @$(call E, pandoc: $@) - $(Q)$(CFG_NODE) $(D)/prep.js $< | \ - $(CFG_PANDOC) $(TEX_OPTS) --output=$@ - -DOCS += doc/tutorial.epub -doc/tutorial.epub: $(D)/tutorial.md - @$(call E, pandoc: $@) - $(Q)$(CFG_NODE) $(D)/prep.js --highlight $< | \ - $(CFG_PANDOC) $(EPUB_OPTS) --output=$@ - - -DOCS_L10N += doc/l10n/ja/tutorial.html -doc/l10n/ja/tutorial.html: doc/l10n/ja/tutorial.md doc/version_info.html doc/rust.css - @$(call E, pandoc: $@) - $(Q)$(CFG_NODE) $(D)/prep.js --highlight doc/l10n/ja/tutorial.md | \ - $(CFG_PANDOC) --standalone --toc \ - --section-divs --number-sections \ - --from=markdown --to=html5 --css=../../rust.css \ - --include-before-body=doc/version_info.html \ - --output=$@ - -# Complementary documentation +# FIXME: I (huonw) haven't actually been able to test properly, since +# e.g. (by default) I'm doing an out-of-tree build (#12763), but even +# adjusting for that, the files are too old(?) and are rejected by +# po4a. # -DOCS += doc/index.html -doc/index.html: $(D)/index.md $(HTML_DEPS) - @$(call E, pandoc: $@) - $(Q)$(CFG_NODE) $(D)/prep.js --highlight $< | \ - $(CFG_PANDOC) $(HTML_OPTS) --output=$@ - -DOCS += doc/complement-lang-faq.html -doc/complement-lang-faq.html: $(D)/complement-lang-faq.md doc/full-toc.inc $(HTML_DEPS) - @$(call E, pandoc: $@) - $(Q)$(CFG_NODE) $(D)/prep.js --highlight $< | \ - $(CFG_PANDOC) $(HTML_OPTS) --include-in-header=doc/full-toc.inc --output=$@ - -DOCS += doc/complement-project-faq.html -doc/complement-project-faq.html: $(D)/complement-project-faq.md $(HTML_DEPS) - @$(call E, pandoc: $@) - $(Q)$(CFG_NODE) $(D)/prep.js --highlight $< | \ - $(CFG_PANDOC) $(HTML_OPTS) --output=$@ - -DOCS += doc/complement-cheatsheet.html -doc/complement-cheatsheet.html: $(D)/complement-cheatsheet.md doc/full-toc.inc $(HTML_DEPS) - @$(call E, pandoc: $@) - $(Q)$(CFG_NODE) $(D)/prep.js --highlight $< | \ - $(CFG_PANDOC) $(HTML_OPTS) --include-in-header=doc/full-toc.inc --output=$@ - -DOCS += doc/complement-bugreport.html -doc/complement-bugreport.html: $(D)/complement-bugreport.md $(HTML_DEPS) - @$(call E, pandoc: $@) - $(Q)$(CFG_NODE) $(D)/prep.js --highlight $< | \ - $(CFG_PANDOC) $(HTML_OPTS) --output=$@ - -# Guides - -DOCS += doc/guide-macros.html -doc/guide-macros.html: $(D)/guide-macros.md $(HTML_DEPS) - @$(call E, pandoc: $@) - $(Q)$(CFG_NODE) $(D)/prep.js --highlight $< | \ - $(CFG_PANDOC) $(HTML_OPTS) --output=$@ - -DOCS += doc/guide-container.html -doc/guide-container.html: $(D)/guide-container.md $(HTML_DEPS) - @$(call E, pandoc: $@) - $(Q)$(CFG_NODE) $(D)/prep.js --highlight $< | \ - $(CFG_PANDOC) $(HTML_OPTS) --output=$@ - -DOCS += doc/guide-ffi.html -doc/guide-ffi.html: $(D)/guide-ffi.md $(HTML_DEPS) - @$(call E, pandoc: $@) - $(Q)$(CFG_NODE) $(D)/prep.js --highlight $< | \ - $(CFG_PANDOC) $(HTML_OPTS) --output=$@ - -DOCS += doc/guide-testing.html -doc/guide-testing.html: $(D)/guide-testing.md $(HTML_DEPS) - @$(call E, pandoc: $@) - $(Q)$(CFG_NODE) $(D)/prep.js --highlight $< | \ - $(CFG_PANDOC) $(HTML_OPTS) --output=$@ - -DOCS += doc/guide-lifetimes.html -doc/guide-lifetimes.html: $(D)/guide-lifetimes.md $(HTML_DEPS) - @$(call E, pandoc: $@) - $(Q)$(CFG_NODE) $(D)/prep.js --highlight $< | \ - $(CFG_PANDOC) $(HTML_OPTS) --output=$@ - -DOCS += doc/guide-tasks.html -doc/guide-tasks.html: $(D)/guide-tasks.md $(HTML_DEPS) - @$(call E, pandoc: $@) - $(Q)$(CFG_NODE) $(D)/prep.js --highlight $< | \ - $(CFG_PANDOC) $(HTML_OPTS) --output=$@ - -DOCS += doc/guide-pointers.html -doc/guide-pointers.html: $(D)/guide-pointers.md $(HTML_DEPS) - @$(call E, pandoc: $@) - $(Q)$(CFG_NODE) $(D)/prep.js --highlight $< | \ - $(CFG_PANDOC) $(HTML_OPTS) --output=$@ - -DOCS += doc/guide-runtime.html -doc/guide-runtime.html: $(D)/guide-runtime.md $(HTML_DEPS) - @$(call E, pandoc: $@) - $(Q)$(CFG_NODE) $(D)/prep.js --highlight $< | \ - $(CFG_PANDOC) $(HTML_OPTS) --output=$@ - - ifeq ($(CFG_PDFLATEX),) - $(info cfg: no pdflatex found, omitting doc/rust.pdf) - else - ifeq ($(CFG_XETEX),) - $(info cfg: no xetex found, disabling doc/rust.pdf) - else - ifeq ($(CFG_LUATEX),) - $(info cfg: lacking luatex, disabling pdflatex) - else - -DOCS += doc/rust.pdf -doc/rust.pdf: doc/rust.tex - @$(call E, pdflatex: $@) - $(Q)$(CFG_PDFLATEX) \ - -interaction=batchmode \ - -output-directory=doc \ - $< - -DOCS += doc/tutorial.pdf -doc/tutorial.pdf: doc/tutorial.tex - @$(call E, pdflatex: $@) - $(Q)$(CFG_PDFLATEX) \ - -interaction=batchmode \ - -output-directory=doc \ - $< - - endif - endif - endif +# As such, I've attempted to get it working as much as possible (and +# switching from pandoc to rustdoc), but preserving the old behaviour +# (e.g. only running on the tutorial) +.PHONY: l10n-mds +l10n-mds: $(D)/po4a.conf \ + $(foreach lang,$(L10N_LANG),$(D)/po/$(lang)/*.md.po) + $(warning WARNING: localized documentation is experimental) + po4a --copyright-holder="The Rust Project Developers" \ + --package-name="Rust" \ + --package-version="$(CFG_RELEASE)" \ + -M UTF-8 -L UTF-8 \ + $(D)/po4a.conf + +define DEF_L10N_DOC +DOC_L10N_TARGETS += doc/l10n/$(1)/$(2).html +doc/l10n/$(1)/$(2).html: l10n-mds $$(HTML_DEPS) $$(RUSTDOC_DEPS_$(2)) + @$$(call E, rustdoc: $$@) + $$(RUSTDOC) $$(RUSTDOC_HTML_OPTS) $$(RUSTDOC_FLAGS_$(1)) doc/l10n/$(1)/$(2).md +endef + +$(foreach lang,$(L10N_LANGS),$(eval $(call DEF_L10N_DOC,$(lang),tutorial))) -endif # No pandoc / node ###################################################################### # LLnextgen (grammar analysis from refman) @@ -278,50 +251,44 @@ endif # Rustdoc (libstd/extra) ###################################################################### -# The rustdoc executable, rpath included in case --disable-rpath was provided to -# ./configure -RUSTDOC = $(HBIN2_H_$(CFG_BUILD))/rustdoc$(X_$(CFG_BUILD)) # The library documenting macro # # $(1) - The crate name (std/extra) # # Passes --cfg stage2 to rustdoc because it uses the stage2 librustc. -define libdoc -doc/$(1)/index.html: \ - $$(CRATEFILE_$(1)) \ - $$(RSINPUTS_$(1)) \ - $$(RUSTDOC) \ - $$(foreach dep,$$(RUST_DEPS_$(1)), \ +define DEF_LIB_DOC + +# If NO_REBUILD is set then break the dependencies on rustdoc so we +# build crate documentation without having to rebuild rustdoc. +ifeq ($(NO_REBUILD),) +LIB_DOC_DEP_$(1) = \ + $$(CRATEFILE_$(1)) \ + $$(RSINPUTS_$(1)) \ + $$(RUSTDOC_EXE) \ + $$(foreach dep,$$(RUST_DEPS_$(1)), \ $$(TLIB2_T_$(CFG_BUILD)_H_$(CFG_BUILD))/stamp.$$(dep)) - @$$(call E, rustdoc: $$@) - $$(Q)$$(RPATH_VAR2_T_$(CFG_BUILD)_H_$(CFG_BUILD)) $$(RUSTDOC) \ - --cfg stage2 $$< +else +LIB_DOC_DEP_$(1) = $$(CRATEFILE_$(1)) $$(RSINPUTS_$(1)) +endif +$(2) += doc/$(1)/index.html +doc/$(1)/index.html: $$(LIB_DOC_DEP_$(1)) + @$$(call E, rustdoc $$@) + $$(Q)$$(RUSTDOC) --cfg stage2 $$< endef -$(foreach crate,$(CRATES),$(eval $(call libdoc,$(crate)))) - -DOCS += $(DOC_CRATES:%=doc/%/index.html) - -CDOCS += doc/rustc/index.html -CDOCS += doc/syntax/index.html +$(foreach crate,$(DOC_CRATES),$(eval $(call DEF_LIB_DOC,$(crate),DOC_TARGETS))) +$(foreach crate,$(COMPILER_DOC_CRATES),$(eval $(call DEF_LIB_DOC,$(crate),COMPILER_DOC_TARGETS))) ifdef CFG_DISABLE_DOCS $(info cfg: disabling doc build (CFG_DISABLE_DOCS)) - DOCS := + DOC_TARGETS := endif -docs: $(DOCS) -compiler-docs: $(CDOCS) - -docs-l10n: $(DOCS_L10N) +docs: $(DOC_TARGETS) +compiler-docs: $(COMPILER_DOC_TARGETS) -doc/l10n/%.md: doc/po/%.md.po doc/po4a.conf - po4a --copyright-holder="The Rust Project Developers" \ - --package-name="Rust" \ - --package-version="$(CFG_RELEASE)" \ - -M UTF-8 -L UTF-8 \ - doc/po4a.conf +docs-l10n: $(DOC_L10N_TARGETS) .PHONY: docs-l10n diff --git a/mk/tests.mk b/mk/tests.mk index c55162d398c2d..23433a4705a39 100644 --- a/mk/tests.mk +++ b/mk/tests.mk @@ -19,12 +19,6 @@ TEST_DOC_CRATES = $(DOC_CRATES) TEST_HOST_CRATES = $(HOST_CRATES) TEST_CRATES = $(TEST_TARGET_CRATES) $(TEST_HOST_CRATES) -# Markdown files under doc/ that should have their code extracted and run -DOC_TEST_NAMES = tutorial guide-ffi guide-macros guide-lifetimes \ - guide-tasks guide-container guide-pointers \ - complement-cheatsheet guide-runtime \ - rust - ###################################################################### # Environment configuration ###################################################################### @@ -315,10 +309,10 @@ endif check-stage$(1)-T-$(2)-H-$(3)-doc-crates-exec: \ $$(foreach crate,$$(TEST_DOC_CRATES), \ - check-stage$(1)-T-$(2)-H-$(3)-doc-$$(crate)-exec) + check-stage$(1)-T-$(2)-H-$(3)-doc-crate-$$(crate)-exec) check-stage$(1)-T-$(2)-H-$(3)-doc-exec: \ - $$(foreach docname,$$(DOC_TEST_NAMES), \ + $$(foreach docname,$$(DOCS), \ check-stage$(1)-T-$(2)-H-$(3)-doc-$$(docname)-exec) check-stage$(1)-T-$(2)-H-$(3)-pretty-exec: \ @@ -662,87 +656,91 @@ $(foreach host,$(CFG_HOST), \ $(foreach pretty-name,$(PRETTY_NAMES), \ $(eval $(call DEF_RUN_PRETTY_TEST,$(stage),$(target),$(host),$(pretty-name))))))) -define DEF_RUN_DOC_TEST - -DOC_TEST_ARGS$(1)-T-$(2)-H-$(3)-doc-$(4) := \ - $$(CTEST_COMMON_ARGS$(1)-T-$(2)-H-$(3)) \ - --src-base $(3)/test/doc-$(4)/ \ - --build-base $(3)/test/doc-$(4)/ \ - --mode run-pass - -check-stage$(1)-T-$(2)-H-$(3)-doc-$(4)-exec: $$(call TEST_OK_FILE,$(1),$(2),$(3),doc-$(4)) -$$(call TEST_OK_FILE,$(1),$(2),$(3),doc-$(4)): \ - $$(TEST_SREQ$(1)_T_$(2)_H_$(3)) \ - doc-$(4)-extract$(3) - @$$(call E, run doc-$(4) [$(2)]: $$<) - $$(Q)$$(call CFG_RUN_CTEST_$(2),$(1),$$<,$(3)) \ - $$(DOC_TEST_ARGS$(1)-T-$(2)-H-$(3)-doc-$(4)) \ - --logfile $$(call TEST_LOG_FILE,$(1),$(2),$(3),doc-$(4)) \ - && touch $$@ +###################################################################### +# Crate & freestanding documentation tests +###################################################################### +define DEF_RUSTDOC +RUSTDOC_EXE_$(1)_T_$(2)_H_$(3) := $$(HBIN$(1)_H_$(3))/rustdoc$$(X_$(3)) +RUSTDOC_$(1)_T_$(2)_H_$(3) := $$(RPATH_VAR$(1)_T_$(2)_H_$(3)) $$(RUSTDOC_EXE_$(1)_T_$(2)_H_$(3)) endef $(foreach host,$(CFG_HOST), \ $(foreach target,$(CFG_TARGET), \ $(foreach stage,$(STAGES), \ - $(foreach docname,$(DOC_TEST_NAMES), \ - $(eval $(call DEF_RUN_DOC_TEST,$(stage),$(target),$(host),$(docname))))))) + $(eval $(call DEF_RUSTDOC,$(stage),$(target),$(host)))))) -define DEF_CRATE_DOC_TEST +# Freestanding + +define DEF_DOC_TEST + +check-stage$(1)-T-$(2)-H-$(3)-doc-$(4)-exec: $$(call TEST_OK_FILE,$(1),$(2),$(3),doc-$(4)) # If NO_REBUILD is set then break the dependencies on everything but -# the source files so we can test crate documentation without -# rebuilding any of the parent crates. +# the source files so we can test documentation without rebuilding +# rustdoc etc. ifeq ($(NO_REBUILD),) DOCTESTDEP_$(1)_$(2)_$(3)_$(4) = \ + $$(D)/$(4).md \ $$(TEST_SREQ$(1)_T_$(2)_H_$(3)) \ - $$(CRATE_FULLDEPS_$(1)_T_$(2)_H_$(3)_$(4)) \ - $$(HBIN$(1)_H_$(3))/rustdoc$$(X_$(3)) + $$(RUSTDOC_EXE_$(1)_T_$(2)_H_$(3)) else -DOCTESTDEP_$(1)_$(2)_$(3)_$(4) = $$(RSINPUTS_$(4)) +DOCTESTDEP_$(1)_$(2)_$(3)_$(4) = $$(D)/$(4).md endif -check-stage$(1)-T-$(2)-H-$(3)-doc-$(4)-exec: $$(call TEST_OK_FILE,$(1),$(2),$(3),doc-$(4)) - ifeq ($(2),$$(CFG_BUILD)) $$(call TEST_OK_FILE,$(1),$(2),$(3),doc-$(4)): $$(DOCTESTDEP_$(1)_$(2)_$(3)_$(4)) @$$(call E, run doc-$(4) [$(2)]) - $$(Q)$$(RPATH_VAR$(1)_T_$(2)_H_$(3)) \ - $$(HBIN$(1)_H_$(3))/rustdoc$$(X_$(3)) --test \ - $$(CRATEFILE_$(4)) --test-args "$$(TESTARGS)" && touch $$@ + $$(Q)$$(RUSTDOC_$(1)_T_$(2)_H_$(3)) --test $$< --test-args "$$(TESTARGS)" && touch $$@ else $$(call TEST_OK_FILE,$(1),$(2),$(3),doc-$(4)): touch $$@ endif - endef $(foreach host,$(CFG_HOST), \ $(foreach target,$(CFG_TARGET), \ $(foreach stage,$(STAGES), \ - $(foreach crate,$(TEST_DOC_CRATES), \ - $(eval $(call DEF_CRATE_DOC_TEST,$(stage),$(target),$(host),$(crate))))))) + $(foreach docname,$(DOCS), \ + $(eval $(call DEF_DOC_TEST,$(stage),$(target),$(host),$(docname))))))) -###################################################################### -# Extracting tests for docs -###################################################################### +# Crates + +define DEF_CRATE_DOC_TEST -EXTRACT_TESTS := "$(CFG_PYTHON)" $(S)src/etc/extract-tests.py +# If NO_REBUILD is set then break the dependencies on everything but +# the source files so we can test crate documentation without +# rebuilding any of the parent crates. +ifeq ($(NO_REBUILD),) +CRATEDOCTESTDEP_$(1)_$(2)_$(3)_$(4) = \ + $$(TEST_SREQ$(1)_T_$(2)_H_$(3)) \ + $$(CRATE_FULLDEPS_$(1)_T_$(2)_H_$(3)_$(4)) \ + $$(RUSTDOC_EXE_$(1)_T_$(2)_H_$(3)) +else +CRATEDOCTESTDEP_$(1)_$(2)_$(3)_$(4) = $$(RSINPUTS_$(4)) +endif -define DEF_DOC_TEST_HOST +check-stage$(1)-T-$(2)-H-$(3)-doc-crate-$(4)-exec: \ + $$(call TEST_OK_FILE,$(1),$(2),$(3),doc-crate-$(4)) -doc-$(2)-extract$(1): - @$$(call E, extract: $(2) tests) - $$(Q)rm -f $(1)/test/doc-$(2)/*.rs - $$(Q)$$(EXTRACT_TESTS) $$(D)/$(2).md $(1)/test/doc-$(2) +ifeq ($(2),$$(CFG_BUILD)) +$$(call TEST_OK_FILE,$(1),$(2),$(3),doc-crate-$(4)): $$(CRATEDOCTESTDEP_$(1)_$(2)_$(3)_$(4)) + @$$(call E, run doc-crate-$(4) [$(2)]) + $$(Q)$$(RUSTDOC_$(1)_T_$(2)_H_$(3)) --test \ + $$(CRATEFILE_$(4)) --test-args "$$(TESTARGS)" && touch $$@ +else +$$(call TEST_OK_FILE,$(1),$(2),$(3),doc-crate-$(4)): + touch $$@ +endif endef $(foreach host,$(CFG_HOST), \ - $(foreach docname,$(DOC_TEST_NAMES), \ - $(eval $(call DEF_DOC_TEST_HOST,$(host),$(docname))))) - + $(foreach target,$(CFG_TARGET), \ + $(foreach stage,$(STAGES), \ + $(foreach crate,$(TEST_DOC_CRATES), \ + $(eval $(call DEF_CRATE_DOC_TEST,$(stage),$(target),$(host),$(crate))))))) ###################################################################### # Shortcut rules @@ -751,7 +749,7 @@ $(foreach host,$(CFG_HOST), \ TEST_GROUPS = \ crates \ $(foreach crate,$(TEST_CRATES),$(crate)) \ - $(foreach crate,$(TEST_DOC_CRATES),doc-$(crate)) \ + $(foreach crate,$(TEST_DOC_CRATES),doc-crate-$(crate)) \ rpass \ rpass-full \ rfail \ @@ -762,7 +760,7 @@ TEST_GROUPS = \ debuginfo \ codegen \ doc \ - $(foreach docname,$(DOC_TEST_NAMES),doc-$(docname)) \ + $(foreach docname,$(DOCS),doc-$(docname)) \ pretty \ pretty-rpass \ pretty-rpass-full \ @@ -830,10 +828,10 @@ $(foreach stage,$(STAGES), \ $(eval $(call DEF_CHECK_FOR_STAGE_AND_HOSTS_AND_GROUP,$(stage),$(host),$(group)))))) define DEF_CHECK_DOC_FOR_STAGE -check-stage$(1)-docs: $$(foreach docname,$$(DOC_TEST_NAMES),\ +check-stage$(1)-docs: $$(foreach docname,$$(DOCS),\ check-stage$(1)-T-$$(CFG_BUILD)-H-$$(CFG_BUILD)-doc-$$(docname)) \ - $$(foreach crate,$$(DOC_CRATE_NAMES),\ - check-stage$(1)-T-$$(CFG_BUILD)-H-$$(CFG_BUILD)-doc-$$(crate)) + $$(foreach crate,$$(TEST_DOC_CRATES),\ + check-stage$(1)-T-$$(CFG_BUILD)-H-$$(CFG_BUILD)-doc-crate-$$(crate)) endef $(foreach stage,$(STAGES), \ diff --git a/src/doc/complement-lang-faq.md b/src/doc/complement-lang-faq.md index bd0a47de44c5d..5b38ef5645e2b 100644 --- a/src/doc/complement-lang-faq.md +++ b/src/doc/complement-lang-faq.md @@ -135,7 +135,7 @@ For simplicity, we do not plan to do so. Implementing automatic semicolon insert **Short answer** set the RUST_LOG environment variable to the name of your source file, sans extension. -```sh +``` {.sh .notrust} rustc hello.rs export RUST_LOG=hello ./hello diff --git a/src/doc/guide-ffi.md b/src/doc/guide-ffi.md index c279ff314b474..b9cab358e909d 100644 --- a/src/doc/guide-ffi.md +++ b/src/doc/guide-ffi.md @@ -263,6 +263,7 @@ to the C library and afterwards be invoked from there. A basic example is: Rust code: + ~~~~ {.ignore} extern fn callback(a:i32) { println!("I'm called from C with value {0}", a); @@ -283,7 +284,8 @@ fn main() { ~~~~ C code: -~~~~ {.ignore} + +~~~~ {.notrust} typedef void (*rust_callback)(int32_t); rust_callback cb; @@ -314,6 +316,7 @@ the notification. This will allow the callback to unsafely access the referenced Rust object. Rust code: + ~~~~ {.ignore} struct RustObject { @@ -346,7 +349,8 @@ fn main() { ~~~~ C code: -~~~~ {.ignore} + +~~~~ {.notrust} typedef void (*rust_callback)(int32_t); void* cb_target; rust_callback cb; diff --git a/src/doc/guide-lifetimes.md b/src/doc/guide-lifetimes.md index d31b0eed8492a..69596b6e30475 100644 --- a/src/doc/guide-lifetimes.md +++ b/src/doc/guide-lifetimes.md @@ -205,7 +205,7 @@ struct X { f: int } fn example1() { let mut x = X { f: 3 }; let y = &mut x.f; // -+ L - ... // | + // ... // | } // -+ ~~~ @@ -221,7 +221,7 @@ The situation gets more complex when borrowing data inside heap boxes: fn example2() { let mut x = @X { f: 3 }; let y = &x.f; // -+ L - ... // | + // ... // | } // -+ ~~~ @@ -251,7 +251,7 @@ fn example2() { let mut x = @X {f: 3}; let x1 = x; let y = &x1.f; // -+ L - ... // | + // ... // | } // -+ ~~~ @@ -282,7 +282,7 @@ fn example3() -> int { return *y; // | } // -+ x = ~Foo {f: 4}; - ... + // ... # return 0; } ~~~ diff --git a/src/doc/guide-testing.md b/src/doc/guide-testing.md index f129f7db72994..1a01fad5da214 100644 --- a/src/doc/guide-testing.md +++ b/src/doc/guide-testing.md @@ -185,6 +185,7 @@ amount. For example: ~~~ +# #[allow(unused_imports)]; extern crate test; use std::vec; @@ -201,6 +202,8 @@ fn initialise_a_vector(b: &mut BenchHarness) { b.iter(|| {vec::from_elem(1024, 0u64);} ); b.bytes = 1024 * 8; } + +# fn main() {} ~~~ The benchmark runner will calibrate measurement of the benchmark @@ -244,6 +247,7 @@ recognize that some calculation has no external effects and remove it entirely. ~~~ +# #[allow(unused_imports)]; extern crate test; use test::BenchHarness; @@ -253,6 +257,8 @@ fn bench_xor_1000_ints(bh: &mut BenchHarness) { range(0, 1000).fold(0, |old, new| old ^ new); }); } + +# fn main() {} ~~~ gives the following results @@ -271,6 +277,7 @@ cannot remove the computation entirely. This could be done for the example above by adjusting the `bh.iter` call to ~~~ +# struct X; impl X { fn iter(&self, _: || -> T) {} } let bh = X; bh.iter(|| range(0, 1000).fold(0, |old, new| old ^ new)) ~~~ @@ -281,9 +288,12 @@ forces it to consider any argument as used. ~~~ extern crate test; +# fn main() { +# struct X; impl X { fn iter(&self, _: || -> T) {} } let bh = X; bh.iter(|| { test::black_box(range(0, 1000).fold(0, |old, new| old ^ new)); }); +# } ~~~ Neither of these read or modify the value, and are very cheap for diff --git a/src/doc/index.md b/src/doc/index.md index cf2d8155f7e51..8dacf0e4de886 100644 --- a/src/doc/index.md +++ b/src/doc/index.md @@ -25,6 +25,8 @@ li {list-style-type: none; } * [The standard library, `std`](std/index.html) + + * [The `arena` allocation library](arena/index.html) * [The `collections` library](collections/index.html) * [The `extra` library of extra stuff](extra/index.html) diff --git a/src/doc/rust.css b/src/doc/rust.css index 17100274688f7..26681adad6d78 100644 --- a/src/doc/rust.css +++ b/src/doc/rust.css @@ -142,25 +142,18 @@ pre code { } /* Code highlighting */ -.cm-s-default span.cm-keyword {color: #8959A8;} -.cm-s-default span.cm-atom {color: #219;} -.cm-s-default span.cm-number {color: #3E999F;} -.cm-s-default span.cm-def {color: #4271AE;} -/*.cm-s-default span.cm-variable {color: #C82829;}*/ -.cm-s-default span.cm-variable-2 {color: #6F906C;} -.cm-s-default span.cm-variable-3 {color: #B76514;} -.cm-s-default span.cm-property {color: black;} -.cm-s-default span.cm-operator {color: black;} -.cm-s-default span.cm-comment {color: #8E908C;} -.cm-s-default span.cm-string {color: #718C00;} -.cm-s-default span.cm-string-2 {color: #866544;} -.cm-s-default span.cm-meta {color: #555;} -/*.cm-s-default span.cm-error {color: #F00;}*/ -.cm-s-default span.cm-qualifier {color: #555;} -.cm-s-default span.cm-builtin {color: #30A;} -.cm-s-default span.cm-bracket {color: #CC7;} -.cm-s-default span.cm-tag {color: #C82829;} -.cm-s-default span.cm-attribute {color: #00C;} +pre.rust .kw { color: #8959A8; } +pre.rust .kw-2, pre.rust .prelude-ty { color: #4271AE; } +pre.rust .number { color: #718C00; } +pre.rust .self { color: #C13928; } +pre.rust .boolval { color: #C13928; } +pre.rust .prelude-val { color: #C13928; } +pre.rust .comment { color: #8E908C; } +pre.rust .doccomment { color: #4D4D4C; } +pre.rust .macro, pre.rust .macro-nonterminal { color: #3E999f; } +pre.rust .string { color: #718C00; } +pre.rust .lifetime { color: #C13928; } +pre.rust .attribute, pre.rust .attribute .ident { color: #C82829; } /* The rest ========================================================================== */ diff --git a/src/doc/rust.md b/src/doc/rust.md index d37b33d2316a6..d1cef9a061477 100644 --- a/src/doc/rust.md +++ b/src/doc/rust.md @@ -60,7 +60,7 @@ dialect of EBNF supported by common automated LL(k) parsing tools such as `llgen`, rather than the dialect given in ISO 14977. The dialect can be defined self-referentially as follows: -~~~~ {.ebnf .notation} +~~~~ {.notrust .ebnf .notation} grammar : rule + ; rule : nonterminal ':' productionrule ';' ; productionrule : production [ '|' production ] * ; @@ -150,7 +150,7 @@ Some productions are defined by exclusion of particular Unicode characters: ## Comments -~~~~ {.ebnf .gram} +~~~~ {.notrust .ebnf .gram} comment : block_comment | line_comment ; block_comment : "/*" block_comment_body * '*' + '/' ; block_comment_body : (block_comment | character) * ; @@ -171,7 +171,7 @@ Non-doc comments are interpreted as a form of whitespace. ## Whitespace -~~~~ {.ebnf .gram} +~~~~ {.notrust .ebnf .gram} whitespace_char : '\x20' | '\x09' | '\x0a' | '\x0d' ; whitespace : [ whitespace_char | comment ] + ; ~~~~ @@ -188,7 +188,7 @@ with any other legal whitespace element, such as a single space character. ## Tokens -~~~~ {.ebnf .gram} +~~~~ {.notrust .ebnf .gram} simple_token : keyword | unop | binop ; token : simple_token | ident | literal | symbol | whitespace token ; ~~~~ @@ -202,7 +202,7 @@ grammar as double-quoted strings. Other tokens have exact rules given. The keywords are the following strings: -~~~~ {.keyword} +~~~~ {.notrust .keyword} as break do @@ -230,13 +230,13 @@ evaluates to, rather than referring to it by name or some other evaluation rule. A literal is a form of constant expression, so is evaluated (primarily) at compile time. -~~~~ {.ebnf .gram} +~~~~ {.notrust .ebnf .gram} literal : string_lit | char_lit | num_lit ; ~~~~ #### Character and string literals -~~~~ {.ebnf .gram} +~~~~ {.notrust .ebnf .gram} char_lit : '\x27' char_body '\x27' ; string_lit : '"' string_body * '"' | 'r' raw_string ; @@ -318,7 +318,7 @@ r##"foo #"# bar"##; // foo #"# bar #### Number literals -~~~~ {.ebnf .gram} +~~~~ {.notrust .ebnf .gram} num_lit : nonzero_dec [ dec_digit | '_' ] * num_suffix ? | '0' [ [ dec_digit | '_' ] * num_suffix ? | 'b' [ '1' | '0' | '_' ] + int_suffix ? @@ -416,7 +416,7 @@ The two values of the boolean type are written `true` and `false`. ### Symbols -~~~~ {.ebnf .gram} +~~~~ {.notrust .ebnf .gram} symbol : "::" "->" | '#' | '[' | ']' | '(' | ')' | '{' | '}' | ',' | ';' ; @@ -431,7 +431,7 @@ operators](#binary-operator-expressions), or [keywords](#keywords). ## Paths -~~~~ {.ebnf .gram} +~~~~ {.notrust .ebnf .gram} expr_path : ident [ "::" expr_path_tail ] + ; expr_path_tail : '<' type_expr [ ',' type_expr ] + '>' | expr_path ; @@ -495,7 +495,7 @@ All of the above extensions are expressions with values. ## Macros -~~~~ {.ebnf .gram} +~~~~ {.notrust .ebnf .gram} expr_macro_rules : "macro_rules" '!' ident '(' macro_rule * ')' macro_rule : '(' matcher * ')' "=>" '(' transcriber * ')' ';' matcher : '(' matcher * ')' | '[' matcher * ']' @@ -635,7 +635,7 @@ each of which may have some number of [attributes](#attributes) attached to it. ## Items -~~~~ {.ebnf .gram} +~~~~ {.notrust .ebnf .gram} item : mod_item | fn_item | type_item | struct_item | enum_item | static_item | trait_item | impl_item | extern_block ; ~~~~ @@ -683,7 +683,7 @@ That is, Rust has no notion of type abstraction: there are no first-class "foral ### Modules -~~~~ {.ebnf .gram} +~~~~ {.notrust .ebnf .gram} mod_item : "mod" ident ( ';' | '{' mod '}' ); mod : [ view_item | item ] * ; ~~~~ @@ -703,15 +703,15 @@ An example of a module: mod math { type Complex = (f64, f64); fn sin(f: f64) -> f64 { - ... + /* ... */ # fail!(); } fn cos(f: f64) -> f64 { - ... + /* ... */ # fail!(); } fn tan(f: f64) -> f64 { - ... + /* ... */ # fail!(); } } @@ -751,7 +751,7 @@ mod task { #### View items -~~~~ {.ebnf .gram} +~~~~ {.notrust .ebnf .gram} view_item : extern_crate_decl | use_decl ; ~~~~ @@ -764,7 +764,7 @@ There are several kinds of view item: ##### Extern crate declarations -~~~~ {.ebnf .gram} +~~~~ {.notrust .ebnf .gram} extern_crate_decl : "extern" "crate" ident [ '(' link_attrs ')' ] ? [ '=' string_lit ] ? ; link_attrs : link_attr [ ',' link_attrs ] + ; link_attr : ident '=' literal ; @@ -796,7 +796,7 @@ extern crate foo = "some/where/rust-foo#foo:1.0"; // a full package ID for exter ##### Use declarations -~~~~ {.ebnf .gram} +~~~~ {.notrust .ebnf .gram} use_decl : "pub" ? "use" ident [ '=' path | "::" path_glob ] ; @@ -1205,7 +1205,7 @@ whereas `Dog` is simply called an enum variant. ### Static items -~~~~ {.ebnf .gram} +~~~~ {.notrust .ebnf .gram} static_item : "static" ident ':' type '=' expr ';' ; ~~~~ @@ -1447,7 +1447,7 @@ Implementation parameters are written after the `impl` keyword. # trait Seq { } impl Seq for ~[T] { - ... + /* ... */ } impl Seq for u32 { /* Treat the integer as a sequence of bits */ @@ -1456,7 +1456,7 @@ impl Seq for u32 { ### External blocks -~~~~ {.ebnf .gram} +~~~~ {.notrust .ebnf .gram} extern_block_item : "extern" '{' extern_block '} ; extern_block : [ foreign_fn ] * ; ~~~~ @@ -1682,7 +1682,7 @@ import public items from their destination, not private items. ## Attributes -~~~~ {.ebnf .gram} +~~~~ {.notrust .ebnf .gram} attribute : '#' '[' attr_list ']' ; attr_list : attr [ ',' attr_list ]* attr : ident [ '=' literal @@ -2226,7 +2226,7 @@ declaring a function-local item. #### Slot declarations -~~~~ {.ebnf .gram} +~~~~ {.notrust .ebnf .gram} let_decl : "let" pat [':' type ] ? [ init ] ? ';' ; init : [ '=' ] expr ; ~~~~ @@ -2326,7 +2326,7 @@ values. ### Structure expressions -~~~~ {.ebnf .gram} +~~~~ {.notrust .ebnf .gram} struct_expr : expr_path '{' ident ':' expr [ ',' ident ':' expr ] * [ ".." expr ] '}' | @@ -2380,7 +2380,7 @@ Point3d {y: 0, z: 10, .. base}; ### Block expressions -~~~~ {.ebnf .gram} +~~~~ {.notrust .ebnf .gram} block_expr : '{' [ view_item ] * [ stmt ';' | item ] * [ expr ] '}' @@ -2398,7 +2398,7 @@ of the block are that of the expression itself. ### Method-call expressions -~~~~ {.ebnf .gram} +~~~~ {.notrust .ebnf .gram} method_call_expr : expr '.' ident paren_expr_list ; ~~~~ @@ -2409,7 +2409,7 @@ or dynamically dispatching if the left-hand-side expression is an indirect [obje ### Field expressions -~~~~ {.ebnf .gram} +~~~~ {.notrust .ebnf .gram} field_expr : expr '.' ident ~~~~ @@ -2417,9 +2417,10 @@ A _field expression_ consists of an expression followed by a single dot and an i when not immediately followed by a parenthesized expression-list (the latter is a [method call expression](#method-call-expressions)). A field expression denotes a field of a [structure](#structure-types). -~~~~ {.field} +~~~~ {.ignore .field} myrecord.myfield; -{a: 10, b: 20}.a; +foo().x; +(Struct {a: 10, b: 20}).a; ~~~~ A field access on a record is an [lvalue](#lvalues-rvalues-and-temporaries) referring to the value of that field. @@ -2430,7 +2431,7 @@ it is automatically dereferenced to make the field access possible. ### Vector expressions -~~~~ {.ebnf .gram} +~~~~ {.notrust .ebnf .gram} vec_expr : '[' "mut" ? vec_elems? ']' vec_elems : [expr [',' expr]*] | [expr ',' ".." expr] @@ -2452,7 +2453,7 @@ as a [literal](#literals) or a [static item](#static-items). ### Index expressions -~~~~ {.ebnf .gram} +~~~~ {.notrust .ebnf .gram} idx_expr : expr '[' expr ']' ~~~~ @@ -2504,7 +2505,7 @@ before the expression they apply to. ### Binary operator expressions -~~~~ {.ebnf .gram} +~~~~ {.notrust .ebnf .gram} binop_expr : expr binop expr ; ~~~~ @@ -2645,7 +2646,7 @@ Any such expression always has the [`unit`](#primitive-types) type. The precedence of Rust binary operators is ordered as follows, going from strong to weak: -~~~~ {.precedence} +~~~~ {.notrust .precedence} * / % as + - @@ -2669,7 +2670,7 @@ An expression enclosed in parentheses evaluates to the result of the enclosed expression. Parentheses can be used to explicitly specify evaluation order within an expression. -~~~~ {.ebnf .gram} +~~~~ {.notrust .ebnf .gram} paren_expr : '(' expr ')' ; ~~~~ @@ -2682,7 +2683,7 @@ let x = (2 + 3) * 4; ### Call expressions -~~~~ {.abnf .gram} +~~~~ {.notrust .ebnf .gram} expr_list : [ expr [ ',' expr ]* ] ? ; paren_expr_list : '(' expr_list ')' ; call_expr : expr paren_expr_list ; @@ -2705,7 +2706,7 @@ let pi: Option = FromStr::from_str("3.14"); ### Lambda expressions -~~~~ {.abnf .gram} +~~~~ {.notrust .ebnf .gram} ident_list : [ ident [ ',' ident ]* ] ? ; lambda_expr : '|' ident_list '|' expr ; ~~~~ @@ -2748,7 +2749,7 @@ ten_times(|j| println!("hello, {}", j)); ### While loops -~~~~ {.ebnf .gram} +~~~~ {.notrust .ebnf .gram} while_expr : "while" expr '{' block '}' ; ~~~~ @@ -2774,7 +2775,7 @@ The keyword `loop` in Rust appears both in _loop expressions_ and in _continue e A loop expression denotes an infinite loop; see [Continue expressions](#continue-expressions) for continue expressions. -~~~~ {.ebnf .gram} +~~~~ {.notrust .ebnf .gram} loop_expr : [ lifetime ':' ] "loop" '{' block '}'; ~~~~ @@ -2785,7 +2786,7 @@ See [Break expressions](#break-expressions). ### Break expressions -~~~~ {.ebnf .gram} +~~~~ {.notrust .ebnf .gram} break_expr : "break" [ lifetime ]; ~~~~ @@ -2798,7 +2799,7 @@ but must enclose it. ### Continue expressions -~~~~ {.ebnf .gram} +~~~~ {.notrust .ebnf .gram} continue_expr : "loop" [ lifetime ]; ~~~~ @@ -2817,7 +2818,7 @@ A `loop` expression is only permitted in the body of a loop. ### For expressions -~~~~ {.ebnf .gram} +~~~~ {.notrust .ebnf .gram} for_expr : "for" pat "in" expr '{' block '}' ; ~~~~ @@ -2851,7 +2852,7 @@ for i in range(0u, 256) { ### If expressions -~~~~ {.ebnf .gram} +~~~~ {.notrust .ebnf .gram} if_expr : "if" expr '{' block '}' else_tail ? ; @@ -2872,7 +2873,7 @@ then any `else` block is executed. ### Match expressions -~~~~ {.ebnf .gram} +~~~~ {.notrust .ebnf .gram} match_expr : "match" expr '{' match_arm [ '|' match_arm ] * '}' ; match_arm : match_pat '=>' [ expr "," | '{' block '}' ] ; @@ -3063,7 +3064,7 @@ let message = match maybe_digit { ### Return expressions -~~~~ {.ebnf .gram} +~~~~ {.notrust .ebnf .gram} return_expr : "return" expr ? ; ~~~~ @@ -3895,7 +3896,7 @@ fn main() { These four log levels correspond to levels 1-4, as controlled by `RUST_LOG`: -```bash +``` {.bash .notrust} $ RUST_LOG=rust=3 ./rust This is an error log This is a warn log diff --git a/src/doc/rustdoc.md b/src/doc/rustdoc.md index 545cafd7f31d1..3359cd4f40d95 100644 --- a/src/doc/rustdoc.md +++ b/src/doc/rustdoc.md @@ -20,6 +20,7 @@ comments": //! the crate index page. The ! makes it apply to the parent of the comment, //! rather than what follows). +# mod workaround_the_outer_function_rustdoc_inserts { /// Widgets are very common (this is a doc comment, and will show up on /// Widget's documentation). pub struct Widget { @@ -36,6 +37,7 @@ pub fn recalibrate() { //! `recalibrate`). /* ... */ } +# } ~~~ Doc comments are markdown, and are currently parsed with the @@ -94,7 +96,7 @@ source code. To test documentation, the `--test` argument is passed to rustdoc: -~~~ +~~~ {.notrust} rustdoc --test crate.rs ~~~ @@ -105,35 +107,44 @@ code blocks as testable-by-default. In order to not run a test over a block of code, the `ignore` string can be added to the three-backtick form of markdown code block. -~~~ -``` -// This is a testable code block -``` + /** + # nested codefences confuse sundown => indentation + comment to + # avoid failing tests + ``` + // This is a testable code block + ``` -```ignore -// This is not a testable code block -``` + ```ignore + // This is not a testable code block + ``` - // This is a testable code block (4-space indent) -~~~ + // This is a testable code block (4-space indent) + */ + # fn foo() {} You can specify that the test's execution should fail with the `should_fail` directive. -~~~ -```should_fail -// This code block is expected to generate a failure when run -``` -~~~ + /** + # nested codefences confuse sundown => indentation + comment to + # avoid failing tests + ```should_fail + // This code block is expected to generate a failure when run + ``` + */ + # fn foo() {} You can specify that the code block should be compiled but not run with the `no_run` directive. -~~~ -```no_run -// This code will be compiled but not executed -``` -~~~ + /** + # nested codefences confuse sundown => indentation + comment to + # avoid failing tests + ```no_run + // This code will be compiled but not executed + ``` + */ + # fn foo() {} Rustdoc also supplies some extra sugar for helping with some tedious documentation examples. If a line is prefixed with `# `, then the line @@ -141,20 +152,23 @@ will not show up in the HTML documentation, but it will be used when testing the code block (NB. the space after the `#` is required, so that one can still write things like `#[deriving(Eq)]`). -~~~ -```rust -# /!\ The three following lines are comments, which are usually stripped off by -# the doc-generating tool. In order to display them anyway in this particular -# case, the character following the leading '#' is not a usual space like in -# these first five lines but a non breakable one. -# -# // showing 'fib' in this documentation would just be tedious and detracts from -# // what's actualy being documented. -# fn fib(n: int) { n + 2 } - -do spawn { fib(200); } -``` -~~~ + /** + # nested codefences confuse sundown => indentation + comment to + # avoid failing tests + ```rust + # /!\ The three following lines are comments, which are usually stripped off by + # the doc-generating tool. In order to display them anyway in this particular + # case, the character following the leading '#' is not a usual space like in + # these first five lines but a non breakable one. + # + # // showing 'fib' in this documentation would just be tedious and detracts from + # // what's actualy being documented. + # fn fib(n: int) { n + 2 } + + do spawn { fib(200); } + ``` + */ + # fn foo() {} The documentation online would look like `do spawn { fib(200); }`, but when testing this code, the `fib` function will be included (so it can compile). @@ -167,12 +181,12 @@ uses is build on crate `test`, which is also used when you compile crates with rustc's `--test` flag. Extra arguments can be passed to rustdoc's test harness with the `--test-args` flag. -~~~ -// Only run tests containing 'foo' in their name -rustdoc --test lib.rs --test-args 'foo' +~~~ {.notrust} +$ # Only run tests containing 'foo' in their name +$ rustdoc --test lib.rs --test-args 'foo' -// See what's possible when running tests -rustdoc --test lib.rs --test-args '--help' +$ # See what's possible when running tests +$ rustdoc --test lib.rs --test-args '--help' ~~~ When testing a library, code examples will often show how functions are used, @@ -181,3 +195,29 @@ rustdoc will implicitly add `extern crate ;` where `` is the name the crate being tested to the top of each code example. This means that rustdoc must be able to find a compiled version of the library crate being tested. Extra search paths may be added via the `-L` flag to `rustdoc`. + +# Standalone Markdown files + +As well as Rust crates, rustdoc supports rendering pure Markdown files +into HTML and testing the code snippets from them. A Markdown file is +detected by a `.md` or `.markdown` extension. + +There are 4 options to modify the output that Rustdoc creates. + +- `--markdown-css PATH`: adds a `` tag pointing to `PATH`. +- `--markdown-in-header FILE`: includes the contents of `FILE` at the + end of the `...` section. +- `--markdown-before-content FILE`: includes the contents of `FILE` + directly after ``, before the rendered content (including the + title). +- `--markdown-after-content FILE`: includes the contents of `FILE` + directly before ``, after all the rendered content. + +All of these can be specified multiple times, and they are output in +the order in which they are specified. The first line of the file must +be the title, prefixed with `%` (e.g. this page has `% Rust +Documentation` on the first line). + +Like with a Rust crate, the `--test` argument will run the code +examples to check they compile, and obeys any `--test-args` flags. The +tests are named after the last `#` heading. diff --git a/src/doc/tutorial.md b/src/doc/tutorial.md index 5069774a2a992..d103b9356d91a 100644 --- a/src/doc/tutorial.md +++ b/src/doc/tutorial.md @@ -1058,10 +1058,12 @@ being destroyed along with the owner. Since the `list` variable above is immutable, the whole list is immutable. The memory allocation itself is the box, while the owner holds onto a pointer to it: - List box List box List box List box - +--------------+ +--------------+ +--------------+ +--------------+ - list -> | Cons | 1 | ~ | -> | Cons | 2 | ~ | -> | Cons | 3 | ~ | -> | Nil | - +--------------+ +--------------+ +--------------+ +--------------+ +~~~ {.notrust} + List box List box List box List box + +--------------+ +--------------+ +--------------+ +--------------+ +list -> | Cons | 1 | ~ | -> | Cons | 2 | ~ | -> | Cons | 3 | ~ | -> | Nil | + +--------------+ +--------------+ +--------------+ +--------------+ +~~~ > ***Note:*** the above diagram shows the logical contents of the enum. The actual > memory layout of the enum may vary. For example, for the `List` enum shown @@ -1173,7 +1175,7 @@ ownership of a list to be passed in rather than just mutating it in-place. The obvious signature for a `List` equality comparison is the following: ~~~{.ignore} -fn eq(xs: List, ys: List) -> bool { ... } +fn eq(xs: List, ys: List) -> bool { /* ... */ } ~~~ However, this will cause both lists to be moved into the function. Ownership @@ -1181,7 +1183,7 @@ isn't required to compare the lists, so the function should take *references* (&T) instead. ~~~{.ignore} -fn eq(xs: &List, ys: &List) -> bool { ... } +fn eq(xs: &List, ys: &List) -> bool { /* ... */ } ~~~ A reference is a *non-owning* view of a value. A reference can be obtained with the `&` (address-of) @@ -1881,9 +1883,9 @@ A caller must in turn have a compatible pointer type to call the method. # Rectangle(Point, Point) # } impl Shape { - fn draw_reference(&self) { ... } - fn draw_owned(~self) { ... } - fn draw_value(self) { ... } + fn draw_reference(&self) { /* ... */ } + fn draw_owned(~self) { /* ... */ } + fn draw_value(self) { /* ... */ } } let s = Circle(Point { x: 1.0, y: 2.0 }, 3.0); @@ -1906,9 +1908,9 @@ to a reference. # Rectangle(Point, Point) # } # impl Shape { -# fn draw_reference(&self) { ... } -# fn draw_owned(~self) { ... } -# fn draw_value(self) { ... } +# fn draw_reference(&self) { /* ... */ } +# fn draw_owned(~self) { /* ... */ } +# fn draw_value(self) { /* ... */ } # } # let s = Circle(Point { x: 1.0, y: 2.0 }, 3.0); // As with typical function arguments, managed and owned pointers @@ -1934,8 +1936,8 @@ These methods are the preferred way to define constructor functions. ~~~~ {.ignore} impl Circle { - fn area(&self) -> f64 { ... } - fn new(area: f64) -> Circle { ... } + fn area(&self) -> f64 { /* ... */ } + fn new(area: f64) -> Circle { /* ... */ } } ~~~~ @@ -2395,8 +2397,8 @@ to an object: # fn new_rectangle() -> Rectangle { true } # fn draw_all(shapes: &[~Drawable]) {} -impl Drawable for Circle { fn draw(&self) { ... } } -impl Drawable for Rectangle { fn draw(&self) { ... } } +impl Drawable for Circle { fn draw(&self) { /* ... */ } } +impl Drawable for Rectangle { fn draw(&self) { /* ... */ } } let c: ~Circle = ~new_circle(); let r: ~Rectangle = ~new_rectangle(); @@ -2510,7 +2512,7 @@ use std::f64::consts::PI; # impl Circle for CircleStruct { fn radius(&self) -> f64 { (self.area() / PI).sqrt() } } # impl Shape for CircleStruct { fn area(&self) -> f64 { PI * square(self.radius) } } -let concrete = @CircleStruct{center:Point{x:3f,y:4f},radius:5f}; +let concrete = @CircleStruct{center:Point{x:3.0,y:4.0},radius:5.0}; let mycircle: @Circle = concrete as @Circle; let nonsense = mycircle.radius() * mycircle.area(); ~~~ @@ -2667,8 +2669,8 @@ mod farm { } impl Farm { - fn feed_chickens(&self) { ... } - pub fn add_chicken(&self, c: Chicken) { ... } + fn feed_chickens(&self) { /* ... */ } + pub fn add_chicken(&self, c: Chicken) { /* ... */ } } pub fn feed_animals(farm: &Farm) { @@ -3144,9 +3146,10 @@ We define two crates, and use one of them as a library in the other. ~~~~ // `world.rs` #[crate_id = "world#0.42"]; -# extern crate extra; + +# mod secret_module_to_make_this_test_run { pub fn explore() -> &'static str { "world" } -# fn main() {} +# } ~~~~ ~~~~ {.ignore} diff --git a/src/etc/extract-tests.py b/src/etc/extract-tests.py deleted file mode 100644 index 2900023ea2b43..0000000000000 --- a/src/etc/extract-tests.py +++ /dev/null @@ -1,217 +0,0 @@ -# Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT -# file at the top-level directory of this distribution and at -# http://rust-lang.org/COPYRIGHT. -# -# Licensed under the Apache License, Version 2.0 or the MIT license -# , at your -# option. This file may not be copied, modified, or distributed -# except according to those terms. - -""" -Script for extracting compilable fragments from markdown documentation. See -prep.js for a description of the format recognized by this tool. Expects -a directory fragments/ to exist under the current directory, and writes the -fragments in there as individual .rs files. -""" -from __future__ import print_function -from codecs import open -from collections import deque -from itertools import imap -import os -import re -import sys - -# regexes -CHAPTER_NAME_REGEX = re.compile(r'# (.*)') -CODE_BLOCK_DELIM_REGEX = re.compile(r'~~~') -COMMENT_REGEX = re.compile(r'^# ') -COMPILER_DIRECTIVE_REGEX = re.compile(r'\#\[(.*)\];') -ELLIPSES_REGEX = re.compile(r'\.\.\.') -EXTERN_CRATE_REGEX = re.compile(r'\bextern crate extra\b') -MAIN_FUNCTION_REGEX = re.compile(r'\bfn main\b') -TAGS_REGEX = re.compile(r'\.([\w-]*)') - -# tags to ignore -IGNORE_TAGS = \ - frozenset(["abnf", "ebnf", "field", "keyword", "notrust", "precedence"]) - -# header for code snippet files -OUTPUT_BLOCK_HEADER = '\n'.join(( - "#[ deny(warnings) ];", - "#[ allow(unused_variable) ];", - "#[ allow(dead_assignment) ];", - "#[ allow(unused_mut) ];", - "#[ allow(attribute_usage) ];", - "#[ allow(dead_code) ];", - "#[ feature(macro_rules, globs, struct_variant, managed_boxes) ];\n",)) - - -def add_extern_mod(block): - if not has_extern_mod(block): - # add `extern crate extra;` after compiler directives - directives = [] - while len(block) and is_compiler_directive(block[0]): - directives.append(block.popleft()) - - block.appendleft("\nextern crate extra;\n\n") - block.extendleft(reversed(directives)) - - return block - - -def add_main_function(block): - if not has_main_function(block): - prepend_spaces = lambda x: ' ' + x - block = deque(imap(prepend_spaces, block)) - block.appendleft("\nfn main() {\n") - block.append("\n}\n") - return block - - -def extract_code_fragments(dest_dir, lines): - """ - Extracts all the code fragments from a file that do not have ignored tags - writing them to the following file: - - [dest dir]/[chapter name]_[chapter_index].rs - """ - chapter_name = None - chapter_index = 0 - - for line in lines: - if is_chapter_title(line): - chapter_name = get_chapter_name(line) - chapter_index = 1 - continue - - if not is_code_block_delim(line): - continue - - assert chapter_name, "Chapter name missing for code block." - tags = get_tags(line) - block = get_code_block(lines) - - if tags & IGNORE_TAGS: - continue - - block = add_extern_mod(add_main_function(block)) - block.appendleft(OUTPUT_BLOCK_HEADER) - - if "ignore" in tags: - block.appendleft("//ignore-test\n") - elif "should_fail" in tags: - block.appendleft("//should-fail\n") - - output_filename = os.path.join( - dest_dir, - chapter_name + '_' + str(chapter_index) + '.rs') - - write_file(output_filename, block) - chapter_index += 1 - - -def has_extern_mod(block): - """Checks if a code block has the line `extern crate extra`.""" - find_extern_mod = lambda x: re.search(EXTERN_CRATE_REGEX, x) - return any(imap(find_extern_mod, block)) - - -def has_main_function(block): - """Checks if a code block has a main function.""" - find_main_fn = lambda x: re.search(MAIN_FUNCTION_REGEX, x) - return any(imap(find_main_fn, block)) - - -def is_chapter_title(line): - return re.match(CHAPTER_NAME_REGEX, line) - - -def is_code_block_delim(line): - return re.match(CODE_BLOCK_DELIM_REGEX, line) - - -def is_compiler_directive(line): - return re.match(COMPILER_DIRECTIVE_REGEX, line) - - -def get_chapter_name(line): - """Get the chapter name from a `# Containers` line.""" - return re.sub( - r'\W', - '_', - re.match(CHAPTER_NAME_REGEX, line).group(1)).lower() - - -def get_code_block(lines): - """ - Get a code block surrounded by ~~~, for example: - - 1: ~~~ { .tag } - 2: let u: ~[u32] = ~[0, 1, 2]; - 3: let v: &[u32] = &[0, 1, 2, 3]; - 4: let w: [u32, .. 5] = [0, 1, 2, 3, 4]; - 5: - 6: println!("u: {}, v: {}, w: {}", u.len(), v.len(), w.len()); - 7: ~~~ - - Returns lines 2-6. Assumes line 1 has been consumed by the caller. - """ - strip_comments = lambda x: re.sub(COMMENT_REGEX, '', x) - strip_ellipses = lambda x: re.sub(ELLIPSES_REGEX, '', x) - - result = deque() - - for line in lines: - if is_code_block_delim(line): - break - result.append(strip_comments(strip_ellipses(line))) - return result - - -def get_lines(filename): - with open(filename) as f: - for line in f: - yield line - - -def get_tags(line): - """ - Retrieves all tags from the line format: - ~~~ { .tag1 .tag2 .tag3 } - """ - return set(re.findall(TAGS_REGEX, line)) - - -def write_file(filename, lines): - with open(filename, 'w', encoding='utf-8') as f: - for line in lines: - f.write(unicode(line, encoding='utf-8', errors='replace')) - - -def main(argv=None): - if not argv: - argv = sys.argv - - if len(sys.argv) < 2: - sys.stderr.write("Please provide an input filename.") - sys.exit(1) - elif len(sys.argv) < 3: - sys.stderr.write("Please provide a destination directory.") - sys.exit(1) - - input_file = sys.argv[1] - dest_dir = sys.argv[2] - - if not os.path.exists(input_file): - sys.stderr.write("Input file does not exist.") - sys.exit(1) - - if not os.path.exists(dest_dir): - os.mkdir(dest_dir) - - extract_code_fragments(dest_dir, get_lines(input_file)) - - -if __name__ == "__main__": - sys.exit(main()) diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs index 19a28931a8a1f..61c1cd734a38d 100644 --- a/src/librustdoc/html/markdown.rs +++ b/src/librustdoc/html/markdown.rs @@ -28,7 +28,6 @@ use std::cast; use std::fmt; -use std::intrinsics; use std::io; use std::libc; use std::local_data; @@ -37,12 +36,16 @@ use std::str; use std::vec; use collections::HashMap; +use html::toc::TocBuilder; use html::highlight; /// A unit struct which has the `fmt::Show` trait implemented. When /// formatted, this struct will emit the HTML corresponding to the rendered /// version of the contained markdown string. pub struct Markdown<'a>(&'a str); +/// A unit struct like `Markdown`, that renders the markdown with a +/// table of contents. +pub struct MarkdownWithToc<'a>(&'a str); static OUTPUT_UNIT: libc::size_t = 64; static MKDEXT_NO_INTRA_EMPHASIS: libc::c_uint = 1 << 0; @@ -76,6 +79,7 @@ struct html_renderopt { struct my_opaque { opt: html_renderopt, dfltblk: extern "C" fn(*buf, *buf, *buf, *libc::c_void), + toc_builder: Option, } struct buf { @@ -122,7 +126,7 @@ fn stripped_filtered_line<'a>(s: &'a str) -> Option<&'a str> { local_data_key!(used_header_map: HashMap<~str, uint>) -pub fn render(w: &mut io::Writer, s: &str) -> fmt::Result { +pub fn render(w: &mut io::Writer, s: &str, print_toc: bool) -> fmt::Result { extern fn block(ob: *buf, text: *buf, lang: *buf, opaque: *libc::c_void) { unsafe { let my_opaque: &my_opaque = cast::transmute(opaque); @@ -163,7 +167,7 @@ pub fn render(w: &mut io::Writer, s: &str) -> fmt::Result { } extern fn header(ob: *buf, text: *buf, level: libc::c_int, - _opaque: *libc::c_void) { + opaque: *libc::c_void) { // sundown does this, we may as well too "\n".with_c_str(|p| unsafe { bufputs(ob, p) }); @@ -184,6 +188,8 @@ pub fn render(w: &mut io::Writer, s: &str) -> fmt::Result { } }).to_owned_vec().connect("-"); + let opaque = unsafe {&mut *(opaque as *mut my_opaque)}; + // Make sure our hyphenated ID is unique for this page let id = local_data::get_mut(used_header_map, |map| { let map = map.unwrap(); @@ -195,9 +201,19 @@ pub fn render(w: &mut io::Writer, s: &str) -> fmt::Result { id.clone() }); + let sec = match opaque.toc_builder { + Some(ref mut builder) => { + builder.push(level as u32, s.clone(), id.clone()) + } + None => {""} + }; + // Render the HTML - let text = format!(r#"{}"#, - s, lvl = level, id = id); + let text = format!(r#"{sec_len,plural,=0{}other{{sec} }}{}"#, + s, lvl = level, id = id, + sec_len = sec.len(), sec = sec); + text.with_c_str(|p| unsafe { bufputs(ob, p) }); } @@ -219,23 +235,30 @@ pub fn render(w: &mut io::Writer, s: &str) -> fmt::Result { let mut callbacks: sd_callbacks = mem::init(); sdhtml_renderer(&callbacks, &options, 0); - let opaque = my_opaque { + let mut opaque = my_opaque { opt: options, dfltblk: callbacks.blockcode.unwrap(), + toc_builder: if print_toc {Some(TocBuilder::new())} else {None} }; callbacks.blockcode = Some(block); callbacks.header = Some(header); let markdown = sd_markdown_new(extensions, 16, &callbacks, - &opaque as *my_opaque as *libc::c_void); + &mut opaque as *mut my_opaque as *libc::c_void); sd_markdown_render(ob, s.as_ptr(), s.len() as libc::size_t, markdown); sd_markdown_free(markdown); - let ret = vec::raw::buf_as_slice((*ob).data, (*ob).size as uint, |buf| { - w.write(buf) - }); + let mut ret = match opaque.toc_builder { + Some(b) => write!(w, "", b.into_toc()), + None => Ok(()) + }; + if ret.is_ok() { + ret = vec::raw::buf_as_slice((*ob).data, (*ob).size as uint, |buf| { + w.write(buf) + }); + } bufrelease(ob); ret } @@ -258,7 +281,7 @@ pub fn find_testable_code(doc: &str, tests: &mut ::test::Collector) { }; if ignore { return } vec::raw::buf_as_slice((*text).data, (*text).size as uint, |text| { - let tests: &mut ::test::Collector = intrinsics::transmute(opaque); + let tests = &mut *(opaque as *mut ::test::Collector); let text = str::from_utf8(text).unwrap(); let mut lines = text.lines().map(|l| stripped_filtered_line(l).unwrap_or(l)); let text = lines.to_owned_vec().connect("\n"); @@ -266,6 +289,19 @@ pub fn find_testable_code(doc: &str, tests: &mut ::test::Collector) { }) } } + extern fn header(_ob: *buf, text: *buf, level: libc::c_int, opaque: *libc::c_void) { + unsafe { + let tests = &mut *(opaque as *mut ::test::Collector); + if text.is_null() { + tests.register_header("", level as u32); + } else { + vec::raw::buf_as_slice((*text).data, (*text).size as uint, |text| { + let text = str::from_utf8(text).unwrap(); + tests.register_header(text, level as u32); + }) + } + } + } unsafe { let ob = bufnew(OUTPUT_UNIT); @@ -276,7 +312,7 @@ pub fn find_testable_code(doc: &str, tests: &mut ::test::Collector) { blockcode: Some(block), blockquote: None, blockhtml: None, - header: None, + header: Some(header), other: mem::init() }; @@ -307,6 +343,13 @@ impl<'a> fmt::Show for Markdown<'a> { let Markdown(md) = *self; // This is actually common enough to special-case if md.len() == 0 { return Ok(()) } - render(fmt.buf, md.as_slice()) + render(fmt.buf, md.as_slice(), false) + } +} + +impl<'a> fmt::Show for MarkdownWithToc<'a> { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + let MarkdownWithToc(md) = *self; + render(fmt.buf, md.as_slice(), true) } } diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs index e985de4d2f578..b705e976e4656 100644 --- a/src/librustdoc/html/render.rs +++ b/src/librustdoc/html/render.rs @@ -1064,7 +1064,8 @@ fn item_module(w: &mut Writer, cx: &Context, clean::ForeignStaticItem(..) => ("ffi-statics", "Foreign Statics"), clean::MacroItem(..) => ("macros", "Macros"), }; - try!(write!(w, "

{}

\n", short, name)); + try!(write!(w, "

{name}

\n
", + id = short, name = name)); } match myitem.inner { diff --git a/src/librustdoc/html/static/main.css b/src/librustdoc/html/static/main.css index 9a5cdaba33c80..0efee51a21909 100644 --- a/src/librustdoc/html/static/main.css +++ b/src/librustdoc/html/static/main.css @@ -320,9 +320,16 @@ pre.rust .string { color: #718C00; } pre.rust .lifetime { color: #C13928; } pre.rust .attribute, pre.rust .attribute .ident { color: #C82829; } -h1 a.anchor, -h2 a.anchor, -h3 a.anchor { display: none; } -h1:hover a.anchor, -h2:hover a.anchor, -h3:hover a.anchor { display: inline-block; } +h1:hover a:after, +h2:hover a:after, +h3:hover a:after, +h4:hover a:after, +h5:hover a:after, +h6:hover a:after { + content: ' § '; +} + +h1.fqn:hover a:after, +:hover a.fnname:after { + content: none; +} diff --git a/src/librustdoc/html/static/main.js b/src/librustdoc/html/static/main.js index 59970ac4508db..3056bca68d784 100644 --- a/src/librustdoc/html/static/main.js +++ b/src/librustdoc/html/static/main.js @@ -600,10 +600,4 @@ initSearch(searchIndex); - $.each($('h1, h2, h3'), function(idx, element) { - if ($(element).attr('id') != undefined) { - $(element).append(' § '); - } - }); }()); diff --git a/src/librustdoc/html/toc.rs b/src/librustdoc/html/toc.rs new file mode 100644 index 0000000000000..61031c222e74a --- /dev/null +++ b/src/librustdoc/html/toc.rs @@ -0,0 +1,269 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Table-of-contents creation. + +use std::fmt; +use std::vec_ng::Vec; + +/// A (recursive) table of contents +#[deriving(Eq)] +pub struct Toc { + /// The levels are strictly decreasing, i.e. + /// + /// entries[0].level >= entries[1].level >= ... + /// + /// Normally they are equal, but can differ in cases like A and B, + /// both of which end up in the same `Toc` as they have the same + /// parent (Main). + /// + /// # Main + /// ### A + /// ## B + priv entries: Vec +} + +impl Toc { + fn count_entries_with_level(&self, level: u32) -> uint { + self.entries.iter().count(|e| e.level == level) + } +} + +#[deriving(Eq)] +pub struct TocEntry { + priv level: u32, + priv sec_number: ~str, + priv name: ~str, + priv id: ~str, + priv children: Toc, +} + +/// Progressive construction of a table of contents. +#[deriving(Eq)] +pub struct TocBuilder { + priv top_level: Toc, + /// The current heirachy of parent headings, the levels are + /// strictly increasing (i.e. chain[0].level < chain[1].level < + /// ...) with each entry being the most recent occurance of a + /// heading with that level (it doesn't include the most recent + /// occurences of every level, just, if *is* in `chain` then is is + /// the most recent one). + /// + /// We also have `chain[0].level <= top_level.entries[last]`. + priv chain: Vec +} + +impl TocBuilder { + pub fn new() -> TocBuilder { + TocBuilder { top_level: Toc { entries: Vec::new() }, chain: Vec::new() } + } + + + /// Convert into a true `Toc` struct. + pub fn into_toc(mut self) -> Toc { + // we know all levels are >= 1. + self.fold_until(0); + self.top_level + } + + /// Collapse the chain until the first heading more important than + /// `level` (i.e. lower level) + /// + /// Example: + /// + /// ## A + /// # B + /// # C + /// ## D + /// ## E + /// ### F + /// #### G + /// ### H + /// + /// If we are considering H (i.e. level 3), then A and B are in + /// self.top_level, D is in C.children, and C, E, F, G are in + /// self.chain. + /// + /// When we attempt to push H, we realise that first G is not the + /// parent (level is too high) so it is popped from chain and put + /// into F.children, then F isn't the parent (level is equal, aka + /// sibling), so it's also popped and put into E.children. + /// + /// This leaves us looking at E, which does have a smaller level, + /// and, by construction, it's the most recent thing with smaller + /// level, i.e. it's the immediate parent of H. + fn fold_until(&mut self, level: u32) { + let mut this = None; + loop { + match self.chain.pop() { + Some(mut next) => { + this.map(|e| next.children.entries.push(e)); + if next.level < level { + // this is the parent we want, so return it to + // its rightful place. + self.chain.push(next); + return + } else { + this = Some(next); + } + } + None => { + this.map(|e| self.top_level.entries.push(e)); + return + } + } + } + } + + /// Push a level `level` heading into the appropriate place in the + /// heirarchy, returning a string containing the section number in + /// `..` format. + pub fn push<'a>(&'a mut self, level: u32, name: ~str, id: ~str) -> &'a str { + assert!(level >= 1); + + // collapse all previous sections into their parents until we + // get to relevant heading (i.e. the first one with a smaller + // level than us) + self.fold_until(level); + + let mut sec_number; + { + let (toc_level, toc) = match self.chain.last() { + None => { + sec_number = ~""; + (0, &self.top_level) + } + Some(entry) => { + sec_number = entry.sec_number.clone(); + sec_number.push_str("."); + (entry.level, &entry.children) + } + }; + // fill in any missing zeros, e.g. for + // # Foo (1) + // ### Bar (1.0.1) + for _ in range(toc_level, level - 1) { + sec_number.push_str("0."); + } + let number = toc.count_entries_with_level(level); + sec_number.push_str(format!("{}", number + 1)) + } + + self.chain.push(TocEntry { + level: level, + name: name, + sec_number: sec_number, + id: id, + children: Toc { entries: Vec::new() } + }); + + // get the thing we just pushed, so we can borrow the string + // out of it with the right lifetime + let just_inserted = self.chain.mut_last().unwrap(); + just_inserted.sec_number.as_slice() + } +} + +impl fmt::Show for Toc { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + try!(write!(fmt.buf, "
    ")); + for entry in self.entries.iter() { + // recursively format this table of contents (the + // `{children}` is the key). + try!(write!(fmt.buf, + "\n
  • {num} {name}{children}
  • ", + id = entry.id, + num = entry.sec_number, name = entry.name, + children = entry.children)) + } + write!(fmt.buf, "
") + } +} + +#[cfg(test)] +mod test { + use super::{TocBuilder, Toc, TocEntry}; + + #[test] + fn builder_smoke() { + let mut builder = TocBuilder::new(); + + // this is purposely not using a fancy macro like below so + // that we're sure that this is doing the correct thing, and + // there's been no macro mistake. + macro_rules! push { + ($level: expr, $name: expr) => { + assert_eq!(builder.push($level, $name.to_owned(), ~""), $name); + } + } + push!(2, "0.1"); + push!(1, "1"); + { + push!(2, "1.1"); + { + push!(3, "1.1.1"); + push!(3, "1.1.2"); + } + push!(2, "1.2"); + { + push!(3, "1.2.1"); + push!(3, "1.2.2"); + } + } + push!(1, "2"); + push!(1, "3"); + { + push!(4, "3.0.0.1"); + { + push!(6, "3.0.0.1.0.1"); + } + push!(4, "3.0.0.2"); + push!(2, "3.1"); + { + push!(4, "3.1.0.1"); + } + } + + macro_rules! toc { + ($(($level: expr, $name: expr, $(($sub: tt))* )),*) => { + Toc { + entries: vec!( + $( + TocEntry { + level: $level, + name: $name.to_owned(), + sec_number: $name.to_owned(), + id: ~"", + children: toc!($($sub),*) + } + ),* + ) + } + } + } + let expected = toc!( + (2, "0.1", ), + + (1, "1", + ((2, "1.1", ((3, "1.1.1", )) ((3, "1.1.2", )))) + ((2, "1.2", ((3, "1.2.1", )) ((3, "1.2.2", )))) + ), + + (1, "2", ), + + (1, "3", + ((4, "3.0.0.1", ((6, "3.0.0.1.0.1", )))) + ((4, "3.0.0.2", )) + ((2, "3.1", ((4, "3.1.0.1", )))) + ) + ); + assert_eq!(expected, builder.into_toc()); + } +} diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index 19e3aed646269..2d08dca97b986 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -14,7 +14,7 @@ #[crate_type = "dylib"]; #[crate_type = "rlib"]; -#[feature(globs, struct_variant, managed_boxes)]; +#[feature(globs, struct_variant, managed_boxes, macro_rules)]; extern crate syntax; extern crate rustc; @@ -26,6 +26,7 @@ extern crate collections; extern crate testing = "test"; extern crate time; +use std::cell::RefCell; use std::local_data; use std::io; use std::io::{File, MemWriter}; @@ -43,7 +44,9 @@ pub mod html { pub mod layout; pub mod markdown; pub mod render; + pub mod toc; } +pub mod markdown; pub mod passes; pub mod plugins; pub mod visit_ast; @@ -105,6 +108,19 @@ pub fn opts() -> ~[getopts::OptGroup] { optflag("", "test", "run code examples as tests"), optmulti("", "test-args", "arguments to pass to the test runner", "ARGS"), + optmulti("", "markdown-css", "CSS files to include via in a rendered Markdown file", + "FILES"), + optmulti("", "markdown-in-header", + "files to include inline in the section of a rendered Markdown file", + "FILES"), + optmulti("", "markdown-before-content", + "files to include inline between and the content of a rendered \ + Markdown file", + "FILES"), + optmulti("", "markdown-after-content", + "files to include inline between the content and of a rendered \ + Markdown file", + "FILES"), ] } @@ -137,8 +153,24 @@ pub fn main_args(args: &[~str]) -> int { } let input = matches.free[0].as_slice(); - if matches.opt_present("test") { - return test::run(input, &matches); + let libs = matches.opt_strs("L").map(|s| Path::new(s.as_slice())); + let libs = @RefCell::new(libs.move_iter().collect()); + + let test_args = matches.opt_strs("test-args"); + let test_args = test_args.iter().flat_map(|s| s.words()).map(|s| s.to_owned()).to_owned_vec(); + + let should_test = matches.opt_present("test"); + let markdown_input = input.ends_with(".md") || input.ends_with(".markdown"); + + let output = matches.opt_str("o").map(|s| Path::new(s)); + + match (should_test, markdown_input) { + (true, true) => return markdown::test(input, libs, test_args), + (true, false) => return test::run(input, libs, test_args), + + (false, true) => return markdown::render(input, output.unwrap_or(Path::new("doc")), + &matches), + (false, false) => {} } if matches.opt_strs("passes") == ~[~"list"] { @@ -163,7 +195,6 @@ pub fn main_args(args: &[~str]) -> int { info!("going to format"); let started = time::precise_time_ns(); - let output = matches.opt_str("o").map(|s| Path::new(s)); match matches.opt_str("w") { Some(~"html") | None => { match html::render::run(krate, output.unwrap_or(Path::new("doc"))) { diff --git a/src/librustdoc/markdown.rs b/src/librustdoc/markdown.rs new file mode 100644 index 0000000000000..5d8e0008b870d --- /dev/null +++ b/src/librustdoc/markdown.rs @@ -0,0 +1,171 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use std::{str, io}; +use std::cell::RefCell; +use std::vec_ng::Vec; + +use collections::HashSet; + +use getopts; +use testing; + +use html::escape::Escape; +use html::markdown::{MarkdownWithToc, find_testable_code, reset_headers}; +use test::Collector; + +fn load_string(input: &Path) -> io::IoResult> { + let mut f = try!(io::File::open(input)); + let d = try!(f.read_to_end()); + Ok(str::from_utf8_owned(d)) +} +macro_rules! load_or_return { + ($input: expr, $cant_read: expr, $not_utf8: expr) => { + { + let input = Path::new($input); + match load_string(&input) { + Err(e) => { + let _ = writeln!(&mut io::stderr(), + "error reading `{}`: {}", input.display(), e); + return $cant_read; + } + Ok(None) => { + let _ = writeln!(&mut io::stderr(), + "error reading `{}`: not UTF-8", input.display()); + return $not_utf8; + } + Ok(Some(s)) => s + } + } + } +} + +/// Separate any lines at the start of the file that begin with `%`. +fn extract_leading_metadata<'a>(s: &'a str) -> (Vec<&'a str>, &'a str) { + let mut metadata = Vec::new(); + for line in s.lines() { + if line.starts_with("%") { + // remove % + metadata.push(line.slice_from(1).trim_left()) + } else { + let line_start_byte = s.subslice_offset(line); + return (metadata, s.slice_from(line_start_byte)); + } + } + // if we're here, then all lines were metadata % lines. + (metadata, "") +} + +fn load_external_files(names: &[~str]) -> Option<~str> { + let mut out = ~""; + for name in names.iter() { + out.push_str(load_or_return!(name.as_slice(), None, None)); + out.push_char('\n'); + } + Some(out) +} + +/// Render `input` (e.g. "foo.md") into an HTML file in `output` +/// (e.g. output = "bar" => "bar/foo.html"). +pub fn render(input: &str, mut output: Path, matches: &getopts::Matches) -> int { + let input_p = Path::new(input); + output.push(input_p.filestem().unwrap()); + output.set_extension("html"); + + let mut css = ~""; + for name in matches.opt_strs("markdown-css").iter() { + let s = format!("\n", name); + css.push_str(s) + } + + let input_str = load_or_return!(input, 1, 2); + + let (in_header, before_content, after_content) = + match (load_external_files(matches.opt_strs("markdown-in-header")), + load_external_files(matches.opt_strs("markdown-before-content")), + load_external_files(matches.opt_strs("markdown-after-content"))) { + (Some(a), Some(b), Some(c)) => (a,b,c), + _ => return 3 + }; + + let mut out = match io::File::create(&output) { + Err(e) => { + let _ = writeln!(&mut io::stderr(), + "error opening `{}` for writing: {}", + output.display(), e); + return 4; + } + Ok(f) => f + }; + + let (metadata, text) = extract_leading_metadata(input_str); + if metadata.len() == 0 { + let _ = writeln!(&mut io::stderr(), + "invalid markdown file: expecting initial line with `% ...TITLE...`"); + return 5; + } + let title = metadata.get(0).as_slice(); + + reset_headers(); + + let err = write!( + &mut out, + r#" + + + + + {title} + + {css} + {in_header} + + + + + {before_content} +

{title}

+ {text} + {after_content} + +"#, + title = Escape(title), + css = css, + in_header = in_header, + before_content = before_content, + text = MarkdownWithToc(text), + after_content = after_content); + + match err { + Err(e) => { + let _ = writeln!(&mut io::stderr(), + "error writing to `{}`: {}", + output.display(), e); + 6 + } + Ok(_) => 0 + } +} + +/// Run any tests/code examples in the markdown file `input`. +pub fn test(input: &str, libs: @RefCell>, mut test_args: ~[~str]) -> int { + let input_str = load_or_return!(input, 1, 2); + + let mut collector = Collector::new(input.to_owned(), libs, true, true); + find_testable_code(input_str, &mut collector); + test_args.unshift(~"rustdoctest"); + testing::test_main(test_args, collector.tests); + 0 +} diff --git a/src/librustdoc/test.rs b/src/librustdoc/test.rs index 5edc24c606659..45607a0992e23 100644 --- a/src/librustdoc/test.rs +++ b/src/librustdoc/test.rs @@ -9,6 +9,7 @@ // except according to those terms. use std::cell::RefCell; +use std::char; use std::io; use std::io::Process; use std::local_data; @@ -22,7 +23,6 @@ use rustc::back::link; use rustc::driver::driver; use rustc::driver::session; use rustc::metadata::creader::Loader; -use getopts; use syntax::diagnostic; use syntax::parse; use syntax::codemap::CodeMap; @@ -35,11 +35,9 @@ use html::markdown; use passes; use visit_ast::RustdocVisitor; -pub fn run(input: &str, matches: &getopts::Matches) -> int { +pub fn run(input: &str, libs: @RefCell>, mut test_args: ~[~str]) -> int { let input_path = Path::new(input); let input = driver::FileInput(input_path.clone()); - let libs = matches.opt_strs("L").map(|s| Path::new(s.as_slice())); - let libs = @RefCell::new(libs.move_iter().collect()); let sessopts = @session::Options { maybe_sysroot: Some(@os::self_exe_path().unwrap().dir_path()), @@ -79,28 +77,19 @@ pub fn run(input: &str, matches: &getopts::Matches) -> int { let (krate, _) = passes::unindent_comments(krate); let (krate, _) = passes::collapse_docs(krate); - let mut collector = Collector { - tests: ~[], - names: ~[], - cnt: 0, - libs: libs, - cratename: krate.name.to_owned(), - }; + let mut collector = Collector::new(krate.name.to_owned(), libs, false, false); collector.fold_crate(krate); - let args = matches.opt_strs("test-args"); - let mut args = args.iter().flat_map(|s| s.words()).map(|s| s.to_owned()); - let mut args = args.to_owned_vec(); - args.unshift(~"rustdoctest"); + test_args.unshift(~"rustdoctest"); - testing::test_main(args, collector.tests); + testing::test_main(test_args, collector.tests); 0 } fn runtest(test: &str, cratename: &str, libs: HashSet, should_fail: bool, - no_run: bool) { - let test = maketest(test, cratename); + no_run: bool, loose_feature_gating: bool) { + let test = maketest(test, cratename, loose_feature_gating); let parsesess = parse::new_parse_sess(); let input = driver::StrInput(test); @@ -173,11 +162,18 @@ fn runtest(test: &str, cratename: &str, libs: HashSet, should_fail: bool, } } -fn maketest(s: &str, cratename: &str) -> ~str { +fn maketest(s: &str, cratename: &str, loose_feature_gating: bool) -> ~str { let mut prog = ~r" #[deny(warnings)]; #[allow(unused_variable, dead_assignment, unused_mut, attribute_usage, dead_code)]; "; + + if loose_feature_gating { + // FIXME #12773: avoid inserting these when the tutorial & manual + // etc. have been updated to not use them so prolifically. + prog.push_str("#[ feature(macro_rules, globs, struct_variant, managed_boxes) ];\n"); + } + if !s.contains("extern crate") { if s.contains("extra") { prog.push_str("extern crate extra;\n"); @@ -198,21 +194,45 @@ fn maketest(s: &str, cratename: &str) -> ~str { } pub struct Collector { - priv tests: ~[testing::TestDescAndFn], + tests: ~[testing::TestDescAndFn], priv names: ~[~str], priv libs: @RefCell>, priv cnt: uint, + priv use_headers: bool, + priv current_header: Option<~str>, priv cratename: ~str, + + priv loose_feature_gating: bool } impl Collector { - pub fn add_test(&mut self, test: &str, should_fail: bool, no_run: bool) { - let test = test.to_owned(); - let name = format!("{}_{}", self.names.connect("::"), self.cnt); + pub fn new(cratename: ~str, libs: @RefCell>, + use_headers: bool, loose_feature_gating: bool) -> Collector { + Collector { + tests: ~[], + names: ~[], + libs: libs, + cnt: 0, + use_headers: use_headers, + current_header: None, + cratename: cratename, + + loose_feature_gating: loose_feature_gating + } + } + + pub fn add_test(&mut self, test: ~str, should_fail: bool, no_run: bool) { + let name = if self.use_headers { + let s = self.current_header.as_ref().map(|s| s.as_slice()).unwrap_or(""); + format!("{}_{}", s, self.cnt) + } else { + format!("{}_{}", self.names.connect("::"), self.cnt) + }; self.cnt += 1; let libs = self.libs.borrow(); let libs = (*libs.get()).clone(); let cratename = self.cratename.to_owned(); + let loose_feature_gating = self.loose_feature_gating; debug!("Creating test {}: {}", name, test); self.tests.push(testing::TestDescAndFn { desc: testing::TestDesc { @@ -221,10 +241,29 @@ impl Collector { should_fail: false, // compiler failures are test failures }, testfn: testing::DynTestFn(proc() { - runtest(test, cratename, libs, should_fail, no_run); + runtest(test, cratename, libs, should_fail, no_run, loose_feature_gating); }), }); } + + pub fn register_header(&mut self, name: &str, level: u32) { + if self.use_headers && level == 1 { + // we use these headings as test names, so it's good if + // they're valid identifiers. + let name = name.chars().enumerate().map(|(i, c)| { + if (i == 0 && char::is_XID_start(c)) || + (i != 0 && char::is_XID_continue(c)) { + c + } else { + '_' + } + }).collect::<~str>(); + + // new header => reset count. + self.cnt = 0; + self.current_header = Some(name); + } + } } impl DocFolder for Collector { @@ -237,7 +276,7 @@ impl DocFolder for Collector { match item.doc_value() { Some(doc) => { self.cnt = 0; - markdown::find_testable_code(doc, self); + markdown::find_testable_code(doc, &mut *self); } None => {} }