How Can I Create a Table of Contents for My Code

I have a decently large Python file, and it is becoming increasingly hard to navigate all of the many functions that I have created. Is there a way to create some sort of table of contents to easily navigate my code… or is it already a feature and I don’t know of it? Is there possibly a way to make an automatically update file (maybe a Markdown, I’m not sure) that does this for me?

1 Like

This sounds like a great place to use multiple modules.
If you can’t do that, you can use the editor’s dropdown to hide functions and classes (but is buggy and doesn’t persist).
I’m pretty sure there’s some sort of Jump to definition feature, but it looks like it doesn’t really work in this case.
You could always just search for occurrences.

3 Likes

I just wish there was a way to view some sort of… idk, maybe like a tree. That tree could show all of the functions and classes so you could easily jump to a function, or such.

Well, it wouldn’t be useful in most cases because most people organize their code into modules. It’s also harder to implement these things for python.

For a kind of weird table of contents, go to the end of a file and place a list of all useful objects, then you can right click it and Jump to definition (there is a shortcut too).

I did something like this once.
######################
# Auto Docs Creation #
######################
# Thanks to @bigminiboss on Replit.com for this function.
def auto_docs(py_file, output_file:str='CoderElijah'):
  '''This function creates an MD file with documentation on a Python Library from the docstrings. DO NOT INCLUDE THE FILE NAME EXTENSIONS!
`py_file`: The name of the Py file you wish to create documentation for. Use `CoderElijah` to get docs on the CoderElijah library.
`output_file`: The name of your file. If left blank, it will create `CoderElijah.md`.
Thanks to [@bigminiboss](https://replit.com/@bigminiboss] for the help with this!'''
  md = ""
  for i in dir(py_file):
      if (not i.startswith("__") and not i.endswith("__")):
          doc_string = getattr(py_file, i).__doc__
          try:
            doc_string = doc_string.split('\n')
            docs = ""
            for line in doc_string:
              docs += line + "\n\n"
            if inspect.isclass(getattr(py_file, i)) == True:
              doc_type = 'class'
            else:
              doc_type = 'function'
            md += f'''<details><summary> The <code>{i}</code> {doc_type}</summary>
  
  {docs}</details>'''
          except:
            pass
  with open(f'{output_file}.md', 'w') as file:
    file.write(md)

Copied from https://replit.com/@CoderElijah/My-Python-Library#CoderElijah/E.py

2 Likes

I don’t think that quite answers the questions, some source file parsing for specific line numbers and links would be needed.
Useful, though. (May I improve the code?)

1 Like

Just providing it for whatever capacity it helps this topic.

Of course! I’d appreciate it if you send me the link to the repl you update it in though so I can see the improvements and maybe learn something.

The function had little functionality, bad practices, and there were some bugs, so I basically rewrote the entire thing.

https://replit.com/@NuclearPasta0/Docs-generator#doc.py

revised
from importlib import import_module
from typing import Any, TextIO, overload


def format_docstring(docstring: str) -> str:
  """Strip each line and make sure the lines are separated correctly."""
  return ''.join([f'{line.strip()}  \n' for line in docstring.strip().split('\n')])

def get_docstring(obj: Any) -> str | None:
  docstring = getattr(obj, '__doc__', None)
  if isinstance(docstring, str):
    docstring = docstring.strip()
    if docstring:
      return format_docstring(docstring)
  return None


@overload
def document(file_path: str, output: TextIO, /) -> int: ...
@overload
def document(file_path: str, output: str, /) -> int: ...
@overload
def document(file_path: str, output: None = None, /) -> str: ...
def document(file_path, output=None, /):
  """Generate markdown documentation from a file.
  
  If __all__ is defined, then it is used,
  otherwise, names that start with '_', including dunder names, are excluded.
  Only objects that are callable, whose __module__ attribute is file_path,
  and whose docstring is not empty are documented.

  file_path is the name of the module to be used, and it is imported
  using importlib.import_module(). (Examples: 'main', 'collections.abc')
  If output is a string, it is a path that will be opened in write mode,
  and the documentation will be written there. Return the return of write().
  If output is a file, the docs will be written using
  its write() method, and its return value will be returned.
  If output is omitted or None, return the generated string instead of writing to a file.

  Objects with aliases may be documented more than once.
  """
  module = import_module(file_path)
  docs = [f'<h4> module <code>{file_path}</code> </h4>\n']
  module_docstring = get_docstring(module)
  if module_docstring is not None:
    docs.append(module_docstring)
  try:
    module_all = module.__all__
  except AttributeError:
    attrs = module.__dict__
  else:
    attrs = {name: module.__dict__[name] for name in module_all}
  for name, value in attrs.items():
    if name.startswith('_') or not callable(value):
      continue
    docstring = get_docstring(value)
    if docstring is None or getattr(value, '__module__', None) != file_path:
      continue
    doc_type = 'class' if isinstance(value, type) else 'function'
    docs.append(
      f'<details><summary> {doc_type} <code>{name}</code></summary>'
      f'\n\n{docstring}\n</details>'
    )
  docs = '\n'.join(docs)
  if output is None:
    return docs
  if isinstance(output, str):
    with open(output, 'w') as file:
      return file.write(docs)
  return output.write(docs)

Erm why didn’t you use replit.com and firewalledreplit.com? Sorry, might be off-topic.

Ehh, the graphical output for pygame is never blocked on firewalled, but sometimes blocked on non-firewalled, for some networks. So I usually use it by default. (I don’t need internet in my repls and I don’t look at replit community.)

Very cool, I’ll be sure to use it if I ever need something of the sort!
No offense intended, but it doesn’t exactly solve my problem.

Yeah, it’s a bit off topic.
The absolute best solution is to organize your code into more modules.

1 Like

You can use the breadcrumbs at the top

If you click the three dots (or the current block name if your cursor is in a block) next to the filename you can see an outline of the whole file

image

3 Likes

Cool, didn’t know replit had that feature. Is it new or did I just miss it?
On a side note, if you have a ton of global variables then stuff is cut off and you can’t scroll to see them.

2 Likes

I’m not sure but I feel like it’s been around for a couple months

Oh yeah you should make a bug report for that (it looks like a simple overflow: auto fixes it)

1 Like

You can use Sphinx to automatically create good documentation/table of contents for your code, which will solve the issue you are facing. Also, you can use tools like pdoc and pycco, as they are more lightweight and place less emphasis on extensive narrative content compared to Sphinx, which is typically used for larger projects.