[Lazarus] Code Structure / SourceEdit and SyneEdit [Re: Mouse Link in SynEdit (only link-able items)]
Martin Friebe
lazarus at mfriebe.de
Sun Dec 14 14:30:59 CET 2008
Hans-Peter Diettrich wrote:
> Martin Friebe schrieb:
>
>> A couple of remarks:
>> -Individual drawer objects fro Gutter and TextArea (I will avoid Grid in
>> the name, Grid is a specialization)
>>
> The Grid is a hint on the organisation of the canvas, in rectangular
> cells. I've spent a lot of time in the various coordinate systems and
> their mapping, for drawing purposes (relative to the window), document
> view (rows/columns, scrolling, line wrapping), and document storage
> (folding, tab expansion, UTF encoding). The need for properly anchored
> bookmarks was a big challenge. Similar, but finally easy to implement,
> was the preservation of the cursor column, when scrolling across lines
> of shorter lenght.
>
The only reason I did skip the "grid" in the name is that a generic
painter base class will not define a griod (or maybe it will but based
on pixels). So it does not block anyone from implementing a proportional
painter
>> This has been started for the Gutter. It does need a lot of clean up still.
>> It would also benefit from the Os-Handle and canvas being moved into a
>> wrapper class. This would:
>> - avoid the need to callback synedit for Invalidates
>> - allow a Handle/Canvas of another Component being passed to SynEdit,
>> and SynEdit painting the other component (e.g. SourceEditor)
>>
> Here we may have quite different viewpoints, on the delegation of the
> responsibilities.
>
That canvas/handle holder does not replace the (Grid)Painter. It is used
by the GridPainter(s), and used by the GutterPainter(s). It may even be
thyat it does not need to exist, and canvas and handle can be passed to
all the Painters in Form of their LCL classes.
As It currently stands all the info about everything is hold by the Main
SynEdit Class, and all other classes need to ask the central SynEdit
Class. That is undesirable.
I extracted 2 classes already (but the ove isn't complete)
- TSynEditCaret:
Storing all info about the caret. It will obviously need help from
other modules, to deal with tabs, double-width chars, wrapped lines
(that will probably be a specialized subclass, completely replacing the
original), and other things
-TSynEditSelection:
To deal with the selected block. This one is not very related to this
discussion. But it has the same needs as the Caret
I will have to add a TSynEditViewPortClass:
This will at least store the Coordinates of the screen in the text (as
in TopLine/LeftChar - LinesInWindow/CharWidthOfScreen), maybe a bit
more. To do so, it will need access to the Painter to get information
about the grid (LineHeight, SingleCharWidth)
>> Similar considerations for the Textarea. However the textdrawer will
>> have to access a lot of other objects, and need ways to merge the
>> result. there are
>> - the highlighter
>> - the MarkUpManager
>>
> In my solution the highlighting (including hyperlinks) is implemented in
> derived classes, by overriding the line-painting method. I ended up in a
> single array, holding the scanner start state for every line - required
>
If I understand your description correct: This special array is
currently part of SynEditCodeBuffer? In any case this is information for
the highlighter. The (Grid)Drawer should never access this info
directly. The grid drawer will ask the highlighter (In the current
Synedit there may be a need to clean up the way the Highlighter is
handled...)
> for proper handling of multi-line comments. Hyperlinks are implemented
> as special highlighting information. When the mouse pointer moves, the
> according characters and attributes are obtained for the current line,
> from the document, then the line eventually is repainted when the
> "active" state of a hyperlink has changed.
>
For Hyper links, had you have a look at the MarkUp class?
(SynEditMarkupCtrlMouseLink in
components\synedit\syneditmarkupctrlmouselink.pp; there is stil some
remains in central Synedit that need moving)
Again this class does not replace the GridDrawer. It is to be used as a
helper class by any GridDrawer.
I have no information about the internals of you r grid drawer, and how
it delegates work and responsibilities, so I can not really comment on it.
I don't know how your grid drawer deals with all the different
tasks/responsibilities, it has to meet (especially within the
LineDrawing Class). You talk a lot of sub classes that implement the
individual bits. This may be needed (and probably is at least for
WordWrapping).
But I believe that the Griddrawer (and it's LineDrawer) should solve a
lot by delegate to helper classes?
Of course this is easily said. And I haven't yet got the full design for
how I will/would do the GridDrawer.
>> - The TextLines itself (e.g. Highlighter token will return a tab as a
>> tab, but the TestLines need to translate this into displayable chars. Either
>> -- spaces
>> -- "show special chars" >" (and if tab is only one char, then maybe
>> there is only one ">" followe by spaces?
>>
> Right, tab expansion is highly configurable in my solution :-)
>
In which way?
I'd like to think of the final Synedit as a collection of some mandatory
and some optional Classes. If you need a feature, you instantiate the
apprpriate Class, either as a kind of plugin, or as a
replacement/wrapper for the default class.
So as far as I am concerned the GridDrawer should ask the
TabHandlingClass how to deal with tabs.
In my current design the TabHandlingClass will be a ViewClass on the
LineBuffer. This allows to hand it in to caret and other point-classes
that need to do byte to screen-char conversation. (Have a look at the
current caret class, it has some initial code for this)
>> This can only be done by the Lines, as they know the layout/tabwidth
>> (see concept of Views/ TabView below)
>>
> Right, the handling of wrapped lines also was a challenge :-)
>
Yes, It will be.
for the TextDrawer it will probably be a another TextView on the
Linebuffer (very similar to the FoldedView). Dealing with the scrollbars
correctly could be "interesting".
It may also need to substitute the caret class, or the view-point class,
to deal with questions like: is the caret in the visible area.
>> The storage and view of the TextLines:
>> I think TSynEditCodeBuffer is a good start for this. Yet tabs are a
>> specialisation, that should go into a ViewClass.
>> ViewClasses TSynEditStrings themself, that will modify how the stored
>> text is seen. (TrimTrailingSpaces is an example. FoldedView too, so
>> FoldedFiew does not yet fully follow the concept)
>>
>
> I've left folding to the document management, for any convenient
> implementation. The visual component manages visible lines only, the
> mapping between stored and visible lines must be implemented outside of
> it. Including notifications of the changed line count, when text blocks
> are collapsed or expanded. If desired, multiple views can have different
> blocks collapsed, different tab width, wrapping on different screen
> boundaries etc. The consideration of multiple views reveals clear
> frontiers for the various responsibilities (what feature to implement
> where).
>
Yes, that's true, multiply views need to be considered. But if you look
at the concept of FoldedView, then several SynEdits viewing the same
LineBuffer can have each there own FoldedView (or tabbedView /
WordWrappedView, once they is there).
>> The following Views (and others) can apply to the text
>>
>> - WordWrapView
>> - FoldView
>> - TabView or ElasticTabView (http://bugs.freepascal.org/view.php?id=9650)
>> modifies Logical(byte) to Phisical (char on screen) calculations.
>> probably still returns tabs, but offers conversation methods.
>> - replacing tabs with spaces in the Strings[] property, may
>> complicate Highlighting and MarkUp and show special chars
>> It would also impact the ability of keeping the caret from being
>> placed in the middle of a tab (which currently can be done)
>> - TrimSpaceView
>> - TSynEditStringBuffer (holding the actual text)
>>
>
> IMO all this is already perfectly implemented in my CharGrid.
>
> With regards to the ElasticTabView, I have my own opinion on a mix of an
> source code editor with a text processor or page layouter - it stinks :-(
>
That's why I like the idea of plugins. Anyone who wants to have it can
have it. Anybody else should not be affected :)
> I neither like subroutine arguments indented to the "(" of the call, nor
> block comments to the right of source code, sensitive to insertion or
> deletion of lines of code. I don't want to open a can of worms for
> people who put more emphasis on the appearance of their(?) source code,
> than on its functionality. It's more annoying when the caret disappears
> in the last line or column of the window, as I observed in the current
> SynEdit implementation.
>
As I said I don't know anything about the internal structure of you
code. One consideration is, I am not going to replace the whole current
solution (or a major part of it) in a single step by anything new. (This
applies for your code as well as any bold single step move to synedit
version 2)
Even provided that your code is matured very well by now, just the work
of integrating it will create a huge amount of bugs. This is unless it
comes with a full test case for SynEdit providing test for each and
every feature, and providing at least 95% coverage of visited code /
condition / branches.
This does not reject your code. As I said I do hardly know about it. But
as far as I am concerned it would mean to integrate it step by step. It
may also mean changes to it, depended were we would meet on the various
goals each of us has described. If you look at the current LazSynEdit
(not the one you had when you started) and you think that you can
provide patches (based on your work) to improve it, then I am happy to
look at them.
If they would require changes to the class structure I had in mind, then
it will be better to discuss those changes first. I also welcome any
ideas how to improve the class idea I have in mind, so if you think any
particular bit of it should be done different from what I said, then
feel free to point it out.
It may even be possible to modularize SynEdit enough that it can chose
between different internal frameworks. Your framework would then be one
of it (admitting that getting SynEdit there is a bold target).
Anywhere, since I pointed you to a lot of structures in the current
SynLazCode. Is your code available anywhere to be studied. (I can't
promise when I will get to this).
Best Regards
Martin
More information about the Lazarus
mailing list