mirror of
https://github.com/rust-lang/rust.git
synced 2024-10-30 05:51:58 +00:00
rustdoc: Out with the old, in with the new
Removes old rustdoc, moves rustdoc_ng into its place instead (plus drops the _ng suffix). Also shreds all reference to rustdoc_ng from the Makefile rules.
This commit is contained in:
parent
5f6a8ae966
commit
7b24efd6f3
@ -214,7 +214,6 @@ CFG_LIBRUSTC_$(1) :=$(call CFG_LIB_NAME_$(1),rustc)
|
||||
CFG_LIBSYNTAX_$(1) :=$(call CFG_LIB_NAME_$(1),syntax)
|
||||
CFG_LIBRUSTPKG_$(1) :=$(call CFG_LIB_NAME_$(1),rustpkg)
|
||||
CFG_LIBRUSTDOC_$(1) :=$(call CFG_LIB_NAME_$(1),rustdoc)
|
||||
CFG_LIBRUSTDOCNG_$(1) :=$(call CFG_LIB_NAME_$(1),rustdoc_ng)
|
||||
CFG_LIBRUSTI_$(1) :=$(call CFG_LIB_NAME_$(1),rusti)
|
||||
CFG_LIBRUST_$(1) :=$(call CFG_LIB_NAME_$(1),rust)
|
||||
|
||||
@ -224,7 +223,6 @@ LIBRUSTC_GLOB_$(1) :=$(call CFG_LIB_GLOB_$(1),rustc)
|
||||
LIBSYNTAX_GLOB_$(1) :=$(call CFG_LIB_GLOB_$(1),syntax)
|
||||
LIBRUSTPKG_GLOB_$(1) :=$(call CFG_LIB_GLOB_$(1),rustpkg)
|
||||
LIBRUSTDOC_GLOB_$(1) :=$(call CFG_LIB_GLOB_$(1),rustdoc)
|
||||
LIBRUSTDOCNG_GLOB_$(1) :=$(call CFG_LIB_GLOB_$(1),rustdoc_ng)
|
||||
LIBRUSTI_GLOB_$(1) :=$(call CFG_LIB_GLOB_$(1),rusti)
|
||||
LIBRUST_GLOB_$(1) :=$(call CFG_LIB_GLOB_$(1),rust)
|
||||
EXTRALIB_DSYM_GLOB_$(1) :=$(call CFG_LIB_DSYM_GLOB_$(1),extra)
|
||||
@ -233,7 +231,6 @@ LIBRUSTC_DSYM_GLOB_$(1) :=$(call CFG_LIB_DSYM_GLOB_$(1),rustc)
|
||||
LIBSYNTAX_DSYM_GLOB_$(1) :=$(call CFG_LIB_DSYM_GLOB_$(1),syntax)
|
||||
LIBRUSTPKG_DSYM_GLOB_$(1) :=$(call CFG_LIB_DSYM_GLOB_$(1),rustpkg)
|
||||
LIBRUSTDOC_DSYM_GLOB_$(1) :=$(call CFG_LIB_DSYM_GLOB_$(1),rustdoc)
|
||||
LIBRUSTDOCNG_DSYM_GLOB_$(1) :=$(call CFG_LIB_DSYM_GLOB_$(1),rustdoc_ng)
|
||||
LIBRUSTI_DSYM_GLOB_$(1) :=$(call CFG_LIB_DSYM_GLOB_$(1),rusti)
|
||||
LIBRUST_DSYM_GLOB_$(1) :=$(call CFG_LIB_DSYM_GLOB_$(1),rust)
|
||||
|
||||
@ -442,12 +439,10 @@ CSREQ$(1)_T_$(2)_H_$(3) = \
|
||||
$$(TSREQ$(1)_T_$(2)_H_$(3)) \
|
||||
$$(HBIN$(1)_H_$(3))/rustpkg$$(X_$(3)) \
|
||||
$$(HBIN$(1)_H_$(3))/rustdoc$$(X_$(3)) \
|
||||
$$(HBIN$(1)_H_$(3))/rustdoc_ng$$(X_$(3)) \
|
||||
$$(HBIN$(1)_H_$(3))/rusti$$(X_$(3)) \
|
||||
$$(HBIN$(1)_H_$(3))/rust$$(X_$(3)) \
|
||||
$$(HLIB$(1)_H_$(3))/$(CFG_LIBRUSTPKG_$(3)) \
|
||||
$$(HLIB$(1)_H_$(3))/$(CFG_LIBRUSTDOC_$(3)) \
|
||||
$$(HLIB$(1)_H_$(3))/$(CFG_LIBRUSTDOCNG_$(3)) \
|
||||
$$(HLIB$(1)_H_$(3))/$(CFG_LIBRUSTI_$(3)) \
|
||||
$$(HLIB$(1)_H_$(3))/$(CFG_LIBRUST_$(3)) \
|
||||
$$(TLIB$(1)_T_$(2)_H_$(3))/$(CFG_STDLIB_$(2)) \
|
||||
@ -456,7 +451,6 @@ CSREQ$(1)_T_$(2)_H_$(3) = \
|
||||
$$(TLIB$(1)_T_$(2)_H_$(3))/$(CFG_LIBRUSTC_$(2)) \
|
||||
$$(TLIB$(1)_T_$(2)_H_$(3))/$(CFG_LIBRUSTPKG_$(2)) \
|
||||
$$(TLIB$(1)_T_$(2)_H_$(3))/$(CFG_LIBRUSTDOC_$(2)) \
|
||||
$$(TLIB$(1)_T_$(2)_H_$(3))/$(CFG_LIBRUSTDOCNG_$(2)) \
|
||||
$$(TLIB$(1)_T_$(2)_H_$(3))/$(CFG_LIBRUSTI_$(2)) \
|
||||
$$(TLIB$(1)_T_$(2)_H_$(3))/$(CFG_LIBRUST_$(2))
|
||||
|
||||
|
@ -143,7 +143,8 @@ Version 0.8 (October 2013)
|
||||
* The runtime uses jemalloc for allocations.
|
||||
* Segmented stacks are temporarily disabled as part of the transition to
|
||||
the new runtime. Stack overflows are possible!
|
||||
* A new documentation backend, rustdoc_ng, is available for use
|
||||
* A new documentation backend, rustdoc_ng, is available for use. It is
|
||||
still invoked through the normal `rustdoc` command.
|
||||
|
||||
Version 0.7 (July 2013)
|
||||
-----------------------
|
||||
|
@ -68,12 +68,10 @@ clean$(1)_H_$(2):
|
||||
$(Q)rm -f $$(HBIN$(1)_H_$(2))/rustpkg$(X_$(2))
|
||||
$(Q)rm -f $$(HBIN$(1)_H_$(2))/serializer$(X_$(2))
|
||||
$(Q)rm -f $$(HBIN$(1)_H_$(2))/rustdoc$(X_$(2))
|
||||
$(Q)rm -f $$(HBIN$(1)_H_$(2))/rustdoc_ng$(X_$(2))
|
||||
$(Q)rm -f $$(HBIN$(1)_H_$(2))/rusti$(X_$(2))
|
||||
$(Q)rm -f $$(HBIN$(1)_H_$(2))/rust$(X_$(2))
|
||||
$(Q)rm -f $$(HLIB$(1)_H_$(2))/$(CFG_LIBRUSTPKG_$(2))
|
||||
$(Q)rm -f $$(HLIB$(1)_H_$(2))/$(CFG_LIBRUSTDOC_$(2))
|
||||
$(Q)rm -f $$(HLIB$(1)_H_$(2))/$(CFG_LIBRUSTDOCNG_$(2))
|
||||
$(Q)rm -f $$(HLIB$(1)_H_$(2))/$(CFG_RUNTIME_$(2))
|
||||
$(Q)rm -f $$(HLIB$(1)_H_$(2))/$(CFG_STDLIB_$(2))
|
||||
$(Q)rm -f $$(HLIB$(1)_H_$(2))/$(CFG_EXTRALIB_$(2))
|
||||
@ -87,7 +85,6 @@ clean$(1)_H_$(2):
|
||||
$(Q)rm -f $$(HLIB$(1)_H_$(2))/$(LIBSYNTAX_GLOB_$(2))
|
||||
$(Q)rm -f $$(HLIB$(1)_H_$(2))/$(LIBRUSTPKG_GLOB_$(2))
|
||||
$(Q)rm -f $$(HLIB$(1)_H_$(2))/$(LIBRUSTDOC_GLOB_$(2))
|
||||
$(Q)rm -f $$(HLIB$(1)_H_$(2))/$(LIBRUSTDOCNG_GLOB_$(2))
|
||||
$(Q)rm -f $$(HLIB$(1)_H_$(2))/$(LIBRUSTI_GLOB_$(2))
|
||||
$(Q)rm -f $$(HLIB$(1)_H_$(2))/$(LIBRUST_GLOB_$(2))
|
||||
$(Q)rm -f $$(HLIB$(1)_H_$(2))/$(CFG_RUSTLLVM_$(2))
|
||||
@ -106,12 +103,10 @@ clean$(1)_T_$(2)_H_$(3):
|
||||
$(Q)rm -f $$(TBIN$(1)_T_$(2)_H_$(3))/rustpkg$(X_$(2))
|
||||
$(Q)rm -f $$(TBIN$(1)_T_$(2)_H_$(3))/serializer$(X_$(2))
|
||||
$(Q)rm -f $$(TBIN$(1)_T_$(2)_H_$(3))/rustdoc$(X_$(2))
|
||||
$(Q)rm -f $$(TBIN$(1)_T_$(2)_H_$(3))/rustdoc_ng$(X_$(2))
|
||||
$(Q)rm -f $$(TBIN$(1)_T_$(2)_H_$(3))/rusti$(X_$(2))
|
||||
$(Q)rm -f $$(TBIN$(1)_T_$(2)_H_$(3))/rust$(X_$(2))
|
||||
$(Q)rm -f $$(TLIB$(1)_T_$(2)_H_$(3))/$(CFG_LIBRUSTPKG_$(2))
|
||||
$(Q)rm -f $$(TLIB$(1)_T_$(2)_H_$(3))/$(CFG_LIBRUSTDOC_$(2))
|
||||
$(Q)rm -f $$(TLIB$(1)_T_$(2)_H_$(3))/$(CFG_LIBRUSTDOCNG_$(2))
|
||||
$(Q)rm -f $$(TLIB$(1)_T_$(2)_H_$(3))/$(CFG_RUNTIME_$(2))
|
||||
$(Q)rm -f $$(TLIB$(1)_T_$(2)_H_$(3))/$(CFG_STDLIB_$(2))
|
||||
$(Q)rm -f $$(TLIB$(1)_T_$(2)_H_$(3))/$(CFG_EXTRALIB_$(2))
|
||||
@ -125,7 +120,6 @@ clean$(1)_T_$(2)_H_$(3):
|
||||
$(Q)rm -f $$(TLIB$(1)_T_$(2)_H_$(3))/$(LIBSYNTAX_GLOB_$(2))
|
||||
$(Q)rm -f $$(TLIB$(1)_T_$(2)_H_$(3))/$(LIBRUSTPKG_GLOB_$(2))
|
||||
$(Q)rm -f $$(TLIB$(1)_T_$(2)_H_$(3))/$(LIBRUSTDOC_GLOB_$(2))
|
||||
$(Q)rm -f $$(TLIB$(1)_T_$(2)_H_$(3))/$(LIBRUSTDOCNG_GLOB_$(2))
|
||||
$(Q)rm -f $$(TLIB$(1)_T_$(2)_H_$(3))/$(LIBRUSTI_GLOB_$(2))
|
||||
$(Q)rm -f $$(TLIB$(1)_T_$(2)_H_$(3))/$(LIBRUST_GLOB_$(2))
|
||||
$(Q)rm -f $$(TLIB$(1)_T_$(2)_H_$(3))/$(CFG_RUSTLLVM_$(2))
|
||||
|
@ -39,7 +39,6 @@ PKG_FILES := \
|
||||
libsyntax \
|
||||
rt \
|
||||
librustdoc \
|
||||
rustdoc_ng \
|
||||
rustllvm \
|
||||
snapshots.txt \
|
||||
test) \
|
||||
|
31
mk/docs.mk
31
mk/docs.mk
@ -213,40 +213,21 @@ else
|
||||
|
||||
# The rustdoc executable
|
||||
RUSTDOC = $(HBIN2_H_$(CFG_BUILD_TRIPLE))/rustdoc$(X_$(CFG_BUILD_TRIPLE))
|
||||
RUSTDOC_NG = $(HBIN2_H_$(CFG_BUILD_TRIPLE))/rustdoc_ng$(X_$(CFG_BUILD_TRIPLE))
|
||||
|
||||
# The library documenting macro
|
||||
# $(1) - The output directory
|
||||
# $(1) - The crate name (std/extra)
|
||||
# $(2) - The crate file
|
||||
# $(3) - The crate soruce files
|
||||
# $(3) - The relevant host build triple (to depend on libstd)
|
||||
define libdoc
|
||||
doc/$(1)/index.html: $(2) $(3) $$(RUSTDOC) doc/$(1)/rust.css
|
||||
doc/$(1)/index.html: $$(RUSTDOC) $$(TLIB2_T_$(3)_H_$(3))/$(CFG_STDLIB_$(3))
|
||||
@$$(call E, rustdoc: $$@)
|
||||
$(Q)$(RUSTDOC) $(2) --output-dir=doc/$(1)
|
||||
|
||||
doc/$(1)/rust.css: rust.css
|
||||
@$$(call E, cp: $$@)
|
||||
$(Q)cp $$< $$@
|
||||
$(Q)$(RUSTDOC) html $(2)
|
||||
|
||||
DOCS += doc/$(1)/index.html
|
||||
endef
|
||||
|
||||
# The "next generation" library documenting macro
|
||||
# $(1) - The crate name (std/extra)
|
||||
# $(2) - The crate file
|
||||
# $(3) - The relevant host build triple (to depend on libstd)
|
||||
define libdocng
|
||||
doc/ng/$(1)/index.html: $$(RUSTDOC_NG) $$(TLIB2_T_$(3)_H_$(3))/$(CFG_STDLIB_$(3))
|
||||
@$$(call E, rustdoc_ng: $$@)
|
||||
$(Q)$(RUSTDOC_NG) html $(2) -o doc/ng
|
||||
|
||||
DOCS += doc/ng/$(1)/index.html
|
||||
endef
|
||||
|
||||
$(eval $(call libdoc,std,$(STDLIB_CRATE),$(STDLIB_INPUTS)))
|
||||
$(eval $(call libdoc,extra,$(EXTRALIB_CRATE),$(EXTRALIB_INPUTS)))
|
||||
$(eval $(call libdocng,std,$(STDLIB_CRATE),$(CFG_BUILD_TRIPLE)))
|
||||
$(eval $(call libdocng,extra,$(EXTRALIB_CRATE),$(CFG_BUILD_TRIPLE)))
|
||||
$(eval $(call libdoc,std,$(STDLIB_CRATE),$(CFG_BUILD_TRIPLE)))
|
||||
$(eval $(call libdoc,extra,$(EXTRALIB_CRATE),$(CFG_BUILD_TRIPLE)))
|
||||
endif
|
||||
|
||||
|
||||
|
@ -104,7 +104,6 @@ install-target-$(1)-host-$(2): $$(CSREQ$$(ISTAGE)_T_$(1)_H_$(2))
|
||||
$$(Q)$$(call INSTALL_LIB,$$(LIBSYNTAX_GLOB_$(1)))
|
||||
$$(Q)$$(call INSTALL_LIB,$$(LIBRUSTPKG_GLOB_$(1)))
|
||||
$$(Q)$$(call INSTALL_LIB,$$(LIBRUSTDOC_GLOB_$(1)))
|
||||
$$(Q)$$(call INSTALL_LIB,$$(LIBRUSTDOCNG_GLOB_$(1)))
|
||||
$$(Q)$$(call INSTALL_LIB,$$(LIBRUSTI_GLOB_$(1)))
|
||||
$$(Q)$$(call INSTALL_LIB,$$(LIBRUST_GLOB_$(1)))
|
||||
$$(Q)$$(call INSTALL_LIB,libmorestack.a)
|
||||
@ -140,7 +139,6 @@ install-host: $(CSREQ$(ISTAGE)_T_$(CFG_BUILD_TRIPLE)_H_$(CFG_BUILD_TRIPLE))
|
||||
$(Q)$(call INSTALL,$(HB2),$(PHB),rustc$(X_$(CFG_BUILD_TRIPLE)))
|
||||
$(Q)$(call INSTALL,$(HB2),$(PHB),rustpkg$(X_$(CFG_BUILD_TRIPLE)))
|
||||
$(Q)$(call INSTALL,$(HB2),$(PHB),rustdoc$(X_$(CFG_BUILD_TRIPLE)))
|
||||
$(Q)$(call INSTALL,$(HB2),$(PHB),rustdoc_ng$(X_$(CFG_BUILD_TRIPLE)))
|
||||
$(Q)$(call INSTALL,$(HB2),$(PHB),rusti$(X_$(CFG_BUILD_TRIPLE)))
|
||||
$(Q)$(call INSTALL,$(HB2),$(PHB),rust$(X_$(CFG_BUILD_TRIPLE)))
|
||||
$(Q)$(call INSTALL_LIB,$(STDLIB_GLOB_$(CFG_BUILD_TRIPLE)))
|
||||
@ -151,7 +149,6 @@ install-host: $(CSREQ$(ISTAGE)_T_$(CFG_BUILD_TRIPLE)_H_$(CFG_BUILD_TRIPLE))
|
||||
$(Q)$(call INSTALL_LIB,$(LIBRUST_GLOB_$(CFG_BUILD_TRIPLE)))
|
||||
$(Q)$(call INSTALL_LIB,$(LIBRUSTPKG_GLOB_$(CFG_BUILD_TRIPLE)))
|
||||
$(Q)$(call INSTALL_LIB,$(LIBRUSTDOC_GLOB_$(CFG_BUILD_TRIPLE)))
|
||||
$(Q)$(call INSTALL_LIB,$(LIBRUSTDOCNG_GLOB_$(CFG_BUILD_TRIPLE)))
|
||||
$(Q)$(call INSTALL,$(HL),$(PHL),$(CFG_RUNTIME_$(CFG_BUILD_TRIPLE)))
|
||||
$(Q)$(call INSTALL,$(HL),$(PHL),$(CFG_RUSTLLVM_$(CFG_BUILD_TRIPLE)))
|
||||
$(Q)$(call INSTALL,$(S)/man, $(PREFIX_ROOT)/share/man/man1,rust.1)
|
||||
@ -172,7 +169,6 @@ uninstall:
|
||||
$(Q)rm -f $(PHB)/rusti$(X_$(CFG_BUILD_TRIPLE))
|
||||
$(Q)rm -f $(PHB)/rust$(X_$(CFG_BUILD_TRIPLE))
|
||||
$(Q)rm -f $(PHB)/rustdoc$(X_$(CFG_BUILD_TRIPLE))
|
||||
$(Q)rm -f $(PHB)/rustdoc_ng$(X_$(CFG_BUILD_TRIPLE))
|
||||
$(Q)rm -f $(PHL)/$(CFG_RUSTLLVM_$(CFG_BUILD_TRIPLE))
|
||||
$(Q)rm -f $(PHL)/$(CFG_RUNTIME_$(CFG_BUILD_TRIPLE))
|
||||
$(Q)for i in \
|
||||
@ -182,7 +178,6 @@ uninstall:
|
||||
$(call HOST_LIB_FROM_HL_GLOB,$(LIBSYNTAX_GLOB_$(CFG_BUILD_TRIPLE))) \
|
||||
$(call HOST_LIB_FROM_HL_GLOB,$(LIBRUSTPKG_GLOB_$(CFG_BUILD_TRIPLE))) \
|
||||
$(call HOST_LIB_FROM_HL_GLOB,$(LIBRUSTDOC_GLOB_$(CFG_BUILD_TRIPLE))) \
|
||||
$(call HOST_LIB_FROM_HL_GLOB,$(LIBRUSTDOCNG_GLOB_$(CFG_BUILD_TRIPLE))) \
|
||||
$(call HOST_LIB_FROM_HL_GLOB,$(LIBRUSTI_GLOB_$(CFG_BUILD_TRIPLE))) \
|
||||
$(call HOST_LIB_FROM_HL_GLOB,$(LIBRUST_GLOB_$(CFG_BUILD_TRIPLE))) \
|
||||
; \
|
||||
|
10
mk/tests.mk
10
mk/tests.mk
@ -15,7 +15,7 @@
|
||||
|
||||
# The names of crates that must be tested
|
||||
TEST_TARGET_CRATES = std extra
|
||||
TEST_HOST_CRATES = rust rusti rustpkg rustc rustdoc rustdocng syntax
|
||||
TEST_HOST_CRATES = rust rusti rustpkg rustc rustdoc syntax
|
||||
TEST_CRATES = $(TEST_TARGET_CRATES) $(TEST_HOST_CRATES)
|
||||
|
||||
# Markdown files under doc/ that should have their code extracted and run
|
||||
@ -393,14 +393,6 @@ $(3)/stage$(1)/test/rustdoctest-$(2)$$(X_$(2)): \
|
||||
@$$(call E, compile_and_link: $$@)
|
||||
$$(STAGE$(1)_T_$(2)_H_$(3)) -o $$@ $$< --test
|
||||
|
||||
$(3)/stage$(1)/test/rustdocngtest-$(2)$$(X_$(2)): \
|
||||
$$(RUSTDOCNG_LIB) $$(RUSTDOCNG_INPUTS) \
|
||||
$$(SREQ$(1)_T_$(2)_H_$(3)) \
|
||||
$$(TLIB$(1)_T_$(2)_H_$(3))/$$(CFG_LIBSYNTAX_$(2)) \
|
||||
$$(TLIB$(1)_T_$(2)_H_$(3))/$$(CFG_LIBRUSTC_$(2))
|
||||
@$$(call E, compile_and_link: $$@)
|
||||
$$(STAGE$(1)_T_$(2)_H_$(3)) -o $$@ $$< --test
|
||||
|
||||
endef
|
||||
|
||||
$(foreach host,$(CFG_HOST_TRIPLES), \
|
||||
|
45
mk/tools.mk
45
mk/tools.mk
@ -23,11 +23,6 @@ RUSTPKG_INPUTS := $(wildcard $(S)src/librustpkg/*.rs)
|
||||
RUSTDOC_LIB := $(S)src/librustdoc/rustdoc.rs
|
||||
RUSTDOC_INPUTS := $(wildcard $(S)src/librustdoc/*.rs)
|
||||
|
||||
# rustdoc_ng, the next generation documentation tool
|
||||
|
||||
RUSTDOCNG_LIB := $(S)src/rustdoc_ng/rustdoc_ng.rs
|
||||
RUSTDOCNG_INPUTS := $(wildcard $(S)src/rustdoc_ng/*.rs)
|
||||
|
||||
# Rusti, the JIT REPL
|
||||
RUSTI_LIB := $(S)src/librusti/rusti.rs
|
||||
RUSTI_INPUTS := $(wildcard $(S)src/librusti/*.rs)
|
||||
@ -83,24 +78,6 @@ $$(TBIN$(1)_T_$(4)_H_$(3))/rustdoc$$(X_$(4)): \
|
||||
@$$(call E, compile_and_link: $$@)
|
||||
$$(STAGE$(1)_T_$(4)_H_$(3)) --cfg rustdoc -o $$@ $$<
|
||||
|
||||
$$(TLIB$(1)_T_$(4)_H_$(3))/$(CFG_LIBRUSTDOCNG_$(4)): \
|
||||
$$(RUSTDOCNG_LIB) $$(RUSTDOCNG_INPUTS) \
|
||||
$$(SREQ$(1)_T_$(4)_H_$(3)) \
|
||||
$$(TLIB$(1)_T_$(4)_H_$(3))/$(CFG_LIBRUSTC_$(4)) \
|
||||
| $$(TLIB$(1)_T_$(4)_H_$(3))/
|
||||
@$$(call E, compile_and_link: $$@)
|
||||
$$(call REMOVE_ALL_OLD_GLOB_MATCHES_EXCEPT,$$(dir $$@),$(LIBRUSTDOCNG_GLOB_$(4)),$$(notdir $$@))
|
||||
$$(STAGE$(1)_T_$(4)_H_$(3)) --out-dir $$(@D) $$< && touch $$@
|
||||
$$(call LIST_ALL_OLD_GLOB_MATCHES_EXCEPT,$$(dir $$@),$(LIBRUSTDOCNG_GLOB_$(4)),$$(notdir $$@))
|
||||
|
||||
$$(TBIN$(1)_T_$(4)_H_$(3))/rustdoc_ng$$(X_$(4)): \
|
||||
$$(DRIVER_CRATE) \
|
||||
$$(TSREQ$(1)_T_$(4)_H_$(3)) \
|
||||
$$(TLIB$(1)_T_$(4)_H_$(3))/$(CFG_LIBRUSTDOCNG_$(4)) \
|
||||
| $$(TBIN$(1)_T_$(4)_H_$(3))/
|
||||
@$$(call E, compile_and_link: $$@)
|
||||
$$(STAGE$(1)_T_$(4)_H_$(3)) --cfg rustdoc_ng -o $$@ $$<
|
||||
|
||||
$$(TLIB$(1)_T_$(4)_H_$(3))/$(CFG_LIBRUSTI_$(4)): \
|
||||
$$(RUSTI_LIB) $$(RUSTI_INPUTS) \
|
||||
$$(SREQ$(1)_T_$(4)_H_$(3)) \
|
||||
@ -125,7 +102,6 @@ $$(TLIB$(1)_T_$(4)_H_$(3))/$(CFG_LIBRUST_$(4)): \
|
||||
$$(TLIB$(1)_T_$(4)_H_$(3))/$(CFG_LIBRUSTPKG_$(4)) \
|
||||
$$(TLIB$(1)_T_$(4)_H_$(3))/$(CFG_LIBRUSTI_$(4)) \
|
||||
$$(TLIB$(1)_T_$(4)_H_$(3))/$(CFG_LIBRUSTDOC_$(4)) \
|
||||
$$(TLIB$(1)_T_$(4)_H_$(3))/$(CFG_LIBRUSTDOCNG_$(4)) \
|
||||
$$(TLIB$(1)_T_$(4)_H_$(3))/$(CFG_LIBRUSTC_$(4)) \
|
||||
| $$(TLIB$(1)_T_$(4)_H_$(3))/
|
||||
@$$(call E, compile_and_link: $$@)
|
||||
@ -195,27 +171,6 @@ $$(HBIN$(2)_H_$(4))/rustdoc$$(X_$(4)): \
|
||||
@$$(call E, cp: $$@)
|
||||
$$(Q)cp $$< $$@
|
||||
|
||||
$$(HLIB$(2)_H_$(4))/$(CFG_LIBRUSTDOCNG_$(4)): \
|
||||
$$(TLIB$(1)_T_$(4)_H_$(3))/$(CFG_LIBRUSTDOCNG_$(4)) \
|
||||
$$(HLIB$(2)_H_$(4))/$(CFG_LIBRUSTC_$(4)) \
|
||||
$$(HSREQ$(2)_H_$(4)) \
|
||||
| $$(HLIB$(2)_H_$(4))/
|
||||
@$$(call E, cp: $$@)
|
||||
$$(call REMOVE_ALL_OLD_GLOB_MATCHES_EXCEPT,$$(dir $$@),$(LIBRUSTDOCNG_GLOB_$(4)),$$(notdir $$@))
|
||||
$$(Q)cp $$< $$@
|
||||
$$(call LIST_ALL_OLD_GLOB_MATCHES_EXCEPT,$$(dir $$@),$(LIBRUSTDOCNG_GLOB_$(4)),$$(notdir $$@))
|
||||
$$(Q)cp -R $$(TLIB$(1)_T_$(4)_H_$(3))/$(LIBRUSTDOCNG_GLOB_$(4)) \
|
||||
$$(wildcard $$(TLIB$(1)_T_$(4)_H_$(3))/$(LIBRUSTDOCNG_DSYM_GLOB_$(4))) \
|
||||
$$(HLIB$(2)_H_$(4))
|
||||
|
||||
$$(HBIN$(2)_H_$(4))/rustdoc_ng$$(X_$(4)): \
|
||||
$$(TBIN$(1)_T_$(4)_H_$(3))/rustdoc_ng$$(X_$(4)) \
|
||||
$$(HLIB$(2)_H_$(4))/$(CFG_LIBRUSTDOCNG_$(4)) \
|
||||
$$(HSREQ$(2)_H_$(4)) \
|
||||
| $$(HBIN$(2)_H_$(4))/
|
||||
@$$(call E, cp: $$@)
|
||||
$$(Q)cp $$< $$@
|
||||
|
||||
$$(HLIB$(2)_H_$(4))/$(CFG_LIBRUSTI_$(4)): \
|
||||
$$(TLIB$(1)_T_$(4)_H_$(3))/$(CFG_LIBRUSTI_$(4)) \
|
||||
$$(HLIB$(2)_H_$(4))/$(CFG_LIBRUSTC_$(4)) \
|
||||
|
@ -92,7 +92,7 @@ static COMMANDS: &'static [Command<'static>] = &'static [
|
||||
cmd: "doc",
|
||||
action: CallMain("rustdoc", rustdoc::main_args),
|
||||
usage_line: "generate documentation from doc comments",
|
||||
usage_full: UsgCall(rustdoc::config::usage),
|
||||
usage_full: UsgCall(rustdoc_help),
|
||||
},
|
||||
Command {
|
||||
cmd: "pkg",
|
||||
@ -122,6 +122,10 @@ fn rustc_help() {
|
||||
rustc::usage(os::args()[0].clone())
|
||||
}
|
||||
|
||||
fn rustdoc_help() {
|
||||
rustdoc::usage(os::args()[0].clone())
|
||||
}
|
||||
|
||||
fn find_cmd(command_string: &str) -> Option<Command> {
|
||||
do COMMANDS.iter().find |command| {
|
||||
command.cmd == command_string
|
||||
|
@ -1,186 +0,0 @@
|
||||
// Copyright 2012-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 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
/*!
|
||||
Provides all access to AST-related, non-sendable info
|
||||
|
||||
Rustdoc is intended to be parallel, and the rustc AST is filled with
|
||||
shared boxes. The AST service attempts to provide a single place to
|
||||
query AST-related information, shielding the rest of Rustdoc from its
|
||||
non-sendableness.
|
||||
*/
|
||||
|
||||
|
||||
use parse;
|
||||
|
||||
use std::cell::Cell;
|
||||
use std::comm::{stream, SharedChan, Port};
|
||||
use std::task;
|
||||
use rustc::driver::driver;
|
||||
use rustc::driver::session::Session;
|
||||
use rustc::driver::session::{basic_options, options};
|
||||
use rustc::front;
|
||||
use syntax::ast;
|
||||
use syntax::ast_map;
|
||||
use syntax::ast_util;
|
||||
use syntax;
|
||||
|
||||
pub struct Ctxt {
|
||||
ast: @ast::Crate,
|
||||
ast_map: ast_map::map
|
||||
}
|
||||
|
||||
type SrvOwner<'self,T> = &'self fn(srv: Srv) -> T;
|
||||
pub type CtxtHandler<T> = ~fn(ctxt: Ctxt) -> T;
|
||||
type Parser = ~fn(Session, s: @str) -> @ast::Crate;
|
||||
|
||||
enum Msg {
|
||||
HandleRequest(~fn(Ctxt)),
|
||||
Exit
|
||||
}
|
||||
|
||||
#[deriving(Clone)]
|
||||
pub struct Srv {
|
||||
ch: SharedChan<Msg>
|
||||
}
|
||||
|
||||
pub fn from_str<T>(source: ~str, owner: SrvOwner<T>) -> T {
|
||||
run(owner, source.clone(), parse::from_str_sess)
|
||||
}
|
||||
|
||||
pub fn from_file<T>(file: ~str, owner: SrvOwner<T>) -> T {
|
||||
run(owner, file.clone(), |sess, f| parse::from_file_sess(sess, &Path(f)))
|
||||
}
|
||||
|
||||
fn run<T>(owner: SrvOwner<T>, source: ~str, parse: Parser) -> T {
|
||||
|
||||
let (po, ch) = stream();
|
||||
|
||||
let source = Cell::new(source);
|
||||
let parse = Cell::new(parse);
|
||||
do task::spawn {
|
||||
act(&po, source.take().to_managed(), parse.take());
|
||||
}
|
||||
|
||||
let srv_ = Srv {
|
||||
ch: SharedChan::new(ch)
|
||||
};
|
||||
|
||||
let res = owner(srv_.clone());
|
||||
srv_.ch.send(Exit);
|
||||
res
|
||||
}
|
||||
|
||||
fn act(po: &Port<Msg>, source: @str, parse: Parser) {
|
||||
let sess = build_session();
|
||||
|
||||
let ctxt = build_ctxt(
|
||||
sess,
|
||||
parse(sess, source)
|
||||
);
|
||||
|
||||
let mut keep_going = true;
|
||||
while keep_going {
|
||||
match po.recv() {
|
||||
HandleRequest(f) => {
|
||||
f(ctxt);
|
||||
}
|
||||
Exit => {
|
||||
keep_going = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn exec<T:Send>(
|
||||
srv: Srv,
|
||||
f: ~fn(ctxt: Ctxt) -> T
|
||||
) -> T {
|
||||
let (po, ch) = stream();
|
||||
let msg = HandleRequest(|ctxt| ch.send(f(ctxt)));
|
||||
srv.ch.send(msg);
|
||||
po.recv()
|
||||
}
|
||||
|
||||
fn assign_node_ids(crate: @ast::Crate) -> @ast::Crate {
|
||||
let next_id = @mut 0;
|
||||
let fold = ast_util::node_id_assigner(|| {
|
||||
let i = *next_id;
|
||||
*next_id += 1;
|
||||
i
|
||||
});
|
||||
@fold.fold_crate(crate)
|
||||
}
|
||||
|
||||
fn build_ctxt(sess: Session,
|
||||
ast: @ast::Crate) -> Ctxt {
|
||||
|
||||
use rustc::front::config;
|
||||
|
||||
let ast = syntax::ext::expand::inject_std_macros(sess.parse_sess,
|
||||
sess.opts.cfg.clone(),
|
||||
ast);
|
||||
let ast = config::strip_unconfigured_items(ast);
|
||||
let ast = syntax::ext::expand::expand_crate(sess.parse_sess,
|
||||
sess.opts.cfg.clone(),
|
||||
ast);
|
||||
let ast = front::test::modify_for_testing(sess, ast);
|
||||
let ast = assign_node_ids(ast);
|
||||
let ast_map = ast_map::map_crate(sess.diagnostic(), ast);
|
||||
|
||||
Ctxt {
|
||||
ast: ast,
|
||||
ast_map: ast_map,
|
||||
}
|
||||
}
|
||||
|
||||
fn build_session() -> Session {
|
||||
let sopts: @options = basic_options();
|
||||
let emitter = syntax::diagnostic::emit;
|
||||
|
||||
let session = driver::build_session(sopts, emitter);
|
||||
session
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_prune_unconfigured_items() {
|
||||
let source = ~"#[cfg(shut_up_and_leave_me_alone)]fn a() { }";
|
||||
do from_str(source) |srv| {
|
||||
do exec(srv) |ctxt| {
|
||||
// one item: the __std_macros secret module
|
||||
assert_eq!(ctxt.ast.module.items.len(), 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn srv_should_build_ast_map() {
|
||||
let source = ~"fn a() { }";
|
||||
do from_str(source) |srv| {
|
||||
do exec(srv) |ctxt| {
|
||||
assert!(!ctxt.ast_map.is_empty())
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_ignore_external_import_paths_that_dont_exist() {
|
||||
let source = ~"use forble; use forble::bippy;";
|
||||
from_str(source, |_srv| { } )
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn srv_should_return_request_result() {
|
||||
let source = ~"fn a() { }";
|
||||
do from_str(source) |srv| {
|
||||
let result = exec(srv, |_ctxt| 1000 );
|
||||
assert_eq!(result, 1000);
|
||||
}
|
||||
}
|
@ -1,149 +0,0 @@
|
||||
// Copyright 2012-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 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
/*!
|
||||
Attribute parsing
|
||||
|
||||
The attribute parser provides methods for pulling documentation out of
|
||||
an AST's attributes.
|
||||
*/
|
||||
|
||||
|
||||
use syntax::ast;
|
||||
use syntax::attr;
|
||||
use syntax::attr::{AttrMetaMethods, AttributeMethods};
|
||||
|
||||
pub struct CrateAttrs {
|
||||
name: Option<~str>
|
||||
}
|
||||
|
||||
fn doc_metas(attrs: ~[ast::Attribute]) -> ~[@ast::MetaItem] {
|
||||
attrs.iter()
|
||||
.filter(|at| "doc" == at.name())
|
||||
.map(|at| at.desugar_doc().meta())
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn parse_crate(attrs: ~[ast::Attribute]) -> CrateAttrs {
|
||||
let link_metas = attr::find_linkage_metas(attrs);
|
||||
let name = attr::last_meta_item_value_str_by_name(link_metas, "name");
|
||||
|
||||
CrateAttrs {
|
||||
name: name.map(|s| s.to_owned())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_desc(attrs: ~[ast::Attribute]) -> Option<~str> {
|
||||
let doc_strs = do doc_metas(attrs).move_iter().filter_map |meta| {
|
||||
meta.value_str()
|
||||
}.collect::<~[@str]>();
|
||||
if doc_strs.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(doc_strs.connect("\n"))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_hidden(attrs: ~[ast::Attribute]) -> bool {
|
||||
let r = doc_metas(attrs);
|
||||
do r.iter().any |meta| {
|
||||
match meta.meta_item_list() {
|
||||
Some(metas) => attr::contains_name(metas, "hidden"),
|
||||
None => false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use syntax::ast;
|
||||
use syntax;
|
||||
use super::{parse_hidden, parse_crate, parse_desc};
|
||||
|
||||
fn parse_attributes(source: @str) -> ~[ast::Attribute] {
|
||||
use syntax::parse;
|
||||
use syntax::parse::attr::parser_attr;
|
||||
|
||||
let parse_sess = syntax::parse::new_parse_sess(None);
|
||||
let parser = parse::new_parser_from_source_str(
|
||||
parse_sess, ~[], @"-", source);
|
||||
|
||||
parser.parse_outer_attributes()
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn should_extract_crate_name_from_link_attribute() {
|
||||
let source = @"#[link(name = \"snuggles\")]";
|
||||
let attrs = parse_attributes(source);
|
||||
let attrs = parse_crate(attrs);
|
||||
assert!(attrs.name == Some(~"snuggles"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_not_extract_crate_name_if_no_link_attribute() {
|
||||
let source = @"";
|
||||
let attrs = parse_attributes(source);
|
||||
let attrs = parse_crate(attrs);
|
||||
assert!(attrs.name == None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_not_extract_crate_name_if_no_name_value_in_link_attribute() {
|
||||
let source = @"#[link(whatever)]";
|
||||
let attrs = parse_attributes(source);
|
||||
let attrs = parse_crate(attrs);
|
||||
assert!(attrs.name == None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_desc_should_handle_undocumented_mods() {
|
||||
let source = @"";
|
||||
let attrs = parse_attributes(source);
|
||||
let attrs = parse_desc(attrs);
|
||||
assert!(attrs == None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_desc_should_parse_simple_doc_attributes() {
|
||||
let source = @"#[doc = \"basic\"]";
|
||||
let attrs = parse_attributes(source);
|
||||
let attrs = parse_desc(attrs);
|
||||
assert!(attrs == Some(~"basic"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_parse_hidden_attribute() {
|
||||
let source = @"#[doc(hidden)]";
|
||||
let attrs = parse_attributes(source);
|
||||
assert!(parse_hidden(attrs) == true);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_parse_hidden_attribute_with_other_docs() {
|
||||
let source = @"#[doc = \"foo\"] #[doc(hidden)] #[doc = \"foo\"]";
|
||||
let attrs = parse_attributes(source);
|
||||
assert!(parse_hidden(attrs) == true);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_not_parse_non_hidden_attribute() {
|
||||
let source = @"#[doc = \"\"]";
|
||||
let attrs = parse_attributes(source);
|
||||
assert!(parse_hidden(attrs) == false);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_concatenate_multiple_doc_comments() {
|
||||
let source = @"/// foo\n/// bar";
|
||||
let desc = parse_desc(parse_attributes(source));
|
||||
assert!(desc == Some(~" foo\n bar"));
|
||||
}
|
||||
}
|
@ -1,326 +0,0 @@
|
||||
// Copyright 2012 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 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
/*!
|
||||
The attribute parsing pass
|
||||
|
||||
Traverses the document tree, pulling relevant documention out of the
|
||||
corresponding AST nodes. The information gathered here is the basis
|
||||
of the natural-language documentation for a crate.
|
||||
*/
|
||||
|
||||
|
||||
use astsrv;
|
||||
use attr_parser;
|
||||
use doc::ItemUtils;
|
||||
use doc;
|
||||
use extract::to_str;
|
||||
use fold::Fold;
|
||||
use fold;
|
||||
use pass::Pass;
|
||||
|
||||
use syntax::ast;
|
||||
use syntax::ast_map;
|
||||
|
||||
pub fn mk_pass() -> Pass {
|
||||
Pass {
|
||||
name: ~"attr",
|
||||
f: run
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run(
|
||||
srv: astsrv::Srv,
|
||||
doc: doc::Doc
|
||||
) -> doc::Doc {
|
||||
let fold = Fold {
|
||||
ctxt: srv.clone(),
|
||||
fold_crate: fold_crate,
|
||||
fold_item: fold_item,
|
||||
fold_enum: fold_enum,
|
||||
fold_trait: fold_trait,
|
||||
fold_impl: fold_impl,
|
||||
.. fold::default_any_fold(srv)
|
||||
};
|
||||
(fold.fold_doc)(&fold, doc)
|
||||
}
|
||||
|
||||
fn fold_crate(
|
||||
fold: &fold::Fold<astsrv::Srv>,
|
||||
doc: doc::CrateDoc
|
||||
) -> doc::CrateDoc {
|
||||
|
||||
let srv = fold.ctxt.clone();
|
||||
let doc = fold::default_seq_fold_crate(fold, doc);
|
||||
|
||||
let attrs = do astsrv::exec(srv) |ctxt| {
|
||||
let attrs = ctxt.ast.attrs.clone();
|
||||
attr_parser::parse_crate(attrs)
|
||||
};
|
||||
|
||||
doc::CrateDoc {
|
||||
topmod: doc::ModDoc {
|
||||
item: doc::ItemDoc {
|
||||
name: attrs.name.clone().unwrap_or(doc.topmod.name_()),
|
||||
.. doc.topmod.item.clone()
|
||||
},
|
||||
.. doc.topmod.clone()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn fold_item(
|
||||
fold: &fold::Fold<astsrv::Srv>,
|
||||
doc: doc::ItemDoc
|
||||
) -> doc::ItemDoc {
|
||||
|
||||
let srv = fold.ctxt.clone();
|
||||
let doc = fold::default_seq_fold_item(fold, doc);
|
||||
|
||||
let desc = if doc.id == ast::CRATE_NODE_ID {
|
||||
// This is the top-level mod, use the crate attributes
|
||||
do astsrv::exec(srv) |ctxt| {
|
||||
attr_parser::parse_desc(ctxt.ast.attrs.clone())
|
||||
}
|
||||
} else {
|
||||
parse_item_attrs(srv, doc.id, attr_parser::parse_desc)
|
||||
};
|
||||
|
||||
doc::ItemDoc {
|
||||
desc: desc,
|
||||
.. doc
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_item_attrs<T:Send>(
|
||||
srv: astsrv::Srv,
|
||||
id: doc::AstId,
|
||||
parse_attrs: ~fn(a: ~[ast::Attribute]) -> T) -> T {
|
||||
do astsrv::exec(srv) |ctxt| {
|
||||
let attrs = match ctxt.ast_map.get_copy(&id) {
|
||||
ast_map::node_item(item, _) => item.attrs.clone(),
|
||||
ast_map::node_foreign_item(item, _, _, _) => item.attrs.clone(),
|
||||
_ => fail!("parse_item_attrs: not an item")
|
||||
};
|
||||
parse_attrs(attrs)
|
||||
}
|
||||
}
|
||||
|
||||
fn fold_enum(
|
||||
fold: &fold::Fold<astsrv::Srv>,
|
||||
doc: doc::EnumDoc
|
||||
) -> doc::EnumDoc {
|
||||
|
||||
let srv = fold.ctxt.clone();
|
||||
let doc_id = doc.id();
|
||||
let doc = fold::default_seq_fold_enum(fold, doc);
|
||||
|
||||
doc::EnumDoc {
|
||||
variants: do doc.variants.iter().map |variant| {
|
||||
let variant = (*variant).clone();
|
||||
let desc = {
|
||||
let variant = variant.clone();
|
||||
do astsrv::exec(srv.clone()) |ctxt| {
|
||||
match ctxt.ast_map.get_copy(&doc_id) {
|
||||
ast_map::node_item(@ast::item {
|
||||
node: ast::item_enum(ref enum_definition, _), _
|
||||
}, _) => {
|
||||
let ast_variant =
|
||||
(*enum_definition.variants.iter().find(|v| {
|
||||
to_str(v.node.name) == variant.name
|
||||
}).unwrap()).clone();
|
||||
|
||||
attr_parser::parse_desc(
|
||||
ast_variant.node.attrs.clone())
|
||||
}
|
||||
_ => {
|
||||
fail!("Enum variant %s has id that's not bound to an enum item",
|
||||
variant.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
doc::VariantDoc {
|
||||
desc: desc,
|
||||
.. variant
|
||||
}
|
||||
}.collect(),
|
||||
.. doc
|
||||
}
|
||||
}
|
||||
|
||||
fn fold_trait(
|
||||
fold: &fold::Fold<astsrv::Srv>,
|
||||
doc: doc::TraitDoc
|
||||
) -> doc::TraitDoc {
|
||||
let srv = fold.ctxt.clone();
|
||||
let doc = fold::default_seq_fold_trait(fold, doc);
|
||||
|
||||
doc::TraitDoc {
|
||||
methods: merge_method_attrs(srv, doc.id(), doc.methods.clone()),
|
||||
.. doc
|
||||
}
|
||||
}
|
||||
|
||||
fn merge_method_attrs(
|
||||
srv: astsrv::Srv,
|
||||
item_id: doc::AstId,
|
||||
docs: ~[doc::MethodDoc]
|
||||
) -> ~[doc::MethodDoc] {
|
||||
|
||||
// Create an assoc list from method name to attributes
|
||||
let attrs: ~[(~str, Option<~str>)] = do astsrv::exec(srv) |ctxt| {
|
||||
match ctxt.ast_map.get_copy(&item_id) {
|
||||
ast_map::node_item(@ast::item {
|
||||
node: ast::item_trait(_, _, ref methods), _
|
||||
}, _) => {
|
||||
methods.iter().map(|method| {
|
||||
match (*method).clone() {
|
||||
ast::required(ty_m) => {
|
||||
(to_str(ty_m.ident),
|
||||
attr_parser::parse_desc(ty_m.attrs.clone()))
|
||||
}
|
||||
ast::provided(m) => {
|
||||
(to_str(m.ident), attr_parser::parse_desc(m.attrs.clone()))
|
||||
}
|
||||
}
|
||||
}).collect()
|
||||
}
|
||||
ast_map::node_item(@ast::item {
|
||||
node: ast::item_impl(_, _, _, ref methods), _
|
||||
}, _) => {
|
||||
methods.iter().map(|method| {
|
||||
(to_str(method.ident),
|
||||
attr_parser::parse_desc(method.attrs.clone()))
|
||||
}).collect()
|
||||
}
|
||||
_ => fail!("unexpected item")
|
||||
}
|
||||
};
|
||||
|
||||
do docs.iter().zip(attrs.iter()).map |(doc, attrs)| {
|
||||
assert!(doc.name == attrs.first());
|
||||
let desc = attrs.second();
|
||||
|
||||
doc::MethodDoc {
|
||||
desc: desc,
|
||||
.. (*doc).clone()
|
||||
}
|
||||
}.collect()
|
||||
}
|
||||
|
||||
|
||||
fn fold_impl(
|
||||
fold: &fold::Fold<astsrv::Srv>,
|
||||
doc: doc::ImplDoc
|
||||
) -> doc::ImplDoc {
|
||||
let srv = fold.ctxt.clone();
|
||||
let doc = fold::default_seq_fold_impl(fold, doc);
|
||||
|
||||
doc::ImplDoc {
|
||||
methods: merge_method_attrs(srv, doc.id(), doc.methods.clone()),
|
||||
.. doc
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
|
||||
use astsrv;
|
||||
use attr_pass::run;
|
||||
use doc;
|
||||
use extract;
|
||||
|
||||
fn mk_doc(source: ~str) -> doc::Doc {
|
||||
do astsrv::from_str(source.clone()) |srv| {
|
||||
let doc = extract::from_srv(srv.clone(), ~"");
|
||||
run(srv.clone(), doc)
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_replace_top_module_name_with_crate_name() {
|
||||
let doc = mk_doc(~"#[link(name = \"bond\")];");
|
||||
assert!(doc.cratemod().name_() == ~"bond");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_should_extract_mod_attributes() {
|
||||
let doc = mk_doc(~"#[doc = \"test\"] mod a { }");
|
||||
// hidden __std_macros module at the start.
|
||||
assert!(doc.cratemod().mods()[1].desc() == Some(~"test"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_extract_top_mod_attributes() {
|
||||
let doc = mk_doc(~"#[doc = \"test\"];");
|
||||
assert!(doc.cratemod().desc() == Some(~"test"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_extract_foreign_fn_attributes() {
|
||||
let doc = mk_doc(~"extern { #[doc = \"test\"] fn a(); }");
|
||||
assert!(doc.cratemod().nmods()[0].fns[0].desc() == Some(~"test"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_extract_fn_attributes() {
|
||||
let doc = mk_doc(~"#[doc = \"test\"] fn a() -> int { }");
|
||||
assert!(doc.cratemod().fns()[0].desc() == Some(~"test"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_extract_enum_docs() {
|
||||
let doc = mk_doc(~"#[doc = \"b\"]\
|
||||
enum a { v }");
|
||||
debug!("%?", doc);
|
||||
assert!(doc.cratemod().enums()[0].desc() == Some(~"b"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_extract_variant_docs() {
|
||||
let doc = mk_doc(~"enum a { #[doc = \"c\"] v }");
|
||||
assert!(doc.cratemod().enums()[0].variants[0].desc == Some(~"c"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_extract_trait_docs() {
|
||||
let doc = mk_doc(~"#[doc = \"whatever\"] trait i { fn a(); }");
|
||||
assert!(doc.cratemod().traits()[0].desc() == Some(~"whatever"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_extract_trait_method_docs() {
|
||||
let doc = mk_doc(
|
||||
~"trait i {\
|
||||
#[doc = \"desc\"]\
|
||||
fn f(a: bool) -> bool;\
|
||||
}");
|
||||
assert!(doc.cratemod().traits()[0].methods[0].desc == Some(~"desc"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_extract_impl_docs() {
|
||||
let doc = mk_doc(
|
||||
~"#[doc = \"whatever\"] impl int { fn a() { } }");
|
||||
assert!(doc.cratemod().impls()[0].desc() == Some(~"whatever"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_extract_impl_method_docs() {
|
||||
let doc = mk_doc(
|
||||
~"impl int {\
|
||||
#[doc = \"desc\"]\
|
||||
fn f(a: bool) -> bool { }\
|
||||
}");
|
||||
assert!(doc.cratemod().impls()[0].methods[0].desc == Some(~"desc"));
|
||||
}
|
||||
}
|
@ -1,373 +0,0 @@
|
||||
// Copyright 2012-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 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
|
||||
use std::cell::Cell;
|
||||
use std::os;
|
||||
use std::result::Result;
|
||||
use std::result;
|
||||
use std::run::ProcessOutput;
|
||||
use std::run;
|
||||
use std::vec;
|
||||
use extra::getopts;
|
||||
|
||||
/// The type of document to output
|
||||
#[deriving(Clone, Eq)]
|
||||
pub enum OutputFormat {
|
||||
/// Markdown
|
||||
Markdown,
|
||||
/// HTML, via markdown and pandoc
|
||||
PandocHtml
|
||||
}
|
||||
|
||||
/// How to organize the output
|
||||
#[deriving(Clone, Eq)]
|
||||
pub enum OutputStyle {
|
||||
/// All in a single document
|
||||
DocPerCrate,
|
||||
/// Each module in its own document
|
||||
DocPerMod
|
||||
}
|
||||
|
||||
/// The configuration for a rustdoc session
|
||||
#[deriving(Clone)]
|
||||
pub struct Config {
|
||||
input_crate: Path,
|
||||
output_dir: Path,
|
||||
output_format: OutputFormat,
|
||||
output_style: OutputStyle,
|
||||
pandoc_cmd: Option<~str>
|
||||
}
|
||||
|
||||
fn opt_output_dir() -> ~str { ~"output-dir" }
|
||||
fn opt_output_format() -> ~str { ~"output-format" }
|
||||
fn opt_output_style() -> ~str { ~"output-style" }
|
||||
fn opt_pandoc_cmd() -> ~str { ~"pandoc-cmd" }
|
||||
fn opt_help() -> ~str { ~"h" }
|
||||
|
||||
fn opts() -> ~[(getopts::Opt, ~str)] {
|
||||
~[
|
||||
(getopts::optopt(opt_output_dir()),
|
||||
~"--output-dir <val> Put documents here (default: .)"),
|
||||
(getopts::optopt(opt_output_format()),
|
||||
~"--output-format <val> 'markdown' or 'html' (default)"),
|
||||
(getopts::optopt(opt_output_style()),
|
||||
~"--output-style <val> 'doc-per-crate' or 'doc-per-mod' (default)"),
|
||||
(getopts::optopt(opt_pandoc_cmd()),
|
||||
~"--pandoc-cmd <val> Command for running pandoc"),
|
||||
(getopts::optflag(opt_help()),
|
||||
~"-h, --help Print help")
|
||||
]
|
||||
}
|
||||
|
||||
pub fn usage() {
|
||||
use std::io::println;
|
||||
|
||||
println("Usage: rustdoc [options] <cratefile>\n");
|
||||
println("Options:\n");
|
||||
let r = opts();
|
||||
for opt in r.iter() {
|
||||
printfln!(" %s", opt.second());
|
||||
}
|
||||
println("");
|
||||
}
|
||||
|
||||
pub fn default_config(input_crate: &Path) -> Config {
|
||||
Config {
|
||||
input_crate: (*input_crate).clone(),
|
||||
output_dir: Path("."),
|
||||
output_format: PandocHtml,
|
||||
output_style: DocPerMod,
|
||||
pandoc_cmd: None
|
||||
}
|
||||
}
|
||||
|
||||
type Process = ~fn((&str), (&[~str])) -> ProcessOutput;
|
||||
|
||||
pub fn mock_process_output(_prog: &str, _args: &[~str]) -> ProcessOutput {
|
||||
ProcessOutput {
|
||||
status: 0,
|
||||
output: ~[],
|
||||
error: ~[]
|
||||
}
|
||||
}
|
||||
|
||||
pub fn process_output(prog: &str, args: &[~str]) -> ProcessOutput {
|
||||
run::process_output(prog, args)
|
||||
}
|
||||
|
||||
pub fn parse_config(args: &[~str]) -> Result<Config, ~str> {
|
||||
parse_config_(args, process_output)
|
||||
}
|
||||
|
||||
pub fn parse_config_(
|
||||
args: &[~str],
|
||||
process_output: Process
|
||||
) -> Result<Config, ~str> {
|
||||
let args = args.tail();
|
||||
let opts = vec::unzip(opts().move_iter()).first();
|
||||
match getopts::getopts(args, opts) {
|
||||
Ok(matches) => {
|
||||
if matches.free.len() == 1 {
|
||||
let input_crate = Path(*matches.free.head());
|
||||
config_from_opts(&input_crate, &matches, process_output)
|
||||
} else if matches.free.is_empty() {
|
||||
Err(~"no crates specified")
|
||||
} else {
|
||||
Err(~"multiple crates specified")
|
||||
}
|
||||
}
|
||||
Err(f) => {
|
||||
Err(f.to_err_msg())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn config_from_opts(
|
||||
input_crate: &Path,
|
||||
matches: &getopts::Matches,
|
||||
process_output: Process
|
||||
) -> Result<Config, ~str> {
|
||||
|
||||
let config = default_config(input_crate);
|
||||
let result = result::Ok(config);
|
||||
let result = do result.and_then |config| {
|
||||
let output_dir = matches.opt_str(opt_output_dir());
|
||||
let output_dir = output_dir.map_move(|s| Path(s));
|
||||
result::Ok(Config {
|
||||
output_dir: output_dir.unwrap_or(config.output_dir.clone()),
|
||||
.. config
|
||||
})
|
||||
};
|
||||
let result = do result.and_then |config| {
|
||||
let output_format = matches.opt_str(opt_output_format());
|
||||
do output_format.map_move_default(result::Ok(config.clone())) |output_format| {
|
||||
do parse_output_format(output_format).and_then |output_format| {
|
||||
result::Ok(Config {
|
||||
output_format: output_format,
|
||||
.. config.clone()
|
||||
})
|
||||
}
|
||||
}
|
||||
};
|
||||
let result = do result.and_then |config| {
|
||||
let output_style =
|
||||
matches.opt_str(opt_output_style());
|
||||
do output_style.map_move_default(result::Ok(config.clone())) |output_style| {
|
||||
do parse_output_style(output_style).and_then |output_style| {
|
||||
result::Ok(Config {
|
||||
output_style: output_style,
|
||||
.. config.clone()
|
||||
})
|
||||
}
|
||||
}
|
||||
};
|
||||
let process_output = Cell::new(process_output);
|
||||
let result = do result.and_then |config| {
|
||||
let pandoc_cmd = matches.opt_str(opt_pandoc_cmd());
|
||||
let pandoc_cmd = maybe_find_pandoc(
|
||||
&config, pandoc_cmd, process_output.take());
|
||||
do pandoc_cmd.and_then |pandoc_cmd| {
|
||||
result::Ok(Config {
|
||||
pandoc_cmd: pandoc_cmd,
|
||||
.. config.clone()
|
||||
})
|
||||
}
|
||||
};
|
||||
return result;
|
||||
}
|
||||
|
||||
fn parse_output_format(output_format: &str) -> Result<OutputFormat, ~str> {
|
||||
match output_format.to_str() {
|
||||
~"markdown" => result::Ok(Markdown),
|
||||
~"html" => result::Ok(PandocHtml),
|
||||
_ => result::Err(fmt!("unknown output format '%s'", output_format))
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_output_style(output_style: &str) -> Result<OutputStyle, ~str> {
|
||||
match output_style.to_str() {
|
||||
~"doc-per-crate" => result::Ok(DocPerCrate),
|
||||
~"doc-per-mod" => result::Ok(DocPerMod),
|
||||
_ => result::Err(fmt!("unknown output style '%s'", output_style))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn maybe_find_pandoc(
|
||||
config: &Config,
|
||||
maybe_pandoc_cmd: Option<~str>,
|
||||
process_output: Process
|
||||
) -> Result<Option<~str>, ~str> {
|
||||
if config.output_format != PandocHtml {
|
||||
return result::Ok(maybe_pandoc_cmd);
|
||||
}
|
||||
|
||||
let possible_pandocs = match maybe_pandoc_cmd {
|
||||
Some(pandoc_cmd) => ~[pandoc_cmd],
|
||||
None => {
|
||||
~[~"pandoc"] + match os::homedir() {
|
||||
Some(dir) => {
|
||||
~[dir.push_rel(&Path(".cabal/bin/pandoc")).to_str()]
|
||||
}
|
||||
None => ~[]
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let pandoc = do possible_pandocs.iter().find |&pandoc| {
|
||||
let output = process_output(*pandoc, [~"--version"]);
|
||||
debug!("testing pandoc cmd %s: %?", *pandoc, output);
|
||||
output.status == 0
|
||||
};
|
||||
|
||||
match pandoc {
|
||||
Some(x) => Ok(Some((*x).clone())), // ugly, shouldn't be doubly wrapped
|
||||
None => Err(~"couldn't find pandoc")
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
|
||||
use config::*;
|
||||
use std::result;
|
||||
use std::run::ProcessOutput;
|
||||
|
||||
fn parse_config(args: &[~str]) -> Result<Config, ~str> {
|
||||
parse_config_(args, mock_process_output)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_find_pandoc() {
|
||||
let config = Config {
|
||||
output_format: PandocHtml,
|
||||
.. default_config(&Path("test"))
|
||||
};
|
||||
let mock_process_output: ~fn(&str, &[~str]) -> ProcessOutput = |_, _| {
|
||||
ProcessOutput { status: 0, output: "pandoc 1.8.2.1".as_bytes().to_owned(), error: ~[] }
|
||||
};
|
||||
let result = maybe_find_pandoc(&config, None, mock_process_output);
|
||||
assert!(result == result::Ok(Some(~"pandoc")));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_error_with_no_pandoc() {
|
||||
let config = Config {
|
||||
output_format: PandocHtml,
|
||||
.. default_config(&Path("test"))
|
||||
};
|
||||
let mock_process_output: ~fn(&str, &[~str]) -> ProcessOutput = |_, _| {
|
||||
ProcessOutput { status: 1, output: ~[], error: ~[] }
|
||||
};
|
||||
let result = maybe_find_pandoc(&config, None, mock_process_output);
|
||||
assert!(result == result::Err(~"couldn't find pandoc"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_error_with_no_crates() {
|
||||
let config = parse_config([~"rustdoc"]);
|
||||
assert!(config.unwrap_err() == ~"no crates specified");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_error_with_multiple_crates() {
|
||||
let config =
|
||||
parse_config([~"rustdoc", ~"crate1.rc", ~"crate2.rc"]);
|
||||
assert!(config.unwrap_err() == ~"multiple crates specified");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_set_output_dir_to_cwd_if_not_provided() {
|
||||
let config = parse_config([~"rustdoc", ~"crate.rc"]);
|
||||
assert!(config.unwrap().output_dir == Path("."));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_set_output_dir_if_provided() {
|
||||
let config = parse_config([
|
||||
~"rustdoc", ~"crate.rc", ~"--output-dir", ~"snuggles"
|
||||
]);
|
||||
assert!(config.unwrap().output_dir == Path("snuggles"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_set_output_format_to_pandoc_html_if_not_provided() {
|
||||
let config = parse_config([~"rustdoc", ~"crate.rc"]);
|
||||
assert!(config.unwrap().output_format == PandocHtml);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_set_output_format_to_markdown_if_requested() {
|
||||
let config = parse_config([
|
||||
~"rustdoc", ~"crate.rc", ~"--output-format", ~"markdown"
|
||||
]);
|
||||
assert!(config.unwrap().output_format == Markdown);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_set_output_format_to_pandoc_html_if_requested() {
|
||||
let config = parse_config([
|
||||
~"rustdoc", ~"crate.rc", ~"--output-format", ~"html"
|
||||
]);
|
||||
assert!(config.unwrap().output_format == PandocHtml);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_error_on_bogus_format() {
|
||||
let config = parse_config([
|
||||
~"rustdoc", ~"crate.rc", ~"--output-format", ~"bogus"
|
||||
]);
|
||||
assert!(config.unwrap_err() == ~"unknown output format 'bogus'");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_set_output_style_to_doc_per_mod_by_default() {
|
||||
let config = parse_config([~"rustdoc", ~"crate.rc"]);
|
||||
assert!(config.unwrap().output_style == DocPerMod);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_set_output_style_to_one_doc_if_requested() {
|
||||
let config = parse_config([
|
||||
~"rustdoc", ~"crate.rc", ~"--output-style", ~"doc-per-crate"
|
||||
]);
|
||||
assert!(config.unwrap().output_style == DocPerCrate);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_set_output_style_to_doc_per_mod_if_requested() {
|
||||
let config = parse_config([
|
||||
~"rustdoc", ~"crate.rc", ~"--output-style", ~"doc-per-mod"
|
||||
]);
|
||||
assert!(config.unwrap().output_style == DocPerMod);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_error_on_bogus_output_style() {
|
||||
let config = parse_config([
|
||||
~"rustdoc", ~"crate.rc", ~"--output-style", ~"bogus"
|
||||
]);
|
||||
assert!(config.unwrap_err() == ~"unknown output style 'bogus'");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_set_pandoc_command_if_requested() {
|
||||
let config = parse_config([
|
||||
~"rustdoc", ~"crate.rc", ~"--pandoc-cmd", ~"panda-bear-doc"
|
||||
]);
|
||||
assert!(config.unwrap().pandoc_cmd == Some(~"panda-bear-doc"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_set_pandoc_command_when_using_pandoc() {
|
||||
let config = parse_config([~"rustdoc", ~"crate.rc"]);
|
||||
assert!(config.unwrap().pandoc_cmd == Some(~"pandoc"));
|
||||
}
|
||||
}
|
@ -1,199 +0,0 @@
|
||||
// Copyright 2012 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 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// no-reformat
|
||||
|
||||
/*!
|
||||
* A demonstration module
|
||||
*
|
||||
* Contains documentation in various forms that rustdoc understands,
|
||||
* for testing purposes. It doesn't surve any functional
|
||||
* purpose. This here, for instance, is just some filler text.
|
||||
*
|
||||
* FIXME (#3731): It would be nice if we could run some automated
|
||||
* tests on this file
|
||||
*/
|
||||
|
||||
|
||||
/// The base price of a muffin on a non-holiday
|
||||
static PRICE_OF_A_MUFFIN: float = 70f;
|
||||
|
||||
struct WaitPerson {
|
||||
hair_color: ~str
|
||||
}
|
||||
|
||||
/// The type of things that produce omnomnom
|
||||
enum OmNomNomy {
|
||||
/// Delicious sugar cookies
|
||||
Cookie,
|
||||
/// It's pizza
|
||||
PizzaPie(~[uint])
|
||||
}
|
||||
|
||||
fn take_my_order_please(
|
||||
_waitperson: WaitPerson,
|
||||
_order: ~[OmNomNomy]
|
||||
) -> uint {
|
||||
|
||||
/*!
|
||||
* OMG would you take my order already?
|
||||
*
|
||||
* # Arguments
|
||||
*
|
||||
* * _waitperson - The waitperson that you want to bother
|
||||
* * _order - The order vector. It should be filled with food
|
||||
*
|
||||
* # Return
|
||||
*
|
||||
* The price of the order, including tax
|
||||
*
|
||||
* Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed nec
|
||||
* molestie nisl. Duis massa risus, pharetra a scelerisque a,
|
||||
* molestie eu velit. Donec mattis ligula at ante imperdiet ut
|
||||
* dapibus mauris malesuada.
|
||||
*
|
||||
* Sed gravida nisi a metus elementum sit amet hendrerit dolor
|
||||
* bibendum. Aenean sit amet neque massa, sed tempus tortor. Sed ut
|
||||
* lobortis enim. Proin a mauris quis nunc fermentum ultrices eget a
|
||||
* erat. Mauris in lectus vitae metus sodales auctor. Morbi nunc
|
||||
* quam, ultricies at venenatis non, pellentesque ac dui.
|
||||
*
|
||||
* # Failure
|
||||
*
|
||||
* This function is full of fail
|
||||
*/
|
||||
|
||||
fail!();
|
||||
}
|
||||
|
||||
mod fortress_of_solitude {
|
||||
/*!
|
||||
* Superman's vacation home
|
||||
*
|
||||
* The fortress of solitude is located in the Arctic and it is
|
||||
* cold. What you may not know about the fortress of solitude
|
||||
* though is that it contains two separate bowling alleys. One of
|
||||
* them features bumper-bowling and is kind of lame.
|
||||
*
|
||||
* Really, it's pretty cool.
|
||||
*/
|
||||
|
||||
}
|
||||
|
||||
mod blade_runner {
|
||||
/*!
|
||||
* Blade Runner is probably the best movie ever
|
||||
*
|
||||
* I like that in the world of Blade Runner it is always
|
||||
* raining, and that it's always night time. And Aliens
|
||||
* was also a really good movie.
|
||||
*
|
||||
* Alien 3 was crap though.
|
||||
*/
|
||||
}
|
||||
|
||||
/**
|
||||
* Bored
|
||||
*
|
||||
* Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed nec
|
||||
* molestie nisl. Duis massa risus, pharetra a scelerisque a,
|
||||
* molestie eu velit. Donec mattis ligula at ante imperdiet ut
|
||||
* dapibus mauris malesuada. Sed gravida nisi a metus elementum sit
|
||||
* amet hendrerit dolor bibendum. Aenean sit amet neque massa, sed
|
||||
* tempus tortor. Sed ut lobortis enim. Proin a mauris quis nunc
|
||||
* fermentum ultrices eget a erat. Mauris in lectus vitae metus
|
||||
* sodales auctor. Morbi nunc quam, ultricies at venenatis non,
|
||||
* pellentesque ac dui.
|
||||
*
|
||||
* Quisque vitae est id eros placerat laoreet sit amet eu
|
||||
* nisi. Curabitur suscipit neque porttitor est euismod
|
||||
* lacinia. Curabitur non quam vitae ipsum adipiscing
|
||||
* condimentum. Mauris ut ante eget metus sollicitudin
|
||||
* blandit. Aliquam erat volutpat. Morbi sed nisl mauris. Nulla
|
||||
* facilisi. Phasellus at mollis ipsum. Maecenas sed convallis
|
||||
* sapien. Nullam in ligula turpis. Pellentesque a neque augue. Sed
|
||||
* eget ante feugiat tortor congue auctor ac quis ante. Proin
|
||||
* condimentum lacinia tincidunt.
|
||||
*/
|
||||
struct Bored {
|
||||
bored: bool,
|
||||
}
|
||||
|
||||
impl Drop for Bored {
|
||||
fn drop(&mut self) { }
|
||||
}
|
||||
|
||||
/**
|
||||
* The Shunned House
|
||||
*
|
||||
* From even the greatest of horrors irony is seldom absent. Sometimes it
|
||||
* enters directly into the composition of the events, while sometimes it
|
||||
* relates only to their fortuitous position among persons and
|
||||
* places. The latter sort is splendidly exemplified by a case in the
|
||||
* ancient city of Providence, where in the late forties Edgar Allan Poe
|
||||
* used to sojourn often during his unsuccessful wooing of the gifted
|
||||
* poetess, Mrs. Whitman. Poe generally stopped at the Mansion House in
|
||||
* Benefit Street--the renamed Golden Ball Inn whose roof has sheltered
|
||||
* Washington, Jefferson, and Lafayette--and his favorite walk led
|
||||
* northward along the same street to Mrs. Whitman's home and the
|
||||
* neighboring hillside churchyard of St. John's, whose hidden expanse of
|
||||
* Eighteenth Century gravestones had for him a peculiar fascination.
|
||||
*/
|
||||
trait TheShunnedHouse {
|
||||
/**
|
||||
* Now the irony is this. In this walk, so many times repeated, the
|
||||
* world's greatest master of the terrible and the bizarre was
|
||||
* obliged to pass a particular house on the eastern side of the
|
||||
* street; a dingy, antiquated structure perched on the abruptly
|
||||
* rising side hill, with a great unkempt yard dating from a time
|
||||
* when the region was partly open country. It does not appear that
|
||||
* he ever wrote or spoke of it, nor is there any evidence that he
|
||||
* even noticed it. And yet that house, to the two persons in
|
||||
* possession of certain information, equals or outranks in horror
|
||||
* the wildest fantasy of the genius who so often passed it
|
||||
* unknowingly, and stands starkly leering as a symbol of all that is
|
||||
* unutterably hideous.
|
||||
*
|
||||
* # Arguments
|
||||
*
|
||||
* * unkempt_yard - A yard dating from a time when the region was partly
|
||||
* open country
|
||||
*/
|
||||
fn dingy_house(&self, unkempt_yard: int);
|
||||
|
||||
/**
|
||||
* The house was--and for that matter still is--of a kind to attract
|
||||
* the attention of the curious. Originally a farm or semi-farm
|
||||
* building, it followed the average New England colonial lines of
|
||||
* the middle Eighteenth Century--the prosperous peaked-roof sort,
|
||||
* with two stories and dormerless attic, and with the Georgian
|
||||
* doorway and interior panelling dictated by the progress of taste
|
||||
* at that time. It faced south, with one gable end buried to the
|
||||
* lower windows in the eastward rising hill, and the other exposed
|
||||
* to the foundations toward the street. Its construction, over a
|
||||
* century and a half ago, had followed the grading and straightening
|
||||
* of the road in that especial vicinity; for Benefit Street--at
|
||||
* first called Back Street--was laid out as a lane winding amongst
|
||||
* the graveyards of the first settlers, and straightened only when
|
||||
* the removal of the bodies to the North Burial Ground made it
|
||||
* decently possible to cut through the old family plots.
|
||||
*/
|
||||
fn construct(&self) -> bool;
|
||||
}
|
||||
|
||||
/// Whatever
|
||||
impl TheShunnedHouse for OmNomNomy {
|
||||
fn dingy_house(&self, _unkempt_yard: int) {
|
||||
}
|
||||
|
||||
fn construct(&self) -> bool {
|
||||
fail!();
|
||||
}
|
||||
}
|
@ -1,288 +0,0 @@
|
||||
// Copyright 2012 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 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
/*!
|
||||
Pulls a brief description out of a long description.
|
||||
|
||||
If the first paragraph of a long description is short enough then it
|
||||
is interpreted as the brief description.
|
||||
*/
|
||||
|
||||
|
||||
use astsrv;
|
||||
use doc::ItemUtils;
|
||||
use doc;
|
||||
use fold::Fold;
|
||||
use fold;
|
||||
use pass::Pass;
|
||||
|
||||
use std::util;
|
||||
|
||||
pub fn mk_pass() -> Pass {
|
||||
Pass {
|
||||
name: ~"desc_to_brief",
|
||||
f: run
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run(
|
||||
_srv: astsrv::Srv,
|
||||
doc: doc::Doc
|
||||
) -> doc::Doc {
|
||||
let fold = Fold {
|
||||
fold_item: fold_item,
|
||||
fold_trait: fold_trait,
|
||||
fold_impl: fold_impl,
|
||||
.. fold::default_any_fold(())
|
||||
};
|
||||
(fold.fold_doc)(&fold, doc)
|
||||
}
|
||||
|
||||
fn fold_item(fold: &fold::Fold<()>, doc: doc::ItemDoc) -> doc::ItemDoc {
|
||||
let doc = fold::default_seq_fold_item(fold, doc);
|
||||
|
||||
doc::ItemDoc {
|
||||
brief: extract(doc.desc.clone()),
|
||||
.. doc
|
||||
}
|
||||
}
|
||||
|
||||
fn fold_trait(fold: &fold::Fold<()>, doc: doc::TraitDoc) -> doc::TraitDoc {
|
||||
let doc =fold::default_seq_fold_trait(fold, doc);
|
||||
|
||||
doc::TraitDoc {
|
||||
methods: doc.methods.map(|doc| doc::MethodDoc {
|
||||
brief: extract(doc.desc.clone()),
|
||||
.. (*doc).clone()
|
||||
}),
|
||||
.. doc
|
||||
}
|
||||
}
|
||||
|
||||
fn fold_impl(fold: &fold::Fold<()>, doc: doc::ImplDoc) -> doc::ImplDoc {
|
||||
let doc =fold::default_seq_fold_impl(fold, doc);
|
||||
|
||||
doc::ImplDoc {
|
||||
methods: doc.methods.map(|doc| doc::MethodDoc {
|
||||
brief: extract(doc.desc.clone()),
|
||||
.. (*doc).clone()
|
||||
}),
|
||||
.. doc
|
||||
}
|
||||
}
|
||||
|
||||
pub fn extract(desc: Option<~str>) -> Option<~str> {
|
||||
if desc.is_none() {
|
||||
return None
|
||||
}
|
||||
|
||||
parse_desc(desc.clone().unwrap())
|
||||
}
|
||||
|
||||
fn parse_desc(desc: ~str) -> Option<~str> {
|
||||
static MAX_BRIEF_LEN: uint = 120u;
|
||||
|
||||
match first_sentence(desc.clone()) {
|
||||
Some(first_sentence) => {
|
||||
if first_sentence.len() <= MAX_BRIEF_LEN {
|
||||
Some(first_sentence)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
None => None
|
||||
}
|
||||
}
|
||||
|
||||
fn first_sentence(s: ~str) -> Option<~str> {
|
||||
let paras = paragraphs(s);
|
||||
if !paras.is_empty() {
|
||||
let first_para = paras.head();
|
||||
Some(first_sentence_(*first_para).replace("\n", " "))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn first_sentence_(s: &str) -> ~str {
|
||||
let mut dotcount = 0;
|
||||
// The index of the character following a single dot. This allows
|
||||
// Things like [0..1) to appear in the brief description
|
||||
let idx = s.find(|ch: char| {
|
||||
if ch == '.' {
|
||||
dotcount += 1;
|
||||
false
|
||||
} else if dotcount == 1 {
|
||||
true
|
||||
} else {
|
||||
dotcount = 0;
|
||||
false
|
||||
}
|
||||
});
|
||||
match idx {
|
||||
Some(idx) if idx > 2u => s.slice(0, idx - 1).to_owned(),
|
||||
_ => {
|
||||
if s.ends_with(".") {
|
||||
s.to_owned()
|
||||
} else {
|
||||
s.to_owned()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn paragraphs(s: &str) -> ~[~str] {
|
||||
let mut whitespace_lines = 0;
|
||||
let mut accum = ~"";
|
||||
let mut paras = do s.any_line_iter().fold(~[]) |paras, line| {
|
||||
let mut res = paras;
|
||||
|
||||
if line.is_whitespace() {
|
||||
whitespace_lines += 1;
|
||||
} else {
|
||||
if whitespace_lines > 0 {
|
||||
if !accum.is_empty() {
|
||||
let v = util::replace(&mut accum, ~"");
|
||||
res.push(v);
|
||||
}
|
||||
}
|
||||
|
||||
whitespace_lines = 0;
|
||||
|
||||
accum = if accum.is_empty() {
|
||||
line.to_owned()
|
||||
} else {
|
||||
fmt!("%s\n%s", accum, line)
|
||||
}
|
||||
}
|
||||
|
||||
res
|
||||
};
|
||||
|
||||
if !accum.is_empty() { paras.push(accum); }
|
||||
paras
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
|
||||
use astsrv;
|
||||
use attr_pass;
|
||||
use super::{extract, paragraphs, run};
|
||||
use doc;
|
||||
use extract;
|
||||
|
||||
fn mk_doc(source: ~str) -> doc::Doc {
|
||||
do astsrv::from_str(source.clone()) |srv| {
|
||||
let doc = extract::from_srv(srv.clone(), ~"");
|
||||
let doc = (attr_pass::mk_pass().f)(srv.clone(), doc);
|
||||
run(srv.clone(), doc)
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_promote_desc() {
|
||||
let doc = mk_doc(~"#[doc = \"desc\"] mod m { }");
|
||||
// hidden __std_macros module at the start.
|
||||
assert_eq!(doc.cratemod().mods()[1].brief(), Some(~"desc"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_promote_trait_method_desc() {
|
||||
let doc = mk_doc(~"trait i { #[doc = \"desc\"] fn a(); }");
|
||||
assert!(doc.cratemod().traits()[0].methods[0].brief ==
|
||||
Some(~"desc"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_promote_impl_method_desc() {
|
||||
let doc = mk_doc(
|
||||
~"impl int { #[doc = \"desc\"] fn a() { } }");
|
||||
assert!(doc.cratemod().impls()[0].methods[0].brief == Some(~"desc"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_paragraphs_1() {
|
||||
let paras = paragraphs("1\n\n2");
|
||||
assert_eq!(paras, ~[~"1", ~"2"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_paragraphs_2() {
|
||||
let paras = paragraphs("\n\n1\n1\n\n2\n\n");
|
||||
assert_eq!(paras, ~[~"1\n1", ~"2"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_promote_short_descs() {
|
||||
let desc = Some(~"desc");
|
||||
let brief = extract(desc.clone());
|
||||
assert_eq!(brief, desc);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_not_promote_long_descs() {
|
||||
let desc = Some(~"Warkworth Castle is a ruined medieval building
|
||||
in the town of the same name in the English county of Northumberland,
|
||||
and the town and castle occupy a loop of the River Coquet, less than a mile
|
||||
from England's north-east coast. When the castle was founded is uncertain,
|
||||
but traditionally its construction has been ascribed to Prince Henry of
|
||||
Scotland in the mid 12th century, although it may have been built by
|
||||
King Henry II of England when he took control of England'snorthern
|
||||
counties.");
|
||||
let brief = extract(desc);
|
||||
assert_eq!(brief, None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_promote_first_sentence() {
|
||||
let desc = Some(~"Warkworth Castle is a ruined medieval building
|
||||
in the town. of the same name in the English county of Northumberland,
|
||||
and the town and castle occupy a loop of the River Coquet, less than a mile
|
||||
from England's north-east coast. When the castle was founded is uncertain,
|
||||
but traditionally its construction has been ascribed to Prince Henry of
|
||||
Scotland in the mid 12th century, although it may have been built by
|
||||
King Henry II of England when he took control of England'snorthern
|
||||
counties.");
|
||||
let brief = extract(desc);
|
||||
assert!(brief == Some(
|
||||
~"Warkworth Castle is a ruined medieval building in the town"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_not_consider_double_period_to_end_sentence() {
|
||||
let desc = Some(~"Warkworth..Castle is a ruined medieval building
|
||||
in the town. of the same name in the English county of Northumberland,
|
||||
and the town and castle occupy a loop of the River Coquet, less than a mile
|
||||
from England's north-east coast. When the castle was founded is uncertain,
|
||||
but traditionally its construction has been ascribed to Prince Henry of
|
||||
Scotland in the mid 12th century, although it may have been built by
|
||||
King Henry II of England when he took control of England'snorthern
|
||||
counties.");
|
||||
let brief = extract(desc);
|
||||
assert!(brief == Some(
|
||||
~"Warkworth..Castle is a ruined medieval building in the town"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_not_consider_triple_period_to_end_sentence() {
|
||||
let desc = Some(~"Warkworth... Castle is a ruined medieval building
|
||||
in the town. of the same name in the English county of Northumberland,
|
||||
and the town and castle occupy a loop of the River Coquet, less than a mile
|
||||
from England's north-east coast. When the castle was founded is uncertain,
|
||||
but traditionally its construction has been ascribed to Prince Henry of
|
||||
Scotland in the mid 12th century, although it may have been built by
|
||||
King Henry II of England when he took control of England'snorthern
|
||||
counties.");
|
||||
let brief = extract(desc);
|
||||
assert!(brief == Some(
|
||||
~"Warkworth... Castle is a ruined medieval building in the town"));
|
||||
}
|
||||
}
|
@ -1,393 +0,0 @@
|
||||
// Copyright 2012 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 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
//! The document model
|
||||
|
||||
|
||||
use doc;
|
||||
|
||||
pub type AstId = int;
|
||||
|
||||
#[deriving(Clone, Eq)]
|
||||
pub struct Doc {
|
||||
pages: ~[Page]
|
||||
}
|
||||
|
||||
#[deriving(Clone, Eq)]
|
||||
pub enum Page {
|
||||
CratePage(CrateDoc),
|
||||
ItemPage(ItemTag)
|
||||
}
|
||||
|
||||
#[deriving(Clone, Eq)]
|
||||
pub enum Implementation {
|
||||
Required,
|
||||
Provided,
|
||||
}
|
||||
|
||||
/**
|
||||
* Most rustdocs can be parsed into 'sections' according to their markdown
|
||||
* headers
|
||||
*/
|
||||
#[deriving(Clone, Eq)]
|
||||
pub struct Section {
|
||||
header: ~str,
|
||||
body: ~str
|
||||
}
|
||||
|
||||
// FIXME (#2596): We currently give topmod the name of the crate. There
|
||||
// would probably be fewer special cases if the crate had its own name
|
||||
// and topmod's name was the empty string.
|
||||
#[deriving(Clone, Eq)]
|
||||
pub struct CrateDoc {
|
||||
topmod: ModDoc
|
||||
}
|
||||
|
||||
#[deriving(Clone, Eq)]
|
||||
pub enum ItemTag {
|
||||
ModTag(ModDoc),
|
||||
NmodTag(NmodDoc),
|
||||
StaticTag(StaticDoc),
|
||||
FnTag(FnDoc),
|
||||
EnumTag(EnumDoc),
|
||||
TraitTag(TraitDoc),
|
||||
ImplTag(ImplDoc),
|
||||
TyTag(TyDoc),
|
||||
StructTag(StructDoc)
|
||||
}
|
||||
|
||||
#[deriving(Clone, Eq)]
|
||||
pub struct ItemDoc {
|
||||
id: AstId,
|
||||
name: ~str,
|
||||
path: ~[~str],
|
||||
brief: Option<~str>,
|
||||
desc: Option<~str>,
|
||||
sections: ~[Section],
|
||||
// Indicates that this node is a reexport of a different item
|
||||
reexport: bool
|
||||
}
|
||||
|
||||
#[deriving(Clone, Eq)]
|
||||
pub struct SimpleItemDoc {
|
||||
item: ItemDoc,
|
||||
sig: Option<~str>
|
||||
}
|
||||
|
||||
#[deriving(Clone, Eq)]
|
||||
pub struct ModDoc {
|
||||
item: ItemDoc,
|
||||
items: ~[ItemTag],
|
||||
index: Option<Index>
|
||||
}
|
||||
|
||||
#[deriving(Clone, Eq)]
|
||||
pub struct NmodDoc {
|
||||
item: ItemDoc,
|
||||
fns: ~[FnDoc],
|
||||
index: Option<Index>
|
||||
}
|
||||
|
||||
pub type StaticDoc = SimpleItemDoc;
|
||||
|
||||
pub type FnDoc = SimpleItemDoc;
|
||||
|
||||
#[deriving(Clone, Eq)]
|
||||
pub struct EnumDoc {
|
||||
item: ItemDoc,
|
||||
variants: ~[VariantDoc]
|
||||
}
|
||||
|
||||
#[deriving(Clone, Eq)]
|
||||
pub struct VariantDoc {
|
||||
name: ~str,
|
||||
desc: Option<~str>,
|
||||
sig: Option<~str>
|
||||
}
|
||||
|
||||
#[deriving(Clone, Eq)]
|
||||
pub struct TraitDoc {
|
||||
item: ItemDoc,
|
||||
methods: ~[MethodDoc]
|
||||
}
|
||||
|
||||
#[deriving(Clone, Eq)]
|
||||
pub struct MethodDoc {
|
||||
name: ~str,
|
||||
brief: Option<~str>,
|
||||
desc: Option<~str>,
|
||||
sections: ~[Section],
|
||||
sig: Option<~str>,
|
||||
implementation: Implementation,
|
||||
}
|
||||
|
||||
#[deriving(Clone, Eq)]
|
||||
pub struct ImplDoc {
|
||||
item: ItemDoc,
|
||||
bounds_str: Option<~str>,
|
||||
trait_types: ~[~str],
|
||||
self_ty: Option<~str>,
|
||||
methods: ~[MethodDoc]
|
||||
}
|
||||
|
||||
pub type TyDoc = SimpleItemDoc;
|
||||
|
||||
#[deriving(Clone, Eq)]
|
||||
pub struct StructDoc {
|
||||
item: ItemDoc,
|
||||
fields: ~[~str],
|
||||
sig: Option<~str>
|
||||
}
|
||||
|
||||
#[deriving(Clone, Eq)]
|
||||
pub struct Index {
|
||||
entries: ~[IndexEntry]
|
||||
}
|
||||
|
||||
/**
|
||||
* A single entry in an index
|
||||
*
|
||||
* Fields:
|
||||
*
|
||||
* * kind - The type of thing being indexed, e.g. 'Module'
|
||||
* * name - The name of the thing
|
||||
* * brief - The brief description
|
||||
* * link - A format-specific string representing the link target
|
||||
*/
|
||||
#[deriving(Clone, Eq)]
|
||||
pub struct IndexEntry {
|
||||
kind: ~str,
|
||||
name: ~str,
|
||||
brief: Option<~str>,
|
||||
link: ~str
|
||||
}
|
||||
|
||||
impl Doc {
|
||||
pub fn CrateDoc(&self) -> CrateDoc {
|
||||
self.pages.iter().fold(None, |_m, page| {
|
||||
match (*page).clone() {
|
||||
doc::CratePage(doc) => Some(doc),
|
||||
_ => None
|
||||
}
|
||||
}).unwrap()
|
||||
}
|
||||
|
||||
pub fn cratemod(&self) -> ModDoc {
|
||||
self.CrateDoc().topmod.clone()
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! filt_mapper {
|
||||
($vec:expr, $pat:pat) => {
|
||||
do ($vec).iter().filter_map |thing| {
|
||||
match thing {
|
||||
&$pat => Some((*x).clone()),
|
||||
_ => None
|
||||
}
|
||||
}.collect()
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! md {
|
||||
($id:ident) => {
|
||||
filt_mapper!(self.items, $id(ref x))
|
||||
}
|
||||
}
|
||||
/// Some helper methods on ModDoc, mostly for testing
|
||||
impl ModDoc {
|
||||
pub fn mods(&self) -> ~[ModDoc] {
|
||||
md!(ModTag)
|
||||
}
|
||||
|
||||
pub fn nmods(&self) -> ~[NmodDoc] {
|
||||
md!(NmodTag)
|
||||
}
|
||||
|
||||
pub fn fns(&self) -> ~[FnDoc] {
|
||||
md!(FnTag)
|
||||
}
|
||||
|
||||
pub fn statics(&self) -> ~[StaticDoc] {
|
||||
md!(StaticTag)
|
||||
}
|
||||
|
||||
pub fn enums(&self) -> ~[EnumDoc] {
|
||||
md!(EnumTag)
|
||||
}
|
||||
|
||||
pub fn traits(&self) -> ~[TraitDoc] {
|
||||
md!(TraitTag)
|
||||
}
|
||||
|
||||
pub fn impls(&self) -> ~[ImplDoc] {
|
||||
md!(ImplTag)
|
||||
}
|
||||
|
||||
pub fn types(&self) -> ~[TyDoc] {
|
||||
md!(TyTag)
|
||||
}
|
||||
|
||||
pub fn structs(&self) -> ~[StructDoc] {
|
||||
md!(StructTag)
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! pu {
|
||||
($id:ident) => {
|
||||
filt_mapper!(*self, ItemPage($id(ref x)))
|
||||
}
|
||||
}
|
||||
|
||||
pub trait PageUtils {
|
||||
fn mods(&self) -> ~[ModDoc];
|
||||
fn nmods(&self) -> ~[NmodDoc];
|
||||
fn fns(&self) -> ~[FnDoc];
|
||||
fn statics(&self) -> ~[StaticDoc];
|
||||
fn enums(&self) -> ~[EnumDoc];
|
||||
fn traits(&self) -> ~[TraitDoc];
|
||||
fn impls(&self) -> ~[ImplDoc];
|
||||
fn types(&self) -> ~[TyDoc];
|
||||
}
|
||||
|
||||
impl PageUtils for ~[Page] {
|
||||
|
||||
fn mods(&self) -> ~[ModDoc] {
|
||||
pu!(ModTag)
|
||||
}
|
||||
|
||||
fn nmods(&self) -> ~[NmodDoc] {
|
||||
pu!(NmodTag)
|
||||
}
|
||||
|
||||
fn fns(&self) -> ~[FnDoc] {
|
||||
pu!(FnTag)
|
||||
}
|
||||
|
||||
fn statics(&self) -> ~[StaticDoc] {
|
||||
pu!(StaticTag)
|
||||
}
|
||||
|
||||
fn enums(&self) -> ~[EnumDoc] {
|
||||
pu!(EnumTag)
|
||||
}
|
||||
|
||||
fn traits(&self) -> ~[TraitDoc] {
|
||||
pu!(TraitTag)
|
||||
}
|
||||
|
||||
fn impls(&self) -> ~[ImplDoc] {
|
||||
pu!(ImplTag)
|
||||
}
|
||||
|
||||
fn types(&self) -> ~[TyDoc] {
|
||||
pu!(TyTag)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Item {
|
||||
fn item(&self) -> ItemDoc;
|
||||
}
|
||||
|
||||
impl Item for ItemTag {
|
||||
fn item(&self) -> ItemDoc {
|
||||
match self {
|
||||
&doc::ModTag(ref doc) => doc.item.clone(),
|
||||
&doc::NmodTag(ref doc) => doc.item.clone(),
|
||||
&doc::FnTag(ref doc) => doc.item.clone(),
|
||||
&doc::StaticTag(ref doc) => doc.item.clone(),
|
||||
&doc::EnumTag(ref doc) => doc.item.clone(),
|
||||
&doc::TraitTag(ref doc) => doc.item.clone(),
|
||||
&doc::ImplTag(ref doc) => doc.item.clone(),
|
||||
&doc::TyTag(ref doc) => doc.item.clone(),
|
||||
&doc::StructTag(ref doc) => doc.item.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Item for SimpleItemDoc {
|
||||
fn item(&self) -> ItemDoc {
|
||||
self.item.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl Item for ModDoc {
|
||||
fn item(&self) -> ItemDoc {
|
||||
self.item.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl Item for NmodDoc {
|
||||
fn item(&self) -> ItemDoc {
|
||||
self.item.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl Item for EnumDoc {
|
||||
fn item(&self) -> ItemDoc {
|
||||
self.item.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl Item for TraitDoc {
|
||||
fn item(&self) -> ItemDoc {
|
||||
self.item.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl Item for ImplDoc {
|
||||
fn item(&self) -> ItemDoc {
|
||||
self.item.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl Item for StructDoc {
|
||||
fn item(&self) -> ItemDoc {
|
||||
self.item.clone()
|
||||
}
|
||||
}
|
||||
|
||||
pub trait ItemUtils {
|
||||
fn id(&self) -> AstId;
|
||||
/// FIXME #5898: This conflicts with
|
||||
/// syntax::attr::AttrMetaMethods.name; This rustdoc seems to be on
|
||||
/// the way out so I'm making this one look bad rather than the
|
||||
/// new methods in attr.
|
||||
fn name_(&self) -> ~str;
|
||||
fn path(&self) -> ~[~str];
|
||||
fn brief(&self) -> Option<~str>;
|
||||
fn desc(&self) -> Option<~str>;
|
||||
fn sections(&self) -> ~[Section];
|
||||
}
|
||||
|
||||
impl<A:Item> ItemUtils for A {
|
||||
fn id(&self) -> AstId {
|
||||
self.item().id
|
||||
}
|
||||
|
||||
fn name_(&self) -> ~str {
|
||||
self.item().name.clone()
|
||||
}
|
||||
|
||||
fn path(&self) -> ~[~str] {
|
||||
self.item().path.clone()
|
||||
}
|
||||
|
||||
fn brief(&self) -> Option<~str> {
|
||||
self.item().brief.clone()
|
||||
}
|
||||
|
||||
fn desc(&self) -> Option<~str> {
|
||||
self.item().desc.clone()
|
||||
}
|
||||
|
||||
fn sections(&self) -> ~[Section] {
|
||||
self.item().sections.clone()
|
||||
}
|
||||
}
|
@ -1,29 +0,0 @@
|
||||
// Copyright 2012 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 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
//! Escapes text sequences
|
||||
|
||||
use pass::Pass;
|
||||
use text_pass;
|
||||
|
||||
pub fn mk_pass() -> Pass {
|
||||
text_pass::mk_pass(~"escape", escape)
|
||||
}
|
||||
|
||||
fn escape(s: &str) -> ~str {
|
||||
s.replace("\\", "\\\\")
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_escape_backslashes() {
|
||||
let s = ~"\\n";
|
||||
let r = escape(s);
|
||||
assert_eq!(r, ~"\\\\n");
|
||||
}
|
@ -1,414 +0,0 @@
|
||||
// Copyright 2012-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 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
//! Converts the Rust AST to the rustdoc document model
|
||||
|
||||
|
||||
use astsrv;
|
||||
use doc::ItemUtils;
|
||||
use doc;
|
||||
|
||||
use syntax::ast;
|
||||
use syntax::parse::token::{ident_interner, ident_to_str};
|
||||
use syntax::parse::token;
|
||||
|
||||
// Hack; rather than thread an interner through everywhere, rely on
|
||||
// thread-local data
|
||||
// Hack-Becomes-Feature: using thread-local-state everywhere...
|
||||
pub fn to_str(id: ast::Ident) -> ~str {
|
||||
/* bad */ ident_to_str(&id).to_owned()
|
||||
}
|
||||
|
||||
// get rid of this pointless function:
|
||||
pub fn interner() -> @ident_interner {
|
||||
return token::get_ident_interner();
|
||||
}
|
||||
|
||||
pub fn from_srv(
|
||||
srv: astsrv::Srv,
|
||||
default_name: ~str
|
||||
) -> doc::Doc {
|
||||
|
||||
//! Use the AST service to create a document tree
|
||||
|
||||
do astsrv::exec(srv) |ctxt| {
|
||||
extract(ctxt.ast, default_name.clone())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn extract(
|
||||
crate: @ast::Crate,
|
||||
default_name: ~str
|
||||
) -> doc::Doc {
|
||||
doc::Doc {
|
||||
pages: ~[
|
||||
doc::CratePage(doc::CrateDoc {
|
||||
topmod: top_moddoc_from_crate(crate, default_name),
|
||||
})
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
fn top_moddoc_from_crate(
|
||||
crate: @ast::Crate,
|
||||
default_name: ~str
|
||||
) -> doc::ModDoc {
|
||||
moddoc_from_mod(mk_itemdoc(ast::CRATE_NODE_ID, default_name),
|
||||
crate.module.clone())
|
||||
}
|
||||
|
||||
fn mk_itemdoc(id: ast::NodeId, name: ~str) -> doc::ItemDoc {
|
||||
doc::ItemDoc {
|
||||
id: id,
|
||||
name: name,
|
||||
path: ~[],
|
||||
brief: None,
|
||||
desc: None,
|
||||
sections: ~[],
|
||||
reexport: false
|
||||
}
|
||||
}
|
||||
|
||||
fn moddoc_from_mod(
|
||||
itemdoc: doc::ItemDoc,
|
||||
module_: ast::_mod
|
||||
) -> doc::ModDoc {
|
||||
doc::ModDoc {
|
||||
item: itemdoc,
|
||||
items: do module_.items.iter().filter_map |item| {
|
||||
let ItemDoc = mk_itemdoc(item.id, to_str(item.ident));
|
||||
match item.node.clone() {
|
||||
ast::item_mod(m) => {
|
||||
Some(doc::ModTag(
|
||||
moddoc_from_mod(ItemDoc, m)
|
||||
))
|
||||
}
|
||||
ast::item_foreign_mod(nm) => {
|
||||
Some(doc::NmodTag(
|
||||
nmoddoc_from_mod(ItemDoc, nm)
|
||||
))
|
||||
}
|
||||
ast::item_fn(*) => {
|
||||
Some(doc::FnTag(
|
||||
fndoc_from_fn(ItemDoc)
|
||||
))
|
||||
}
|
||||
ast::item_static(*) => {
|
||||
Some(doc::StaticTag(
|
||||
staticdoc_from_static(ItemDoc)
|
||||
))
|
||||
}
|
||||
ast::item_enum(enum_definition, _) => {
|
||||
Some(doc::EnumTag(
|
||||
enumdoc_from_enum(ItemDoc, enum_definition.variants.clone())
|
||||
))
|
||||
}
|
||||
ast::item_trait(_, _, methods) => {
|
||||
Some(doc::TraitTag(
|
||||
traitdoc_from_trait(ItemDoc, methods)
|
||||
))
|
||||
}
|
||||
ast::item_impl(_, _, _, methods) => {
|
||||
Some(doc::ImplTag(
|
||||
impldoc_from_impl(ItemDoc, methods)
|
||||
))
|
||||
}
|
||||
ast::item_ty(_, _) => {
|
||||
Some(doc::TyTag(
|
||||
tydoc_from_ty(ItemDoc)
|
||||
))
|
||||
}
|
||||
ast::item_struct(def, _) => {
|
||||
Some(doc::StructTag(
|
||||
structdoc_from_struct(ItemDoc, def)
|
||||
))
|
||||
}
|
||||
_ => None
|
||||
}
|
||||
}.collect(),
|
||||
index: None
|
||||
}
|
||||
}
|
||||
|
||||
fn nmoddoc_from_mod(
|
||||
itemdoc: doc::ItemDoc,
|
||||
module_: ast::foreign_mod
|
||||
) -> doc::NmodDoc {
|
||||
let mut fns = ~[];
|
||||
for item in module_.items.iter() {
|
||||
let ItemDoc = mk_itemdoc(item.id, to_str(item.ident));
|
||||
match item.node {
|
||||
ast::foreign_item_fn(*) => {
|
||||
fns.push(fndoc_from_fn(ItemDoc));
|
||||
}
|
||||
ast::foreign_item_static(*) => {} // XXX: Not implemented.
|
||||
}
|
||||
}
|
||||
doc::NmodDoc {
|
||||
item: itemdoc,
|
||||
fns: fns,
|
||||
index: None
|
||||
}
|
||||
}
|
||||
|
||||
fn fndoc_from_fn(itemdoc: doc::ItemDoc) -> doc::FnDoc {
|
||||
doc::SimpleItemDoc {
|
||||
item: itemdoc,
|
||||
sig: None
|
||||
}
|
||||
}
|
||||
|
||||
fn staticdoc_from_static(itemdoc: doc::ItemDoc) -> doc::StaticDoc {
|
||||
doc::SimpleItemDoc {
|
||||
item: itemdoc,
|
||||
sig: None
|
||||
}
|
||||
}
|
||||
|
||||
fn enumdoc_from_enum(
|
||||
itemdoc: doc::ItemDoc,
|
||||
variants: ~[ast::variant]
|
||||
) -> doc::EnumDoc {
|
||||
doc::EnumDoc {
|
||||
item: itemdoc,
|
||||
variants: variantdocs_from_variants(variants)
|
||||
}
|
||||
}
|
||||
|
||||
fn variantdocs_from_variants(
|
||||
variants: ~[ast::variant]
|
||||
) -> ~[doc::VariantDoc] {
|
||||
variants.iter().map(variantdoc_from_variant).collect()
|
||||
}
|
||||
|
||||
fn variantdoc_from_variant(variant: &ast::variant) -> doc::VariantDoc {
|
||||
doc::VariantDoc {
|
||||
name: to_str(variant.node.name),
|
||||
desc: None,
|
||||
sig: None
|
||||
}
|
||||
}
|
||||
|
||||
fn traitdoc_from_trait(
|
||||
itemdoc: doc::ItemDoc,
|
||||
methods: ~[ast::trait_method]
|
||||
) -> doc::TraitDoc {
|
||||
doc::TraitDoc {
|
||||
item: itemdoc,
|
||||
methods: do methods.iter().map |method| {
|
||||
match (*method).clone() {
|
||||
ast::required(ty_m) => {
|
||||
doc::MethodDoc {
|
||||
name: to_str(ty_m.ident),
|
||||
brief: None,
|
||||
desc: None,
|
||||
sections: ~[],
|
||||
sig: None,
|
||||
implementation: doc::Required,
|
||||
}
|
||||
}
|
||||
ast::provided(m) => {
|
||||
doc::MethodDoc {
|
||||
name: to_str(m.ident),
|
||||
brief: None,
|
||||
desc: None,
|
||||
sections: ~[],
|
||||
sig: None,
|
||||
implementation: doc::Provided,
|
||||
}
|
||||
}
|
||||
}
|
||||
}.collect()
|
||||
}
|
||||
}
|
||||
|
||||
fn impldoc_from_impl(
|
||||
itemdoc: doc::ItemDoc,
|
||||
methods: ~[@ast::method]
|
||||
) -> doc::ImplDoc {
|
||||
doc::ImplDoc {
|
||||
item: itemdoc,
|
||||
bounds_str: None,
|
||||
trait_types: ~[],
|
||||
self_ty: None,
|
||||
methods: do methods.iter().map |method| {
|
||||
doc::MethodDoc {
|
||||
name: to_str(method.ident),
|
||||
brief: None,
|
||||
desc: None,
|
||||
sections: ~[],
|
||||
sig: None,
|
||||
implementation: doc::Provided,
|
||||
}
|
||||
}.collect()
|
||||
}
|
||||
}
|
||||
|
||||
fn tydoc_from_ty(
|
||||
itemdoc: doc::ItemDoc
|
||||
) -> doc::TyDoc {
|
||||
doc::SimpleItemDoc {
|
||||
item: itemdoc,
|
||||
sig: None
|
||||
}
|
||||
}
|
||||
|
||||
fn structdoc_from_struct(
|
||||
itemdoc: doc::ItemDoc,
|
||||
struct_def: @ast::struct_def
|
||||
) -> doc::StructDoc {
|
||||
doc::StructDoc {
|
||||
item: itemdoc,
|
||||
fields: do struct_def.fields.map |field| {
|
||||
match field.node.kind {
|
||||
ast::named_field(ident, _) => to_str(ident),
|
||||
ast::unnamed_field => ~"(unnamed)",
|
||||
}
|
||||
},
|
||||
sig: None
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use astsrv;
|
||||
use doc;
|
||||
use extract::{extract, from_srv};
|
||||
use parse;
|
||||
|
||||
fn mk_doc(source: @str) -> doc::Doc {
|
||||
let ast = parse::from_str(source);
|
||||
debug!("ast=%?", ast);
|
||||
extract(ast, ~"")
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn extract_empty_crate() {
|
||||
let doc = mk_doc(@"");
|
||||
assert!(doc.cratemod().mods().is_empty());
|
||||
assert!(doc.cratemod().fns().is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn extract_mods() {
|
||||
let doc = mk_doc(@"mod a { mod b { } mod c { } }");
|
||||
assert!(doc.cratemod().mods()[0].name_() == ~"a");
|
||||
assert!(doc.cratemod().mods()[0].mods()[0].name_() == ~"b");
|
||||
assert!(doc.cratemod().mods()[0].mods()[1].name_() == ~"c");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn extract_fns_from_foreign_mods() {
|
||||
let doc = mk_doc(@"extern { fn a(); }");
|
||||
assert!(doc.cratemod().nmods()[0].fns[0].name_() == ~"a");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn extract_mods_deep() {
|
||||
let doc = mk_doc(@"mod a { mod b { mod c { } } }");
|
||||
assert!(doc.cratemod().mods()[0].mods()[0].mods()[0].name_() ==
|
||||
~"c");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn extract_should_set_mod_ast_id() {
|
||||
let doc = mk_doc(@"mod a { }");
|
||||
assert!(doc.cratemod().mods()[0].id() != 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn extract_fns() {
|
||||
let doc = mk_doc(
|
||||
@"fn a() { } \
|
||||
mod b { fn c() {
|
||||
} }");
|
||||
assert!(doc.cratemod().fns()[0].name_() == ~"a");
|
||||
assert!(doc.cratemod().mods()[0].fns()[0].name_() == ~"c");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn extract_should_set_fn_ast_id() {
|
||||
let doc = mk_doc(@"fn a() { }");
|
||||
assert!(doc.cratemod().fns()[0].id() != 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn extract_should_use_default_crate_name() {
|
||||
let source = @"";
|
||||
let ast = parse::from_str(source);
|
||||
let doc = extract(ast, ~"burp");
|
||||
assert!(doc.cratemod().name_() == ~"burp");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn extract_from_seq_srv() {
|
||||
let source = ~"";
|
||||
do astsrv::from_str(source) |srv| {
|
||||
let doc = from_srv(srv, ~"name");
|
||||
assert!(doc.cratemod().name_() == ~"name");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_extract_static_name_and_id() {
|
||||
let doc = mk_doc(@"static a: int = 0;");
|
||||
assert!(doc.cratemod().statics()[0].id() != 0);
|
||||
assert!(doc.cratemod().statics()[0].name_() == ~"a");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_extract_enums() {
|
||||
let doc = mk_doc(@"enum e { v }");
|
||||
assert!(doc.cratemod().enums()[0].id() != 0);
|
||||
assert!(doc.cratemod().enums()[0].name_() == ~"e");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_extract_enum_variants() {
|
||||
let doc = mk_doc(@"enum e { v }");
|
||||
assert!(doc.cratemod().enums()[0].variants[0].name == ~"v");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_extract_traits() {
|
||||
let doc = mk_doc(@"trait i { fn f(); }");
|
||||
assert!(doc.cratemod().traits()[0].name_() == ~"i");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_extract_trait_methods() {
|
||||
let doc = mk_doc(@"trait i { fn f(); }");
|
||||
assert!(doc.cratemod().traits()[0].methods[0].name == ~"f");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_extract_impl_methods() {
|
||||
let doc = mk_doc(@"impl int { fn f() { } }");
|
||||
assert!(doc.cratemod().impls()[0].methods[0].name == ~"f");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_extract_tys() {
|
||||
let doc = mk_doc(@"type a = int;");
|
||||
assert!(doc.cratemod().types()[0].name_() == ~"a");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_extract_structs() {
|
||||
let doc = mk_doc(@"struct Foo { field: () }");
|
||||
assert!(doc.cratemod().structs()[0].name_() == ~"Foo");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_extract_struct_fields() {
|
||||
let doc = mk_doc(@"struct Foo { field: () }");
|
||||
assert!(doc.cratemod().structs()[0].fields[0] == ~"field");
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
|
||||
// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
@ -8,397 +8,92 @@
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use std;
|
||||
use clean::*;
|
||||
use std::iter::Extendable;
|
||||
|
||||
use doc;
|
||||
#[cfg(test)] use extract;
|
||||
#[cfg(test)] use parse;
|
||||
pub trait DocFolder {
|
||||
fn fold_item(&mut self, item: Item) -> Option<Item> {
|
||||
self.fold_item_recur(item)
|
||||
}
|
||||
|
||||
pub struct Fold<T> {
|
||||
ctxt: T,
|
||||
fold_doc: FoldDoc<T>,
|
||||
fold_crate: FoldCrate<T>,
|
||||
fold_item: FoldItem<T>,
|
||||
fold_mod: FoldMod<T>,
|
||||
fold_nmod: FoldNmod<T>,
|
||||
fold_fn: FoldFn<T>,
|
||||
fold_static: FoldStatic<T>,
|
||||
fold_enum: FoldEnum<T>,
|
||||
fold_trait: FoldTrait<T>,
|
||||
fold_impl: FoldImpl<T>,
|
||||
fold_type: FoldType<T>,
|
||||
fold_struct: FoldStruct<T>
|
||||
}
|
||||
/// don't override!
|
||||
fn fold_item_recur(&mut self, item: Item) -> Option<Item> {
|
||||
use std::util::swap;
|
||||
let Item { attrs, name, source, visibility, id, inner } = item;
|
||||
let inner = inner;
|
||||
let c = |x| self.fold_item(x);
|
||||
let inner = match inner {
|
||||
StructItem(i) => {
|
||||
let mut i = i;
|
||||
let mut foo = ~[]; swap(&mut foo, &mut i.fields);
|
||||
i.fields.extend(&mut foo.move_iter().filter_map(|x| self.fold_item(x)));
|
||||
StructItem(i)
|
||||
},
|
||||
ModuleItem(i) => {
|
||||
ModuleItem(self.fold_mod(i))
|
||||
},
|
||||
EnumItem(i) => {
|
||||
let mut i = i;
|
||||
let mut foo = ~[]; swap(&mut foo, &mut i.variants);
|
||||
i.variants.extend(&mut foo.move_iter().filter_map(|x| self.fold_item(x)));
|
||||
EnumItem(i)
|
||||
},
|
||||
TraitItem(i) => {
|
||||
fn vtrm<T: DocFolder>(this: &mut T, trm: TraitMethod) -> Option<TraitMethod> {
|
||||
match trm {
|
||||
Required(it) => {
|
||||
match this.fold_item(it) {
|
||||
Some(x) => return Some(Required(x)),
|
||||
None => return None,
|
||||
}
|
||||
},
|
||||
Provided(it) => {
|
||||
match this.fold_item(it) {
|
||||
Some(x) => return Some(Provided(x)),
|
||||
None => return None,
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
let mut i = i;
|
||||
let mut foo = ~[]; swap(&mut foo, &mut i.methods);
|
||||
i.methods.extend(&mut foo.move_iter().filter_map(|x| vtrm(self, x)));
|
||||
TraitItem(i)
|
||||
},
|
||||
ImplItem(i) => {
|
||||
let mut i = i;
|
||||
let mut foo = ~[]; swap(&mut foo, &mut i.methods);
|
||||
i.methods.extend(&mut foo.move_iter().filter_map(|x| self.fold_item(x)));
|
||||
ImplItem(i)
|
||||
},
|
||||
VariantItem(i) => {
|
||||
let i2 = i.clone(); // this clone is small
|
||||
match i.kind {
|
||||
StructVariant(j) => {
|
||||
let mut j = j;
|
||||
let mut foo = ~[]; swap(&mut foo, &mut j.fields);
|
||||
j.fields.extend(&mut foo.move_iter().filter_map(c));
|
||||
VariantItem(Variant {kind: StructVariant(j), ..i2})
|
||||
},
|
||||
_ => VariantItem(i2)
|
||||
}
|
||||
},
|
||||
x => x
|
||||
};
|
||||
|
||||
impl<T:Clone> Clone for Fold<T> {
|
||||
fn clone(&self) -> Fold<T> {
|
||||
Fold {
|
||||
ctxt: self.ctxt.clone(),
|
||||
fold_doc: self.fold_doc,
|
||||
fold_crate: self.fold_crate,
|
||||
fold_item: self.fold_item,
|
||||
fold_mod: self.fold_mod,
|
||||
fold_nmod: self.fold_nmod,
|
||||
fold_fn: self.fold_fn,
|
||||
fold_static: self.fold_static,
|
||||
fold_enum: self.fold_enum,
|
||||
fold_trait: self.fold_trait,
|
||||
fold_impl: self.fold_impl,
|
||||
fold_type: self.fold_type,
|
||||
fold_struct: self.fold_struct
|
||||
}
|
||||
Some(Item { attrs: attrs, name: name, source: source, inner: inner,
|
||||
visibility: visibility, id: id })
|
||||
}
|
||||
|
||||
fn fold_mod(&mut self, m: Module) -> Module {
|
||||
Module { items: m.items.move_iter().filter_map(|i| self.fold_item(i)).collect() }
|
||||
}
|
||||
|
||||
fn fold_crate(&mut self, mut c: Crate) -> Crate {
|
||||
c.module = match std::util::replace(&mut c.module, None) {
|
||||
Some(module) => self.fold_item(module), None => None
|
||||
};
|
||||
return c;
|
||||
}
|
||||
}
|
||||
|
||||
type FoldDoc<T> = @fn(fold: &Fold<T>, doc: doc::Doc) -> doc::Doc;
|
||||
type FoldCrate<T> = @fn(fold: &Fold<T>, doc: doc::CrateDoc) -> doc::CrateDoc;
|
||||
type FoldItem<T> = @fn(fold: &Fold<T>, doc: doc::ItemDoc) -> doc::ItemDoc;
|
||||
type FoldMod<T> = @fn(fold: &Fold<T>, doc: doc::ModDoc) -> doc::ModDoc;
|
||||
type FoldNmod<T> = @fn(fold: &Fold<T>, doc: doc::NmodDoc) -> doc::NmodDoc;
|
||||
type FoldFn<T> = @fn(fold: &Fold<T>, doc: doc::FnDoc) -> doc::FnDoc;
|
||||
type FoldStatic<T> = @fn(fold: &Fold<T>, doc: doc::StaticDoc) -> doc::StaticDoc;
|
||||
type FoldEnum<T> = @fn(fold: &Fold<T>, doc: doc::EnumDoc) -> doc::EnumDoc;
|
||||
type FoldTrait<T> = @fn(fold: &Fold<T>, doc: doc::TraitDoc) -> doc::TraitDoc;
|
||||
type FoldImpl<T> = @fn(fold: &Fold<T>, doc: doc::ImplDoc) -> doc::ImplDoc;
|
||||
type FoldType<T> = @fn(fold: &Fold<T>, doc: doc::TyDoc) -> doc::TyDoc;
|
||||
type FoldStruct<T> = @fn(fold: &Fold<T>,
|
||||
doc: doc::StructDoc) -> doc::StructDoc;
|
||||
|
||||
// This exists because fn types don't infer correctly as record
|
||||
// initializers, but they do as function arguments
|
||||
fn mk_fold<T>(
|
||||
ctxt: T,
|
||||
fold_doc: FoldDoc<T>,
|
||||
fold_crate: FoldCrate<T>,
|
||||
fold_item: FoldItem<T>,
|
||||
fold_mod: FoldMod<T>,
|
||||
fold_nmod: FoldNmod<T>,
|
||||
fold_fn: FoldFn<T>,
|
||||
fold_static: FoldStatic<T>,
|
||||
fold_enum: FoldEnum<T>,
|
||||
fold_trait: FoldTrait<T>,
|
||||
fold_impl: FoldImpl<T>,
|
||||
fold_type: FoldType<T>,
|
||||
fold_struct: FoldStruct<T>
|
||||
) -> Fold<T> {
|
||||
Fold {
|
||||
ctxt: ctxt,
|
||||
fold_doc: fold_doc,
|
||||
fold_crate: fold_crate,
|
||||
fold_item: fold_item,
|
||||
fold_mod: fold_mod,
|
||||
fold_nmod: fold_nmod,
|
||||
fold_fn: fold_fn,
|
||||
fold_static: fold_static,
|
||||
fold_enum: fold_enum,
|
||||
fold_trait: fold_trait,
|
||||
fold_impl: fold_impl,
|
||||
fold_type: fold_type,
|
||||
fold_struct: fold_struct
|
||||
}
|
||||
}
|
||||
|
||||
pub fn default_any_fold<T:Clone>(ctxt: T) -> Fold<T> {
|
||||
mk_fold(
|
||||
ctxt,
|
||||
|f, d| default_seq_fold_doc(f, d),
|
||||
|f, d| default_seq_fold_crate(f, d),
|
||||
|f, d| default_seq_fold_item(f, d),
|
||||
|f, d| default_any_fold_mod(f, d),
|
||||
|f, d| default_any_fold_nmod(f, d),
|
||||
|f, d| default_seq_fold_fn(f, d),
|
||||
|f, d| default_seq_fold_static(f, d),
|
||||
|f, d| default_seq_fold_enum(f, d),
|
||||
|f, d| default_seq_fold_trait(f, d),
|
||||
|f, d| default_seq_fold_impl(f, d),
|
||||
|f, d| default_seq_fold_type(f, d),
|
||||
|f, d| default_seq_fold_struct(f, d)
|
||||
)
|
||||
}
|
||||
|
||||
pub fn default_seq_fold<T:Clone>(ctxt: T) -> Fold<T> {
|
||||
mk_fold(
|
||||
ctxt,
|
||||
|f, d| default_seq_fold_doc(f, d),
|
||||
|f, d| default_seq_fold_crate(f, d),
|
||||
|f, d| default_seq_fold_item(f, d),
|
||||
|f, d| default_seq_fold_mod(f, d),
|
||||
|f, d| default_seq_fold_nmod(f, d),
|
||||
|f, d| default_seq_fold_fn(f, d),
|
||||
|f, d| default_seq_fold_static(f, d),
|
||||
|f, d| default_seq_fold_enum(f, d),
|
||||
|f, d| default_seq_fold_trait(f, d),
|
||||
|f, d| default_seq_fold_impl(f, d),
|
||||
|f, d| default_seq_fold_type(f, d),
|
||||
|f, d| default_seq_fold_struct(f, d)
|
||||
)
|
||||
}
|
||||
|
||||
pub fn default_par_fold<T:Clone>(ctxt: T) -> Fold<T> {
|
||||
mk_fold(
|
||||
ctxt,
|
||||
|f, d| default_seq_fold_doc(f, d),
|
||||
|f, d| default_seq_fold_crate(f, d),
|
||||
|f, d| default_seq_fold_item(f, d),
|
||||
|f, d| default_par_fold_mod(f, d),
|
||||
|f, d| default_par_fold_nmod(f, d),
|
||||
|f, d| default_seq_fold_fn(f, d),
|
||||
|f, d| default_seq_fold_static(f, d),
|
||||
|f, d| default_seq_fold_enum(f, d),
|
||||
|f, d| default_seq_fold_trait(f, d),
|
||||
|f, d| default_seq_fold_impl(f, d),
|
||||
|f, d| default_seq_fold_type(f, d),
|
||||
|f, d| default_seq_fold_struct(f, d)
|
||||
)
|
||||
}
|
||||
|
||||
pub fn default_seq_fold_doc<T>(fold: &Fold<T>, doc: doc::Doc) -> doc::Doc {
|
||||
doc::Doc {
|
||||
pages: do doc.pages.iter().map |page| {
|
||||
match (*page).clone() {
|
||||
doc::CratePage(doc) => {
|
||||
doc::CratePage((fold.fold_crate)(fold, doc))
|
||||
}
|
||||
doc::ItemPage(doc) => {
|
||||
doc::ItemPage(fold_ItemTag(fold, doc))
|
||||
}
|
||||
}
|
||||
}.collect(),
|
||||
.. doc
|
||||
}
|
||||
}
|
||||
|
||||
pub fn default_seq_fold_crate<T>(
|
||||
fold: &Fold<T>,
|
||||
doc: doc::CrateDoc
|
||||
) -> doc::CrateDoc {
|
||||
doc::CrateDoc {
|
||||
topmod: (fold.fold_mod)(fold, doc.topmod.clone())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn default_seq_fold_item<T>(
|
||||
_fold: &Fold<T>,
|
||||
doc: doc::ItemDoc
|
||||
) -> doc::ItemDoc {
|
||||
doc
|
||||
}
|
||||
|
||||
pub fn default_any_fold_mod<T:Clone>(
|
||||
fold: &Fold<T>,
|
||||
doc: doc::ModDoc
|
||||
) -> doc::ModDoc {
|
||||
doc::ModDoc {
|
||||
item: (fold.fold_item)(fold, doc.item.clone()),
|
||||
items: doc.items.iter().map(|ItemTag| {
|
||||
fold_ItemTag(fold, (*ItemTag).clone())
|
||||
}).collect(),
|
||||
.. doc
|
||||
}
|
||||
}
|
||||
|
||||
pub fn default_seq_fold_mod<T>(
|
||||
fold: &Fold<T>,
|
||||
doc: doc::ModDoc
|
||||
) -> doc::ModDoc {
|
||||
doc::ModDoc {
|
||||
item: (fold.fold_item)(fold, doc.item.clone()),
|
||||
items: doc.items.iter().map(|ItemTag| {
|
||||
fold_ItemTag(fold, (*ItemTag).clone())
|
||||
}).collect(),
|
||||
.. doc
|
||||
}
|
||||
}
|
||||
|
||||
pub fn default_par_fold_mod<T:Clone>(
|
||||
fold: &Fold<T>,
|
||||
doc: doc::ModDoc
|
||||
) -> doc::ModDoc {
|
||||
doc::ModDoc {
|
||||
item: (fold.fold_item)(fold, doc.item.clone()),
|
||||
items: doc.items.iter().map(|ItemTag| {
|
||||
fold_ItemTag(fold, (*ItemTag).clone())
|
||||
}).collect(),
|
||||
.. doc
|
||||
}
|
||||
}
|
||||
|
||||
pub fn default_any_fold_nmod<T:Clone>(
|
||||
fold: &Fold<T>,
|
||||
doc: doc::NmodDoc
|
||||
) -> doc::NmodDoc {
|
||||
doc::NmodDoc {
|
||||
item: (fold.fold_item)(fold, doc.item.clone()),
|
||||
fns: doc.fns.iter().map(|FnDoc| {
|
||||
(fold.fold_fn)(fold, (*FnDoc).clone())
|
||||
}).collect(),
|
||||
.. doc
|
||||
}
|
||||
}
|
||||
|
||||
pub fn default_seq_fold_nmod<T>(
|
||||
fold: &Fold<T>,
|
||||
doc: doc::NmodDoc
|
||||
) -> doc::NmodDoc {
|
||||
doc::NmodDoc {
|
||||
item: (fold.fold_item)(fold, doc.item.clone()),
|
||||
fns: doc.fns.iter().map(|FnDoc| {
|
||||
(fold.fold_fn)(fold, (*FnDoc).clone())
|
||||
}).collect(),
|
||||
.. doc
|
||||
}
|
||||
}
|
||||
|
||||
pub fn default_par_fold_nmod<T:Clone>(
|
||||
fold: &Fold<T>,
|
||||
doc: doc::NmodDoc
|
||||
) -> doc::NmodDoc {
|
||||
doc::NmodDoc {
|
||||
item: (fold.fold_item)(fold, doc.item.clone()),
|
||||
fns: doc.fns.iter().map(|FnDoc| {
|
||||
(fold.fold_fn)(fold, (*FnDoc).clone())
|
||||
}).collect(),
|
||||
.. doc
|
||||
}
|
||||
}
|
||||
|
||||
pub fn fold_ItemTag<T>(fold: &Fold<T>, doc: doc::ItemTag) -> doc::ItemTag {
|
||||
match doc {
|
||||
doc::ModTag(ModDoc) => {
|
||||
doc::ModTag((fold.fold_mod)(fold, ModDoc))
|
||||
}
|
||||
doc::NmodTag(nModDoc) => {
|
||||
doc::NmodTag((fold.fold_nmod)(fold, nModDoc))
|
||||
}
|
||||
doc::FnTag(FnDoc) => {
|
||||
doc::FnTag((fold.fold_fn)(fold, FnDoc))
|
||||
}
|
||||
doc::StaticTag(StaticDoc) => {
|
||||
doc::StaticTag((fold.fold_static)(fold, StaticDoc))
|
||||
}
|
||||
doc::EnumTag(EnumDoc) => {
|
||||
doc::EnumTag((fold.fold_enum)(fold, EnumDoc))
|
||||
}
|
||||
doc::TraitTag(TraitDoc) => {
|
||||
doc::TraitTag((fold.fold_trait)(fold, TraitDoc))
|
||||
}
|
||||
doc::ImplTag(ImplDoc) => {
|
||||
doc::ImplTag((fold.fold_impl)(fold, ImplDoc))
|
||||
}
|
||||
doc::TyTag(TyDoc) => {
|
||||
doc::TyTag((fold.fold_type)(fold, TyDoc))
|
||||
}
|
||||
doc::StructTag(StructDoc) => {
|
||||
doc::StructTag((fold.fold_struct)(fold, StructDoc))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn default_seq_fold_fn<T>(
|
||||
fold: &Fold<T>,
|
||||
doc: doc::FnDoc
|
||||
) -> doc::FnDoc {
|
||||
doc::SimpleItemDoc {
|
||||
item: (fold.fold_item)(fold, doc.item.clone()),
|
||||
.. doc
|
||||
}
|
||||
}
|
||||
|
||||
pub fn default_seq_fold_static<T>(
|
||||
fold: &Fold<T>,
|
||||
doc: doc::StaticDoc
|
||||
) -> doc::StaticDoc {
|
||||
doc::SimpleItemDoc {
|
||||
item: (fold.fold_item)(fold, doc.item.clone()),
|
||||
.. doc
|
||||
}
|
||||
}
|
||||
|
||||
pub fn default_seq_fold_enum<T>(
|
||||
fold: &Fold<T>,
|
||||
doc: doc::EnumDoc
|
||||
) -> doc::EnumDoc {
|
||||
doc::EnumDoc {
|
||||
item: (fold.fold_item)(fold, doc.item.clone()),
|
||||
.. doc
|
||||
}
|
||||
}
|
||||
|
||||
pub fn default_seq_fold_trait<T>(
|
||||
fold: &Fold<T>,
|
||||
doc: doc::TraitDoc
|
||||
) -> doc::TraitDoc {
|
||||
doc::TraitDoc {
|
||||
item: (fold.fold_item)(fold, doc.item.clone()),
|
||||
.. doc
|
||||
}
|
||||
}
|
||||
|
||||
pub fn default_seq_fold_impl<T>(
|
||||
fold: &Fold<T>,
|
||||
doc: doc::ImplDoc
|
||||
) -> doc::ImplDoc {
|
||||
doc::ImplDoc {
|
||||
item: (fold.fold_item)(fold, doc.item.clone()),
|
||||
.. doc
|
||||
}
|
||||
}
|
||||
|
||||
pub fn default_seq_fold_type<T>(
|
||||
fold: &Fold<T>,
|
||||
doc: doc::TyDoc
|
||||
) -> doc::TyDoc {
|
||||
doc::SimpleItemDoc {
|
||||
item: (fold.fold_item)(fold, doc.item.clone()),
|
||||
.. doc
|
||||
}
|
||||
}
|
||||
|
||||
pub fn default_seq_fold_struct<T>(
|
||||
fold: &Fold<T>,
|
||||
doc: doc::StructDoc
|
||||
) -> doc::StructDoc {
|
||||
doc::StructDoc {
|
||||
item: (fold.fold_item)(fold, doc.item.clone()),
|
||||
.. doc
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn default_fold_should_produce_same_doc() {
|
||||
let source = @"mod a { fn b() { } mod c { fn d() { } } }";
|
||||
let ast = parse::from_str(source);
|
||||
let doc = extract::extract(ast, ~"");
|
||||
let fld = default_seq_fold(());
|
||||
let folded = (fld.fold_doc)(&fld, doc.clone());
|
||||
assert_eq!(doc, folded);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn default_fold_should_produce_same_statics() {
|
||||
let source = @"static a: int = 0;";
|
||||
let ast = parse::from_str(source);
|
||||
let doc = extract::extract(ast, ~"");
|
||||
let fld = default_seq_fold(());
|
||||
let folded = (fld.fold_doc)(&fld, doc.clone());
|
||||
assert_eq!(doc, folded);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn default_fold_should_produce_same_enums() {
|
||||
let source = @"enum a { b }";
|
||||
let ast = parse::from_str(source);
|
||||
let doc = extract::extract(ast, ~"");
|
||||
let fld = default_seq_fold(());
|
||||
let folded = (fld.fold_doc)(&fld, doc.clone());
|
||||
assert_eq!(doc, folded);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn default_parallel_fold_should_produce_same_doc() {
|
||||
let source = @"mod a { fn b() { } mod c { fn d() { } } }";
|
||||
let ast = parse::from_str(source);
|
||||
let doc = extract::extract(ast, ~"");
|
||||
let fld = default_par_fold(());
|
||||
let folded = (fld.fold_doc)(&fld, doc.clone());
|
||||
assert_eq!(doc, folded);
|
||||
}
|
||||
|
@ -1,284 +0,0 @@
|
||||
// Copyright 2012 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 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
//! Build indexes as appropriate for the markdown pass
|
||||
|
||||
|
||||
use astsrv;
|
||||
use config;
|
||||
use doc::ItemUtils;
|
||||
use doc;
|
||||
use fold::Fold;
|
||||
use fold;
|
||||
use markdown_pass;
|
||||
use markdown_writer;
|
||||
use pass::Pass;
|
||||
|
||||
pub fn mk_pass(config: config::Config) -> Pass {
|
||||
Pass {
|
||||
name: ~"markdown_index",
|
||||
f: |srv, doc| run(srv, doc, config.clone())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run(
|
||||
_srv: astsrv::Srv,
|
||||
doc: doc::Doc,
|
||||
config: config::Config
|
||||
) -> doc::Doc {
|
||||
let fold = Fold {
|
||||
fold_mod: fold_mod,
|
||||
fold_nmod: fold_nmod,
|
||||
.. fold::default_any_fold(config)
|
||||
};
|
||||
(fold.fold_doc)(&fold, doc)
|
||||
}
|
||||
|
||||
fn fold_mod(
|
||||
fold: &fold::Fold<config::Config>,
|
||||
doc: doc::ModDoc
|
||||
) -> doc::ModDoc {
|
||||
|
||||
let doc = fold::default_any_fold_mod(fold, doc);
|
||||
|
||||
doc::ModDoc {
|
||||
index: Some(build_mod_index(doc.clone(), fold.ctxt.clone())),
|
||||
.. doc
|
||||
}
|
||||
}
|
||||
|
||||
fn fold_nmod(
|
||||
fold: &fold::Fold<config::Config>,
|
||||
doc: doc::NmodDoc
|
||||
) -> doc::NmodDoc {
|
||||
|
||||
let doc = fold::default_any_fold_nmod(fold, doc);
|
||||
|
||||
doc::NmodDoc {
|
||||
index: Some(build_nmod_index(doc.clone(), fold.ctxt.clone())),
|
||||
.. doc
|
||||
}
|
||||
}
|
||||
|
||||
fn build_mod_index(
|
||||
doc: doc::ModDoc,
|
||||
config: config::Config
|
||||
) -> doc::Index {
|
||||
doc::Index {
|
||||
entries: doc.items.map(|doc| {
|
||||
item_to_entry((*doc).clone(), &config)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn build_nmod_index(
|
||||
doc: doc::NmodDoc,
|
||||
config: config::Config
|
||||
) -> doc::Index {
|
||||
doc::Index {
|
||||
entries: doc.fns.map(|doc| {
|
||||
item_to_entry(doc::FnTag((*doc).clone()), &config)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn item_to_entry(
|
||||
doc: doc::ItemTag,
|
||||
config: &config::Config
|
||||
) -> doc::IndexEntry {
|
||||
let link = match doc {
|
||||
doc::ModTag(_) | doc::NmodTag(_)
|
||||
if config.output_style == config::DocPerMod => {
|
||||
markdown_writer::make_filename(config,
|
||||
doc::ItemPage(doc.clone())).to_str()
|
||||
}
|
||||
_ => {
|
||||
~"#" + pandoc_header_id(markdown_pass::header_text(doc.clone()))
|
||||
}
|
||||
};
|
||||
|
||||
doc::IndexEntry {
|
||||
kind: markdown_pass::header_kind(doc.clone()),
|
||||
name: markdown_pass::header_name(doc.clone()),
|
||||
brief: doc.brief(),
|
||||
link: link
|
||||
}
|
||||
}
|
||||
|
||||
pub fn pandoc_header_id(header: &str) -> ~str {
|
||||
|
||||
// http://johnmacfarlane.net/pandoc/README.html#headers
|
||||
|
||||
let header = remove_formatting(header);
|
||||
let header = remove_punctuation(header);
|
||||
let header = replace_with_hyphens(header);
|
||||
let header = convert_to_lowercase(header);
|
||||
let header = remove_up_to_first_letter(header);
|
||||
let header = maybe_use_section_id(header);
|
||||
return header;
|
||||
|
||||
fn remove_formatting(s: &str) -> ~str {
|
||||
s.replace("`", "")
|
||||
}
|
||||
fn remove_punctuation(s: &str) -> ~str {
|
||||
let s = s.replace("<", "");
|
||||
let s = s.replace(">", "");
|
||||
let s = s.replace("[", "");
|
||||
let s = s.replace("]", "");
|
||||
let s = s.replace("(", "");
|
||||
let s = s.replace(")", "");
|
||||
let s = s.replace("@~", "");
|
||||
let s = s.replace("~", "");
|
||||
let s = s.replace("/", "");
|
||||
let s = s.replace(":", "");
|
||||
let s = s.replace("&", "");
|
||||
let s = s.replace("^", "");
|
||||
let s = s.replace(",", "");
|
||||
let s = s.replace("'", "");
|
||||
let s = s.replace("+", "");
|
||||
return s;
|
||||
}
|
||||
fn replace_with_hyphens(s: &str) -> ~str {
|
||||
// Collapse sequences of whitespace to a single dash
|
||||
// XXX: Hacky implementation here that only covers
|
||||
// one or two spaces.
|
||||
let s = s.trim();
|
||||
let s = s.replace(" ", "-");
|
||||
let s = s.replace(" ", "-");
|
||||
return s;
|
||||
}
|
||||
// FIXME: #4318 Instead of to_ascii and to_str_ascii, could use
|
||||
// to_ascii_consume and to_str_consume to not do a unnecessary copy.
|
||||
fn convert_to_lowercase(s: &str) -> ~str { s.to_ascii().to_lower().to_str_ascii() }
|
||||
fn remove_up_to_first_letter(s: &str) -> ~str { s.to_str() }
|
||||
fn maybe_use_section_id(s: &str) -> ~str { s.to_str() }
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
|
||||
use astsrv;
|
||||
use attr_pass;
|
||||
use config;
|
||||
use desc_to_brief_pass;
|
||||
use doc;
|
||||
use extract;
|
||||
use markdown_index_pass::run;
|
||||
use path_pass;
|
||||
use prune_hidden_pass;
|
||||
use super::pandoc_header_id;
|
||||
|
||||
fn mk_doc(output_style: config::OutputStyle, source: ~str)
|
||||
-> doc::Doc {
|
||||
do astsrv::from_str(source) |srv| {
|
||||
let config = config::Config {
|
||||
output_style: output_style,
|
||||
.. config::default_config(&Path("whatever"))
|
||||
};
|
||||
let doc = extract::from_srv(srv.clone(), ~"");
|
||||
let doc = (attr_pass::mk_pass().f)(srv.clone(), doc);
|
||||
let doc = (prune_hidden_pass::mk_pass().f)(srv.clone(), doc);
|
||||
let doc = (desc_to_brief_pass::mk_pass().f)(srv.clone(), doc);
|
||||
let doc = (path_pass::mk_pass().f)(srv.clone(), doc);
|
||||
|
||||
run(srv.clone(), doc, config)
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_remove_punctuation_from_headers() {
|
||||
assert!(pandoc_header_id("impl foo of bar<A>") ==
|
||||
~"impl-foo-of-bara");
|
||||
assert!(pandoc_header_id("impl of num::num for int")
|
||||
== ~"impl-of-numnum-for-int");
|
||||
assert!(pandoc_header_id("impl of num::num for int/&")
|
||||
== ~"impl-of-numnum-for-int");
|
||||
assert!(pandoc_header_id("impl of num::num for ^int")
|
||||
== ~"impl-of-numnum-for-int");
|
||||
assert!(pandoc_header_id("impl for & condvar")
|
||||
== ~"impl-for-condvar");
|
||||
assert!(pandoc_header_id("impl of Select<T, U> for (Left, Right)")
|
||||
== ~"impl-of-selectt-u-for-left-right");
|
||||
assert!(pandoc_header_id("impl of Condition<'self, T, U>")
|
||||
== ~"impl-of-conditionself-t-u");
|
||||
assert!(pandoc_header_id("impl of Condition<T: Clone>")
|
||||
== ~"impl-of-conditiont-clone");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_trim_whitespace_after_removing_punctuation() {
|
||||
assert_eq!(pandoc_header_id("impl foo for ()"), ~"impl-foo-for");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_index_mod_contents() {
|
||||
let doc = mk_doc(
|
||||
config::DocPerCrate,
|
||||
~"mod a { } fn b() { }"
|
||||
);
|
||||
assert!(doc.cratemod().index.unwrap().entries[0] == doc::IndexEntry {
|
||||
kind: ~"Module",
|
||||
name: ~"a",
|
||||
brief: None,
|
||||
link: ~"#module-a"
|
||||
});
|
||||
assert!(doc.cratemod().index.unwrap().entries[1] == doc::IndexEntry {
|
||||
kind: ~"Function",
|
||||
name: ~"b",
|
||||
brief: None,
|
||||
link: ~"#function-b"
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_index_mod_contents_multi_page() {
|
||||
let doc = mk_doc(
|
||||
config::DocPerMod,
|
||||
~"mod a { } fn b() { }"
|
||||
);
|
||||
assert_eq!(doc.cratemod().index.unwrap().entries[0], doc::IndexEntry {
|
||||
kind: ~"Module",
|
||||
name: ~"a",
|
||||
brief: None,
|
||||
link: ~"a.html"
|
||||
});
|
||||
assert_eq!(doc.cratemod().index.unwrap().entries[1], doc::IndexEntry {
|
||||
kind: ~"Function",
|
||||
name: ~"b",
|
||||
brief: None,
|
||||
link: ~"#function-b"
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_add_brief_desc_to_index() {
|
||||
let doc = mk_doc(
|
||||
config::DocPerMod,
|
||||
~"#[doc = \"test\"] mod a { }"
|
||||
);
|
||||
assert_eq!(doc.cratemod().index.unwrap().entries[0].brief, Some(~"test"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_index_foreign_mod_contents() {
|
||||
let doc = mk_doc(
|
||||
config::DocPerCrate,
|
||||
~"extern { fn b(); }"
|
||||
);
|
||||
// hidden __std_macros module at the start.
|
||||
assert_eq!(doc.cratemod().nmods()[0].index.unwrap().entries[0],
|
||||
doc::IndexEntry {
|
||||
kind: ~"Function",
|
||||
name: ~"b",
|
||||
brief: None,
|
||||
link: ~"#function-b"
|
||||
});
|
||||
}
|
||||
}
|
@ -1,941 +0,0 @@
|
||||
// Copyright 2012 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 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
//! Generate markdown from a document tree
|
||||
|
||||
|
||||
use astsrv;
|
||||
use doc::ItemUtils;
|
||||
use doc;
|
||||
use markdown_pass;
|
||||
use markdown_writer::Writer;
|
||||
use markdown_writer::WriterUtils;
|
||||
use markdown_writer::WriterFactory;
|
||||
use pass::Pass;
|
||||
use sort_pass;
|
||||
|
||||
use std::cell::Cell;
|
||||
use std::str;
|
||||
use std::vec;
|
||||
use syntax;
|
||||
|
||||
pub fn mk_pass(writer_factory: WriterFactory) -> Pass {
|
||||
let writer_factory = Cell::new(writer_factory);
|
||||
Pass {
|
||||
name: ~"markdown",
|
||||
f: |srv, doc| run(srv, doc, writer_factory.take())
|
||||
}
|
||||
}
|
||||
|
||||
fn run(
|
||||
srv: astsrv::Srv,
|
||||
doc: doc::Doc,
|
||||
writer_factory: WriterFactory
|
||||
) -> doc::Doc {
|
||||
|
||||
fn mods_last(item1: &doc::ItemTag, item2: &doc::ItemTag) -> bool {
|
||||
fn is_mod(item: &doc::ItemTag) -> bool {
|
||||
match *item {
|
||||
doc::ModTag(_) => true,
|
||||
_ => false
|
||||
}
|
||||
}
|
||||
|
||||
let lteq = !is_mod(item1) || is_mod(item2);
|
||||
lteq
|
||||
}
|
||||
|
||||
// Sort the items so mods come last. All mods will be
|
||||
// output at the same header level so sorting mods last
|
||||
// makes the headers come out nested correctly.
|
||||
let sorted_doc = (sort_pass::mk_pass(
|
||||
~"mods last", mods_last
|
||||
).f)(srv, doc.clone());
|
||||
|
||||
write_markdown(sorted_doc, writer_factory);
|
||||
|
||||
return doc;
|
||||
}
|
||||
|
||||
struct Ctxt {
|
||||
w: Writer
|
||||
}
|
||||
|
||||
pub fn write_markdown(
|
||||
doc: doc::Doc,
|
||||
writer_factory: WriterFactory
|
||||
) {
|
||||
// There is easy parallelism to be had here, but
|
||||
// we don't want to spawn too many pandoc processes.
|
||||
// (See #2484, which is closed.)
|
||||
do doc.pages.map |page| {
|
||||
let ctxt = Ctxt {
|
||||
w: writer_factory((*page).clone())
|
||||
};
|
||||
write_page(&ctxt, page)
|
||||
};
|
||||
}
|
||||
|
||||
fn write_page(ctxt: &Ctxt, page: &doc::Page) {
|
||||
write_title(ctxt, (*page).clone());
|
||||
match (*page).clone() {
|
||||
doc::CratePage(doc) => {
|
||||
write_crate(ctxt, doc);
|
||||
}
|
||||
doc::ItemPage(doc) => {
|
||||
// We don't write a header for item's pages because their
|
||||
// header in the html output is created by the page title
|
||||
write_item_no_header(ctxt, doc);
|
||||
}
|
||||
}
|
||||
ctxt.w.put_done();
|
||||
}
|
||||
|
||||
fn write_title(ctxt: &Ctxt, page: doc::Page) {
|
||||
ctxt.w.put_line(fmt!("%% %s", make_title(page)));
|
||||
ctxt.w.put_line(~"");
|
||||
}
|
||||
|
||||
fn make_title(page: doc::Page) -> ~str {
|
||||
let item = match page {
|
||||
doc::CratePage(CrateDoc) => {
|
||||
doc::ModTag(CrateDoc.topmod.clone())
|
||||
}
|
||||
doc::ItemPage(ItemTag) => {
|
||||
ItemTag
|
||||
}
|
||||
};
|
||||
let title = markdown_pass::header_text(item);
|
||||
let title = title.replace("`", "");
|
||||
return title;
|
||||
}
|
||||
|
||||
enum Hlvl {
|
||||
H1 = 1,
|
||||
H2 = 2,
|
||||
H3 = 3,
|
||||
H4 = 4
|
||||
}
|
||||
|
||||
fn write_header(ctxt: &Ctxt, lvl: Hlvl, doc: doc::ItemTag) {
|
||||
let text = header_text(doc);
|
||||
write_header_(ctxt, lvl, text);
|
||||
}
|
||||
|
||||
fn write_header_(ctxt: &Ctxt, lvl: Hlvl, title: ~str) {
|
||||
let hashes = str::from_chars(vec::from_elem(lvl as uint, '#'));
|
||||
ctxt.w.put_line(fmt!("%s %s", hashes, title));
|
||||
ctxt.w.put_line(~"");
|
||||
}
|
||||
|
||||
pub fn header_kind(doc: doc::ItemTag) -> ~str {
|
||||
match doc {
|
||||
doc::ModTag(_) => {
|
||||
if doc.id() == syntax::ast::CRATE_NODE_ID {
|
||||
~"Crate"
|
||||
} else {
|
||||
~"Module"
|
||||
}
|
||||
}
|
||||
doc::NmodTag(_) => {
|
||||
~"Foreign module"
|
||||
}
|
||||
doc::FnTag(_) => {
|
||||
~"Function"
|
||||
}
|
||||
doc::StaticTag(_) => {
|
||||
~"Static"
|
||||
}
|
||||
doc::EnumTag(_) => {
|
||||
~"Enum"
|
||||
}
|
||||
doc::TraitTag(_) => {
|
||||
~"Trait"
|
||||
}
|
||||
doc::ImplTag(_) => {
|
||||
~"Implementation"
|
||||
}
|
||||
doc::TyTag(_) => {
|
||||
~"Type"
|
||||
}
|
||||
doc::StructTag(_) => {
|
||||
~"Struct"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn header_name(doc: doc::ItemTag) -> ~str {
|
||||
let fullpath = (doc.path() + &[doc.name_()]).connect("::");
|
||||
match &doc {
|
||||
&doc::ModTag(_) if doc.id() != syntax::ast::CRATE_NODE_ID => {
|
||||
fullpath
|
||||
}
|
||||
&doc::NmodTag(_) => {
|
||||
fullpath
|
||||
}
|
||||
&doc::ImplTag(ref doc) => {
|
||||
assert!(doc.self_ty.is_some());
|
||||
let bounds = if doc.bounds_str.is_some() {
|
||||
fmt!(" where %s", *doc.bounds_str.get_ref())
|
||||
} else {
|
||||
~""
|
||||
};
|
||||
let self_ty = doc.self_ty.get_ref();
|
||||
let mut trait_part = ~"";
|
||||
for (i, trait_type) in doc.trait_types.iter().enumerate() {
|
||||
if i == 0 {
|
||||
trait_part.push_str(" of ");
|
||||
} else {
|
||||
trait_part.push_str(", ");
|
||||
}
|
||||
trait_part.push_str(*trait_type);
|
||||
}
|
||||
fmt!("%s for %s%s", trait_part, *self_ty, bounds)
|
||||
}
|
||||
_ => {
|
||||
doc.name_()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn header_text(doc: doc::ItemTag) -> ~str {
|
||||
match &doc {
|
||||
&doc::ImplTag(ref ImplDoc) => {
|
||||
let header_kind = header_kind(doc.clone());
|
||||
let bounds = if ImplDoc.bounds_str.is_some() {
|
||||
fmt!(" where `%s`", *ImplDoc.bounds_str.get_ref())
|
||||
} else {
|
||||
~""
|
||||
};
|
||||
let desc = if ImplDoc.trait_types.is_empty() {
|
||||
fmt!("for `%s`%s", *ImplDoc.self_ty.get_ref(), bounds)
|
||||
} else {
|
||||
fmt!("of `%s` for `%s`%s",
|
||||
ImplDoc.trait_types[0],
|
||||
*ImplDoc.self_ty.get_ref(),
|
||||
bounds)
|
||||
};
|
||||
return fmt!("%s %s", header_kind, desc);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
header_text_(header_kind(doc.clone()),
|
||||
header_name(doc))
|
||||
}
|
||||
|
||||
fn header_text_(kind: &str, name: &str) -> ~str {
|
||||
fmt!("%s `%s`", kind, name)
|
||||
}
|
||||
|
||||
fn write_crate(
|
||||
ctxt: &Ctxt,
|
||||
doc: doc::CrateDoc
|
||||
) {
|
||||
write_top_module(ctxt, doc.topmod.clone());
|
||||
}
|
||||
|
||||
fn write_top_module(
|
||||
ctxt: &Ctxt,
|
||||
ModDoc: doc::ModDoc
|
||||
) {
|
||||
write_mod_contents(ctxt, ModDoc);
|
||||
}
|
||||
|
||||
fn write_mod(
|
||||
ctxt: &Ctxt,
|
||||
ModDoc: doc::ModDoc
|
||||
) {
|
||||
write_mod_contents(ctxt, ModDoc);
|
||||
}
|
||||
|
||||
fn write_common(
|
||||
ctxt: &Ctxt,
|
||||
desc: Option<~str>,
|
||||
sections: &[doc::Section]
|
||||
) {
|
||||
write_desc(ctxt, desc);
|
||||
write_sections(ctxt, sections);
|
||||
}
|
||||
|
||||
fn write_desc(
|
||||
ctxt: &Ctxt,
|
||||
desc: Option<~str>
|
||||
) {
|
||||
match desc {
|
||||
Some(desc) => {
|
||||
ctxt.w.put_line(desc);
|
||||
ctxt.w.put_line(~"");
|
||||
}
|
||||
None => ()
|
||||
}
|
||||
}
|
||||
|
||||
fn write_sections(ctxt: &Ctxt, sections: &[doc::Section]) {
|
||||
for section in sections.iter() {
|
||||
write_section(ctxt, (*section).clone());
|
||||
}
|
||||
}
|
||||
|
||||
fn write_section(ctxt: &Ctxt, section: doc::Section) {
|
||||
write_header_(ctxt, H4, section.header.clone());
|
||||
ctxt.w.put_line(section.body.clone());
|
||||
ctxt.w.put_line(~"");
|
||||
}
|
||||
|
||||
fn write_mod_contents(
|
||||
ctxt: &Ctxt,
|
||||
doc: doc::ModDoc
|
||||
) {
|
||||
write_common(ctxt, doc.desc(), doc.sections());
|
||||
if doc.index.is_some() {
|
||||
write_index(ctxt, doc.index.get_ref());
|
||||
}
|
||||
|
||||
for itemTag in doc.items.iter() {
|
||||
write_item(ctxt, (*itemTag).clone());
|
||||
}
|
||||
}
|
||||
|
||||
fn write_item(ctxt: &Ctxt, doc: doc::ItemTag) {
|
||||
write_item_(ctxt, doc, true);
|
||||
}
|
||||
|
||||
fn write_item_no_header(ctxt: &Ctxt, doc: doc::ItemTag) {
|
||||
write_item_(ctxt, doc, false);
|
||||
}
|
||||
|
||||
fn write_item_(ctxt: &Ctxt, doc: doc::ItemTag, write_header: bool) {
|
||||
if write_header {
|
||||
write_item_header(ctxt, doc.clone());
|
||||
}
|
||||
|
||||
match doc {
|
||||
doc::ModTag(ModDoc) => write_mod(ctxt, ModDoc),
|
||||
doc::NmodTag(nModDoc) => write_nmod(ctxt, nModDoc),
|
||||
doc::FnTag(FnDoc) => write_fn(ctxt, FnDoc),
|
||||
doc::StaticTag(StaticDoc) => write_static(ctxt, StaticDoc),
|
||||
doc::EnumTag(EnumDoc) => write_enum(ctxt, EnumDoc),
|
||||
doc::TraitTag(TraitDoc) => write_trait(ctxt, TraitDoc),
|
||||
doc::ImplTag(ImplDoc) => write_impl(ctxt, ImplDoc),
|
||||
doc::TyTag(TyDoc) => write_type(ctxt, TyDoc),
|
||||
doc::StructTag(StructDoc) => put_struct(ctxt, StructDoc),
|
||||
}
|
||||
}
|
||||
|
||||
fn write_item_header(ctxt: &Ctxt, doc: doc::ItemTag) {
|
||||
write_header(ctxt, item_header_lvl(&doc), doc);
|
||||
}
|
||||
|
||||
fn item_header_lvl(doc: &doc::ItemTag) -> Hlvl {
|
||||
match doc {
|
||||
&doc::ModTag(_) | &doc::NmodTag(_) => H1,
|
||||
_ => H2
|
||||
}
|
||||
}
|
||||
|
||||
fn write_index(ctxt: &Ctxt, index: &doc::Index) {
|
||||
if index.entries.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
ctxt.w.put_line(~"<div class='index'>");
|
||||
ctxt.w.put_line(~"");
|
||||
|
||||
for entry in index.entries.iter() {
|
||||
let header = header_text_(entry.kind, entry.name);
|
||||
let id = entry.link.clone();
|
||||
if entry.brief.is_some() {
|
||||
ctxt.w.put_line(fmt!("* [%s](%s) - %s",
|
||||
header, id, *entry.brief.get_ref()));
|
||||
} else {
|
||||
ctxt.w.put_line(fmt!("* [%s](%s)", header, id));
|
||||
}
|
||||
}
|
||||
ctxt.w.put_line(~"");
|
||||
ctxt.w.put_line(~"</div>");
|
||||
ctxt.w.put_line(~"");
|
||||
}
|
||||
|
||||
fn write_nmod(ctxt: &Ctxt, doc: doc::NmodDoc) {
|
||||
write_common(ctxt, doc.desc(), doc.sections());
|
||||
if doc.index.is_some() {
|
||||
write_index(ctxt, doc.index.get_ref());
|
||||
}
|
||||
|
||||
for FnDoc in doc.fns.iter() {
|
||||
write_item_header(ctxt, doc::FnTag((*FnDoc).clone()));
|
||||
write_fn(ctxt, (*FnDoc).clone());
|
||||
}
|
||||
}
|
||||
|
||||
fn write_fn(
|
||||
ctxt: &Ctxt,
|
||||
doc: doc::FnDoc
|
||||
) {
|
||||
write_fnlike(ctxt, doc.sig.clone(), doc.desc(), doc.sections());
|
||||
}
|
||||
|
||||
fn write_fnlike(
|
||||
ctxt: &Ctxt,
|
||||
sig: Option<~str>,
|
||||
desc: Option<~str>,
|
||||
sections: &[doc::Section]
|
||||
) {
|
||||
write_sig(ctxt, sig);
|
||||
write_common(ctxt, desc, sections);
|
||||
}
|
||||
|
||||
fn write_sig(ctxt: &Ctxt, sig: Option<~str>) {
|
||||
match sig {
|
||||
Some(sig) => {
|
||||
ctxt.w.put_line(code_block(sig));
|
||||
ctxt.w.put_line(~"");
|
||||
}
|
||||
None => fail!("unimplemented")
|
||||
}
|
||||
}
|
||||
|
||||
fn code_block(s: ~str) -> ~str {
|
||||
fmt!("~~~ {.rust}
|
||||
%s
|
||||
~~~", s)
|
||||
}
|
||||
|
||||
fn write_static(
|
||||
ctxt: &Ctxt,
|
||||
doc: doc::StaticDoc
|
||||
) {
|
||||
write_sig(ctxt, doc.sig.clone());
|
||||
write_common(ctxt, doc.desc(), doc.sections());
|
||||
}
|
||||
|
||||
fn write_enum(
|
||||
ctxt: &Ctxt,
|
||||
doc: doc::EnumDoc
|
||||
) {
|
||||
write_common(ctxt, doc.desc(), doc.sections());
|
||||
write_variants(ctxt, doc.variants);
|
||||
}
|
||||
|
||||
fn write_variants(
|
||||
ctxt: &Ctxt,
|
||||
docs: &[doc::VariantDoc]
|
||||
) {
|
||||
if docs.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
write_header_(ctxt, H4, ~"Variants");
|
||||
|
||||
for variant in docs.iter() {
|
||||
write_variant(ctxt, (*variant).clone());
|
||||
}
|
||||
|
||||
ctxt.w.put_line(~"");
|
||||
}
|
||||
|
||||
fn write_variant(ctxt: &Ctxt, doc: doc::VariantDoc) {
|
||||
assert!(doc.sig.is_some());
|
||||
let sig = doc.sig.get_ref();
|
||||
|
||||
// space out list items so they all end up within paragraph elements
|
||||
ctxt.w.put_line(~"");
|
||||
|
||||
match doc.desc.clone() {
|
||||
Some(desc) => {
|
||||
ctxt.w.put_line(list_item_indent(fmt!("* `%s` - %s", *sig, desc)));
|
||||
}
|
||||
None => {
|
||||
ctxt.w.put_line(fmt!("* `%s`", *sig));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn list_item_indent(item: &str) -> ~str {
|
||||
let indented = item.any_line_iter().collect::<~[&str]>();
|
||||
|
||||
// separate markdown elements within `*` lists must be indented by four
|
||||
// spaces, or they will escape the list context. indenting everything
|
||||
// seems fine though.
|
||||
indented.connect("\n ")
|
||||
}
|
||||
|
||||
fn write_trait(ctxt: &Ctxt, doc: doc::TraitDoc) {
|
||||
write_common(ctxt, doc.desc(), doc.sections());
|
||||
write_methods(ctxt, doc.methods);
|
||||
}
|
||||
|
||||
fn write_methods(ctxt: &Ctxt, docs: &[doc::MethodDoc]) {
|
||||
for doc in docs.iter() {
|
||||
write_method(ctxt, (*doc).clone());
|
||||
}
|
||||
}
|
||||
|
||||
fn write_method(ctxt: &Ctxt, doc: doc::MethodDoc) {
|
||||
write_header_(ctxt, H3, header_text_("Method", doc.name));
|
||||
write_fnlike(ctxt, doc.sig.clone(), doc.desc.clone(), doc.sections);
|
||||
}
|
||||
|
||||
fn write_impl(ctxt: &Ctxt, doc: doc::ImplDoc) {
|
||||
write_common(ctxt, doc.desc(), doc.sections());
|
||||
write_methods(ctxt, doc.methods);
|
||||
}
|
||||
|
||||
fn write_type(
|
||||
ctxt: &Ctxt,
|
||||
doc: doc::TyDoc
|
||||
) {
|
||||
write_sig(ctxt, doc.sig.clone());
|
||||
write_common(ctxt, doc.desc(), doc.sections());
|
||||
}
|
||||
|
||||
fn put_struct(
|
||||
ctxt: &Ctxt,
|
||||
doc: doc::StructDoc
|
||||
) {
|
||||
write_sig(ctxt, doc.sig.clone());
|
||||
write_common(ctxt, doc.desc(), doc.sections());
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
|
||||
use astsrv;
|
||||
use attr_pass;
|
||||
use config;
|
||||
use desc_to_brief_pass;
|
||||
use doc;
|
||||
use extract;
|
||||
use markdown_index_pass;
|
||||
use markdown_pass::{mk_pass, write_markdown};
|
||||
use markdown_writer;
|
||||
use path_pass;
|
||||
use page_pass;
|
||||
use prune_hidden_pass;
|
||||
use sectionalize_pass;
|
||||
use trim_pass;
|
||||
use tystr_pass;
|
||||
use unindent_pass;
|
||||
|
||||
fn render(source: ~str) -> ~str {
|
||||
let (srv, doc) = create_doc_srv(source);
|
||||
let markdown = write_markdown_str_srv(srv, doc);
|
||||
debug!("markdown: %s", markdown);
|
||||
markdown
|
||||
}
|
||||
|
||||
fn create_doc_srv(source: ~str) -> (astsrv::Srv, doc::Doc) {
|
||||
do astsrv::from_str(source) |srv| {
|
||||
|
||||
let config = config::Config {
|
||||
output_style: config::DocPerCrate,
|
||||
.. config::default_config(&Path("whatever"))
|
||||
};
|
||||
|
||||
let doc = extract::from_srv(srv.clone(), ~"");
|
||||
debug!("doc (extract): %?", doc);
|
||||
let doc = (tystr_pass::mk_pass().f)(srv.clone(), doc);
|
||||
debug!("doc (tystr): %?", doc);
|
||||
let doc = (path_pass::mk_pass().f)(srv.clone(), doc);
|
||||
debug!("doc (path): %?", doc);
|
||||
let doc = (attr_pass::mk_pass().f)(srv.clone(), doc);
|
||||
debug!("doc (attr): %?", doc);
|
||||
let doc = (prune_hidden_pass::mk_pass().f)(srv.clone(), doc);
|
||||
debug!("doc (prune_hidden): %?", doc);
|
||||
let doc = (desc_to_brief_pass::mk_pass().f)(srv.clone(), doc);
|
||||
debug!("doc (desc_to_brief): %?", doc);
|
||||
let doc = (unindent_pass::mk_pass().f)(srv.clone(), doc);
|
||||
debug!("doc (unindent): %?", doc);
|
||||
let doc = (sectionalize_pass::mk_pass().f)(srv.clone(), doc);
|
||||
debug!("doc (trim): %?", doc);
|
||||
let doc = (trim_pass::mk_pass().f)(srv.clone(), doc);
|
||||
debug!("doc (sectionalize): %?", doc);
|
||||
let doc = (markdown_index_pass::mk_pass(config).f)(
|
||||
srv.clone(), doc);
|
||||
debug!("doc (index): %?", doc);
|
||||
(srv.clone(), doc)
|
||||
}
|
||||
}
|
||||
|
||||
fn create_doc(source: ~str) -> doc::Doc {
|
||||
let (_, doc) = create_doc_srv(source);
|
||||
doc
|
||||
}
|
||||
|
||||
fn write_markdown_str(
|
||||
doc: doc::Doc
|
||||
) -> ~str {
|
||||
let (writer_factory, po) = markdown_writer::future_writer_factory();
|
||||
write_markdown(doc, writer_factory);
|
||||
return po.recv().second();
|
||||
}
|
||||
|
||||
fn write_markdown_str_srv(
|
||||
srv: astsrv::Srv,
|
||||
doc: doc::Doc
|
||||
) -> ~str {
|
||||
let (writer_factory, po) = markdown_writer::future_writer_factory();
|
||||
let pass = mk_pass(writer_factory);
|
||||
(pass.f)(srv, doc);
|
||||
return po.recv().second();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn write_markdown_should_write_mod_headers() {
|
||||
let markdown = render(~"mod moo { }");
|
||||
assert!(markdown.contains("# Module `moo`"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_leave_blank_line_after_header() {
|
||||
let markdown = render(~"mod morp { }");
|
||||
assert!(markdown.contains("Module `morp`\n\n"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_write_modules_last() {
|
||||
/*
|
||||
Because the markdown pass writes all modules at the same level of
|
||||
indentation (it doesn't 'nest' them), we need to make sure that we
|
||||
write all of the modules contained in each module after all other
|
||||
types of items, or else the header nesting will end up wrong, with
|
||||
modules appearing to contain items that they do not.
|
||||
*/
|
||||
let markdown = render(
|
||||
~"mod a { }\
|
||||
fn b() { }\
|
||||
mod c {
|
||||
}\
|
||||
fn d() { }"
|
||||
);
|
||||
|
||||
let idx_a = markdown.find_str("# Module `a`").unwrap();
|
||||
let idx_b = markdown.find_str("## Function `b`").unwrap();
|
||||
let idx_c = markdown.find_str("# Module `c`").unwrap();
|
||||
let idx_d = markdown.find_str("## Function `d`").unwrap();
|
||||
|
||||
assert!(idx_b < idx_d);
|
||||
assert!(idx_d < idx_a);
|
||||
assert!(idx_a < idx_c);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_request_new_writer_for_each_page() {
|
||||
// This port will send us a (page, str) pair for every writer
|
||||
// that was created
|
||||
let (writer_factory, po) = markdown_writer::future_writer_factory();
|
||||
let (srv, doc) = create_doc_srv(~"mod a { }");
|
||||
// Split the document up into pages
|
||||
let doc = (page_pass::mk_pass(config::DocPerMod).f)(srv, doc);
|
||||
write_markdown(doc, writer_factory);
|
||||
// We expect two pages to have been written
|
||||
for _ in range(0, 2u) {
|
||||
po.recv();
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_write_title_for_each_page() {
|
||||
let (writer_factory, po) = markdown_writer::future_writer_factory();
|
||||
let (srv, doc) = create_doc_srv(
|
||||
~"#[link(name = \"core\")]; mod a { }");
|
||||
let doc = (page_pass::mk_pass(config::DocPerMod).f)(srv, doc);
|
||||
write_markdown(doc, writer_factory);
|
||||
for _ in range(0, 2u) {
|
||||
let (page, markdown) = po.recv();
|
||||
match page {
|
||||
doc::CratePage(_) => {
|
||||
assert!(markdown.contains("% Crate core"));
|
||||
}
|
||||
doc::ItemPage(_) => {
|
||||
assert!(markdown.contains("% Module a"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_write_full_path_to_mod() {
|
||||
let markdown = render(~"mod a { mod b { mod c { } } }");
|
||||
assert!(markdown.contains("# Module `a::b::c`"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_write_sections() {
|
||||
let markdown = render(
|
||||
~"#[doc = \"\
|
||||
# Header\n\
|
||||
Body\"]\
|
||||
mod a {
|
||||
}");
|
||||
assert!(markdown.contains("#### Header\n\nBody\n\n"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_write_crate_description() {
|
||||
let markdown = render(~"#[doc = \"this is the crate\"];");
|
||||
assert!(markdown.contains("this is the crate"));
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn should_write_index() {
|
||||
let markdown = render(~"mod a { } mod b { }");
|
||||
assert!(markdown.contains(
|
||||
"\n\n* [Module `a`](#module-a)\n\
|
||||
* [Module `b`](#module-b)\n\n"
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_write_index_brief() {
|
||||
let markdown = render(~"#[doc = \"test\"] mod a { }");
|
||||
assert!(markdown.contains("(#module-a) - test\n"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_not_write_index_if_no_entries() {
|
||||
let markdown = render(~"");
|
||||
assert!(!markdown.contains("\n\n\n"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_write_index_for_foreign_mods() {
|
||||
let markdown = render(~"extern { fn a(); }");
|
||||
assert!(markdown.contains(
|
||||
"\n\n* [Function `a`](#function-a)\n\n"
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_write_foreign_fns() {
|
||||
let markdown = render(
|
||||
~"extern { #[doc = \"test\"] fn a(); }");
|
||||
assert!(markdown.contains("test"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_write_foreign_fn_headers() {
|
||||
let markdown = render(
|
||||
~"extern { #[doc = \"test\"] fn a(); }");
|
||||
assert!(markdown.contains("## Function `a`"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn write_markdown_should_write_function_header() {
|
||||
let markdown = render(~"fn func() { }");
|
||||
assert!(markdown.contains("## Function `func`"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_write_the_function_signature() {
|
||||
let markdown = render(~"#[doc = \"f\"] fn a() { }");
|
||||
assert!(markdown.contains("\n~~~ {.rust}\nfn a()\n"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_insert_blank_line_after_fn_signature() {
|
||||
let markdown = render(~"#[doc = \"f\"] fn a() { }");
|
||||
assert!(markdown.contains("fn a()\n~~~\n\n"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_correctly_bracket_fn_signature() {
|
||||
let doc = create_doc(~"fn a() { }");
|
||||
let doc = doc::Doc{
|
||||
pages: ~[
|
||||
doc::CratePage(doc::CrateDoc{
|
||||
topmod: doc::ModDoc{
|
||||
items: ~[doc::FnTag(doc::SimpleItemDoc{
|
||||
sig: Some(~"line 1\nline 2"),
|
||||
.. (doc.cratemod().fns()[0]).clone()
|
||||
})],
|
||||
.. doc.cratemod()
|
||||
},
|
||||
.. doc.CrateDoc()
|
||||
})
|
||||
]
|
||||
};
|
||||
let markdown = write_markdown_str(doc);
|
||||
assert!(markdown.contains("~~~ {.rust}\nline 1\nline 2\n~~~"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_leave_blank_line_between_fn_header_and_sig() {
|
||||
let markdown = render(~"fn a() { }");
|
||||
assert!(markdown.contains("Function `a`\n\n~~~ {.rust}\nfn a()"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_write_static_header() {
|
||||
let markdown = render(~"static a: bool = true;");
|
||||
assert!(markdown.contains("## Static `a`\n\n"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_write_static_description() {
|
||||
let markdown = render(
|
||||
~"#[doc = \"b\"]\
|
||||
static a: bool = true;");
|
||||
assert!(markdown.contains("\n\nb\n\n"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_write_enum_header() {
|
||||
let markdown = render(~"enum a { b }");
|
||||
assert!(markdown.contains("## Enum `a`\n\n"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_write_enum_description() {
|
||||
let markdown = render(~"#[doc = \"b\"] enum a { b }");
|
||||
assert!(markdown.contains("\n\nb\n\n"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_write_variant_list() {
|
||||
let markdown = render(
|
||||
~"enum a { \
|
||||
#[doc = \"test\"] b, \
|
||||
#[doc = \"test\"] c }");
|
||||
assert!(markdown.contains(
|
||||
"\n\n#### Variants\n\
|
||||
\n\
|
||||
\n* `b` - test\
|
||||
\n\
|
||||
\n* `c` - test\n\n"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_write_variant_list_without_descs() {
|
||||
let markdown = render(~"enum a { b, c }");
|
||||
assert!(markdown.contains(
|
||||
"\n\n#### Variants\n\
|
||||
\n\
|
||||
\n* `b`\
|
||||
\n\
|
||||
\n* `c`\n\n"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_write_variant_list_with_indent() {
|
||||
let markdown = render(
|
||||
~"enum a { #[doc = \"line 1\\n\\nline 2\"] b, c }");
|
||||
assert!(markdown.contains(
|
||||
"\n\n#### Variants\n\
|
||||
\n\
|
||||
\n* `b` - line 1\
|
||||
\n \
|
||||
\n line 2\
|
||||
\n\
|
||||
\n* `c`\n\n"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_write_variant_list_with_signatures() {
|
||||
let markdown = render(~"enum a { b(int), #[doc = \"a\"] c(int) }");
|
||||
assert!(markdown.contains(
|
||||
"\n\n#### Variants\n\
|
||||
\n\
|
||||
\n* `b(int)`\
|
||||
\n\
|
||||
\n* `c(int)` - a\n\n"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_write_trait_header() {
|
||||
let markdown = render(~"trait i { fn a(); }");
|
||||
assert!(markdown.contains("## Trait `i`"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_write_trait_desc() {
|
||||
let markdown = render(~"#[doc = \"desc\"] trait i { fn a(); }");
|
||||
assert!(markdown.contains("desc"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_write_trait_method_header() {
|
||||
let markdown = render(~"trait i { fn a(); }");
|
||||
assert!(markdown.contains("### Method `a`"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_write_trait_method_signature() {
|
||||
let markdown = render(~"trait i { fn a(&self); }");
|
||||
assert!(markdown.contains("\n~~~ {.rust}\nfn a(&self)"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_write_impl_header() {
|
||||
let markdown = render(~"impl int { fn a() { } }");
|
||||
assert!(markdown.contains("## Implementation for `int`"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_write_impl_header_with_bounds() {
|
||||
let markdown = render(~"impl <T> int<T> { }");
|
||||
assert!(markdown.contains("## Implementation for `int<T>` where `<T>`"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_write_impl_header_with_trait() {
|
||||
let markdown = render(~"impl j for int { fn a() { } }");
|
||||
assert!(markdown.contains(
|
||||
"## Implementation of `j` for `int`"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_write_impl_desc() {
|
||||
let markdown = render(
|
||||
~"#[doc = \"desc\"] impl int { fn a() { } }");
|
||||
assert!(markdown.contains("desc"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_write_impl_method_header() {
|
||||
let markdown = render(
|
||||
~"impl int { fn a() { } }");
|
||||
assert!(markdown.contains("### Method `a`"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_write_impl_method_signature() {
|
||||
let markdown = render(
|
||||
~"impl int { fn a(&mut self) { } }");
|
||||
assert!(markdown.contains("~~~ {.rust}\nfn a(&mut self)"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_write_type_header() {
|
||||
let markdown = render(~"type t = int;");
|
||||
assert!(markdown.contains("## Type `t`"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_write_type_desc() {
|
||||
let markdown = render(
|
||||
~"#[doc = \"desc\"] type t = int;");
|
||||
assert!(markdown.contains("\n\ndesc\n\n"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_write_type_signature() {
|
||||
let markdown = render(~"type t = int;");
|
||||
assert!(markdown.contains("\n\n~~~ {.rust}\ntype t = int\n~~~\n"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_put_struct_header() {
|
||||
let markdown = render(~"struct S { field: () }");
|
||||
assert!(markdown.contains("## Struct `S`\n\n"));
|
||||
}
|
||||
}
|
@ -1,287 +0,0 @@
|
||||
// Copyright 2012-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 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
|
||||
use config;
|
||||
use doc::ItemUtils;
|
||||
use doc;
|
||||
|
||||
use std::comm::*;
|
||||
use std::comm;
|
||||
use std::io;
|
||||
use std::result;
|
||||
use std::run;
|
||||
use std::str;
|
||||
use std::task;
|
||||
use extra::future::Future;
|
||||
|
||||
#[deriving(Clone)]
|
||||
pub enum WriteInstr {
|
||||
Write(~str),
|
||||
Done
|
||||
}
|
||||
|
||||
pub type Writer = ~fn(v: WriteInstr);
|
||||
pub type WriterFactory = ~fn(page: doc::Page) -> Writer;
|
||||
|
||||
pub trait WriterUtils {
|
||||
fn put_str(&self, str: ~str);
|
||||
fn put_line(&self, str: ~str);
|
||||
fn put_done(&self);
|
||||
}
|
||||
|
||||
impl WriterUtils for Writer {
|
||||
fn put_str(&self, str: ~str) {
|
||||
(*self)(Write(str));
|
||||
}
|
||||
|
||||
fn put_line(&self, str: ~str) {
|
||||
self.put_str(str + "\n");
|
||||
}
|
||||
|
||||
fn put_done(&self) {
|
||||
(*self)(Done)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn make_writer_factory(config: config::Config) -> WriterFactory {
|
||||
match config.output_format {
|
||||
config::Markdown => {
|
||||
markdown_writer_factory(config)
|
||||
}
|
||||
config::PandocHtml => {
|
||||
pandoc_writer_factory(config)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn markdown_writer_factory(config: config::Config) -> WriterFactory {
|
||||
let result: ~fn(page: doc::Page) -> Writer = |page| {
|
||||
markdown_writer(&config, page)
|
||||
};
|
||||
result
|
||||
}
|
||||
|
||||
fn pandoc_writer_factory(config: config::Config) -> WriterFactory {
|
||||
let result: ~fn(doc::Page) -> Writer = |page| {
|
||||
pandoc_writer(&config, page)
|
||||
};
|
||||
result
|
||||
}
|
||||
|
||||
fn markdown_writer(
|
||||
config: &config::Config,
|
||||
page: doc::Page
|
||||
) -> Writer {
|
||||
let filename = make_local_filename(config, page);
|
||||
do generic_writer |markdown| {
|
||||
write_file(&filename, markdown);
|
||||
}
|
||||
}
|
||||
|
||||
fn pandoc_writer(
|
||||
config: &config::Config,
|
||||
page: doc::Page
|
||||
) -> Writer {
|
||||
assert!(config.pandoc_cmd.is_some());
|
||||
let pandoc_cmd = (*config.pandoc_cmd.get_ref()).clone();
|
||||
let filename = make_local_filename(config, page);
|
||||
|
||||
let pandoc_args = ~[
|
||||
~"--standalone",
|
||||
~"--section-divs",
|
||||
~"--from=markdown",
|
||||
~"--to=html",
|
||||
~"--css=rust.css",
|
||||
~"--output=" + filename.to_str()
|
||||
];
|
||||
|
||||
do generic_writer |markdown| {
|
||||
use std::io::WriterUtil;
|
||||
|
||||
debug!("pandoc cmd: %s", pandoc_cmd);
|
||||
debug!("pandoc args: %s", pandoc_args.connect(" "));
|
||||
|
||||
let mut proc = run::Process::new(pandoc_cmd, pandoc_args, run::ProcessOptions::new());
|
||||
|
||||
proc.input().write_str(markdown);
|
||||
let output = proc.finish_with_output();
|
||||
|
||||
debug!("pandoc result: %i", output.status);
|
||||
if output.status != 0 {
|
||||
error!("pandoc-out: %s", str::from_utf8(output.output));
|
||||
error!("pandoc-err: %s", str::from_utf8(output.error));
|
||||
fail!("pandoc failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn generic_writer(process: ~fn(markdown: ~str)) -> Writer {
|
||||
let (po, ch) = stream::<WriteInstr>();
|
||||
do task::spawn || {
|
||||
let mut markdown = ~"";
|
||||
let mut keep_going = true;
|
||||
while keep_going {
|
||||
match po.recv() {
|
||||
Write(s) => markdown.push_str(s),
|
||||
Done => keep_going = false
|
||||
}
|
||||
}
|
||||
process(markdown);
|
||||
};
|
||||
let result: ~fn(instr: WriteInstr) = |instr| ch.send(instr);
|
||||
result
|
||||
}
|
||||
|
||||
pub fn make_local_filename(
|
||||
config: &config::Config,
|
||||
page: doc::Page
|
||||
) -> Path {
|
||||
let filename = make_filename(config, page);
|
||||
config.output_dir.push_rel(&filename)
|
||||
}
|
||||
|
||||
pub fn make_filename(
|
||||
config: &config::Config,
|
||||
page: doc::Page
|
||||
) -> Path {
|
||||
let filename = {
|
||||
match page {
|
||||
doc::CratePage(doc) => {
|
||||
if config.output_format == config::PandocHtml &&
|
||||
config.output_style == config::DocPerMod {
|
||||
~"index"
|
||||
} else {
|
||||
assert!(doc.topmod.name_() != ~"");
|
||||
doc.topmod.name_()
|
||||
}
|
||||
}
|
||||
doc::ItemPage(doc) => {
|
||||
(doc.path() + &[doc.name_()]).connect("_")
|
||||
}
|
||||
}
|
||||
};
|
||||
let ext = match config.output_format {
|
||||
config::Markdown => ~"md",
|
||||
config::PandocHtml => ~"html"
|
||||
};
|
||||
|
||||
Path(filename).with_filetype(ext)
|
||||
}
|
||||
|
||||
fn write_file(path: &Path, s: ~str) {
|
||||
use std::io::WriterUtil;
|
||||
|
||||
match io::file_writer(path, [io::Create, io::Truncate]) {
|
||||
result::Ok(writer) => {
|
||||
writer.write_str(s);
|
||||
}
|
||||
result::Err(e) => fail!(e)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn future_writer_factory(
|
||||
) -> (WriterFactory, Port<(doc::Page, ~str)>) {
|
||||
let (markdown_po, markdown_ch) = stream();
|
||||
let markdown_ch = SharedChan::new(markdown_ch);
|
||||
let writer_factory: WriterFactory = |page| {
|
||||
let (writer_po, writer_ch) = comm::stream();
|
||||
let markdown_ch = markdown_ch.clone();
|
||||
do task::spawn || {
|
||||
let (writer, future) = future_writer();
|
||||
let mut future = future;
|
||||
writer_ch.send(writer);
|
||||
let s = future.get();
|
||||
markdown_ch.send((page.clone(), s));
|
||||
}
|
||||
writer_po.recv()
|
||||
};
|
||||
|
||||
(writer_factory, markdown_po)
|
||||
}
|
||||
|
||||
fn future_writer() -> (Writer, Future<~str>) {
|
||||
let (port, chan) = comm::stream();
|
||||
let writer: ~fn(instr: WriteInstr) = |instr| chan.send(instr.clone());
|
||||
let future = do Future::from_fn || {
|
||||
let mut res = ~"";
|
||||
loop {
|
||||
match port.recv() {
|
||||
Write(s) => res.push_str(s),
|
||||
Done => break
|
||||
}
|
||||
}
|
||||
res
|
||||
};
|
||||
(writer, future)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
|
||||
use astsrv;
|
||||
use doc;
|
||||
use extract;
|
||||
use path_pass;
|
||||
use config;
|
||||
use super::make_local_filename;
|
||||
|
||||
fn mk_doc(name: ~str, source: ~str) -> doc::Doc {
|
||||
do astsrv::from_str(source) |srv| {
|
||||
let doc = extract::from_srv(srv.clone(), name.clone());
|
||||
let doc = (path_pass::mk_pass().f)(srv.clone(), doc);
|
||||
doc
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_use_markdown_file_name_based_off_crate() {
|
||||
let config = config::Config {
|
||||
output_dir: Path("output/dir"),
|
||||
output_format: config::Markdown,
|
||||
output_style: config::DocPerCrate,
|
||||
.. config::default_config(&Path("input/test.rc"))
|
||||
};
|
||||
let doc = mk_doc(~"test", ~"");
|
||||
let page = doc::CratePage(doc.CrateDoc());
|
||||
let filename = make_local_filename(&config, page);
|
||||
assert_eq!(filename.to_str(), ~"output/dir/test.md");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_name_html_crate_file_name_index_html_when_doc_per_mod() {
|
||||
let config = config::Config {
|
||||
output_dir: Path("output/dir"),
|
||||
output_format: config::PandocHtml,
|
||||
output_style: config::DocPerMod,
|
||||
.. config::default_config(&Path("input/test.rc"))
|
||||
};
|
||||
let doc = mk_doc(~"", ~"");
|
||||
let page = doc::CratePage(doc.CrateDoc());
|
||||
let filename = make_local_filename(&config, page);
|
||||
assert_eq!(filename.to_str(), ~"output/dir/index.html");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_name_mod_file_names_by_path() {
|
||||
let config = config::Config {
|
||||
output_dir: Path("output/dir"),
|
||||
output_format: config::PandocHtml,
|
||||
output_style: config::DocPerMod,
|
||||
.. config::default_config(&Path("input/test.rc"))
|
||||
};
|
||||
let doc = mk_doc(~"", ~"mod a { mod b { } }");
|
||||
// hidden __std_macros module at the start.
|
||||
let modb = doc.cratemod().mods()[1].mods()[0].clone();
|
||||
let page = doc::ItemPage(doc::ModTag(modb));
|
||||
let filename = make_local_filename(&config, page);
|
||||
assert_eq!(filename, Path("output/dir/a_b.html"));
|
||||
}
|
||||
}
|
@ -1,185 +0,0 @@
|
||||
// Copyright 2012 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 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
/*!
|
||||
Divides the document tree into pages.
|
||||
|
||||
Each page corresponds is a logical section. There may be pages for
|
||||
individual modules, pages for the crate, indexes, etc.
|
||||
*/
|
||||
|
||||
|
||||
use astsrv;
|
||||
use config;
|
||||
use doc::ItemUtils;
|
||||
use doc;
|
||||
use fold::Fold;
|
||||
use fold;
|
||||
use pass::Pass;
|
||||
|
||||
use std::comm::*;
|
||||
use std::task;
|
||||
use syntax::ast;
|
||||
|
||||
#[cfg(test)] use doc::PageUtils;
|
||||
|
||||
pub fn mk_pass(output_style: config::OutputStyle) -> Pass {
|
||||
Pass {
|
||||
name: ~"page",
|
||||
f: |srv, doc| run(srv, doc, output_style)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run(
|
||||
_srv: astsrv::Srv,
|
||||
doc: doc::Doc,
|
||||
output_style: config::OutputStyle
|
||||
) -> doc::Doc {
|
||||
|
||||
if output_style == config::DocPerCrate {
|
||||
return doc;
|
||||
}
|
||||
|
||||
let (result_port, result_chan) = stream();
|
||||
let (page_port, page_chan) = stream();
|
||||
let page_chan = SharedChan::new(page_chan);
|
||||
do task::spawn {
|
||||
result_chan.send(make_doc_from_pages(&page_port));
|
||||
};
|
||||
|
||||
find_pages(doc, page_chan);
|
||||
result_port.recv()
|
||||
}
|
||||
|
||||
type PagePort = Port<Option<doc::Page>>;
|
||||
type PageChan = SharedChan<Option<doc::Page>>;
|
||||
|
||||
fn make_doc_from_pages(page_port: &PagePort) -> doc::Doc {
|
||||
let mut pages = ~[];
|
||||
loop {
|
||||
let val = page_port.recv();
|
||||
if val.is_some() {
|
||||
pages.push(val.unwrap());
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
doc::Doc {
|
||||
pages: pages
|
||||
}
|
||||
}
|
||||
|
||||
fn find_pages(doc: doc::Doc, page_chan: PageChan) {
|
||||
let fold = Fold {
|
||||
ctxt: page_chan.clone(),
|
||||
fold_crate: fold_crate,
|
||||
fold_mod: fold_mod,
|
||||
fold_nmod: fold_nmod,
|
||||
.. fold::default_any_fold(page_chan.clone())
|
||||
};
|
||||
(fold.fold_doc)(&fold, doc.clone());
|
||||
|
||||
page_chan.send(None);
|
||||
}
|
||||
|
||||
fn fold_crate(fold: &fold::Fold<PageChan>, doc: doc::CrateDoc)
|
||||
-> doc::CrateDoc {
|
||||
let doc = fold::default_seq_fold_crate(fold, doc);
|
||||
|
||||
let page = doc::CratePage(doc::CrateDoc {
|
||||
topmod: strip_mod(doc.topmod.clone()),
|
||||
.. doc.clone()
|
||||
});
|
||||
|
||||
fold.ctxt.send(Some(page));
|
||||
|
||||
doc
|
||||
}
|
||||
|
||||
fn fold_mod(fold: &fold::Fold<PageChan>, doc: doc::ModDoc) -> doc::ModDoc {
|
||||
let doc = fold::default_any_fold_mod(fold, doc);
|
||||
|
||||
if doc.id() != ast::CRATE_NODE_ID {
|
||||
|
||||
let doc = strip_mod(doc.clone());
|
||||
let page = doc::ItemPage(doc::ModTag(doc));
|
||||
fold.ctxt.send(Some(page));
|
||||
}
|
||||
|
||||
doc
|
||||
}
|
||||
|
||||
fn strip_mod(doc: doc::ModDoc) -> doc::ModDoc {
|
||||
doc::ModDoc {
|
||||
items: do doc.items.iter().filter |item| {
|
||||
match **item {
|
||||
doc::ModTag(_) | doc::NmodTag(_) => false,
|
||||
_ => true
|
||||
}
|
||||
}.map(|x| (*x).clone()).collect::<~[doc::ItemTag]>(),
|
||||
.. doc.clone()
|
||||
}
|
||||
}
|
||||
|
||||
fn fold_nmod(fold: &fold::Fold<PageChan>, doc: doc::NmodDoc) -> doc::NmodDoc {
|
||||
let doc = fold::default_seq_fold_nmod(fold, doc);
|
||||
let page = doc::ItemPage(doc::NmodTag(doc.clone()));
|
||||
fold.ctxt.send(Some(page));
|
||||
return doc;
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use astsrv;
|
||||
use config;
|
||||
use attr_pass;
|
||||
use doc;
|
||||
use extract;
|
||||
use prune_hidden_pass;
|
||||
use page_pass::run;
|
||||
|
||||
fn mk_doc_(
|
||||
output_style: config::OutputStyle,
|
||||
source: ~str
|
||||
) -> doc::Doc {
|
||||
do astsrv::from_str(source.clone()) |srv| {
|
||||
let doc = extract::from_srv(srv.clone(), ~"");
|
||||
let doc = (attr_pass::mk_pass().f)(srv.clone(), doc);
|
||||
let doc = (prune_hidden_pass::mk_pass().f)(srv.clone(), doc);
|
||||
run(srv.clone(), doc, output_style)
|
||||
}
|
||||
}
|
||||
|
||||
fn mk_doc(source: ~str) -> doc::Doc {
|
||||
mk_doc_(config::DocPerMod, source.clone())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_not_split_the_doc_into_pages_for_doc_per_crate() {
|
||||
let doc = mk_doc_(
|
||||
config::DocPerCrate,
|
||||
~"mod a { } mod b { mod c { } }"
|
||||
);
|
||||
assert_eq!(doc.pages.len(), 1u);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_make_a_page_for_every_mod() {
|
||||
let doc = mk_doc(~"mod a { }");
|
||||
// hidden __std_macros module at the start.
|
||||
assert_eq!(doc.pages.mods()[0].name_(), ~"a");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_remove_mods_from_containing_mods() {
|
||||
let doc = mk_doc(~"mod a { }");
|
||||
assert!(doc.cratemod().mods().is_empty());
|
||||
}
|
||||
}
|
@ -1,41 +0,0 @@
|
||||
// Copyright 2012 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 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
//! AST-parsing helpers
|
||||
|
||||
use rustc::driver::driver;
|
||||
use rustc::driver::session;
|
||||
use syntax::ast;
|
||||
use syntax::parse;
|
||||
|
||||
pub fn from_file(file: &Path) -> @ast::Crate {
|
||||
parse::parse_crate_from_file(
|
||||
file, ~[], parse::new_parse_sess(None))
|
||||
}
|
||||
|
||||
pub fn from_str(source: @str) -> @ast::Crate {
|
||||
parse::parse_crate_from_source_str(
|
||||
@"-", source, ~[], parse::new_parse_sess(None))
|
||||
}
|
||||
|
||||
pub fn from_file_sess(sess: session::Session, file: &Path) -> @ast::Crate {
|
||||
parse::parse_crate_from_file(
|
||||
file, cfg(sess), sess.parse_sess)
|
||||
}
|
||||
|
||||
pub fn from_str_sess(sess: session::Session, source: @str) -> @ast::Crate {
|
||||
parse::parse_crate_from_source_str(
|
||||
@"-", source, cfg(sess), sess.parse_sess)
|
||||
}
|
||||
|
||||
fn cfg(sess: session::Session) -> ast::CrateConfig {
|
||||
driver::build_configuration(sess)
|
||||
}
|
||||
|
@ -1,96 +0,0 @@
|
||||
// Copyright 2012 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 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
|
||||
|
||||
use astsrv;
|
||||
use doc;
|
||||
use time;
|
||||
|
||||
#[cfg(test)] use extract;
|
||||
|
||||
/// A single operation on the document model
|
||||
pub struct Pass {
|
||||
name: ~str,
|
||||
f: @fn(srv: astsrv::Srv, doc: doc::Doc) -> doc::Doc
|
||||
}
|
||||
|
||||
pub fn run_passes(
|
||||
srv: astsrv::Srv,
|
||||
doc: doc::Doc,
|
||||
passes: ~[Pass]
|
||||
) -> doc::Doc {
|
||||
let mut passno = 0;
|
||||
do passes.iter().fold(doc) |doc, pass| {
|
||||
debug!("pass #%d", passno);
|
||||
passno += 1;
|
||||
do time(pass.name.clone()) {
|
||||
(pass.f)(srv.clone(), doc.clone())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_run_passes() {
|
||||
fn pass1(
|
||||
_srv: astsrv::Srv,
|
||||
doc: doc::Doc
|
||||
) -> doc::Doc {
|
||||
doc::Doc{
|
||||
pages: ~[
|
||||
doc::CratePage(doc::CrateDoc{
|
||||
topmod: doc::ModDoc{
|
||||
item: doc::ItemDoc {
|
||||
name: doc.cratemod().name_() + "two",
|
||||
.. doc.cratemod().item.clone()
|
||||
},
|
||||
items: ~[],
|
||||
index: None
|
||||
}
|
||||
})
|
||||
]
|
||||
}
|
||||
}
|
||||
fn pass2(
|
||||
_srv: astsrv::Srv,
|
||||
doc: doc::Doc
|
||||
) -> doc::Doc {
|
||||
doc::Doc{
|
||||
pages: ~[
|
||||
doc::CratePage(doc::CrateDoc{
|
||||
topmod: doc::ModDoc{
|
||||
item: doc::ItemDoc {
|
||||
name: doc.cratemod().name_() + "three",
|
||||
.. doc.cratemod().item.clone()
|
||||
},
|
||||
items: ~[],
|
||||
index: None
|
||||
}
|
||||
})
|
||||
]
|
||||
}
|
||||
}
|
||||
let source = ~"";
|
||||
do astsrv::from_str(source) |srv| {
|
||||
let passes = ~[
|
||||
Pass {
|
||||
name: ~"",
|
||||
f: pass1
|
||||
},
|
||||
Pass {
|
||||
name: ~"",
|
||||
f: pass2
|
||||
}
|
||||
];
|
||||
let doc = extract::from_srv(srv.clone(), ~"one");
|
||||
let doc = run_passes(srv, doc, passes);
|
||||
assert_eq!(doc.cratemod().name_(), ~"onetwothree");
|
||||
}
|
||||
}
|
@ -1,115 +0,0 @@
|
||||
// Copyright 2012 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 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
//! Records the full path to items
|
||||
|
||||
|
||||
use astsrv;
|
||||
use doc::ItemUtils;
|
||||
use doc;
|
||||
use fold::Fold;
|
||||
use fold;
|
||||
use pass::Pass;
|
||||
|
||||
#[cfg(test)] use extract;
|
||||
|
||||
use syntax::ast;
|
||||
|
||||
pub fn mk_pass() -> Pass {
|
||||
Pass {
|
||||
name: ~"path",
|
||||
f: run
|
||||
}
|
||||
}
|
||||
|
||||
struct Ctxt {
|
||||
srv: astsrv::Srv,
|
||||
path: @mut ~[~str]
|
||||
}
|
||||
|
||||
impl Clone for Ctxt {
|
||||
fn clone(&self) -> Ctxt {
|
||||
Ctxt {
|
||||
srv: self.srv.clone(),
|
||||
path: @mut (*self.path).clone()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn run(srv: astsrv::Srv, doc: doc::Doc) -> doc::Doc {
|
||||
let ctxt = Ctxt {
|
||||
srv: srv,
|
||||
path: @mut ~[]
|
||||
};
|
||||
let fold = Fold {
|
||||
ctxt: ctxt.clone(),
|
||||
fold_item: fold_item,
|
||||
fold_mod: fold_mod,
|
||||
fold_nmod: fold_nmod,
|
||||
.. fold::default_any_fold(ctxt)
|
||||
};
|
||||
(fold.fold_doc)(&fold, doc)
|
||||
}
|
||||
|
||||
fn fold_item(fold: &fold::Fold<Ctxt>, doc: doc::ItemDoc) -> doc::ItemDoc {
|
||||
doc::ItemDoc {
|
||||
path: (*fold.ctxt.path).clone(),
|
||||
.. doc
|
||||
}
|
||||
}
|
||||
|
||||
fn fold_mod(fold: &fold::Fold<Ctxt>, doc: doc::ModDoc) -> doc::ModDoc {
|
||||
let is_topmod = doc.id() == ast::CRATE_NODE_ID;
|
||||
|
||||
if !is_topmod { fold.ctxt.path.push(doc.name_()); }
|
||||
let doc = fold::default_any_fold_mod(fold, doc);
|
||||
if !is_topmod { fold.ctxt.path.pop(); }
|
||||
|
||||
doc::ModDoc {
|
||||
item: (fold.fold_item)(fold, doc.item.clone()),
|
||||
.. doc
|
||||
}
|
||||
}
|
||||
|
||||
fn fold_nmod(fold: &fold::Fold<Ctxt>, doc: doc::NmodDoc) -> doc::NmodDoc {
|
||||
fold.ctxt.path.push(doc.name_());
|
||||
let doc = fold::default_seq_fold_nmod(fold, doc);
|
||||
fold.ctxt.path.pop();
|
||||
|
||||
doc::NmodDoc {
|
||||
item: (fold.fold_item)(fold, doc.item.clone()),
|
||||
.. doc
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_record_mod_paths() {
|
||||
let source = ~"mod a { mod b { mod c { } } mod d { mod e { } } }";
|
||||
do astsrv::from_str(source) |srv| {
|
||||
let doc = extract::from_srv(srv.clone(), ~"");
|
||||
let doc = run(srv.clone(), doc);
|
||||
// hidden __std_macros module at the start.
|
||||
assert_eq!(doc.cratemod().mods()[1].mods()[0].mods()[0].path(),
|
||||
~[~"a", ~"b"]);
|
||||
assert_eq!(doc.cratemod().mods()[1].mods()[1].mods()[0].path(),
|
||||
~[~"a", ~"d"]);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_record_fn_paths() {
|
||||
let source = ~"mod a { fn b() { } }";
|
||||
do astsrv::from_str(source) |srv| {
|
||||
let doc = extract::from_srv(srv.clone(), ~"");
|
||||
let doc = run(srv.clone(), doc);
|
||||
// hidden __std_macros module at the start.
|
||||
assert_eq!(doc.cratemod().mods()[1].fns()[0].path(), ~[~"a"]);
|
||||
}
|
||||
}
|
@ -1,83 +0,0 @@
|
||||
// Copyright 2012 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 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
//! Prunes things with the #[doc(hidden)] attribute
|
||||
|
||||
use astsrv;
|
||||
use attr_parser;
|
||||
use doc::ItemUtils;
|
||||
use doc;
|
||||
use fold::Fold;
|
||||
use fold;
|
||||
use pass::Pass;
|
||||
|
||||
pub fn mk_pass() -> Pass {
|
||||
Pass {
|
||||
name: ~"prune_hidden",
|
||||
f: run
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run(srv: astsrv::Srv, doc: doc::Doc) -> doc::Doc {
|
||||
let fold = Fold {
|
||||
ctxt: srv.clone(),
|
||||
fold_mod: fold_mod,
|
||||
.. fold::default_any_fold(srv)
|
||||
};
|
||||
(fold.fold_doc)(&fold, doc)
|
||||
}
|
||||
|
||||
fn fold_mod(
|
||||
fold: &fold::Fold<astsrv::Srv>,
|
||||
doc: doc::ModDoc
|
||||
) -> doc::ModDoc {
|
||||
let doc = fold::default_any_fold_mod(fold, doc);
|
||||
|
||||
doc::ModDoc {
|
||||
items: do doc.items.iter().filter |item_tag| {
|
||||
!is_hidden(fold.ctxt.clone(), item_tag.item())
|
||||
}.map(|x| (*x).clone()).collect(),
|
||||
.. doc
|
||||
}
|
||||
}
|
||||
|
||||
fn is_hidden(srv: astsrv::Srv, doc: doc::ItemDoc) -> bool {
|
||||
use syntax::ast_map;
|
||||
|
||||
let id = doc.id;
|
||||
do astsrv::exec(srv) |ctxt| {
|
||||
let attrs = match ctxt.ast_map.get_copy(&id) {
|
||||
ast_map::node_item(item, _) => item.attrs.clone(),
|
||||
_ => ~[]
|
||||
};
|
||||
attr_parser::parse_hidden(attrs)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use astsrv;
|
||||
use doc;
|
||||
use extract;
|
||||
use prune_hidden_pass::run;
|
||||
|
||||
fn mk_doc(source: ~str) -> doc::Doc {
|
||||
do astsrv::from_str(source.clone()) |srv| {
|
||||
let doc = extract::from_srv(srv.clone(), ~"");
|
||||
run(srv.clone(), doc)
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_prune_hidden_items() {
|
||||
let doc = mk_doc(~"#[doc(hidden)] mod a { }");
|
||||
assert!(doc.cratemod().mods().is_empty())
|
||||
}
|
||||
}
|
@ -1,255 +0,0 @@
|
||||
// Copyright 2012 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 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
//! Prune things that are private
|
||||
|
||||
|
||||
use extract;
|
||||
use syntax::ast;
|
||||
use syntax::ast_map;
|
||||
use astsrv;
|
||||
use doc;
|
||||
use fold::Fold;
|
||||
use fold;
|
||||
use pass::Pass;
|
||||
|
||||
pub fn mk_pass() -> Pass {
|
||||
Pass {
|
||||
name: ~"prune_private",
|
||||
f: run
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run(srv: astsrv::Srv, doc: doc::Doc) -> doc::Doc {
|
||||
// First strip private methods out of impls
|
||||
let fold = Fold {
|
||||
ctxt: srv.clone(),
|
||||
fold_impl: fold_impl,
|
||||
.. fold::default_any_fold(srv.clone())
|
||||
};
|
||||
let doc = (fold.fold_doc)(&fold, doc);
|
||||
|
||||
// Then strip private items and empty impls
|
||||
let fold = Fold {
|
||||
ctxt: srv.clone(),
|
||||
fold_mod: fold_mod,
|
||||
.. fold::default_any_fold(srv)
|
||||
};
|
||||
let doc = (fold.fold_doc)(&fold, doc);
|
||||
|
||||
return doc;
|
||||
}
|
||||
|
||||
fn fold_impl(
|
||||
fold: &fold::Fold<astsrv::Srv>,
|
||||
doc: doc::ImplDoc
|
||||
) -> doc::ImplDoc {
|
||||
let doc = fold::default_seq_fold_impl(fold, doc);
|
||||
|
||||
do astsrv::exec(fold.ctxt.clone()) |ctxt| {
|
||||
match ctxt.ast_map.get_copy(&doc.item.id) {
|
||||
ast_map::node_item(item, _) => {
|
||||
match item.node {
|
||||
ast::item_impl(_, None, _, ref methods) => {
|
||||
// Associated impls have complex rules for method visibility
|
||||
strip_priv_methods(doc.clone(), *methods, item.vis)
|
||||
}
|
||||
ast::item_impl(_, Some(_), _ ,_) => {
|
||||
// Trait impls don't
|
||||
doc.clone()
|
||||
}
|
||||
_ => fail!()
|
||||
}
|
||||
}
|
||||
_ => fail!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn strip_priv_methods(
|
||||
doc: doc::ImplDoc,
|
||||
methods: &[@ast::method],
|
||||
item_vis: ast::visibility
|
||||
) -> doc::ImplDoc {
|
||||
let methods = do doc.methods.iter().filter |method| {
|
||||
let ast_method = do methods.iter().find |m| {
|
||||
extract::to_str(m.ident) == method.name
|
||||
};
|
||||
assert!(ast_method.is_some());
|
||||
let ast_method = ast_method.unwrap();
|
||||
match ast_method.vis {
|
||||
ast::public => true,
|
||||
ast::private => false,
|
||||
ast::inherited => item_vis == ast::public
|
||||
}
|
||||
}.map(|x| (*x).clone()).collect();
|
||||
|
||||
doc::ImplDoc {
|
||||
methods: methods,
|
||||
.. doc
|
||||
}
|
||||
}
|
||||
|
||||
fn fold_mod(
|
||||
fold: &fold::Fold<astsrv::Srv>,
|
||||
doc: doc::ModDoc
|
||||
) -> doc::ModDoc {
|
||||
let doc = fold::default_any_fold_mod(fold, doc);
|
||||
|
||||
doc::ModDoc {
|
||||
items: doc.items.iter().filter(|item_tag| {
|
||||
match item_tag {
|
||||
& &doc::ImplTag(ref doc) => {
|
||||
if doc.trait_types.is_empty() {
|
||||
// This is an associated impl. We have already pruned the
|
||||
// non-visible methods. If there are any left then
|
||||
// retain the impl, otherwise throw it away
|
||||
!doc.methods.is_empty()
|
||||
} else {
|
||||
// This is a trait implementation, make it visible
|
||||
// NB: This is not quite right since this could be an impl
|
||||
// of a private trait. We can't know that without running
|
||||
// resolve though.
|
||||
true
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
is_visible(fold.ctxt.clone(), item_tag.item())
|
||||
}
|
||||
}
|
||||
}).map(|x| (*x).clone()).collect(),
|
||||
.. doc
|
||||
}
|
||||
}
|
||||
|
||||
fn is_visible(srv: astsrv::Srv, doc: doc::ItemDoc) -> bool {
|
||||
let id = doc.id;
|
||||
|
||||
do astsrv::exec(srv) |ctxt| {
|
||||
match ctxt.ast_map.get_copy(&id) {
|
||||
ast_map::node_item(item, _) => {
|
||||
match &item.node {
|
||||
&ast::item_impl(*) => {
|
||||
// Impls handled elsewhere
|
||||
fail!()
|
||||
}
|
||||
_ => {
|
||||
// Otherwise just look at the visibility
|
||||
item.vis == ast::public
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => unreachable!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use astsrv;
|
||||
use doc;
|
||||
use extract;
|
||||
use tystr_pass;
|
||||
use prune_private_pass::run;
|
||||
|
||||
fn mk_doc(source: ~str) -> doc::Doc {
|
||||
do astsrv::from_str(source.clone()) |srv| {
|
||||
let doc = extract::from_srv(srv.clone(), ~"");
|
||||
let doc = tystr_pass::run(srv.clone(), doc);
|
||||
run(srv.clone(), doc)
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_prune_items_without_pub_modifier() {
|
||||
let doc = mk_doc(~"mod a { }");
|
||||
assert!(doc.cratemod().mods().is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_not_prune_trait_impls() {
|
||||
// Impls are more complicated
|
||||
let doc = mk_doc(
|
||||
~" \
|
||||
trait Foo { } \
|
||||
impl Foo for int { } \
|
||||
");
|
||||
assert!(!doc.cratemod().impls().is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_prune_associated_methods_without_vis_modifier_on_impls_without_vis_modifier() {
|
||||
let doc = mk_doc(
|
||||
~"impl Foo {\
|
||||
pub fn bar() { }\
|
||||
fn baz() { }\
|
||||
}");
|
||||
assert_eq!(doc.cratemod().impls()[0].methods.len(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_prune_priv_associated_methods_on_impls_without_vis_modifier() {
|
||||
let doc = mk_doc(
|
||||
~"impl Foo {\
|
||||
pub fn bar() { }\
|
||||
fn baz() { }\
|
||||
}");
|
||||
assert_eq!(doc.cratemod().impls()[0].methods.len(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_prune_priv_associated_methods_on_pub_impls() {
|
||||
let doc = mk_doc(
|
||||
~"impl Foo {\
|
||||
pub fn bar() { }\
|
||||
fn baz() { }\
|
||||
}");
|
||||
assert_eq!(doc.cratemod().impls()[0].methods.len(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_prune_associated_methods_without_vis_modifier_on_priv_impls() {
|
||||
let doc = mk_doc(
|
||||
~"impl Foo {\
|
||||
pub fn bar() { }\
|
||||
fn baz() { }\
|
||||
}");
|
||||
assert_eq!(doc.cratemod().impls()[0].methods.len(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_prune_priv_associated_methods_on_priv_impls() {
|
||||
let doc = mk_doc(
|
||||
~"impl Foo {\
|
||||
pub fn bar() { }\
|
||||
fn baz() { }\
|
||||
}");
|
||||
assert_eq!(doc.cratemod().impls()[0].methods.len(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_prune_associated_impls_with_no_pub_methods() {
|
||||
let doc = mk_doc(
|
||||
~"impl Foo {\
|
||||
fn baz() { }\
|
||||
}");
|
||||
assert!(doc.cratemod().impls().is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_not_prune_associated_impls_with_pub_methods() {
|
||||
let doc = mk_doc(
|
||||
~" \
|
||||
impl Foo { pub fn bar() { } } \
|
||||
");
|
||||
assert!(!doc.cratemod().impls().is_empty());
|
||||
}
|
||||
}
|
@ -8,140 +8,209 @@
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
//! Rustdoc - The Rust documentation generator
|
||||
|
||||
#[link(name = "rustdoc",
|
||||
vers = "0.8",
|
||||
uuid = "f8abd014-b281-484d-a0c3-26e3de8e2412",
|
||||
url = "https://github.com/mozilla/rust/tree/master/src/rustdoc")];
|
||||
uuid = "8c6e4598-1596-4aa5-a24c-b811914bbbc6",
|
||||
url = "https://github.com/mozilla/rust/tree/master/src/librustdoc")];
|
||||
|
||||
#[comment = "The Rust documentation generator"];
|
||||
#[desc = "rustdoc, the Rust documentation extractor"];
|
||||
#[license = "MIT/ASL2"];
|
||||
#[crate_type = "lib"];
|
||||
|
||||
extern mod extra;
|
||||
extern mod rustc;
|
||||
extern mod syntax;
|
||||
extern mod rustc;
|
||||
extern mod extra;
|
||||
|
||||
use std::os;
|
||||
use extra::serialize::Encodable;
|
||||
use extra::time;
|
||||
use extra::getopts::groups;
|
||||
use std::cell::Cell;
|
||||
use std::rt::io;
|
||||
use std::rt::io::Writer;
|
||||
use std::rt::io::file::FileInfo;
|
||||
|
||||
use config::Config;
|
||||
use doc::Item;
|
||||
use doc::ItemUtils;
|
||||
|
||||
pub mod pass;
|
||||
pub mod config;
|
||||
pub mod parse;
|
||||
pub mod extract;
|
||||
pub mod attr_parser;
|
||||
pub mod doc;
|
||||
pub mod markdown_index_pass;
|
||||
pub mod markdown_pass;
|
||||
pub mod markdown_writer;
|
||||
pub mod clean;
|
||||
pub mod core;
|
||||
pub mod doctree;
|
||||
pub mod fold;
|
||||
pub mod path_pass;
|
||||
pub mod attr_pass;
|
||||
pub mod tystr_pass;
|
||||
pub mod prune_hidden_pass;
|
||||
pub mod desc_to_brief_pass;
|
||||
pub mod text_pass;
|
||||
pub mod unindent_pass;
|
||||
pub mod trim_pass;
|
||||
pub mod astsrv;
|
||||
pub mod demo;
|
||||
pub mod sort_pass;
|
||||
pub mod sort_item_name_pass;
|
||||
pub mod sort_item_type_pass;
|
||||
pub mod page_pass;
|
||||
pub mod sectionalize_pass;
|
||||
pub mod escape_pass;
|
||||
pub mod prune_private_pass;
|
||||
pub mod html {
|
||||
pub mod render;
|
||||
pub mod layout;
|
||||
pub mod markdown;
|
||||
pub mod format;
|
||||
}
|
||||
pub mod passes;
|
||||
pub mod plugins;
|
||||
pub mod visit_ast;
|
||||
|
||||
pub static SCHEMA_VERSION: &'static str = "0.8.0";
|
||||
|
||||
local_data_key!(pub ctxtkey: @core::DocContext)
|
||||
|
||||
enum OutputFormat {
|
||||
HTML, JSON
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
let args = os::args();
|
||||
main_args(args);
|
||||
main_args(std::os::args());
|
||||
}
|
||||
|
||||
pub fn opts() -> ~[groups::OptGroup] {
|
||||
use extra::getopts::groups::*;
|
||||
~[
|
||||
optmulti("L", "library-path", "directory to add to crate search path",
|
||||
"DIR"),
|
||||
optmulti("", "plugin-path", "directory to load plugins from", "DIR"),
|
||||
optmulti("", "passes", "space separated list of passes to also run",
|
||||
"PASSES"),
|
||||
optmulti("", "plugins", "space separated list of plugins to also load",
|
||||
"PLUGINS"),
|
||||
optflag("h", "help", "show this help message"),
|
||||
optflag("", "nodefaults", "don't run the default passes"),
|
||||
optopt("o", "output", "where to place the output", "PATH"),
|
||||
]
|
||||
}
|
||||
|
||||
pub fn usage(argv0: &str) {
|
||||
println(groups::usage(format!("{} [options] [html|json] <crate>",
|
||||
argv0), opts()));
|
||||
}
|
||||
|
||||
pub fn main_args(args: &[~str]) {
|
||||
if args.iter().any(|x| "-h" == *x) || args.iter().any(|x| "--help" == *x) {
|
||||
config::usage();
|
||||
//use extra::getopts::groups::*;
|
||||
|
||||
let matches = groups::getopts(args.tail(), opts()).unwrap();
|
||||
|
||||
if matches.opt_present("h") || matches.opt_present("help") {
|
||||
usage(args[0]);
|
||||
return;
|
||||
}
|
||||
|
||||
let config = match config::parse_config(args) {
|
||||
Ok(config) => config,
|
||||
Err(err) => {
|
||||
printfln!("error: %s", err);
|
||||
return;
|
||||
}
|
||||
let (format, cratefile) = match matches.free.clone() {
|
||||
[~"json", crate] => (JSON, crate),
|
||||
[~"html", crate] => (HTML, crate),
|
||||
[s, _] => {
|
||||
println!("Unknown output format: `{}`", s);
|
||||
usage(args[0]);
|
||||
exit(1);
|
||||
}
|
||||
[_, .._] => {
|
||||
println!("Expected exactly one crate to process");
|
||||
usage(args[0]);
|
||||
exit(1);
|
||||
}
|
||||
_ => {
|
||||
println!("Expected an output format and then one crate");
|
||||
usage(args[0]);
|
||||
exit(1);
|
||||
}
|
||||
};
|
||||
|
||||
run(config);
|
||||
}
|
||||
// First, parse the crate and extract all relevant information.
|
||||
let libs = Cell::new(matches.opt_strs("L").map(|s| Path(*s)));
|
||||
let cr = Cell::new(Path(cratefile));
|
||||
info2!("starting to run rustc");
|
||||
let crate = do std::task::try {
|
||||
let cr = cr.take();
|
||||
core::run_core(libs.take(), &cr)
|
||||
}.unwrap();
|
||||
info2!("finished with rustc");
|
||||
|
||||
/// Runs rustdoc over the given file
|
||||
fn run(config: Config) {
|
||||
|
||||
let source_file = config.input_crate.clone();
|
||||
|
||||
// Create an AST service from the source code
|
||||
do astsrv::from_file(source_file.to_str()) |srv| {
|
||||
|
||||
// Just time how long it takes for the AST to become available
|
||||
do time(~"wait_ast") {
|
||||
do astsrv::exec(srv.clone()) |_ctxt| { }
|
||||
};
|
||||
|
||||
// Extract the initial doc tree from the AST. This contains
|
||||
// just names and node ids.
|
||||
let doc = time(~"extract", || {
|
||||
let default_name = source_file.clone();
|
||||
extract::from_srv(srv.clone(), default_name.to_str())
|
||||
});
|
||||
|
||||
// Refine and publish the document
|
||||
pass::run_passes(srv, doc, ~[
|
||||
// Generate type and signature strings
|
||||
tystr_pass::mk_pass(),
|
||||
// Record the full paths to various nodes
|
||||
path_pass::mk_pass(),
|
||||
// Extract the docs attributes and attach them to doc nodes
|
||||
attr_pass::mk_pass(),
|
||||
// Perform various text escaping
|
||||
escape_pass::mk_pass(),
|
||||
// Remove things marked doc(hidden)
|
||||
prune_hidden_pass::mk_pass(),
|
||||
// Remove things that are private
|
||||
prune_private_pass::mk_pass(),
|
||||
// Extract brief documentation from the full descriptions
|
||||
desc_to_brief_pass::mk_pass(),
|
||||
// Massage the text to remove extra indentation
|
||||
unindent_pass::mk_pass(),
|
||||
// Split text into multiple sections according to headers
|
||||
sectionalize_pass::mk_pass(),
|
||||
// Trim extra spaces from text
|
||||
trim_pass::mk_pass(),
|
||||
// Sort items by name
|
||||
sort_item_name_pass::mk_pass(),
|
||||
// Sort items again by kind
|
||||
sort_item_type_pass::mk_pass(),
|
||||
// Create indexes appropriate for markdown
|
||||
markdown_index_pass::mk_pass(config.clone()),
|
||||
// Break the document into pages if required by the
|
||||
// output format
|
||||
page_pass::mk_pass(config.output_style),
|
||||
// Render
|
||||
markdown_pass::mk_pass(
|
||||
markdown_writer::make_writer_factory(config.clone())
|
||||
)
|
||||
]);
|
||||
// Process all of the crate attributes, extracting plugin metadata along
|
||||
// with the passes which we are supposed to run.
|
||||
let mut default_passes = !matches.opt_present("nodefaults");
|
||||
let mut passes = matches.opt_strs("passes");
|
||||
let mut plugins = matches.opt_strs("plugins");
|
||||
match crate.module.get_ref().doc_list() {
|
||||
Some(nested) => {
|
||||
for inner in nested.iter() {
|
||||
match *inner {
|
||||
clean::Word(~"no_default_passes") => {
|
||||
default_passes = false;
|
||||
}
|
||||
clean::NameValue(~"passes", ref value) => {
|
||||
for pass in value.word_iter() {
|
||||
passes.push(pass.to_owned());
|
||||
}
|
||||
}
|
||||
clean::NameValue(~"plugins", ref value) => {
|
||||
for p in value.word_iter() {
|
||||
plugins.push(p.to_owned());
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
None => {}
|
||||
}
|
||||
if default_passes {
|
||||
passes.unshift(~"collapse-docs");
|
||||
passes.unshift(~"unindent-comments");
|
||||
}
|
||||
|
||||
// Load all plugins/passes into a PluginManager
|
||||
let mut pm = plugins::PluginManager::new(Path("/tmp/rustdoc_ng/plugins"));
|
||||
for pass in passes.iter() {
|
||||
let plugin = match pass.as_slice() {
|
||||
"strip-hidden" => passes::strip_hidden,
|
||||
"unindent-comments" => passes::unindent_comments,
|
||||
"collapse-docs" => passes::collapse_docs,
|
||||
"collapse-privacy" => passes::collapse_privacy,
|
||||
s => { error!("unknown pass %s, skipping", s); loop },
|
||||
};
|
||||
pm.add_plugin(plugin);
|
||||
}
|
||||
info2!("loading plugins...");
|
||||
for pname in plugins.move_iter() {
|
||||
pm.load_plugin(pname);
|
||||
}
|
||||
|
||||
// Run everything!
|
||||
info2!("Executing passes/plugins");
|
||||
let (crate, res) = pm.run_plugins(crate);
|
||||
|
||||
info2!("going to format");
|
||||
let started = time::precise_time_ns();
|
||||
let output = matches.opt_str("o").map(|s| Path(*s));
|
||||
match format {
|
||||
HTML => { html::render::run(crate, output.unwrap_or(Path("doc"))) }
|
||||
JSON => { jsonify(crate, res, output.unwrap_or(Path("doc.json"))) }
|
||||
}
|
||||
let ended = time::precise_time_ns();
|
||||
info2!("Took {:.03f}s", (ended as f64 - started as f64) / 1000000000f64);
|
||||
}
|
||||
|
||||
pub fn time<T>(what: ~str, f: &fn() -> T) -> T {
|
||||
let start = extra::time::precise_time_s();
|
||||
let rv = f();
|
||||
let end = extra::time::precise_time_s();
|
||||
info!("time: %3.3f s %s", end - start, what);
|
||||
rv
|
||||
fn jsonify(crate: clean::Crate, res: ~[plugins::PluginJson], dst: Path) {
|
||||
// {
|
||||
// "schema": version,
|
||||
// "crate": { parsed crate ... },
|
||||
// "plugins": { output of plugins ... }
|
||||
// }
|
||||
let mut json = ~extra::treemap::TreeMap::new();
|
||||
json.insert(~"schema", extra::json::String(SCHEMA_VERSION.to_owned()));
|
||||
let plugins_json = ~res.move_iter().filter_map(|opt| opt).collect();
|
||||
|
||||
// FIXME #8335: yuck, Rust -> str -> JSON round trip! No way to .encode
|
||||
// straight to the Rust JSON representation.
|
||||
let crate_json_str = do std::io::with_str_writer |w| {
|
||||
crate.encode(&mut extra::json::Encoder(w));
|
||||
};
|
||||
let crate_json = match extra::json::from_str(crate_json_str) {
|
||||
Ok(j) => j,
|
||||
Err(_) => fail!("Rust generated JSON is invalid??")
|
||||
};
|
||||
|
||||
json.insert(~"crate", crate_json);
|
||||
json.insert(~"plugins", extra::json::Object(plugins_json));
|
||||
|
||||
let mut file = dst.open_writer(io::Create).unwrap();
|
||||
let output = extra::json::Object(json).to_str();
|
||||
file.write(output.as_bytes());
|
||||
}
|
||||
|
||||
fn exit(status: int) -> ! {
|
||||
#[fixed_stack_segment]; #[inline(never)];
|
||||
use std::libc;
|
||||
unsafe { libc::exit(status as libc::c_int) }
|
||||
}
|
||||
|
@ -1,257 +0,0 @@
|
||||
// Copyright 2012 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 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
//! Breaks rustdocs into sections according to their headers
|
||||
|
||||
|
||||
use astsrv;
|
||||
use doc::ItemUtils;
|
||||
use doc;
|
||||
use fold::Fold;
|
||||
use fold;
|
||||
use pass::Pass;
|
||||
|
||||
pub fn mk_pass() -> Pass {
|
||||
Pass {
|
||||
name: ~"sectionalize",
|
||||
f: run
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run(_srv: astsrv::Srv, doc: doc::Doc) -> doc::Doc {
|
||||
let fold = Fold {
|
||||
fold_item: fold_item,
|
||||
fold_trait: fold_trait,
|
||||
fold_impl: fold_impl,
|
||||
.. fold::default_any_fold(())
|
||||
};
|
||||
(fold.fold_doc)(&fold, doc)
|
||||
}
|
||||
|
||||
fn fold_item(fold: &fold::Fold<()>, doc: doc::ItemDoc) -> doc::ItemDoc {
|
||||
let doc = fold::default_seq_fold_item(fold, doc);
|
||||
let (desc, sections) = sectionalize(doc.desc.clone());
|
||||
|
||||
doc::ItemDoc {
|
||||
desc: desc,
|
||||
sections: sections,
|
||||
.. doc
|
||||
}
|
||||
}
|
||||
|
||||
fn fold_trait(fold: &fold::Fold<()>, doc: doc::TraitDoc) -> doc::TraitDoc {
|
||||
let doc = fold::default_seq_fold_trait(fold, doc);
|
||||
|
||||
doc::TraitDoc {
|
||||
methods: do doc.methods.map |method| {
|
||||
let (desc, sections) = sectionalize(method.desc.clone());
|
||||
|
||||
doc::MethodDoc {
|
||||
desc: desc,
|
||||
sections: sections,
|
||||
.. (*method).clone()
|
||||
}
|
||||
},
|
||||
.. doc
|
||||
}
|
||||
}
|
||||
|
||||
fn fold_impl(fold: &fold::Fold<()>, doc: doc::ImplDoc) -> doc::ImplDoc {
|
||||
let doc = fold::default_seq_fold_impl(fold, doc);
|
||||
|
||||
doc::ImplDoc {
|
||||
methods: do doc.methods.map |method| {
|
||||
let (desc, sections) = sectionalize(method.desc.clone());
|
||||
|
||||
doc::MethodDoc {
|
||||
desc: desc,
|
||||
sections: sections,
|
||||
.. (*method).clone()
|
||||
}
|
||||
},
|
||||
.. doc
|
||||
}
|
||||
}
|
||||
|
||||
fn sectionalize(desc: Option<~str>) -> (Option<~str>, ~[doc::Section]) {
|
||||
|
||||
/*!
|
||||
* Take a description of the form
|
||||
*
|
||||
* General text
|
||||
*
|
||||
* # Section header
|
||||
*
|
||||
* Section text
|
||||
*
|
||||
* # Section header
|
||||
*
|
||||
* Section text
|
||||
*
|
||||
* and remove each header and accompanying text into section records.
|
||||
*/
|
||||
|
||||
if desc.is_none() {
|
||||
return (None, ~[]);
|
||||
}
|
||||
|
||||
let mut new_desc = None::<~str>;
|
||||
let mut current_section: Option<doc::Section> = None;
|
||||
let mut sections = ~[];
|
||||
|
||||
for line in desc.get_ref().any_line_iter() {
|
||||
match parse_header(line) {
|
||||
Some(header) => {
|
||||
if current_section.is_some() {
|
||||
sections.push((*current_section.get_ref()).clone());
|
||||
}
|
||||
current_section = Some(doc::Section {
|
||||
header: header.to_owned(),
|
||||
body: ~""
|
||||
});
|
||||
}
|
||||
None => {
|
||||
match current_section.clone() {
|
||||
Some(section) => {
|
||||
current_section = Some(doc::Section {
|
||||
body: fmt!("%s\n%s", section.body, line),
|
||||
.. section
|
||||
});
|
||||
}
|
||||
None => {
|
||||
new_desc = match new_desc.clone() {
|
||||
Some(desc) => {
|
||||
Some(fmt!("%s\n%s", desc, line))
|
||||
}
|
||||
None => {
|
||||
Some(line.to_owned())
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if current_section.is_some() {
|
||||
sections.push(current_section.unwrap());
|
||||
}
|
||||
|
||||
(new_desc, sections)
|
||||
}
|
||||
|
||||
fn parse_header<'a>(line: &'a str) -> Option<&'a str> {
|
||||
if line.starts_with("# ") {
|
||||
Some(line.slice_from(2))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
|
||||
use astsrv;
|
||||
use attr_pass;
|
||||
use doc;
|
||||
use extract;
|
||||
use prune_hidden_pass;
|
||||
use sectionalize_pass::run;
|
||||
|
||||
fn mk_doc(source: ~str) -> doc::Doc {
|
||||
do astsrv::from_str(source.clone()) |srv| {
|
||||
let doc = extract::from_srv(srv.clone(), ~"");
|
||||
let doc = (attr_pass::mk_pass().f)(srv.clone(), doc);
|
||||
let doc = (prune_hidden_pass::mk_pass().f)(srv.clone(), doc);
|
||||
run(srv.clone(), doc)
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_create_section_headers() {
|
||||
let doc = mk_doc(
|
||||
~"#[doc = \"\
|
||||
# Header\n\
|
||||
Body\"]\
|
||||
mod a {
|
||||
}");
|
||||
assert!(doc.cratemod().mods()[0].item.sections[0].header.contains("Header"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_create_section_bodies() {
|
||||
let doc = mk_doc(
|
||||
~"#[doc = \"\
|
||||
# Header\n\
|
||||
Body\"]\
|
||||
mod a {
|
||||
}");
|
||||
assert!(doc.cratemod().mods()[0].item.sections[0].body.contains("Body"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_not_create_sections_from_indented_headers() {
|
||||
let doc = mk_doc(
|
||||
~"#[doc = \"\n\
|
||||
Text\n # Header\n\
|
||||
Body\"]\
|
||||
mod a {
|
||||
}");
|
||||
assert!(doc.cratemod().mods()[0].item.sections.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_remove_section_text_from_main_desc() {
|
||||
let doc = mk_doc(
|
||||
~"#[doc = \"\
|
||||
Description\n\n\
|
||||
# Header\n\
|
||||
Body\"]\
|
||||
mod a {
|
||||
}");
|
||||
assert!(!doc.cratemod().mods()[0].desc().unwrap().contains("Header"));
|
||||
assert!(!doc.cratemod().mods()[0].desc().unwrap().contains("Body"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_eliminate_desc_if_it_is_just_whitespace() {
|
||||
let doc = mk_doc(
|
||||
~"#[doc = \"\
|
||||
# Header\n\
|
||||
Body\"]\
|
||||
mod a {
|
||||
}");
|
||||
assert_eq!(doc.cratemod().mods()[0].desc(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_sectionalize_trait_methods() {
|
||||
let doc = mk_doc(
|
||||
~"trait i {
|
||||
#[doc = \"\
|
||||
# Header\n\
|
||||
Body\"]\
|
||||
fn a(); }");
|
||||
assert_eq!(doc.cratemod().traits()[0].methods[0].sections.len(), 1u);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_sectionalize_impl_methods() {
|
||||
let doc = mk_doc(
|
||||
~"impl bool {
|
||||
#[doc = \"\
|
||||
# Header\n\
|
||||
Body\"]\
|
||||
fn a() { } }");
|
||||
assert_eq!(doc.cratemod().impls()[0].methods[0].sections.len(), 1u);
|
||||
}
|
||||
}
|
@ -1,38 +0,0 @@
|
||||
// Copyright 2012 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 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
//! Sorts items by name
|
||||
|
||||
use doc::ItemUtils;
|
||||
use doc;
|
||||
use pass::Pass;
|
||||
use sort_pass;
|
||||
|
||||
pub fn mk_pass() -> Pass {
|
||||
fn by_item_name(item1: &doc::ItemTag, item2: &doc::ItemTag) -> bool {
|
||||
(*item1).name_() <= (*item2).name_()
|
||||
}
|
||||
sort_pass::mk_pass(~"sort_item_name", by_item_name)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test() {
|
||||
use astsrv;
|
||||
use extract;
|
||||
|
||||
let source = ~"mod z { } fn y() { }";
|
||||
do astsrv::from_str(source) |srv| {
|
||||
let doc = extract::from_srv(srv.clone(), ~"");
|
||||
let doc = (mk_pass().f)(srv.clone(), doc);
|
||||
// hidden __std_macros module at the start.
|
||||
assert_eq!(doc.cratemod().items[1].name_(), ~"y");
|
||||
assert_eq!(doc.cratemod().items[2].name_(), ~"z");
|
||||
}
|
||||
}
|
@ -1,67 +0,0 @@
|
||||
// Copyright 2012 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 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
//! Sorts items by type
|
||||
|
||||
use doc;
|
||||
use pass::Pass;
|
||||
use sort_pass;
|
||||
|
||||
pub fn mk_pass() -> Pass {
|
||||
fn by_score(item1: &doc::ItemTag, item2: &doc::ItemTag) -> bool {
|
||||
fn score(item: &doc::ItemTag) -> int {
|
||||
match *item {
|
||||
doc::StaticTag(_) => 0,
|
||||
doc::TyTag(_) => 1,
|
||||
doc::EnumTag(_) => 2,
|
||||
doc::StructTag(_) => 3,
|
||||
doc::TraitTag(_) => 4,
|
||||
doc::ImplTag(_) => 5,
|
||||
doc::FnTag(_) => 6,
|
||||
doc::ModTag(_) => 7,
|
||||
doc::NmodTag(_) => 8
|
||||
}
|
||||
}
|
||||
|
||||
score(item1) <= score(item2)
|
||||
}
|
||||
|
||||
sort_pass::mk_pass(~"sort_item_type", by_score)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test() {
|
||||
use astsrv;
|
||||
use extract;
|
||||
|
||||
let source =
|
||||
~"mod imod { } \
|
||||
static istatic: int = 0; \
|
||||
fn ifn() { } \
|
||||
enum ienum { ivar } \
|
||||
trait itrait { fn a(); } \
|
||||
impl int { fn a() { } } \
|
||||
type itype = int; \
|
||||
struct istruct { f: () }";
|
||||
do astsrv::from_str(source) |srv| {
|
||||
let doc = extract::from_srv(srv.clone(), ~"");
|
||||
let doc = (mk_pass().f)(srv.clone(), doc);
|
||||
// hidden __std_macros module at the start.
|
||||
assert_eq!(doc.cratemod().items[0].name_(), ~"istatic");
|
||||
assert_eq!(doc.cratemod().items[1].name_(), ~"itype");
|
||||
assert_eq!(doc.cratemod().items[2].name_(), ~"ienum");
|
||||
assert_eq!(doc.cratemod().items[3].name_(), ~"istruct");
|
||||
assert_eq!(doc.cratemod().items[4].name_(), ~"itrait");
|
||||
assert_eq!(doc.cratemod().items[5].name_(), ~"__extensions__");
|
||||
assert_eq!(doc.cratemod().items[6].name_(), ~"ifn");
|
||||
// hidden __std_macros module fits here.
|
||||
assert_eq!(doc.cratemod().items[8].name_(), ~"imod");
|
||||
}
|
||||
}
|
@ -1,103 +0,0 @@
|
||||
// Copyright 2012 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 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
//! A general sorting pass
|
||||
|
||||
use astsrv;
|
||||
use doc;
|
||||
use fold::Fold;
|
||||
use fold;
|
||||
use pass::Pass;
|
||||
|
||||
#[cfg(test)] use extract;
|
||||
|
||||
use extra::sort;
|
||||
use std::clone::Clone;
|
||||
|
||||
pub type ItemLtEqOp = @fn(v1: &doc::ItemTag, v2: &doc::ItemTag) -> bool;
|
||||
|
||||
struct ItemLtEq {
|
||||
op: ItemLtEqOp,
|
||||
}
|
||||
|
||||
impl Clone for ItemLtEq {
|
||||
fn clone(&self) -> ItemLtEq {
|
||||
ItemLtEq {
|
||||
op: self.op,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn mk_pass(name: ~str, lteq: ItemLtEqOp) -> Pass {
|
||||
Pass {
|
||||
name: name.clone(),
|
||||
f: |srv, doc| run(srv, doc, ItemLtEq { op: lteq })
|
||||
}
|
||||
}
|
||||
|
||||
fn run(
|
||||
_srv: astsrv::Srv,
|
||||
doc: doc::Doc,
|
||||
lteq: ItemLtEq
|
||||
) -> doc::Doc {
|
||||
let fold = Fold {
|
||||
fold_mod: fold_mod,
|
||||
.. fold::default_any_fold(lteq)
|
||||
};
|
||||
(fold.fold_doc)(&fold, doc)
|
||||
}
|
||||
|
||||
fn fold_mod(
|
||||
fold: &fold::Fold<ItemLtEq>,
|
||||
doc: doc::ModDoc
|
||||
) -> doc::ModDoc {
|
||||
let doc = fold::default_any_fold_mod(fold, doc);
|
||||
doc::ModDoc {
|
||||
items: sort::merge_sort(doc.items, fold.ctxt.op),
|
||||
.. doc
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test() {
|
||||
fn name_lteq(item1: &doc::ItemTag, item2: &doc::ItemTag) -> bool {
|
||||
(*item1).name_() <= (*item2).name_()
|
||||
}
|
||||
|
||||
let source = ~"mod z { mod y { } fn x() { } } mod w { }";
|
||||
do astsrv::from_str(source) |srv| {
|
||||
let doc = extract::from_srv(srv.clone(), ~"");
|
||||
let doc = (mk_pass(~"", name_lteq).f)(srv.clone(), doc);
|
||||
// hidden __std_macros module at the start.
|
||||
assert_eq!(doc.cratemod().mods()[1].name_(), ~"w");
|
||||
assert_eq!(doc.cratemod().mods()[2].items[0].name_(), ~"x");
|
||||
assert_eq!(doc.cratemod().mods()[2].items[1].name_(), ~"y");
|
||||
assert_eq!(doc.cratemod().mods()[2].name_(), ~"z");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_be_stable() {
|
||||
fn always_eq(_item1: &doc::ItemTag, _item2: &doc::ItemTag) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
let source = ~"mod a { mod b { } } mod c { mod d { } }";
|
||||
do astsrv::from_str(source) |srv| {
|
||||
let doc = extract::from_srv(srv.clone(), ~"");
|
||||
let doc = (mk_pass(~"", always_eq).f)(srv.clone(), doc);
|
||||
// hidden __std_macros module at the start.
|
||||
assert_eq!(doc.cratemod().mods()[1].items[0].name_(), ~"b");
|
||||
assert_eq!(doc.cratemod().mods()[2].items[0].name_(), ~"d");
|
||||
let doc = (mk_pass(~"", always_eq).f)(srv.clone(), doc);
|
||||
assert_eq!(doc.cratemod().mods()[1].items[0].name_(), ~"b");
|
||||
assert_eq!(doc.cratemod().mods()[2].items[0].name_(), ~"d");
|
||||
}
|
||||
}
|
@ -1,313 +0,0 @@
|
||||
// Copyright 2012 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 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
//! Generic pass for performing an operation on all descriptions
|
||||
|
||||
|
||||
use astsrv;
|
||||
use doc::ItemUtils;
|
||||
use doc;
|
||||
use fold::Fold;
|
||||
use fold;
|
||||
use pass::Pass;
|
||||
|
||||
use std::cell::Cell;
|
||||
|
||||
pub fn mk_pass(name: ~str, op: @fn(&str) -> ~str) -> Pass {
|
||||
let op = Cell::new(op);
|
||||
Pass {
|
||||
name: name.clone(),
|
||||
f: |srv: astsrv::Srv, doc: doc::Doc| -> doc::Doc {
|
||||
run(srv, doc, op.take())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type Op = @fn(&str) -> ~str;
|
||||
|
||||
struct WrappedOp {
|
||||
op: Op,
|
||||
}
|
||||
|
||||
impl Clone for WrappedOp {
|
||||
fn clone(&self) -> WrappedOp {
|
||||
WrappedOp {
|
||||
op: self.op,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn run(
|
||||
_srv: astsrv::Srv,
|
||||
doc: doc::Doc,
|
||||
op: Op
|
||||
) -> doc::Doc {
|
||||
let op = WrappedOp {
|
||||
op: op
|
||||
};
|
||||
let fold = Fold {
|
||||
fold_item: fold_item,
|
||||
fold_enum: fold_enum,
|
||||
fold_trait: fold_trait,
|
||||
fold_impl: fold_impl,
|
||||
.. fold::default_any_fold(op)
|
||||
};
|
||||
(fold.fold_doc)(&fold, doc)
|
||||
}
|
||||
|
||||
fn maybe_apply_op(op: WrappedOp, s: &Option<~str>) -> Option<~str> {
|
||||
s.map(|s| (op.op)(*s) )
|
||||
}
|
||||
|
||||
fn fold_item(fold: &fold::Fold<WrappedOp>, doc: doc::ItemDoc)
|
||||
-> doc::ItemDoc {
|
||||
let doc = fold::default_seq_fold_item(fold, doc);
|
||||
|
||||
doc::ItemDoc {
|
||||
brief: maybe_apply_op(fold.ctxt, &doc.brief),
|
||||
desc: maybe_apply_op(fold.ctxt, &doc.desc),
|
||||
sections: apply_to_sections(fold.ctxt, doc.sections.clone()),
|
||||
.. doc
|
||||
}
|
||||
}
|
||||
|
||||
fn apply_to_sections(op: WrappedOp, sections: ~[doc::Section])
|
||||
-> ~[doc::Section] {
|
||||
sections.map(|section| doc::Section {
|
||||
header: (op.op)(section.header.clone()),
|
||||
body: (op.op)(section.body.clone())
|
||||
})
|
||||
}
|
||||
|
||||
fn fold_enum(fold: &fold::Fold<WrappedOp>, doc: doc::EnumDoc)
|
||||
-> doc::EnumDoc {
|
||||
let doc = fold::default_seq_fold_enum(fold, doc);
|
||||
let fold_copy = *fold;
|
||||
|
||||
doc::EnumDoc {
|
||||
variants: do doc.variants.map |variant| {
|
||||
doc::VariantDoc {
|
||||
desc: maybe_apply_op(fold_copy.ctxt, &variant.desc),
|
||||
.. (*variant).clone()
|
||||
}
|
||||
},
|
||||
.. doc
|
||||
}
|
||||
}
|
||||
|
||||
fn fold_trait(fold: &fold::Fold<WrappedOp>, doc: doc::TraitDoc)
|
||||
-> doc::TraitDoc {
|
||||
let doc = fold::default_seq_fold_trait(fold, doc);
|
||||
|
||||
doc::TraitDoc {
|
||||
methods: apply_to_methods(fold.ctxt, doc.methods.clone()),
|
||||
.. doc
|
||||
}
|
||||
}
|
||||
|
||||
fn apply_to_methods(op: WrappedOp, docs: ~[doc::MethodDoc])
|
||||
-> ~[doc::MethodDoc] {
|
||||
do docs.map |doc| {
|
||||
doc::MethodDoc {
|
||||
brief: maybe_apply_op(op, &doc.brief),
|
||||
desc: maybe_apply_op(op, &doc.desc),
|
||||
sections: apply_to_sections(op, doc.sections.clone()),
|
||||
.. (*doc).clone()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn fold_impl(fold: &fold::Fold<WrappedOp>, doc: doc::ImplDoc)
|
||||
-> doc::ImplDoc {
|
||||
let doc = fold::default_seq_fold_impl(fold, doc);
|
||||
|
||||
doc::ImplDoc {
|
||||
methods: apply_to_methods(fold.ctxt, doc.methods.clone()),
|
||||
.. doc
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
|
||||
use astsrv;
|
||||
use attr_pass;
|
||||
use desc_to_brief_pass;
|
||||
use doc;
|
||||
use extract;
|
||||
use sectionalize_pass;
|
||||
use text_pass::mk_pass;
|
||||
|
||||
fn mk_doc(source: ~str) -> doc::Doc {
|
||||
do astsrv::from_str(source.clone()) |srv| {
|
||||
let doc = extract::from_srv(srv.clone(), ~"");
|
||||
let doc = (attr_pass::mk_pass().f)(srv.clone(), doc);
|
||||
let doc = (desc_to_brief_pass::mk_pass().f)(srv.clone(), doc);
|
||||
let doc = (sectionalize_pass::mk_pass().f)(srv.clone(), doc);
|
||||
(mk_pass(~"", |s| s.trim().to_owned() ).f)(srv.clone(), doc)
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_execute_op_on_enum_brief() {
|
||||
let doc = mk_doc(~"#[doc = \" a \"] enum a { b }");
|
||||
assert_eq!(doc.cratemod().enums()[0].brief(), Some(~"a"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_execute_op_on_enum_desc() {
|
||||
let doc = mk_doc(~"#[doc = \" a \"] enum a { b }");
|
||||
assert_eq!(doc.cratemod().enums()[0].desc(), Some(~"a"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_execute_op_on_variant_desc() {
|
||||
let doc = mk_doc(~"enum a { #[doc = \" a \"] b }");
|
||||
assert!(doc.cratemod().enums()[0].variants[0].desc == Some(~"a"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_execute_op_on_trait_brief() {
|
||||
let doc = mk_doc(
|
||||
~"#[doc = \" a \"] trait i { fn a(); }");
|
||||
assert_eq!(doc.cratemod().traits()[0].brief(), Some(~"a"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_execute_op_on_trait_desc() {
|
||||
let doc = mk_doc(
|
||||
~"#[doc = \" a \"] trait i { fn a(); }");
|
||||
assert_eq!(doc.cratemod().traits()[0].desc(), Some(~"a"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_execute_op_on_trait_method_brief() {
|
||||
let doc = mk_doc(
|
||||
~"trait i { #[doc = \" a \"] fn a(); }");
|
||||
assert!(doc.cratemod().traits()[0].methods[0].brief == Some(~"a"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_execute_op_on_trait_method_desc() {
|
||||
let doc = mk_doc(
|
||||
~"trait i { #[doc = \" a \"] fn a(); }");
|
||||
assert!(doc.cratemod().traits()[0].methods[0].desc == Some(~"a"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_execute_op_on_impl_brief() {
|
||||
let doc = mk_doc(
|
||||
~"#[doc = \" a \"] impl int { fn a() { } }");
|
||||
assert_eq!(doc.cratemod().impls()[0].brief(), Some(~"a"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_execute_op_on_impl_desc() {
|
||||
let doc = mk_doc(
|
||||
~"#[doc = \" a \"] impl int { fn a() { } }");
|
||||
assert_eq!(doc.cratemod().impls()[0].desc(), Some(~"a"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_execute_op_on_impl_method_brief() {
|
||||
let doc = mk_doc(
|
||||
~"impl int { #[doc = \" a \"] fn a() { } }");
|
||||
assert!(doc.cratemod().impls()[0].methods[0].brief == Some(~"a"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_execute_op_on_impl_method_desc() {
|
||||
let doc = mk_doc(
|
||||
~"impl int { #[doc = \" a \"] fn a() { } }");
|
||||
assert!(doc.cratemod().impls()[0].methods[0].desc == Some(~"a"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_execute_op_on_type_brief() {
|
||||
let doc = mk_doc(
|
||||
~"#[doc = \" a \"] type t = int;");
|
||||
assert_eq!(doc.cratemod().types()[0].brief(), Some(~"a"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_execute_op_on_type_desc() {
|
||||
let doc = mk_doc(
|
||||
~"#[doc = \" a \"] type t = int;");
|
||||
assert_eq!(doc.cratemod().types()[0].desc(), Some(~"a"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_execute_on_item_section_headers() {
|
||||
let doc = mk_doc(
|
||||
~"#[doc = \"\
|
||||
# Header \n\
|
||||
Body\"]\
|
||||
fn a() { }");
|
||||
assert!(doc.cratemod().fns()[0].sections()[0].header == ~"Header");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_execute_on_item_section_bodies() {
|
||||
let doc = mk_doc(
|
||||
~"#[doc = \"\
|
||||
# Header\n\
|
||||
Body \"]\
|
||||
fn a() { }");
|
||||
assert!(doc.cratemod().fns()[0].sections()[0].body == ~"Body");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_execute_on_trait_method_section_headers() {
|
||||
let doc = mk_doc(
|
||||
~"trait i {
|
||||
#[doc = \"\
|
||||
# Header \n\
|
||||
Body\"]\
|
||||
fn a(); }");
|
||||
assert!(doc.cratemod().traits()[0].methods[0].sections[0].header
|
||||
== ~"Header");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_execute_on_trait_method_section_bodies() {
|
||||
let doc = mk_doc(
|
||||
~"trait i {
|
||||
#[doc = \"\
|
||||
# Header\n\
|
||||
Body \"]\
|
||||
fn a(); }");
|
||||
assert!(doc.cratemod().traits()[0].methods[0].sections[0].body ==
|
||||
~"Body");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_execute_on_impl_method_section_headers() {
|
||||
let doc = mk_doc(
|
||||
~"impl bool {
|
||||
#[doc = \"\
|
||||
# Header \n\
|
||||
Body\"]\
|
||||
fn a() { } }");
|
||||
assert!(doc.cratemod().impls()[0].methods[0].sections[0].header
|
||||
== ~"Header");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_execute_on_impl_method_section_bodies() {
|
||||
let doc = mk_doc(
|
||||
~"impl bool {
|
||||
#[doc = \"\
|
||||
# Header\n\
|
||||
Body \"]\
|
||||
fn a() { } }");
|
||||
assert!(doc.cratemod().impls()[0].methods[0].sections[0].body ==
|
||||
~"Body");
|
||||
}
|
||||
}
|
@ -1,52 +0,0 @@
|
||||
// Copyright 2012 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 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
/*!
|
||||
Pulls a brief description out of a long description.
|
||||
|
||||
If the first paragraph of a long description is short enough then it
|
||||
is interpreted as the brief description.
|
||||
*/
|
||||
|
||||
use pass::Pass;
|
||||
use text_pass;
|
||||
|
||||
pub fn mk_pass() -> Pass {
|
||||
text_pass::mk_pass(~"trim", |s| s.trim().to_owned() )
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use astsrv;
|
||||
use attr_pass;
|
||||
use doc;
|
||||
use extract;
|
||||
use prune_hidden_pass;
|
||||
use trim_pass::mk_pass;
|
||||
|
||||
fn mk_doc(source: ~str) -> doc::Doc {
|
||||
do astsrv::from_str(source.clone()) |srv| {
|
||||
let doc = extract::from_srv(srv.clone(), ~"");
|
||||
let doc = (attr_pass::mk_pass().f)(srv.clone(), doc);
|
||||
let doc = (prune_hidden_pass::mk_pass().f)(srv.clone(), doc);
|
||||
(mk_pass().f)(srv.clone(), doc)
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_trim_text() {
|
||||
use std::option::Some;
|
||||
|
||||
let doc = mk_doc(~"#[doc = \" desc \"] \
|
||||
mod m {
|
||||
}");
|
||||
assert_eq!(doc.cratemod().mods()[0].desc(), Some(~"desc"));
|
||||
}
|
||||
}
|
@ -1,456 +0,0 @@
|
||||
// Copyright 2012 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 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
//! Pulls type information out of the AST and attaches it to the document
|
||||
|
||||
|
||||
use astsrv;
|
||||
use doc::ItemUtils;
|
||||
use doc;
|
||||
use extract::to_str;
|
||||
use extract;
|
||||
use fold::Fold;
|
||||
use fold;
|
||||
use pass::Pass;
|
||||
|
||||
use syntax::ast;
|
||||
use syntax::print::pprust;
|
||||
use syntax::parse::token;
|
||||
use syntax::ast_map;
|
||||
|
||||
pub fn mk_pass() -> Pass {
|
||||
Pass {
|
||||
name: ~"tystr",
|
||||
f: run
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run(
|
||||
srv: astsrv::Srv,
|
||||
doc: doc::Doc
|
||||
) -> doc::Doc {
|
||||
let fold = Fold {
|
||||
ctxt: srv.clone(),
|
||||
fold_fn: fold_fn,
|
||||
fold_static: fold_static,
|
||||
fold_enum: fold_enum,
|
||||
fold_trait: fold_trait,
|
||||
fold_impl: fold_impl,
|
||||
fold_type: fold_type,
|
||||
fold_struct: fold_struct,
|
||||
.. fold::default_any_fold(srv)
|
||||
};
|
||||
(fold.fold_doc)(&fold, doc)
|
||||
}
|
||||
|
||||
fn fold_fn(
|
||||
fold: &fold::Fold<astsrv::Srv>,
|
||||
doc: doc::FnDoc
|
||||
) -> doc::FnDoc {
|
||||
|
||||
let srv = fold.ctxt.clone();
|
||||
|
||||
doc::SimpleItemDoc {
|
||||
sig: get_fn_sig(srv, doc.id()),
|
||||
.. doc
|
||||
}
|
||||
}
|
||||
|
||||
fn get_fn_sig(srv: astsrv::Srv, fn_id: doc::AstId) -> Option<~str> {
|
||||
do astsrv::exec(srv) |ctxt| {
|
||||
match ctxt.ast_map.get_copy(&fn_id) {
|
||||
ast_map::node_item(@ast::item {
|
||||
ident: ident,
|
||||
node: ast::item_fn(ref decl, purity, _, ref tys, _), _
|
||||
}, _) => {
|
||||
Some(pprust::fun_to_str(decl,
|
||||
purity,
|
||||
ident,
|
||||
None,
|
||||
tys,
|
||||
token::get_ident_interner()))
|
||||
}
|
||||
ast_map::node_foreign_item(@ast::foreign_item {
|
||||
ident: ident,
|
||||
node: ast::foreign_item_fn(ref decl, ref tys), _
|
||||
}, _, _, _) => {
|
||||
Some(pprust::fun_to_str(decl,
|
||||
ast::impure_fn,
|
||||
ident,
|
||||
None,
|
||||
tys,
|
||||
token::get_ident_interner()))
|
||||
}
|
||||
_ => fail!("get_fn_sig: fn_id not bound to a fn item")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn fold_static(
|
||||
fold: &fold::Fold<astsrv::Srv>,
|
||||
doc: doc::StaticDoc
|
||||
) -> doc::StaticDoc {
|
||||
let srv = fold.ctxt.clone();
|
||||
|
||||
doc::SimpleItemDoc {
|
||||
sig: Some({
|
||||
let doc = doc.clone();
|
||||
do astsrv::exec(srv) |ctxt| {
|
||||
match ctxt.ast_map.get_copy(&doc.id()) {
|
||||
ast_map::node_item(@ast::item {
|
||||
node: ast::item_static(ref ty, _, _), _
|
||||
}, _) => {
|
||||
pprust::ty_to_str(ty, extract::interner())
|
||||
}
|
||||
_ => fail!("fold_static: id not bound to a static item")
|
||||
}
|
||||
}}),
|
||||
.. doc
|
||||
}
|
||||
}
|
||||
|
||||
fn fold_enum(
|
||||
fold: &fold::Fold<astsrv::Srv>,
|
||||
doc: doc::EnumDoc
|
||||
) -> doc::EnumDoc {
|
||||
let doc_id = doc.id();
|
||||
let srv = fold.ctxt.clone();
|
||||
|
||||
doc::EnumDoc {
|
||||
variants: do doc.variants.iter().map |variant| {
|
||||
let sig = {
|
||||
let variant = (*variant).clone();
|
||||
do astsrv::exec(srv.clone()) |ctxt| {
|
||||
match ctxt.ast_map.get_copy(&doc_id) {
|
||||
ast_map::node_item(@ast::item {
|
||||
node: ast::item_enum(ref enum_definition, _), _
|
||||
}, _) => {
|
||||
let ast_variant =
|
||||
(*do enum_definition.variants.iter().find |v| {
|
||||
to_str(v.node.name) == variant.name
|
||||
}.unwrap()).clone();
|
||||
|
||||
pprust::variant_to_str(
|
||||
&ast_variant, extract::interner())
|
||||
}
|
||||
_ => fail!("enum variant not bound to an enum item")
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
doc::VariantDoc {
|
||||
sig: Some(sig),
|
||||
.. (*variant).clone()
|
||||
}
|
||||
}.collect(),
|
||||
.. doc
|
||||
}
|
||||
}
|
||||
|
||||
fn fold_trait(
|
||||
fold: &fold::Fold<astsrv::Srv>,
|
||||
doc: doc::TraitDoc
|
||||
) -> doc::TraitDoc {
|
||||
doc::TraitDoc {
|
||||
methods: merge_methods(fold.ctxt.clone(), doc.id(), doc.methods.clone()),
|
||||
.. doc
|
||||
}
|
||||
}
|
||||
|
||||
fn merge_methods(
|
||||
srv: astsrv::Srv,
|
||||
item_id: doc::AstId,
|
||||
docs: ~[doc::MethodDoc]
|
||||
) -> ~[doc::MethodDoc] {
|
||||
do docs.iter().map |doc| {
|
||||
doc::MethodDoc {
|
||||
sig: get_method_sig(srv.clone(), item_id, doc.name.clone()),
|
||||
.. (*doc).clone()
|
||||
}
|
||||
}.collect()
|
||||
}
|
||||
|
||||
fn get_method_sig(
|
||||
srv: astsrv::Srv,
|
||||
item_id: doc::AstId,
|
||||
method_name: ~str
|
||||
) -> Option<~str> {
|
||||
do astsrv::exec(srv) |ctxt| {
|
||||
match ctxt.ast_map.get_copy(&item_id) {
|
||||
ast_map::node_item(@ast::item {
|
||||
node: ast::item_trait(_, _, ref methods), _
|
||||
}, _) => {
|
||||
match methods.iter().find(|&method| {
|
||||
match (*method).clone() {
|
||||
ast::required(ty_m) => to_str(ty_m.ident) == method_name,
|
||||
ast::provided(m) => to_str(m.ident) == method_name,
|
||||
}
|
||||
}) {
|
||||
Some(method) => {
|
||||
match (*method).clone() {
|
||||
ast::required(ty_m) => {
|
||||
Some(pprust::fun_to_str(
|
||||
&ty_m.decl,
|
||||
ty_m.purity,
|
||||
ty_m.ident,
|
||||
Some(ty_m.explicit_self.node),
|
||||
&ty_m.generics,
|
||||
extract::interner()
|
||||
))
|
||||
}
|
||||
ast::provided(m) => {
|
||||
Some(pprust::fun_to_str(
|
||||
&m.decl,
|
||||
m.purity,
|
||||
m.ident,
|
||||
Some(m.explicit_self.node),
|
||||
&m.generics,
|
||||
extract::interner()
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => fail!("method not found")
|
||||
}
|
||||
}
|
||||
ast_map::node_item(@ast::item {
|
||||
node: ast::item_impl(_, _, _, ref methods), _
|
||||
}, _) => {
|
||||
match methods.iter().find(|method| {
|
||||
to_str(method.ident) == method_name
|
||||
}) {
|
||||
Some(method) => {
|
||||
Some(pprust::fun_to_str(
|
||||
&method.decl,
|
||||
method.purity,
|
||||
method.ident,
|
||||
Some(method.explicit_self.node),
|
||||
&method.generics,
|
||||
extract::interner()
|
||||
))
|
||||
}
|
||||
None => fail!("method not found")
|
||||
}
|
||||
}
|
||||
_ => fail!("get_method_sig: item ID not bound to trait or impl")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn fold_impl(
|
||||
fold: &fold::Fold<astsrv::Srv>,
|
||||
doc: doc::ImplDoc
|
||||
) -> doc::ImplDoc {
|
||||
|
||||
let srv = fold.ctxt.clone();
|
||||
|
||||
let (bounds, trait_types, self_ty) = {
|
||||
let doc = doc.clone();
|
||||
do astsrv::exec(srv) |ctxt| {
|
||||
match ctxt.ast_map.get_copy(&doc.id()) {
|
||||
ast_map::node_item(@ast::item {
|
||||
node: ast::item_impl(ref generics, ref opt_trait_type, ref self_ty, _), _
|
||||
}, _) => {
|
||||
let bounds = pprust::generics_to_str(generics, extract::interner());
|
||||
let bounds = if bounds.is_empty() { None } else { Some(bounds) };
|
||||
let trait_types = do opt_trait_type.map_default(~[]) |p| {
|
||||
~[pprust::path_to_str(&p.path, extract::interner())]
|
||||
};
|
||||
(bounds,
|
||||
trait_types,
|
||||
Some(pprust::ty_to_str(
|
||||
self_ty, extract::interner())))
|
||||
}
|
||||
_ => fail!("expected impl")
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
doc::ImplDoc {
|
||||
bounds_str: bounds,
|
||||
trait_types: trait_types,
|
||||
self_ty: self_ty,
|
||||
methods: merge_methods(fold.ctxt.clone(), doc.id(), doc.methods.clone()),
|
||||
.. doc
|
||||
}
|
||||
}
|
||||
|
||||
fn fold_type(
|
||||
fold: &fold::Fold<astsrv::Srv>,
|
||||
doc: doc::TyDoc
|
||||
) -> doc::TyDoc {
|
||||
|
||||
let srv = fold.ctxt.clone();
|
||||
|
||||
doc::SimpleItemDoc {
|
||||
sig: {
|
||||
let doc = doc.clone();
|
||||
do astsrv::exec(srv) |ctxt| {
|
||||
match ctxt.ast_map.get_copy(&doc.id()) {
|
||||
ast_map::node_item(@ast::item {
|
||||
ident: ident,
|
||||
node: ast::item_ty(ref ty, ref params), _
|
||||
}, _) => {
|
||||
Some(fmt!(
|
||||
"type %s%s = %s",
|
||||
to_str(ident),
|
||||
pprust::generics_to_str(params,
|
||||
extract::interner()),
|
||||
pprust::ty_to_str(ty, extract::interner())
|
||||
))
|
||||
}
|
||||
_ => fail!("expected type")
|
||||
}
|
||||
}
|
||||
},
|
||||
.. doc
|
||||
}
|
||||
}
|
||||
|
||||
fn fold_struct(
|
||||
fold: &fold::Fold<astsrv::Srv>,
|
||||
doc: doc::StructDoc
|
||||
) -> doc::StructDoc {
|
||||
let srv = fold.ctxt.clone();
|
||||
|
||||
doc::StructDoc {
|
||||
sig: {
|
||||
let doc = doc.clone();
|
||||
do astsrv::exec(srv) |ctxt| {
|
||||
match ctxt.ast_map.get_copy(&doc.id()) {
|
||||
ast_map::node_item(item, _) => {
|
||||
let item = strip_struct_extra_stuff(item);
|
||||
Some(pprust::item_to_str(item,
|
||||
extract::interner()))
|
||||
}
|
||||
_ => fail!("not an item")
|
||||
}
|
||||
}
|
||||
},
|
||||
.. doc
|
||||
}
|
||||
}
|
||||
|
||||
/// Removes various things from the struct item definition that
|
||||
/// shouldn't be displayed in the struct signature. Probably there
|
||||
/// should be a simple pprust::struct_to_str function that does
|
||||
/// what I actually want
|
||||
fn strip_struct_extra_stuff(item: @ast::item) -> @ast::item {
|
||||
let node = match item.node.clone() {
|
||||
ast::item_struct(def, tys) => ast::item_struct(def, tys),
|
||||
_ => fail!("not a struct")
|
||||
};
|
||||
|
||||
@ast::item {
|
||||
attrs: ~[], // Remove the attributes
|
||||
node: node,
|
||||
.. (*item).clone()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
|
||||
use astsrv;
|
||||
use doc;
|
||||
use extract;
|
||||
use tystr_pass::run;
|
||||
|
||||
fn mk_doc(source: ~str) -> doc::Doc {
|
||||
do astsrv::from_str(source.clone()) |srv| {
|
||||
let doc = extract::from_srv(srv.clone(), ~"");
|
||||
run(srv.clone(), doc)
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_add_fn_sig() {
|
||||
let doc = mk_doc(~"fn a<T>() -> int { }");
|
||||
assert!(doc.cratemod().fns()[0].sig == Some(~"fn a<T>() -> int"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_add_foreign_fn_sig() {
|
||||
let doc = mk_doc(~"extern { fn a<T>() -> int; }");
|
||||
assert!(doc.cratemod().nmods()[0].fns[0].sig ==
|
||||
Some(~"fn a<T>() -> int"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_add_static_types() {
|
||||
let doc = mk_doc(~"static a: bool = true;");
|
||||
assert!(doc.cratemod().statics()[0].sig == Some(~"bool"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_add_variant_sigs() {
|
||||
let doc = mk_doc(~"enum a { b(int) }");
|
||||
assert!(doc.cratemod().enums()[0].variants[0].sig ==
|
||||
Some(~"b(int)"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_add_trait_method_sigs() {
|
||||
let doc = mk_doc(~"trait i { fn a<T>(&mut self) -> int; }");
|
||||
assert!(doc.cratemod().traits()[0].methods[0].sig
|
||||
== Some(~"fn a<T>(&mut self) -> int"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_add_impl_bounds() {
|
||||
let doc = mk_doc(~"impl<T, U, V: Clone> Option<T, U, V> { }");
|
||||
assert!(doc.cratemod().impls()[0].bounds_str == Some(~"<T, U, V: Clone>"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_add_impl_trait_types() {
|
||||
let doc = mk_doc(~"impl j for int { fn a<T>() { } }");
|
||||
assert!(doc.cratemod().impls()[0].trait_types[0] == ~"j");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_not_add_impl_trait_types_if_none() {
|
||||
let doc = mk_doc(~"impl int { fn a() { } }");
|
||||
assert!(doc.cratemod().impls()[0].trait_types.len() == 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_add_impl_self_ty() {
|
||||
let doc = mk_doc(~"impl int { fn a() { } }");
|
||||
assert!(doc.cratemod().impls()[0].self_ty == Some(~"int"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_add_impl_method_sigs() {
|
||||
let doc = mk_doc(~"impl int { fn a<T>(&self) -> int { fail!() } }");
|
||||
assert!(doc.cratemod().impls()[0].methods[0].sig
|
||||
== Some(~"fn a<T>(&self) -> int"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_add_type_signatures() {
|
||||
let doc = mk_doc(~"type t<T> = int;");
|
||||
assert!(doc.cratemod().types()[0].sig == Some(~"type t<T> = int"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_add_struct_defs() {
|
||||
let doc = mk_doc(~"struct S { field: () }");
|
||||
assert!(doc.cratemod().structs()[0].sig.unwrap().contains(
|
||||
"struct S {"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_not_serialize_struct_attrs() {
|
||||
// All we care about are the fields
|
||||
let doc = mk_doc(~"#[wut] struct S { field: () }");
|
||||
assert!(!doc.cratemod().structs()[0].sig.unwrap().contains("wut"));
|
||||
}
|
||||
}
|
@ -1,134 +0,0 @@
|
||||
// Copyright 2012-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 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
/*!
|
||||
Removes the common level of indention from description strings. For
|
||||
instance, if an entire doc comment is indented 8 spaces we want to
|
||||
remove those 8 spaces from every line.
|
||||
|
||||
The first line of a string is allowed to be intend less than
|
||||
subsequent lines in the same paragraph in order to account for
|
||||
instances where the string containing the doc comment is opened in the
|
||||
middle of a line, and each of the following lines is indented.
|
||||
*/
|
||||
|
||||
|
||||
use std::num;
|
||||
use std::uint;
|
||||
use pass::Pass;
|
||||
use text_pass;
|
||||
|
||||
pub fn mk_pass() -> Pass {
|
||||
text_pass::mk_pass(~"unindent", unindent)
|
||||
}
|
||||
|
||||
fn unindent(s: &str) -> ~str {
|
||||
let lines = s.any_line_iter().collect::<~[&str]>();
|
||||
let mut saw_first_line = false;
|
||||
let mut saw_second_line = false;
|
||||
let min_indent = do lines.iter().fold(uint::max_value)
|
||||
|min_indent, line| {
|
||||
|
||||
// After we see the first non-whitespace line, look at
|
||||
// the line we have. If it is not whitespace, and therefore
|
||||
// part of the first paragraph, then ignore the indentation
|
||||
// level of the first line
|
||||
let ignore_previous_indents =
|
||||
saw_first_line &&
|
||||
!saw_second_line &&
|
||||
!line.is_whitespace();
|
||||
|
||||
let min_indent = if ignore_previous_indents {
|
||||
uint::max_value
|
||||
} else {
|
||||
min_indent
|
||||
};
|
||||
|
||||
if saw_first_line {
|
||||
saw_second_line = true;
|
||||
}
|
||||
|
||||
if line.is_whitespace() {
|
||||
min_indent
|
||||
} else {
|
||||
saw_first_line = true;
|
||||
let mut spaces = 0;
|
||||
do line.iter().all |char| {
|
||||
// Only comparing against space because I wouldn't
|
||||
// know what to do with mixed whitespace chars
|
||||
if char == ' ' {
|
||||
spaces += 1;
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
};
|
||||
num::min(min_indent, spaces)
|
||||
}
|
||||
};
|
||||
|
||||
match lines {
|
||||
[head, .. tail] => {
|
||||
let mut unindented = ~[ head.trim() ];
|
||||
unindented.push_all(do tail.map |&line| {
|
||||
if line.is_whitespace() {
|
||||
line
|
||||
} else {
|
||||
assert!(line.len() >= min_indent);
|
||||
line.slice_from(min_indent)
|
||||
}
|
||||
});
|
||||
unindented.connect("\n")
|
||||
}
|
||||
[] => s.to_owned()
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_unindent() {
|
||||
let s = ~" line1\n line2";
|
||||
let r = unindent(s);
|
||||
assert_eq!(r, ~"line1\nline2");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_unindent_multiple_paragraphs() {
|
||||
let s = ~" line1\n\n line2";
|
||||
let r = unindent(s);
|
||||
assert_eq!(r, ~"line1\n\nline2");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_leave_multiple_indent_levels() {
|
||||
// Line 2 is indented another level beyond the
|
||||
// base indentation and should be preserved
|
||||
let s = ~" line1\n\n line2";
|
||||
let r = unindent(s);
|
||||
assert_eq!(r, ~"line1\n\n line2");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_ignore_first_line_indent() {
|
||||
// Thi first line of the first paragraph may not be indented as
|
||||
// far due to the way the doc string was written:
|
||||
//
|
||||
// #[doc = "Start way over here
|
||||
// and continue here"]
|
||||
let s = ~"line1\n line2";
|
||||
let r = unindent(s);
|
||||
assert_eq!(r, ~"line1\nline2");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_not_ignore_first_line_indent_in_a_single_line_para() {
|
||||
let s = ~"line1\n\n line2";
|
||||
let r = unindent(s);
|
||||
assert_eq!(r, ~"line1\n\n line2");
|
||||
}
|
2
src/rustdoc_ng/.gitignore
vendored
2
src/rustdoc_ng/.gitignore
vendored
@ -1,2 +0,0 @@
|
||||
*.swp
|
||||
main
|
@ -1,99 +0,0 @@
|
||||
// Copyright 2012-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 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use std;
|
||||
use clean::*;
|
||||
use std::iter::Extendable;
|
||||
|
||||
pub trait DocFolder {
|
||||
fn fold_item(&mut self, item: Item) -> Option<Item> {
|
||||
self.fold_item_recur(item)
|
||||
}
|
||||
|
||||
/// don't override!
|
||||
fn fold_item_recur(&mut self, item: Item) -> Option<Item> {
|
||||
use std::util::swap;
|
||||
let Item { attrs, name, source, visibility, id, inner } = item;
|
||||
let inner = inner;
|
||||
let c = |x| self.fold_item(x);
|
||||
let inner = match inner {
|
||||
StructItem(i) => {
|
||||
let mut i = i;
|
||||
let mut foo = ~[]; swap(&mut foo, &mut i.fields);
|
||||
i.fields.extend(&mut foo.move_iter().filter_map(|x| self.fold_item(x)));
|
||||
StructItem(i)
|
||||
},
|
||||
ModuleItem(i) => {
|
||||
ModuleItem(self.fold_mod(i))
|
||||
},
|
||||
EnumItem(i) => {
|
||||
let mut i = i;
|
||||
let mut foo = ~[]; swap(&mut foo, &mut i.variants);
|
||||
i.variants.extend(&mut foo.move_iter().filter_map(|x| self.fold_item(x)));
|
||||
EnumItem(i)
|
||||
},
|
||||
TraitItem(i) => {
|
||||
fn vtrm<T: DocFolder>(this: &mut T, trm: TraitMethod) -> Option<TraitMethod> {
|
||||
match trm {
|
||||
Required(it) => {
|
||||
match this.fold_item(it) {
|
||||
Some(x) => return Some(Required(x)),
|
||||
None => return None,
|
||||
}
|
||||
},
|
||||
Provided(it) => {
|
||||
match this.fold_item(it) {
|
||||
Some(x) => return Some(Provided(x)),
|
||||
None => return None,
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
let mut i = i;
|
||||
let mut foo = ~[]; swap(&mut foo, &mut i.methods);
|
||||
i.methods.extend(&mut foo.move_iter().filter_map(|x| vtrm(self, x)));
|
||||
TraitItem(i)
|
||||
},
|
||||
ImplItem(i) => {
|
||||
let mut i = i;
|
||||
let mut foo = ~[]; swap(&mut foo, &mut i.methods);
|
||||
i.methods.extend(&mut foo.move_iter().filter_map(|x| self.fold_item(x)));
|
||||
ImplItem(i)
|
||||
},
|
||||
VariantItem(i) => {
|
||||
let i2 = i.clone(); // this clone is small
|
||||
match i.kind {
|
||||
StructVariant(j) => {
|
||||
let mut j = j;
|
||||
let mut foo = ~[]; swap(&mut foo, &mut j.fields);
|
||||
j.fields.extend(&mut foo.move_iter().filter_map(c));
|
||||
VariantItem(Variant {kind: StructVariant(j), ..i2})
|
||||
},
|
||||
_ => VariantItem(i2)
|
||||
}
|
||||
},
|
||||
x => x
|
||||
};
|
||||
|
||||
Some(Item { attrs: attrs, name: name, source: source, inner: inner,
|
||||
visibility: visibility, id: id })
|
||||
}
|
||||
|
||||
fn fold_mod(&mut self, m: Module) -> Module {
|
||||
Module { items: m.items.move_iter().filter_map(|i| self.fold_item(i)).collect() }
|
||||
}
|
||||
|
||||
fn fold_crate(&mut self, mut c: Crate) -> Crate {
|
||||
c.module = match std::util::replace(&mut c.module, None) {
|
||||
Some(module) => self.fold_item(module), None => None
|
||||
};
|
||||
return c;
|
||||
}
|
||||
}
|
@ -1,211 +0,0 @@
|
||||
// Copyright 2012-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 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
#[link(name = "rustdoc_ng",
|
||||
vers = "0.8",
|
||||
uuid = "8c6e4598-1596-4aa5-a24c-b811914bbbc6",
|
||||
url = "https://github.com/mozilla/rust/tree/master/src/rustdoc_ng")];
|
||||
|
||||
#[desc = "rustdoc, the Rust documentation extractor"];
|
||||
#[license = "MIT/ASL2"];
|
||||
#[crate_type = "lib"];
|
||||
|
||||
extern mod syntax;
|
||||
extern mod rustc;
|
||||
extern mod extra;
|
||||
|
||||
use extra::serialize::Encodable;
|
||||
use extra::time;
|
||||
use std::cell::Cell;
|
||||
use std::rt::io;
|
||||
use std::rt::io::Writer;
|
||||
use std::rt::io::file::FileInfo;
|
||||
|
||||
pub mod clean;
|
||||
pub mod core;
|
||||
pub mod doctree;
|
||||
pub mod fold;
|
||||
pub mod html {
|
||||
pub mod render;
|
||||
pub mod layout;
|
||||
pub mod markdown;
|
||||
pub mod format;
|
||||
}
|
||||
pub mod passes;
|
||||
pub mod plugins;
|
||||
pub mod visit_ast;
|
||||
|
||||
pub static SCHEMA_VERSION: &'static str = "0.8.0";
|
||||
|
||||
local_data_key!(pub ctxtkey: @core::DocContext)
|
||||
|
||||
enum OutputFormat {
|
||||
HTML, JSON
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
main_args(std::os::args());
|
||||
}
|
||||
|
||||
pub fn main_args(args: &[~str]) {
|
||||
use extra::getopts::groups::*;
|
||||
|
||||
let opts = ~[
|
||||
optmulti("L", "library-path", "directory to add to crate search path",
|
||||
"DIR"),
|
||||
optmulti("", "plugin-path", "directory to load plugins from", "DIR"),
|
||||
optmulti("", "passes", "space separated list of passes to also run",
|
||||
"PASSES"),
|
||||
optmulti("", "plugins", "space separated list of plugins to also load",
|
||||
"PLUGINS"),
|
||||
optflag("h", "help", "show this help message"),
|
||||
optflag("", "nodefaults", "don't run the default passes"),
|
||||
optopt("o", "output", "where to place the output", "PATH"),
|
||||
];
|
||||
|
||||
let matches = getopts(args.tail(), opts).unwrap();
|
||||
|
||||
let myusage = || {
|
||||
println(usage(format!("{} [options] [html|json] <crate>", args[0]), opts));
|
||||
};
|
||||
|
||||
if matches.opt_present("h") || matches.opt_present("help") {
|
||||
myusage();
|
||||
return;
|
||||
}
|
||||
|
||||
let (format, cratefile) = match matches.free.clone() {
|
||||
[~"json", crate] => (JSON, crate),
|
||||
[~"html", crate] => (HTML, crate),
|
||||
[s, _] => {
|
||||
println!("Unknown output format: `{}`", s);
|
||||
myusage();
|
||||
exit(1);
|
||||
}
|
||||
[_, .._] => {
|
||||
println!("Expected exactly one crate to process");
|
||||
myusage();
|
||||
exit(1);
|
||||
}
|
||||
_ => {
|
||||
println!("Expected an output format and then one crate");
|
||||
myusage();
|
||||
exit(1);
|
||||
}
|
||||
};
|
||||
|
||||
// First, parse the crate and extract all relevant information.
|
||||
let libs = Cell::new(matches.opt_strs("L").map(|s| Path(*s)));
|
||||
let cr = Cell::new(Path(cratefile));
|
||||
info2!("starting to run rustc");
|
||||
let crate = do std::task::try {
|
||||
let cr = cr.take();
|
||||
core::run_core(libs.take(), &cr)
|
||||
}.unwrap();
|
||||
info2!("finished with rustc");
|
||||
|
||||
// Process all of the crate attributes, extracting plugin metadata along
|
||||
// with the passes which we are supposed to run.
|
||||
let mut default_passes = !matches.opt_present("nodefaults");
|
||||
let mut passes = matches.opt_strs("passes");
|
||||
let mut plugins = matches.opt_strs("plugins");
|
||||
match crate.module.get_ref().doc_list() {
|
||||
Some(nested) => {
|
||||
for inner in nested.iter() {
|
||||
match *inner {
|
||||
clean::Word(~"no_default_passes") => {
|
||||
default_passes = false;
|
||||
}
|
||||
clean::NameValue(~"passes", ref value) => {
|
||||
for pass in value.word_iter() {
|
||||
passes.push(pass.to_owned());
|
||||
}
|
||||
}
|
||||
clean::NameValue(~"plugins", ref value) => {
|
||||
for p in value.word_iter() {
|
||||
plugins.push(p.to_owned());
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
None => {}
|
||||
}
|
||||
if default_passes {
|
||||
passes.unshift(~"collapse-docs");
|
||||
passes.unshift(~"unindent-comments");
|
||||
}
|
||||
|
||||
// Load all plugins/passes into a PluginManager
|
||||
let mut pm = plugins::PluginManager::new(Path("/tmp/rustdoc_ng/plugins"));
|
||||
for pass in passes.iter() {
|
||||
let plugin = match pass.as_slice() {
|
||||
"strip-hidden" => passes::strip_hidden,
|
||||
"unindent-comments" => passes::unindent_comments,
|
||||
"collapse-docs" => passes::collapse_docs,
|
||||
"collapse-privacy" => passes::collapse_privacy,
|
||||
s => { error!("unknown pass %s, skipping", s); loop },
|
||||
};
|
||||
pm.add_plugin(plugin);
|
||||
}
|
||||
info2!("loading plugins...");
|
||||
for pname in plugins.move_iter() {
|
||||
pm.load_plugin(pname);
|
||||
}
|
||||
|
||||
// Run everything!
|
||||
info2!("Executing passes/plugins");
|
||||
let (crate, res) = pm.run_plugins(crate);
|
||||
|
||||
info2!("going to format");
|
||||
let started = time::precise_time_ns();
|
||||
let output = matches.opt_str("o").map(|s| Path(*s));
|
||||
match format {
|
||||
HTML => { html::render::run(crate, output.unwrap_or(Path("doc"))) }
|
||||
JSON => { jsonify(crate, res, output.unwrap_or(Path("doc.json"))) }
|
||||
}
|
||||
let ended = time::precise_time_ns();
|
||||
info2!("Took {:.03f}s", (ended as f64 - started as f64) / 1000000000f64);
|
||||
}
|
||||
|
||||
fn jsonify(crate: clean::Crate, res: ~[plugins::PluginJson], dst: Path) {
|
||||
// {
|
||||
// "schema": version,
|
||||
// "crate": { parsed crate ... },
|
||||
// "plugins": { output of plugins ... }
|
||||
// }
|
||||
let mut json = ~extra::treemap::TreeMap::new();
|
||||
json.insert(~"schema", extra::json::String(SCHEMA_VERSION.to_owned()));
|
||||
let plugins_json = ~res.move_iter().filter_map(|opt| opt).collect();
|
||||
|
||||
// FIXME #8335: yuck, Rust -> str -> JSON round trip! No way to .encode
|
||||
// straight to the Rust JSON representation.
|
||||
let crate_json_str = do std::io::with_str_writer |w| {
|
||||
crate.encode(&mut extra::json::Encoder(w));
|
||||
};
|
||||
let crate_json = match extra::json::from_str(crate_json_str) {
|
||||
Ok(j) => j,
|
||||
Err(_) => fail!("Rust generated JSON is invalid??")
|
||||
};
|
||||
|
||||
json.insert(~"crate", crate_json);
|
||||
json.insert(~"plugins", extra::json::Object(plugins_json));
|
||||
|
||||
let mut file = dst.open_writer(io::Create).unwrap();
|
||||
let output = extra::json::Object(json).to_str();
|
||||
file.write(output.as_bytes());
|
||||
}
|
||||
|
||||
fn exit(status: int) -> ! {
|
||||
#[fixed_stack_segment]; #[inline(never)];
|
||||
use std::libc;
|
||||
unsafe { libc::exit(status as libc::c_int) }
|
||||
}
|
Loading…
Reference in New Issue
Block a user