Fortran-FOSS-Programmers / ford

Automatically generates FORtran Documentation from comments within the code.
https://forddocs.readthedocs.io
GNU General Public License v3.0
402 stars 131 forks source link

Graphs with long edge labels become degenerated #628

Open mscfd opened 5 months ago

mscfd commented 5 months ago

If a derived type has many components of the same type, inheritance graphs becomes degenerated, as edges length depends on length of label. In order to handle this, I added linebreaks. Additionally items from the label list are dropped if there are too many (more than 3 lines of width 40 required).

Here is a contrived example module

module test

implicit none
private

type, public :: t0
end type t0

type, extends(t0), public :: t1
end type t1

type, public :: t2
   type(t1) :: component_1
   type(t1) :: component_2
   type(t1) :: component_3
   type(t1) :: component_4
   type(t1) :: component_5
   type(t1) :: component_6
   type(t1) :: component_7
   type(t1) :: component_8
   type(t1) :: component_9
end type t2

type, extends(t2), public :: t3
end type t3

type, extends(t3), public :: t4
end type t4

end module test
mscfd commented 5 months ago

The code in commit 185121f is a proof of concept, but works fine so far.

ZedThree commented 5 months ago

Something a bit simpler that might be sufficient:

modified   ford/graphs.py
@@ -450,11 +450,11 @@ class TypeNode(BaseNode):

             node.visible = getattr(proto, "visible", True)
             if self in node.comp_of:
-                node.comp_of[self] += ", " + var.name
+                node.comp_of[self] += f"\n{var.name}"
             else:
                 node.comp_of[self] = var.name
             if node in self.comp_types:
-                self.comp_types[node] += ", " + var.name
+                self.comp_types[node] += f"\n{var.name}"
             else:
                 self.comp_types[node] = var.name
mscfd commented 5 months ago

This stacks names in a column, instead of printing them in a row, right? I have tried this, and it is definitely an improvement. But from looking at some of my graphs, I concluded that a compact box (a few lines with restricted width) uses the typical real estate in a graph optimally. That's why I arrived at my rather complex logic. I definitely would restrict the size of the label horizontally as well as vertically if there are too many items to avoid degenerating the graph.

ZedThree commented 5 months ago

Have you looked at textwrap.wrap?

I do also think the graphs should be comprehensive rather than compact, so I don't think it should cut off components.

mscfd commented 5 months ago

Thanks for pointing to this module, did not know about it. Indeed something like textwrap.fill(s, width=40, max_lines=3, placeholder='...') yields the same result as my code block.

This is not about having compact graphs. It is about having readable graphs. I have already replaced the container by a container-fluid to really use my screen size for graphs, otherwise most graphs are unusable, see #293. With long edges (or long nodes), the whole graph shrinks and labels become unreadable without zooming.

One could question whether nesting levels greater than 2 or 3 are really necessary. But it really helps with refactoring and decoupling in a matured and grown code base.