📄 docmaker.py
字号:
#!/usr/bin/env python## DocMaker 0.1 (c) 2000-2001 David Turner <david@freetype.org>## DocMaker is a very simple program used to generate the API Reference# of programs by extracting comments from source files, and generating# the equivalent HTML documentation.## DocMaker is very similar to other tools like Doxygen, with the# following differences:## - It is written in Python (so it is slow, but easy to maintain and# improve).## - The comment syntax used by DocMaker is simpler and makes for# clearer comments.## Of course, it doesn't have all the goodies of most similar tools,# (e.g. C++ class hierarchies), but hey, it is only 2000 lines of# Python.## DocMaker is mainly used to generate the API references of several# FreeType packages.## - David#import fileinput, sys, os, time, string, glob, getopt# The Project's title. This can be overridden from the command line with# the options "-t" or "--title".#project_title = "Project"# The project's filename prefix. This can be set from the command line with# the options "-p" or "--prefix"#project_prefix = ""# The project's documentation output directory. This can be set from the# command line with the options "-o" or "--output".#output_dir = None# The following defines the HTML header used by all generated pages.#html_header_1 = """\<html><header><title>"""html_header_2= """ API Reference</title><basefont face="Verdana,Geneva,Arial,Helvetica"><style content="text/css"> P { text-align=justify } H1 { text-align=center } LI { text-align=justify }</style></header><body text=#000000 bgcolor=#FFFFFF link=#0000EF vlink=#51188E alink=#FF0000><center><h1>"""html_header_3=""" API Reference</h1></center>"""# This is recomputed later when the project title changes.#html_header = html_header_1 + project_title + html_header_2 + project_title + html_header_3# The HTML footer used by all generated pages.#html_footer = """\</body></html>"""# The header and footer used for each section.#section_title_header = "<center><h1>"section_title_footer = "</h1></center>"# The header and footer used for code segments.#code_header = "<font color=blue><pre>"code_footer = "</pre></font>"# Paragraph header and footer.#para_header = "<p>"para_footer = "</p>"# Block header and footer.#block_header = "<center><table width=75%><tr><td>"block_footer = "</td></tr></table><hr width=75%></center>"# Description header/footer.#description_header = "<center><table width=87%><tr><td>"description_footer = "</td></tr></table></center><br>"# Marker header/inter/footer combination.#marker_header = "<center><table width=87% cellpadding=5><tr bgcolor=#EEEEFF><td><em><b>"marker_inter = "</b></em></td></tr><tr><td>"marker_footer = "</td></tr></table></center>"# Source code extracts header/footer.#source_header = "<center><table width=87%><tr bgcolor=#D6E8FF width=100%><td><pre>"source_footer = "</pre></table></center><br>"# Chapter header/inter/footer.#chapter_header = "<center><table width=75%><tr><td><h2>"chapter_inter = "</h2><ul>"chapter_footer = "</ul></td></tr></table></center>"current_section = None# This function is used to sort the index. It is a simple lexicographical# sort, except that it places capital letters before lowercase ones.#def index_sort( s1, s2 ): if not s1: return -1 if not s2: return 1 l1 = len( s1 ) l2 = len( s2 ) m1 = string.lower( s1 ) m2 = string.lower( s2 ) for i in range( l1 ): if i >= l2 or m1[i] > m2[i]: return 1 if m1[i] < m2[i]: return -1 if s1[i] < s2[i]: return -1 if s1[i] > s2[i]: return 1 if l2 > l1: return -1 return 0# Sort input_list, placing the elements of order_list in front.#def sort_order_list( input_list, order_list ): new_list = order_list[:] for id in input_list: if not id in order_list: new_list.append( id ) return new_list# Translate a single line of source to HTML. This will convert# a "<" into "<.", ">" into ">.", etc.#def html_quote( line ): result = string.replace( line, "&", "&" ) result = string.replace( result, "<", "<" ) result = string.replace( result, ">", ">" ) return result# same as 'html_quote', but ignores left and right brackets#def html_quote0( line ): return string.replace( line, "&", "&" )# Open the standard output to a given project documentation file. Use# "output_dir" to determine the filename location if necessary and save the# old stdout in a tuple that is returned by this function.#def open_output( filename ): global output_dir if output_dir and output_dir != "": filename = output_dir + os.sep + filename old_stdout = sys.stdout new_file = open( filename, "w" ) sys.stdout = new_file return ( new_file, old_stdout )# Close the output that was returned by "close_output".#def close_output( output ): output[0].close() sys.stdout = output[1]# Check output directory.#def check_output( ): global output_dir if output_dir: if output_dir != "": if not os.path.isdir( output_dir ): sys.stderr.write( "argument" + " '" + output_dir + "' " + "is not a valid directory" ) sys.exit( 2 ) else: output_dir = Nonedef compute_time_html( ): global html_footer time_string = time.asctime( time.localtime( time.time() ) ) html_footer = "<p><center><font size=""-2"">generated on " + time_string + "</font></p></center>" + html_footer# The FreeType 2 reference is extracted from the source files. These# contain various comment blocks that follow one of the following formats:## /**************************# *# * FORMAT1# *# *# *# *# *************************/## /**************************/# /* */# /* FORMAT2 */# /* */# /* */# /* */# /* */## /**************************/# /* */# /* FORMAT3 */# /* */# /* */# /* */# /* */# /**************************/## Each block contains a list of markers; each one can be followed by# some arbitrary text or a list of fields. Here an example:## <Struct># MyStruct## <Description># this structure holds some data## <Fields># x :: horizontal coordinate# y :: vertical coordinate### This example defines three markers: 'Struct', 'Description' & 'Fields'.# The first two markers contain arbitrary text, while the last one contains# a list of fields.## Each field is simply of the format: WORD :: TEXT...## Note that typically each comment block is followed by some source code# declaration that may need to be kept in the reference.## Note that markers can alternatively be written as "@MARKER:" instead of# "<MARKER>". All marker identifiers are converted to lower case during# parsing in order to simply sorting.## We associate with each block the following source lines that do not begin# with a comment. For example, the following:## /**********************************# *# * <mytag> blabla# *# */## bla_bla_bla# bilip_bilip## /* - this comment acts as a separator - */## blo_blo_blo### will only keep the first two lines of sources with# the "blabla" block.## However, the comment will be kept, with following source lines if it# contains a starting '#' or '@' as in:## /*@.....*/# /*#.....*/# /* @.....*/# /* #.....*/################################################################################ The DocCode class is used to store source code lines.## 'self.lines' contains a set of source code lines that will be dumped as# HTML in a <PRE> tag.## The object is filled line by line by the parser; it strips the leading# "margin" space from each input line before storing it in 'self.lines'.#class DocCode: def __init__( self, margin = 0 ): self.lines = [] self.margin = margin def add( self, line ): # remove margin whitespace # if string.strip( line[: self.margin] ) == "": line = line[self.margin :] self.lines.append( line ) def dump( self ): for line in self.lines: print "--" + line print "" def get_identifier( self ): # this function should never be called # return "UNKNOWN_CODE_IDENTIFIER!" def dump_html( self, identifiers = None ): # clean the last empty lines # l = len( self.lines ) - 1 while l > 0 and string.strip( self.lines[l - 1] ) == "": l = l - 1 # The code footer should be directly appended to the last code # line to avoid an additional blank line. # print code_header, for line in self.lines[0 : l+1]: print '\n' + html_quote(line), print code_footer,############################################################################### The DocParagraph is used to store text paragraphs.# 'self.words' is simply a list of words for the paragraph.## The paragraph is filled line by line by the parser.#class DocParagraph: def __init__( self ): self.words = [] def add( self, line ): # Get rid of unwanted spaces in the paragraph. # # The following two lines are the same as # # self.words.extend( string.split( line ) ) # # but older Python versions don't have the `extend' attribute. # last = len( self.words ) self.words[last : last] = string.split( line ) # This function is used to retrieve the first word of a given # paragraph. # def get_identifier( self ): if self.words: return self.words[0] # should never happen # return "UNKNOWN_PARA_IDENTIFIER!" def get_words( self ): return self.words[:] def dump( self, identifiers = None ): max_width = 50 cursor = 0 line = "" extra = None alphanum = string.lowercase + string.uppercase + string.digits + '_' for word in self.words: # process cross references if needed # if identifiers and word and word[0] == '@': word = word[1 :] # we need to find non-alphanumeric characters # l = len( word ) i = 0 while i < l and word[i] in alphanum: i = i + 1 if i < l: extra = word[i :] word = word[0 : i] block = identifiers.get( word ) if block: word = '<a href="' + block.html_address() + '">' + word + '</a>' else: word = '?' + word if cursor + len( word ) + 1 > max_width: print html_quote0(line) cursor = 0 line = "" line = line + word if not extra: line = line + " " cursor = cursor + len( word ) + 1 # Handle trailing periods, commas, etc. at the end of cross # references. # if extra: if cursor + len( extra ) + 1 > max_width: print html_quote0(line) cursor = 0 line = "" line = line + extra + " " cursor = cursor + len( extra ) + 1 extra = None if cursor > 0: print html_quote0(line) # print "
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -