[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