]> git.uio.no Git - u/mrichter/AliRoot.git/blobdiff - doxygen/thtml2doxy.py
added printout of equipment-ID in case of decode errors
[u/mrichter/AliRoot.git] / doxygen / thtml2doxy.py
index 10726d054a007c5c633320db8a2196bec0c625b4..4d17e511874ee5837be53d4f8d25e03d157e72d9 100755 (executable)
@@ -321,23 +321,37 @@ def comment_datamember(cursor, comments):
 #  before any other comment found so far in the file (the comments array is inspected to ensure
 #  this).
 #
-#  Multi-line comments (/* ... */) are not considered as they are commonly used to display
-#  copyright notice.
+#  Multi-line comments (/* ... */) that *immediately* follow a series of single-line comments
+#  (*i.e.* without empty lines in-between) are also considered. A class description can eventually
+#  be a series of single-line and multi-line comments, with no blank spaces between them, and always
+#  starting with a single-line sequence.
+#
+#  The reason why they cannot start with a multi-line sequence is that those are commonly used to
+#  display a copyright notice.
 #
 #  @param filename Name of the current file
 #  @param comments Array of comments: new ones will be appended there
 #  @param look_no_further_than_line Stop before reaching this line when looking for class comment
 def comment_classdesc(filename, comments, look_no_further_than_line):
 
-  recomm = r'^\s*///?(\s*.*?)\s*/*\s*$'
+  # Single-line comment
+  recomm = r'^\s*///?(\s*(.*?))\s*/*\s*$'
+
+  # Multi-line comment (only either /* or */ on a single line)
+  remlcomm_in = r'^\s*/\*\s*$'
+  remlcomm_out = r'^\s*\*/\s*$'
+  in_mlcomm = False
 
   reclass_doxy = r'(?i)^\s*\\(class|file):?\s*([^.]*)'
   class_name_doxy = None
 
   reauthor = r'(?i)^\s*\\?(authors?|origin):?\s*(.*?)\s*(,?\s*([0-9./-]+))?\s*$'
   redate = r'(?i)^\s*\\?date:?\s*([0-9./-]+)\s*$'
+  rebrief = r'(?i)^\s*\\brief\s*(.*)\s*$'
   author = None
   date = None
+  brief = None
+  brief_len_threshold = 80
 
   comment_lines = []
 
@@ -345,6 +359,7 @@ def comment_classdesc(filename, comments, look_no_further_than_line):
   end_line = -1
 
   line_num = 0
+  last_comm_line_num = 0
 
   is_macro = filename.endswith('.C')
 
@@ -359,13 +374,38 @@ def comment_classdesc(filename, comments, look_no_further_than_line):
           look_no_further_than_line)
         break
 
-      if raw.strip() == '' and start_line > 0:
+      if in_mlcomm == False and raw.strip() == '' and start_line > 0:
         # Skip empty lines
         continue
 
       stripped = strip_html(raw)
-      mcomm = re.search(recomm, stripped)
-      if mcomm:
+      mcomm = None
+      this_comment = None
+
+      if not in_mlcomm:
+        mcomm = re.search(recomm, stripped)
+
+      if last_comm_line_num == 0 or last_comm_line_num == line_num-1:
+
+        if mcomm and not mcomm.group(2).startswith('#'):
+          # Single-line comment
+          this_comment = mcomm.group(1)
+        elif start_line > -1:
+          # Not a single-line comment. But it cannot be the first.
+          if in_mlcomm == False:
+            mmlcomm = re.search(remlcomm_in, stripped)
+            if mmlcomm:
+              in_mlcomm = True
+              this_comment = ''
+          else:
+            mmlcomm = re.search(remlcomm_out, stripped)
+            if mmlcomm:
+              in_mlcomm = False
+              this_comment = ''
+            else:
+              this_comment = stripped
+
+      if this_comment is not None:
 
         if start_line == -1:
 
@@ -385,12 +425,12 @@ def comment_classdesc(filename, comments, look_no_further_than_line):
         end_line = line_num
         append = True
 
-        mclass_doxy = re.search(reclass_doxy, mcomm.group(1))
+        mclass_doxy = re.search(reclass_doxy, this_comment)
         if mclass_doxy:
           class_name_doxy = mclass_doxy.group(2)
           append = False
         else:
-          mauthor = re.search(reauthor, mcomm.group(1))
+          mauthor = re.search(reauthor, this_comment)
           if mauthor:
             author = mauthor.group(2)
             if date is None:
@@ -398,18 +438,26 @@ def comment_classdesc(filename, comments, look_no_further_than_line):
               date = mauthor.group(4)
             append = False
           else:
