Recent

Author Topic: Invite to Colorizing TSynEdit ! {Improving Editor for Editing Complex Codes}  (Read 157974 times)

Edson

  • Hero Member
  • *****
  • Posts: 1301
1-the whole job consist of two parts :
1-1-coloring the keywords with different colors on different depths that seems to be possible just by extending the Syntax Highlighter class
1-2-drawing the vertical lines behind the code blocks that seems to need editing the main TSynEdit class

IMHO, the first part is entirely work for the highlighter.

SynFacilSyn have a mode of colour blocks using level of nesting. It does something like:

Code: Pascal  [Select][+][-]
  1. function TSynFacilSyn.GetTokenAttribute: TSynHighlighterAttributes;
  2. ...
  3.     case ColBlock of
  4.     cbLevel: begin  
  5.         Result.Background:=RGB(255- CodeFoldRange.CodeFoldStackSize*25,255- CodeFoldRange.CodeFoldStackSize*25,255);
  6.       end;
  7. ...
  8.  

I remember having some problems on getting nesting information from TSynCustomFoldHighlighter. IIRC it was not designed to manage the block information properly. It was designed just for folding.
Lazarus 2.2.6 - FPC 3.2.2 - x86_64-win64 on Windows 10

Martin_fr

  • Administrator
  • Hero Member
  • *
  • Posts: 9792
  • Debugger - SynEdit - and more
    • wiki
Just because it is possible in the HL, doesnt mean it should be there.

Each feature should have its own class (or at least not be spread to more places than absolutely needed). That makes it more maintainable.
Not all of SynEdits code is like that yet, but I spent lots of times over the year to move into that direction. That is why even the highlight of the selected text is done in a markup class. It keeps the painting code more generic.

The vertical lines, have nothing to do with the HL at all. And the paint-class is generic to all languages, so the vertical lines must be done in a place in the middle.
So if one part of it can/should not be done in the HL, then why split it into different places, when it can be done all in one place?

If it should be included in the official release, then markup is the place to go.

Quote
Defining a variable for keeping current token depth that will increase on StartCodeFoldBlock and decrease on EndCodeFoldBlock.

This exists, and is accessible from the Markup. The foldlevel is explained on the wiki I linked.
I also gave you the name of a class that helps searching them.


Martin_fr

  • Administrator
  • Hero Member
  • *
  • Posts: 9792
  • Debugger - SynEdit - and more
    • wiki
IMHO, the first part is entirely work for the highlighter.

I agree that it is easy to archive in the HL. Though it is also easy in Markup, as it would still be based on the same code.

As to "Should it be in the HL", that depend on the bigger design of SynEdit, and the exact definition of what it should archive.

If the goal was to write code that
- does only the keywords, bet NOT the vertical lines
- that can only work on pascal, and due to its properties can never possible work on other languages
then the HL is the place.

But with the exception of incorrect implementation, there is nothing to stop this from working on lfm, html, .... Even if not implemented today (and that is fine), it can be easily extended later (without making copies of the same code in every HL)

Also since it needs the vertical lines, it can not go into the HL. SynEdit (the lazarus variant) had the markup classes specially designed for this.

A-M

  • New Member
  • *
  • Posts: 13
Ok martin due to your experience in this field your opinion should be more accurate , so i'm going on TSynEditMarkup way ...
unfortunately i didn't found any sample or good explained document about markup classes till now

PS: i don't know why ! but i think it will takes just a few hours for you to add this excellent feature to SynEdit  ;)
Like a circle in a spiral
Like a wheel within a wheel
Never ending or beginning,
On an ever spinning wheel
Like the circles that you find
In the windmills of your mind

Martin_fr

  • Administrator
  • Hero Member
  • *
  • Posts: 9792
  • Debugger - SynEdit - and more
    • wiki
Well it is on my list, and has been there for some long time.  Right now I have other priorities.

One of the issues is, that I would only do this if I have time to optimize it, so the amount of searching can be minimized.
On a desktop it does not matter, on a laptop (used by many) it drains the battery.

But I accept a patch without this optimization.

