This post will be pre-writing for a LeoU issue. Recent discussions
<https://groups.google.com/d/msg/leo-editor/Nm0ZiVfd_ko/LlVXXXFCAQAJ> about
Leo's design have stimulated this post.
Here, I'd like to simulate a formal code review of Leo's vnode (VNode)
class. I'll wear three hats: the presenter, the secretary and the project
manager. I'll use Vitalije's comments as a starting point for a
discussion. This discussion would not normally be part of a code view, but
imo it's a good teachable moment.
See the summary for conclusions.
*Presentation*
The VNode class contains all data in an outline, as well as crucial,
low-level operations on those data. There is no other place to store
node-related data! In particular, the position class concerns itself only
with traversing the tree. The position class contains *no* node-related
data.
The VNode class is one of the foundations of Leo's scripting. As a result,
none of the ivars or methods of the VNode class can change in any way. That
is, all of the ivars of the VNode class are "official". Scripts can, and
do, access these ivars directly.
Theoretically, it would be possible to add new kwargs to VNode methods, but
that is extremely unlikely. It would also be possible to add new ivars to
the VNode class, but uA's make that unnecessary.
The properties of the VNode class, v.b, v.h, v.gnx, v.u, allow higher-level
access to various VNode ivars, but in some cases Leo's code accesses the
corresponding ivars directly, for greater performance.
*Discussion*
Here I'll respond to Vitalije's comments in detail. This would typically
not happen during a formal code review, but these comments will give me a
chance to more fully explain the design of the VNode class.
> one of the most basic element of Leo's data is certainly VNode. And yet
one can not instantiate VNode class without acquiring full blown commander,
which in VNode is known as context. In the comment of context attribute is
explicitly declared that it is called context to indicate its limited
usage.
Perhaps the "context" kwarg should just be called "c", but of course that
can't be done now. Almost all of Leo's classes have access to "c", the
present commander. There is nothing wrong with that.
What *data hiding* means to me is that the *boundary *between the "inside"
and the "outside" of a class is (relatively) strict. As far as the
"context" arg goes, the caller (creator) of VNode instances does not need
to know *anything *about how the VNode class uses the context.
The clients of the VNode class must not (and do not) know *anything *about
how the methods of the VNode class work. All methods (of all classes)
*must* be black boxes.
> However [the context ivar] is used not only as a holder of hiddenRootNode
but there is a method on VNode that knows about c.frame.body.wrapper and
knows that c.frame.body.wrapper has setInsertPoint, setYScrollPosition...
The VNode class must know everything about c.hiddenRootNode. It's another
VNode instance.
The vnode class must contain the v.scrollBarSpot ivar.
Yes, it would be possible to use c.restoreCursorAndScroll() instead of
v.restoreCursorAndScroll(). Ditto for c/v.saveCursorAndScroll(). But such
changes would have *no *consequences for Leo's architecture.
> [The VNode class] also knows indirectly about c.fileCommands.gnxDict.
To my knowledge, this is not true. The VNode class does not in any way
become entangled with other classes merely because v.context exists! The
VNode class knows *nothing* about how other classes use gnx's.
> Now, whichever module needs to access VNode class, can't just import
leo.core.leoNodes and use it. It needs to acquire commander instance before.
True, but so what? Every class in Leo has access to a commander instance
via its "c" ivar. That's why all Leonine scripts have the "c" var
predefined. Imo, it's useless to complain about this.
However, this comment does suggest creating c.new_vnode method:
import leo.core.leoNodes as leoNodes
# already exists in leoCommands.py
...
def new_vnode(self, gnx=None):
c = self
return leoNodes.VNode(context=c, gnx=gnx)
The (small) advantage is that clients of c.new_vnode(gnx) would no longer
need to import leo.core.leoNodes.
> Recently, we discussed the copying and pasting outline. It delegates its
task to fileCommands. FileCommands OTOH knows about
c.frame.resizePaneToRatio, c.selectPosition, c.frame.initialRatios,
c.atFileCommands.readAll, c.frame.body.onBodyChanged...
> It seems to me that copying and pasting outlines should belong just to
VNode. I would expect that v instance knows best how to encode itself into
a string, and to decode itself from string.
The VNode class does know about unicode. v._headString and v._bodyString
contain unicode characters. The v.b and v.h setter properties call
v.setBodyString() and v._setHeadString(), which handle the conversion to
unicode.
Ironically, the reason this is not obvious is that there is a strong
boundary between the VNode class and client code.
> There is no real need for v instance to know anything about
c.fileCommands, c.atFileCommands, c.frame.body.wrapper.
Nor does it, in any harmful way.
> You could say that fileCommands is designed to encode/decode vnodes.
> But why then it selects position, sets frame ratios
FileCommands class handles *everything *associated with reading .leo files.
That's its purpose. Yes, it must do a lot, but that can't be helped.
The FileCommands class creates VNode instances, without in any way knowing
the internal details of the VNode class. Yes, the FileCommands class must
know about gnx's, *but the converse is not true*. The VNode class knows
*nothing
*about how other classes use gnx's.
Crucially, the v.context ivar does not "pollute" the VNode class in any
way. The boundary between the VNode class and *all* other classes remains
intact.
> If fileCommands need to provide reading and saving Leo documents, then it
would probably use the same low level helper module as Vnodes for
encoding/decoding them from/to string.
v.b and v.h are properties that handle all the details of converting to
unicode.
The v.b and v.h properties are a perfect example of enforcing the
boundaries between the "inside" of the VNode class and everything else.
The boundary may not be apparent (because v.b and v.h look like ivars), but
the boundary exists nonetheless.
There is a huge practical advantage to using properties instead of
getter/setter methods on the VNode class.
Most Leonine scripters don't know what v.b and v.h actually are, which is
kinda the point. Otoh, those who want to understand the VNode class in full
detail *must *understand that v.context has no harmful architectural
consequences *whatsoever*.
> I could write many more examples but these few would suffice to make my
point clear.
I am glad to discuss any other Leo class.
*Summary*
The VNode class contains all node-related data. That's it's primary
purpose. It also contains crucial low-level operations on VNodes.
The ivars and methods VNode class must never change, except in an upward
compatible manner.
The VNode class is, in fact, an excellent example of information hiding.
The boundary between the VNode class is well defined and well enforced.
The v.context ivar is completely benign. It has never caused problems, and
it never will. It does not, in any significant way, make the VNode class
dependent on any other class. It has no harmful architectural consequences.
In my role as project manager, I pronounce the VNode class to be completely
sound, for the reasons just given.
See the Post Script for a summary of possible minor changes.
This thread is the place for all comments and debate about Leo's VNode
class.
Edward
P.S. Here is the "secretary's report", a summary of possible changes
related to Leo's VNode class:
1. A new c.new_vnode() method would slightly simplify the creation of new
VNodes by making it unnecessary for clients to import leo.core.leoNodes.
2. Leo could use c.restoreCursorAndScroll() instead of
v.restoreCursorAndScroll().
Ditto for c/v.saveCursorAndScroll().
Imo, having v.restoreCursorAndScroll() call a commander method is, for all
practical purposes, completely benign. This detail does not violate the
boundary between VNodes and the rest of Leo. In particular, code that uses
VNodes is completely unaware of this harmless "connection".
EKR
--
You received this message because you are subscribed to the Google Groups
"leo-editor" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To post to this group, send email to [email protected].
Visit this group at https://groups.google.com/group/leo-editor.
For more options, visit https://groups.google.com/d/optout.