-            mdate = re.search(redate, mcomm.group(1))
+            mdate = re.search(redate, this_comment)
             if mdate:
               date = mdate.group(1)
               append = False
+            else:
+              mbrief = re.search(rebrief, this_comment)
+              if mbrief:
+                brief = mbrief.group(1)
+                append = False
 
         if append:
-          comment_lines.append( mcomm.group(1) )
+          comment_lines.append( this_comment )
 
       else:
         if start_line > 0:
           break
 
+      # This line had a valid comment
+      last_comm_line_num = line_num
+
   if class_name_doxy is None:
 
     # No \class specified: guess it from file name
@@ -428,9 +476,16 @@ def comment_classdesc(filename, comments, look_no_further_than_line):
 
   if start_line > 0:
 
-    # Prepend \class or \file specifier (and an empty line)
-    comment_lines[:0] = [ file_class_line ]
-    comment_lines.append('')
+    prepend_to_comment = []
+
+    # Prepend \class or \file specifier, then the \brief, then an empty line
+    prepend_to_comment.append( file_class_line )
+
+    if brief is not None:
+      prepend_to_comment.append( '\\brief ' + brief )
+    prepend_to_comment.append( '' )
+
+    comment_lines = prepend_to_comment + comment_lines  # join lists
 
     # Append author and date if they exist
     if author is not None:
@@ -439,7 +494,37 @@ def comment_classdesc(filename, comments, look_no_further_than_line):
     if date is not None:
       comment_lines.append( '\\date ' + date )
 
+    # We should erase the "dumb" comments, such as "<class_name> class"
+    comm_idx = 0
+    regac = r'\s*%s\s+class\.?\s*' % class_name_doxy
+    mgac = None
+    for comm in comment_lines:
+      mgac = re.search(regac, comm)
+      if mgac:
+        break
+      comm_idx = comm_idx + 1
+    if mgac:
+      logging.debug('Removing dumb comment line: {%s}' % Colt(comment_lines[comm_idx]).magenta())
+      del comment_lines[comm_idx]
+
     comment_lines = refactor_comment(comment_lines, do_strip_html=False, infilename=filename)