Code: Pascal  [Select][+][-]
  1.     Procedure PrepareMarkupForRow(aRow : Integer); virtual;
  2. // called before each row that needs painting.  May only be part of the screen, and skips folded lines
  3. // normally top down, but if you relay on it, verify it in code (compare to last line no, if you stored that)
  4.  
  5.  
  6.     Procedure FinishMarkupForRow(aRow : Integer); virtual;
  7. // when the line is painted
  8.  
  9.     Procedure EndMarkup; virtual;
  10. // when all lines are painted / reset any cache, that is not otherwise reseted
  11.  
  12.     Procedure GetNextMarkupColAfterRowCol(const aRow: Integer;
  13.                                           const aStartCol: TLazSynDisplayTokenBound;
  14.                                           const AnRtlInfo: TLazSynDisplayRtlInfo;
  15.                                           out   ANextPhys, ANextLog: Integer); virtual; abstract;
  16. // set ANextPhys, ANextLog to the next pos at which you need to change color
  17. // start or end of "begin"
  18. // pos of vertical line
  19.  
  20.     Function  GetMarkupAttributeAtRowCol(const aRow: Integer;
  21.                                          const aStartCol: TLazSynDisplayTokenBound;
  22.                                          const AnRtlInfo: TLazSynDisplayRtlInfo) : TSynSelectedColor; virtual; abstract;
  23. // return markupinfo with desired color from aStartCol to GetNextMarkupColAfterRowCol
  24. // the colors will be merged according to their priorities
  25.  
  26. // I need to remind myself, or you search existing code, when setting frame start/end (vertical line) you need to call MarkupInfo.SetFrameBoundsLog
  27.  
  28.  

priorities: check out the ide color config

In order to have many colors add more markupinfos, then return the correct one in GetMarkupAttributeAtRowCol(

try and understand SynEditMarkupWordGroup. this is essential.

Then look at TLazSynEditNestedFoldsList  it can help you find all begin end on current line and all the ones you are nested in.



Edson

  • Hero Member
  • *****
  • Posts: 1301
Personally, I'm no sure if many colors on the editor, can help on visualizing better the code. I prefer to have background colors.

Vertical lines, can be useful. I see it like an extension of the folding marks.

I like the way Notepad++ highlight the color of the folding lines, according to the cursor position.
Lazarus 2.2.6 - FPC 3.2.2 - x86_64-win64 on Windows 10

A-M

  • New Member
  • *
  • Posts: 13
Personally, I'm no sure if many colors on the editor, can help on visualizing better the code. I prefer to have background colors.

Vertical lines, can be useful. I see it like an extension of the folding marks.

I like the way Notepad++ highlight the color of the folding lines, according to the cursor position.

there is really not many colors ... a color for a certain block depth & most of the time there is maximum 4 colors on the screen for keywords
and the colors can be very similar if you want

did you worked with delphi when colorized by CNPack ? its really amazing and your mind will not be weary even after several hours of coding
i really don't know any serious and not retired  delphi programmer around me who not using CNPack

vertical lines are what the modern & professional IDE like IntelliJ equipped with and i believe the small area of brackets in languages other than pascal is not suitable for coloring the code blocks ...
Like a circle in a spiral
Like a wheel within a wheel
Never ending or beginning,
On an ever spinning wheel
Like the circles that you find
In the windmills of your mind

A-M

  • New Member
  • *
  • Posts: 13
I would confess that the job is harder than i thought ....
there is a lot of records , classes and functions that needs a long time to understand
but i really want this feature on Lazarus so there is no surrender

I have to ask you martin for explanation on each one but please don't look at this as a waste of time because i will integrate all as a Wiki Page , if God will.
Like a circle in a spiral
Like a wheel within a wheel
Never ending or beginning,
On an ever spinning wheel
Like the circles that you find
In the windmills of your mind

Martin_fr

  • Administrator
  • Hero Member
  • *
  • Posts: 9792
  • Debugger - SynEdit - and more
    • wiki
The 3 main steps (in that order) / use latest svn


Well play around with the markup to see how it works, subclass it.
- Add it in the synedit constructor (find the others) line 2085
     fMarkupManager.AddMarkUp(....);
- add a color (background/frame) to its YourMarkup.markupinfo
- return it conditonal (eg odd lines) as a result of GetMarkupAttributeAtRowCol
- start setting AnextLog/Phys column in GetNextMarkupColAfterRowCol (dependend on AstartCol), and return markupinfo in GetMarkupAttributeAtRowCol dependend on color.

That should give you the basic feeling of how it works.
btw Markupmanager should probably be public in SynEdit.

You need more than one color? Add more MarkupInfo objects.

Then you can check how to only return a vertical line (frame on left only) SetFrameBound()

-----------
Once you know the markup

to find token on the current line
Code: Pascal  [Select][+][-]
  1. FFoldNodeInfoList := AHighlighter.FoldNodeInfo[ALine];
attention the result must be freed using "FFoldNodeInfoList.ReleaseReference()"

This is used in SynEditMarkupWordGroup;

Look at it, and explore the classes/records.

Node that there are 2 foldlevel
1) level for items that are configured to be foldable by the user (not of interest)
2) level for all items. (that you need)

This allows you to add colors to the begin/end etc.

-----------
look at ide/sourcesyneditor
TIDESynEditor.SrcSynCaretChanged
Code: Pascal  [Select][+][-]
  1.       List := TextView.FoldProvider.NestedFoldsList;
  2.  
this returns all the begin from previous lines, that the current line is nested in, so all the x pos for vertical lines


Martin_fr

  • Administrator
  • Hero Member
  • *
  • Posts: 9792
  • Debugger - SynEdit - and more
    • wiki
I added a public property in svn/trunk
SynEdit.MarkupManager

So you can drop a SynEdit on a form, subclass your own Markupclass, and play with it.

A-M

  • New Member
  • *
  • Posts: 13
thank you for your explanations , i will ask my questions ...

I added a public property in svn/trunk
SynEdit.MarkupManager
i got it
why is this private in normal releases ?
Quote
    property MarkupManager: TSynEditMarkupManager read fMarkupManager;
Like a circle in a spiral
Like a wheel within a wheel
Never ending or beginning,
On an ever spinning wheel
Like the circles that you find
In the windmills of your mind

A-M

  • New Member
  • *
  • Posts: 13
Q1: what the caret really are in SynEdit ? text blinking cursor as written on wiki ?
or can we say simply this is a method for specifying a point on TSynEdith ?
if its just for cursor position so can we say this is not related to our MarkUp class ?

Q2: in below procedure its just three keywords (tokens) to find
Code: Pascal  [Select][+][-]
  1. procedure TSynEditMarkupWordGroup.FindMatchingWords(LogCaret: TPoint;
  2.   out Word1, Word2, Word3: TWordPoint);

it seems enough for drawing vertical lines but what about coloring the keywords on each depth ?
it will be unlimited keywords (tokens) that can be related to each other (on the same depth)...
similar to below code


Code: Pascal  [Select][+][-]
  1.      
  2. while True do
  3.      if x then
  4.      begin
  5.      //...
  6.      end
  7.      else if y then
  8.      begin
  9.      //...
  10.      end
  11.      else ...
  12.  

what is the solution ? using a dynamic array for keeping related keywords (tokens) ? or what ?

Q3: how we want to draw that vertical lines ? using GetMarkupAttributeAtRowCol ? if son i think this will not be a continuous line ...
or directly using SynEdit's Canvas for drawing the line ? or what ?
« Last Edit: October 24, 2015, 11:18:43 pm by A-M »
Like a circle in a spiral
Like a wheel within a wheel
Never ending or beginning,
On an ever spinning wheel
Like the circles that you find
In the windmills of your mind

A-M

  • New Member
  • *
  • Posts: 13

to find token on the current line
Code: Pascal  [Select][+][-]
  1. FFoldNodeInfoList := AHighlighter.FoldNodeInfo[ALine];
attention the result must be freed using "FFoldNodeInfoList.ReleaseReference()"

This is used in SynEditMarkupWordGroup;

Look at it, and explore the classes/records.

as i seen there is no such a thing in SynEditMarkupWordGroup even in svn ...
Are you sure ?
Like a circle in a spiral
Like a wheel within a wheel
Never ending or beginning,
On an ever spinning wheel
Like the circles that you find
In the windmills of your mind