+
+    # Now we look for a possible \brief
+    if brief is None:
+      comm_idx = 0
+      for comm in comment_lines:
+        if comm.startswith('\\class') or comm.startswith('\\file') or comm == '':
+          pass
+        else:
+          if len(comm) <= brief_len_threshold:
+            brief = comm
+          break
+        comm_idx = comm_idx + 1
+      if brief is not None:
+        comment_lines = refactor_comment(
+          [ comment_lines[0], '\\brief ' + brief ] + comment_lines[1:comm_idx] + comment_lines[comm_idx+1:],
+          do_strip_html=False, infilename=filename)
+
     logging.debug('Comment found for class %s' % Colt(class_name_doxy).magenta())
     comments.append(Comment(
       comment_lines,
@@ -480,6 +565,10 @@ def comment_classimp(filename, comments):
 
   with open(filename, 'r') as fp:
 
+    # Array of tuples: classimp, startcond, endcond
+    found_classimp = []
+
+    # Reset to nothing found
     line_classimp = -1
     line_startcond = -1
     line_endcond = -1
@@ -493,6 +582,13 @@ def comment_classimp(filename, comments):
       mclassimp = re.search(reclassimp, line)
       if mclassimp:
 
+        # Dump previous one if appropriate, and reset
+        if line_classimp != -1:
+          found_classimp.append( (line_classimp, line_startcond, line_endcond) )
+          line_classimp = -1
+          line_startcond = -1
+          line_endcond = -1
+
         # Adjust indent
         classimp_indent = len( mclassimp.group(1) )
 
@@ -509,7 +605,15 @@ def comment_classimp(filename, comments):
 
         mstartcond = re.search(restartcond, line)
         if mstartcond:
-          logging.debug('Found Doxygen opening condition for ClassImp in {%s}' % line)
+
+          # Dump previous one if appropriate, and reset
+          if line_classimp != -1:
+            found_classimp.append( (line_classimp, line_startcond, line_endcond) )
+            line_classimp = -1
+            line_startcond = -1
+            line_endcond = -1
+
+          logging.debug('Found Doxygen opening condition for ClassImp')
           in_classimp_cond = True
           line_startcond = line_num
 
@@ -521,36 +625,46 @@ def comment_classimp(filename, comments):
             in_classimp_cond = False
             line_endcond = line_num
 
-  # Did we find something?
-  if line_classimp != -1:
+    # Dump previous one if appropriate, and reset (out of the loop)
+    if line_classimp != -1:
+      found_classimp.append( (line_classimp, line_startcond, line_endcond) )
+      line_classimp = -1
+      line_startcond = -1
+      line_endcond = -1
 
-    if line_startcond != -1:
-      comments.append(Comment(
-        ['\cond CLASSIMP'],
-        line_startcond, 1, line_startcond, 1,
-        classimp_indent, 'ClassImp/Def(%s)' % classimp_class,
-        append_empty=False
-      ))
-    else:
-      comments.append(PrependComment(
-        ['\cond CLASSIMP'],
-        line_classimp, 1, line_classimp, 1,
-        classimp_indent, 'ClassImp/Def(%s)' % classimp_class
-      ))
-
-    if line_endcond != -1:
-      comments.append(Comment(
-        ['\endcond'],
-        line_endcond, 1, line_endcond, 1,
-        classimp_indent, 'ClassImp/Def(%s)' % classimp_class,
-        append_empty=False
-      ))
-    else:
-      comments.append(PrependComment(
-        ['\endcond'],
-        line_classimp+1, 1, line_classimp+1, 1,
-        classimp_indent, 'ClassImp/Def(%s)' % classimp_class
-      ))
+    for line_classimp,line_startcond,line_endcond in found_classimp:
+
+      # Loop over the ClassImp conditions we've found
+
+      if line_startcond != -1:
+        logging.debug('Looks like we are in a condition here %d,%d,%d' % (line_classimp, line_startcond, line_endcond))
+        comments.append(Comment(
+          ['\cond CLASSIMP'],
+          line_startcond, 1, line_startcond, 1,
+          classimp_indent, 'ClassImp/Def(%s)' % classimp_class,
+          append_empty=False
+        ))
+      else:
+        logging.debug('Looks like we are  NOT NOT  in a condition here %d,%d,%d' % (line_classimp, line_startcond, line_endcond))
+        comments.append(PrependComment(
+          ['\cond CLASSIMP'],
+          line_classimp, 1, line_classimp, 1,
+          classimp_indent, 'ClassImp/Def(%s)' % classimp_class
+        ))
+
+      if line_endcond != -1:
+        comments.append(Comment(
+          ['\endcond'],
+          line_endcond, 1, line_endcond, 1,
+          classimp_indent, 'ClassImp/Def(%s)' % classimp_class,
+          append_empty=False
+        ))
+      else:
+        comments.append(PrependComment(
+          ['\endcond'],
+          line_classimp+1, 1, line_classimp+1, 1,
+          classimp_indent, 'ClassImp/Def(%s)' % classimp_class
+        ))
 
 
 ## Traverse the AST recursively starting from the current cursor.
@@ -650,36 +764,13 @@ def refactor_comment(comment, do_strip_html=True, infilename=None):
   lowest_indent_level = None
 
   # Indentation threshold: if too much indented, don't indent at all
-  indent_level_threshold = 15
+  indent_level_threshold = 7
 
   new_comment = []
   insert_blank = False
   wait_first_non_blank = True
   for line_comment in comment:
 
-    # Check if we are in a macro block
-    mmacro = re.search(remacro, line_comment)
-    if mmacro:
-      if in_macro:
-        in_macro = False
-
-        # Dump macro
-        outimg = write_macro(infilename, current_macro) + '.png'
-        current_macro = []
-
-        # Insert image
-        new_comment.append( '![Picture from ROOT macro](%s)' % (os.path.basename(outimg)) )
-
-        logging.debug( 'Found macro for generating image %s' % Colt(outimg).magenta() )
-
-      else:
-        in_macro = True
-
-      continue
-    elif in_macro:
-      current_macro.append( line_comment )
-      continue
-
     # Strip some HTML tags
     if do_strip_html:
       line_comment = strip_html(line_comment)
@@ -688,6 +779,29 @@ def refactor_comment(comment, do_strip_html=True, infilename=None):
     if mcomm:
       new_line_comment = mcomm.group(2) + mcomm.group(3)  # indent + comm
 
+      # Check if we are in a macro block
+      mmacro = re.search(remacro, new_line_comment)
+      if mmacro:
+        if in_macro:
+          in_macro = False
+
+          # Dump macro
+          outimg = write_macro(infilename, current_macro) + '.png'
+          current_macro = []
+
+          # Insert image
+          new_comment.append( '![Picture from ROOT macro](%s)' % (os.path.basename(outimg)) )
+
+          logging.debug( 'Found macro for generating image %s' % Colt(outimg).magenta() )
+
+        else:
+          in_macro = True
+
+        continue
+      elif in_macro:
+        current_macro.append( new_line_comment )
+        continue
+
       mgarbage = re.search( regarbage, new_line_comment )
 
       if mgarbage is None and not mcomm.group(3).startswith('\\') and mcomm.group(3) != '':
@@ -829,7 +943,10 @@ def write_macro(infilename, macro_lines):
     digh.update('\n')
   short_digest = digh.hexdigest()[0:7]
 
-  outdir = '%s/imgdoc' % os.path.dirname(infilename)
+  infiledir = os.path.dirname(infilename)
+  if infiledir == '':
+    infiledir = '.'
+  outdir = '%s/imgdoc' % infiledir
   outprefix = '%s/%s_%s' % (
     outdir,
     os.path.basename(infilename).replace('.', '_'),
@@ -906,15 +1023,42 @@ def rewrite_comments(fhin, fhout, comments):
     # Find current comment
     prev_comm = comm
     comm = None
+    comm_list = []
     for c in comments:
       if c.has_comment(line_num):
         comm = c
+        comm_list.append(c)
+
+    if len(comm_list) > 1:
+
+      merged = True
+
+      if len(comm_list) == 2:
+        c1,c2 = comm_list
+        if isinstance(c1, Comment) and isinstance(c2, Comment):
+          c1.lines = c1.lines + c2.lines  # list merge
+          comm = c1
+          logging.debug('Two adjacent comments merged. Result: {%s}' % Colt(comm).cyan())
+        else:
+          merged = False
+      else:
+        merged = False
+
+      if merged == False:
+        logging.warning('Too many unmergeable comments on the same line (%d), picking the last one' % len(comm_list))
+        for c in comm_list:
+          logging.warning('>> %s' % c)
+          comm = c  # considering the last one
 
     if comm:
 
       # First thing to check: are we in the same comment as before?
-      if comm is not prev_comm and isinstance(comm, Comment) and isinstance(prev_comm, Comment) \
-        and not isinstance(prev_comm, RemoveComment):
+      if comm is not prev_comm and \
+         isinstance(comm, Comment) and \
+         isinstance(prev_comm, Comment) and \
+         not isinstance(prev_comm, RemoveComment):
+
+        # We are NOT in the same comment as before, and this comment is dumpable
 
         skip_empty = dump_comment_block(prev_comm, restore_lines)
         in_comment = False
@@ -988,7 +1132,7 @@ def rewrite_comments(fhin, fhout, comments):
           in_comment = False
           restore_lines = None
 
-      elif prev_comm is None:
+      elif restore_lines is None:
 
         # Beginning of a new comment block of type Comment or PrependComment
         in_comment = True
@@ -996,20 +1140,24 @@ def rewrite_comments(fhin, fhout, comments):
         if isinstance(comm, PrependComment):
           # Prepare array of lines to dump right after the comment
           restore_lines = [ line.rstrip('\n') ]
+          logging.debug('Commencing lines to restore: {%s}' % Colt(restore_lines[0]).cyan())
         else:
-          # Extract the non-comment part and print it if it exists
-          non_comment = line[ 0:comm.first_col-1 ].rstrip()
-          if non_comment != '':
-            fhout.write( non_comment + '\n' )
+          # Extract the non-comment part and print it if it exists. If this is the first line of a
+          # comment, it might happen something like `valid_code;  // this is a comment`.
+          if comm.first_line == line_num:
+            non_comment = line[ 0:comm.first_col-1 ].rstrip()
+            if non_comment != '':
+              fhout.write( non_comment + '\n' )
 
       elif isinstance(comm, Comment):
 
         if restore_lines is not None:
           # From the 2nd line on of comment to prepend
           restore_lines.append( line.rstrip('\n') )
+          logging.debug('Appending lines to restore. All lines: {%s}' % Colt(restore_lines).cyan())
 
       else:
-        assert False, 'Unhandled parser state. line=%d comm=%s prev_comm=%s' % \
+        assert False, 'Unhandled parser state: line=%d comm={%s} prev_comm={%s}' % \
           (line_num, comm, prev_comm)
 
     else:
@@ -1032,6 +1180,16 @@ def rewrite_comments(fhin, fhout, comments):
       else:
         fhout.write( line_out + '\n' )
 
+  # Is there some comment left here?
+  if restore_lines is not None:
+    dump_comment_block(comm, restore_lines)
+
+  # Is there some other comment beyond the last line?
+  for c in comments:
+    if c.has_comment(line_num+1):
+      dump_comment_block(c, None)
+      break
+
 
 ## The main function.
 #