A-M

  • New Member
  • *
  • Posts: 13
look at ide/sourcesyneditor
TIDESynEditor.SrcSynCaretChanged
Code: Pascal  [Select][+][-]
  1.       List := TextView.FoldProvider.NestedFoldsList;
  2.  
this returns all the begin from previous lines, that the current line is nested in, so all the x pos for vertical lines

if we draw the vertical line from (x,y1) to (x,y2) ? what about y ?
Like a circle in a spiral
Like a wheel within a wheel
Never ending or beginning,
On an ever spinning wheel
Like the circles that you find
In the windmills of your mind

Martin_fr

  • Administrator
  • Hero Member
  • *
  • Posts: 9792
  • Debugger - SynEdit - and more
    • wiki
Quote
why is this private in normal releases ?
It will be public in 1.6 release. I didnt make it public when I first implemented it, in case it would still change. Then I forgot.

Quote
Q1: what the caret really are in SynEdit ? text blinking cursor as written on wiki ?
or can we say simply this is a method for specifying a point on TSynEdith ?
if its just for cursor position so can we say this is not related to our MarkUp class ?

Normally it refers to the blinking line. Put the term is often used for variables that hold a XY coordinate.
Actually those variable are misnamed. "LogCaret" in this case should be "LogXY", since it is not a caret. (You got me there)

Quote
it seems enough for drawing vertical lines but what about coloring the keywords on each depth ?
it will be unlimited keywords (tokens) that can be related to each other (on the same depth)...
similar to below code
I suggest to get back to this when you done:
- getting to know TSynEditMarkup
- highlight keywords on each line

But as I wrote in my 3rd point TextView.FoldProvider.NestedFoldsList; / TLazSynEditNestedFoldsList  will do help you.

This list, if created for a line will find ALL blocks that are open at the begin of that line.

Actually with
Code: Pascal  [Select][+][-]
  1. List.IncludeOpeningOnLine := False;
it might help for the current line too.

This should include those, that open on the current line.
You still need those that close at the current line.

Code: Pascal  [Select][+][-]
  1. procedure TIDESynEditor.SrcSynCaretChanged(Sender: TObject);
  2.   function RealTopLine: Integer;
  3.   begin
  4.     Result := TopLine - TSourceLazSynSurfaceManager(FPaintArea).TopLineCount;
  5.   end;
  6. var
  7.   NodeFoldType: TPascalCodeFoldBlockType;
  8.   List: TLazSynEditNestedFoldsList;
  9. begin
  10.   if (not FShowTopInfo) or (not HandleAllocated) or (TextView.HighLighter = nil) then exit;
  11.   if FSrcSynCaretChangedLock or not(TextView.HighLighter is TSynPasSyn) then exit;
  12.  
  13.   if TextView.HighLighter.NeedScan then begin
  14.     FSrcSynCaretChangedNeeded := True;
  15.     // USING TLazSynEditNestedFoldsList   for this line
  16.  

will give you:
* from line 1 procedure
* from line 9 begin
* from line 13 begin

I am not sure it includes the "then". But that is covered by the begin in that line.

that are the ones for which you need a vertical line on this text line.

Doing so for every line over and over again. (And that is where caching may be needed. / I do not remember, maybe TLazSynEditNestedFoldsList actually does that already)

Quote
Q3: how we want to draw that vertical lines ? using GetMarkupAttributeAtRowCol ? if son i think this will not be a continuous line ...

Set the left frame side. If testing shows that is not enough then we need a new attribute on MarkupInfo : TSynSelectedColor.
Drawing directly on the canvas would not support the priority and alpha info on TSynSelectedColor.

Having said that, the drawer will need a fix for continuous dotted lines, but I suggest to discuss that at the end.

Quote
as i seen there is no such a thing in SynEditMarkupWordGroup even in svn ...
Are you sure ?

As I wrote:
look at ide/sourcesyneditor
TIDESynEditor.SrcSynCaretChanged

Quote
if we draw the vertical line from (x,y1) to (x,y2) ? what about y ?

sorry not sure what you mean.
« Last Edit: October 25, 2015, 02:15:04 am by Martin_fr »

 

TinyPortal © 2005-2018