Unfortunately it is not the Highlighters job.
The highlighter is doing the basic parsing. Though the line is not 100% clear in this case. But the Markup is match better suited.
The Paint class paints the text token by token. It pulls them from the HL but they have a long way.
pardon the intrusion, you meant something similar like SynUniHighlighter (https://www.openhub.net/p/unihighlighter) or SynFacilSyn (https://github.com/t-edson/SynFacilSyn) ? (first seems hard to find, here (https://github.com/alrieckert/lazarus/tree/master/components/synunihighlighter) perhaps ?)you are welcomed to take a part in discussion
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
Defining a variable for keeping current token depth that will increase on StartCodeFoldBlock and decrease on EndCodeFoldBlock.
IMHO, the first part is entirely work for the highlighter.
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.
I added a public property in svn/trunki got it
SynEdit.MarkupManager
property MarkupManager: TSynEditMarkupManager read fMarkupManager;
to find token on the current lineattention the result must be freed using "FFoldNodeInfoList.ReleaseReference()"
FFoldNodeInfoList := AHighlighter.FoldNodeInfo[ALine];
This is used in SynEditMarkupWordGroup;
Look at it, and explore the classes/records.
look at ide/sourcesyneditor
TIDESynEditor.SrcSynCaretChangedthis returns all the begin from previous lines, that the current line is nested in, so all the x pos for vertical lines
List := TextView.FoldProvider.NestedFoldsList;
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.
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 ?
it seems enough for drawing vertical lines but what about coloring the keywords on each depth ?I suggest to get back to this when you done:
it will be unlimited keywords (tokens) that can be related to each other (on the same depth)...
similar to below code
Q3: how we want to draw that vertical lines ? using GetMarkupAttributeAtRowCol ? if son i think this will not be a continuous line ...
as i seen there is no such a thing in SynEditMarkupWordGroup even in svn ...
Are you sure ?
if we draw the vertical line from (x,y1) to (x,y2) ? what about y ?
property Line: TLineIdx read FLine write SetLine;"Idx" that means almost always 0 based.
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. ...
there is really not many colors ... a color for a certain block depth & most of the time there is maximum 4 colors ...
..
There is a method PrepareMarkupForRow in which you should prepare this list.
--------------------
There will be calls to
Procedure GetNextMarkupColAfterRowCol(const aRow: Integer;
const aStartCol: TLazSynDisplayTokenBound;
const AnRtlInfo: TLazSynDisplayRtlInfo;
out ANextPhys, ANextLog: Integer); virtual; abstract;
Find the first pos after aStartCol on which you need to draw a line, in your list of blocks and return it.
and in GetMarkupAttributeAtRowCol
wait until that column is due. then return a markup with a frame on the left side only (and set the frameStartBound (search examples in other markup)
Now, the problem is it only care about the folding signal.
So, how we can continue this progress to care of pascal keywords (while, do, if, then .. ) ?
if an "if" or "while" has a begin/end: will they both highlight?Yes it is. Maybe they (if-then-begin-end-else-begin-end) should be painted in one color per level.
If you have a begin block, you can check the outer block.sfaMarkupOutline is not found in current lazarus trunk, okay?
But maybe better, the HL has sfaMarkup to indicate word pair highlight. It may introduce sfaMarkupOutline. Then the HL can determine which nodes to outline and which not. (so the markup will stay independent)
--------------
When this works there may be a need to add things to the painter, because if a vertical line is dotted or dashed, it will not look good. A new fragment of it is painted for each text line, and that resets the dotted pattern.
This needs to be fixed in the painter, but this is for the very end.
At this moment the only thing that goes outside is a fix in the painter for dotted vertical lines continued over several lines of text.I disagree.
They still need to be drawn in individual bits per text line, in order to obey priority against other lines. The painter can use clipping to ensure they are joined correctly
* The [sfbIncludeDisabled] doesn't work, because No method ables to make it applicable to NodeList.property FoldFlags: TSynFoldBlockFilterFlags
* Yes, nested "while-do--while-do;" can be painted by one color. Therefore "if-then-else--if-then-else" too;
the higlighter reports inconsistency in if~then block. see the attachment.if-then-else are 2 or even 3 blocks. The "else" is in both.
I am now working in drawing vertical lines.What cache?
I found that most efficient way to draw these lines is by: just reading the cache.
so I will move the PrepareMarkupForRow's content into DoTextChanged.DoTextChanged can be called a lot more often. THat is not a good idea. It can also be called for of screen changes.
I also play with the TSynFoldAction. The cons is it work only with 'and' mode, while I need the 'or' mode.Then do not filter at all, and filter the result you read. Or suggest a way to add it to the list, and a patch can be discussed.
So, NodeList.ActionFilter := [sfaSingleLine,sfaMultiLine]; (or similar combination) doesn't work.
The funny thing: It's running well: NodeList.ActionFilter := []; 8-)Yes, IIRC filter only filters, if at least one filter is set.
So, I keep this way.
The cons of current TSynPascalSyn is : It doesn't report the "with~do" as fold signal, while the "repeat~until" works.ok, so "with" and "while" are both missing.
Quote from: Martin_fr on December 01, 2015, 11:44:22 pmthe HL can be modified to deliver correct info for that, later more.
if an "if" or "while" has a begin/end: will they both highlight?
Yes it is. Maybe they (if-then-begin-end-else-begin-end) should be painted in one color per level.
But, configurable (highlighted or not) may be better for other user.
Yes you use the frame left site.When this works there may be a need to add things to the painter, because if a vertical line is dotted or dashed, it will not look good. A new fragment of it is painted for each text line, and that resets the dotted pattern.
This needs to be fixed in the painter, but this is for the very end.At this moment the only thing that goes outside is a fix in the painter for dotted vertical lines continued over several lines of text.I disagree.
They still need to be drawn in individual bits per text line, in order to obey priority against other lines. The painter can use clipping to ensure they are joined correctly
I have different approach that doesn't require the change of painter:
* We can use "GetMarkupAttributeAtRowCol()" as a command instruction to painter, and let the only painter do painting work.
* what we send to painter is just as usually (X,X2, colorForeground, backgroundColor, framecolor).
The trick is we send instruction to the painter to paint only the left border on given highlighted area.
* but the only problem is: how to send the "bsLeft" part of TLazSynBorderSide ? Is the TSynEditMarkup.MergeMarkupAttributeAtRowCol the right place to do that? :o
QuoteI am now working in drawing vertical lines.What cache?
I found that most efficient way to draw these lines is by: just reading the cache.Quoteso I will move the PrepareMarkupForRow's content into DoTextChanged.DoTextChanged can be called a lot more often. THat is not a good idea. It can also be called for of screen changes.
It is meant to be used to invalidate cached data, usually be setting a flag (which does not cost time) "InvalidateNeeded := true;"
What cache?The NodeList thats done calculated outside the PrepareMarkupForRow, that is cache in my mind.
Well, that means we still use PrepareMarkupForRow?, and DoTextChanged was not the right place to save the open~close--open~close folding list through several lines?Yes, even so that I have not yet seen what is in DoTextChanged.
IMHO, Then the problem (with current version of SynEdit) is: identifiying of verticals line couldn't be efficient.
Further more progress / bugfix:
if-begin-end vs else-begin-end coloring bug was my fault !! :-[ I've resolved that.
Now, the problem of nested-color is the ChildProcedure-begin-end. It should has different level than the MainProcedure-begin-end.
But I found some bug made by me. Such as when a "begin" moved to the left (by deleting trailing space).
I will fix them later.
-----------------------------
You need to add invalidation calls.
Add or remove space in front of a "begin" (then resize the editor to invalidate)
In real world, this SynMarkup can help programmer to quickly identify the wrong logic caused by any mismatch of block-sign (such as missing block "end").
It's crucial feature when we work in complex nested block code such PHP, Pascal, and XML based code.
Yes and No.In real world, this SynMarkup can help programmer to quickly identify the wrong logic caused by any mismatch of block-sign (such as missing block "end").
It's crucial feature when we work in complex nested block code such PHP, Pascal, and XML based code.
Does it work on the indentation rather than on open/close pairs ?
To make this useful for any other SynHighlighter, i think we need to add some nice feature to TSynCustomFoldHighlighter such easy way to find any pair of begin~end, or C++ "{"~"}" enabled by default.What I refer to begin~end, or C++ "{"~"}" is the X/Y location of that (act as opening folding & closing mark)
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.
Well, This SynEdit Fold Color is only works fine with TSynHighlighterPasSyn.
To make this useful for any other SynHighlighter,
i think we need to add some nice feature to TSynCustomFoldHighlighter 8-)
such easy way to find any pair of begin~end, or C++ "{"~"}" enabled by default.
I think it would be better when the bugfix happens in TSynCustomFoldHighlighter (SynEditHighlighterFoldBase.pas),
Because, the job (invalidate rest line) has already been done (without my bugfix)
when I delete "b" from "begin", Also, when I edit again the "egin" into "begin".
Well, This SynEdit Fold Color is only works fine with TSynHighlighterPasSyn.Yep, some code needs to be factored up from pas to the base class.
To make this useful for any other SynHighlighter,
i think we need to add some nice feature to TSynCustomFoldHighlighter
It's crucial featureagreed, and I try to allocate as much of my time as I can.
I need to check if -1 is used anywhere....
function TLazSynFoldNodeInfoList.Count: Integer; begin //if not FValid then exit(-1); if not FValid then exit(0); //x2nie
So only your code can know if an edit in a code-line changes the vertical lines. You then need to know how many lines below need to be invalidated.
You probably do not need to store the height of any vertical line. ...I did it. I've able to identify when a 'begin' is moved or not, by comparing the both texts :
But you need to know when a vertical line moves x pos.
if my bugfixed invalidation runs, so the rest lines possibly invalidated twices !QuoteI think it would be better when the bugfix happens in TSynCustomFoldHighlighter (SynEditHighlighterFoldBase.pas),
Because, the job (invalidate rest line) has already been done (without my bugfix)
when I delete "b" from "begin", Also, when I edit again the "egin" into "begin".
The HL does indeed some of the invalidation. If a begin is inserted/removed it will invalidate down to the last affected end (potentially end of file).
But: if a "begin" simply changes x pos (add a space on that line, but somewhere before the begin) then for the HL no invalidation is needed. SynEdit will invalidate just that line (sometimes 1 or 2 extra)
Both of the above are as intended.
Thanks you!QuoteWell, This SynEdit Fold Color is only works fine with TSynHighlighterPasSyn.Yep, some code needs to be factored up from pas to the base class.
To make this useful for any other SynHighlighter,
i think we need to add some nice feature to TSynCustomFoldHighlighterQuoteIt's crucial featureagreed, and I try to allocate as much of my time as I can.
I think the easiest way is when HL puts this info on StartCodeFoldBlock & EndCodeFoldBlock.Yes that code needs to move up to the base class.
PasHl has a lot of custom code. I wouldnt move it just yet.All above is done. I have created TSynColorFoldHighlighter that can be used by other HL as base class for quick creating HL with folding+ nested color ability. Yes, it harvest the node info only when requested, so it is efficient.
Add virtual methods to the base class with whatever generic code is possible, ....
It is important to collect node info only when requested. (per line). So it does not slow down normal hl scans.
Then you can start getting code for the other HL.
All above is done. I have created TSynColorFoldHighlighter that can be used by other HL as base class for quick creating HL with folding+ nested color ability. Yes, it harvest the node info only when requested, so it is efficient.
Yesterday and before, I experimented with adding properties directly to TSynCustomHighlighterRange.Not sure what you did, but the range (and all data/classes held by it) must be immutable.
It was more efficient because doesn't need to re-scan, no recalculation needed.
Now the problem is TLazSynEditNestedFoldsList doesn't work at all. It is a bit mysterious for my brain.
Any clue ?
Owh? okay, I think that is good idea. 8).. TSynColorFoldHighlighter can be used by other HL as base class.. The aim should be to patch the TSynCustomFoldHighlighter.
for quick creating HL with folding+ nested color ability. Yes, it harvest the node info only when requested, so it is efficient.
The vertical lines (generated by nested fold info) aren't painted. I guess it will be solved by following your (last post) guidance.QuoteNow the problem is TLazSynEditNestedFoldsList doesn't work at all. It is a bit mysterious for my brain.what exactly goes wrong?
Any clue ?
The vertical lines (generated by nested fold info) aren't painted. I guess it will be solved by following your (last post) guidance.
looks like it fails here
if l >= GrpCnt[t] then continue;
maybe AHighlighter.FoldBlockEndLevel()
when debuginh the "-(-" HL, GrpCnt[t] is always 0, that is wrong, it is set in InitSubGroupEndLevelsIt is now too complicated for me to support both old and new classes.
so I guess AHighlighter.FoldBlockEndLevel() is not working.
on the other hand, I suggest: drop the pas level again.I am ready now. What is exactly the idea of "drop the pas level"?
the levels in the HL are not always correct for your purpose of color picking. and even if they are now, they may change....
see my earlier mail about counting while going through the list.I didn't understand of what and how of that counting... . Would you like to be more specific?
Well, This SynEdit Fold Color is only works fine with TSynHighlighterPasSyn.
To make this useful for any other SynHighlighter,
i think we need to add some nice feature to TSynCustomFoldHighlighter 8-)
such easy way to find any pair of begin~end, or C++ "{"~"}" enabled by default.
Well, I felt sad when discovered "TSynCustomFoldHighlighter" has not complete information about the fold levels, when I was designing my highlighter: http://forum.lazarus.freepascal.org/index.php/topic,21727.msg145713.html#msg145713 (http://forum.lazarus.freepascal.org/index.php/topic,21727.msg145713.html#msg145713)
@Edson, can we continue discussing that here? if you still want to design a folding-highliter.
Fold Level SynHL was very hard to create, I am sure because I have tried it my self.
but it seem to be (nearly) easier nowadays.
What capabilities was your expected HL?
Hi @x2nie. I have already designed a folding-highlighter: https://github.com/t-edson/SynFacilSyn
In it, the support for folding is not as good as I wanted, mainly because of some limitation I found in TSynCustomFoldHighlighter.
Summarizing, I wanted this class understood about block of syntax, but it only knows about showing folding marks on the gutter > >:(.
I felt understood what Edson refer to.I wanted this class understood about block of syntax, but it only knows about showing folding marks on the gutter .I am not exactly sure what you try to describe?
... the HL only provides what is foldable. It does not provide the GUI to fold it.
...I have the feeling.., the feature you are implementing need to know about the syntax of the language.No, it wouldn't.
Maybe it's better open a new thread "Modernizing the TSynCustomFoldHighlighter class" :-\ .Yeah, good idea.
I was talking about some limitations (Maybe "features not implemented" sounds better) of TSynCustomFoldHighlighter:You can add tracking them like pas-hl, but as I said maybe pas-hl can even get rid of it. Yet even then your hl can add it. It is not a feature that all HL need, so it still is open if or where to add it
1: There is not way to obtain the total of blocks opened (IIRC FoldIndex only count the opened with folding mark). I know Pascal HL do :D.
2. There is not a way to know if a block was opened and closed at the same line. I know Pascal HL do :-\.then there is a way, isn't there?
3. (This is a new feature I wish) I would like to have some facilities to close a block before a token starts (http://forum.lazarus.freepascal.org/index.php/topic,23411.msg139608.html#msg139608). I know Pascal HL do :o.like the "var" folding?
Therefore, knowing the LineIndex is enough for TSynCustomFoldHighlighter it self to make foldable feature works.(and then example code....)
But, for ancestor (like Edson own), it seem not enough. Because it also need to know the column index too for their internal work (such differentiate coloring per block-fold according to it's fold-dept).
Do not pass the data as param. It is only needed if CatchNodeInfo is on.
Result := StartCodeFoldBlock(FTokenPos, Run, p + Pointer(PtrInt(ABlockType)), FoldBlock);
- SingleLine still appears as foldable everywhere, but when is clicked the folded area is wrong (see LFM tab)still happening?
For example: FoldHL.pas (shipped with lazarus) doesn't work; see my FoldHL.pas for what is difference.ups then it needs to be fixed. Do you have a patch.
see comment above. Keep old interface, and define a getter.I think we should drop the old gate, and only accept the new one ?
//new gate: function StartCodeFoldBlock(LogX1,LogX2 : Integer; ABlockType: Pointer=nil; IncreaseLevel: Boolean = true): TSynCustomCodeFoldBlock; virtual; overload; procedure EndCodeFoldBlock(LogX1,LogX2 : Integer; DecreaseLevel: Boolean = True); virtual; overload;
The code can be downloaded:Eventually a patch will be needed.
https://github.com/x2nie/syneditmarkupnestedcolors/blob/master/SynEdit-modified/synedithighlighterfoldbase.pas (https://github.com/x2nie/syneditmarkupnestedcolors/blob/master/SynEdit-modified/synedithighlighterfoldbase.pas)
I think No, its not about the normal way of how to get x pos.Therefore, knowing the LineIndex is enough for TSynCustomFoldHighlighter it self to make foldable feature works.(and then example code....)
But, for ancestor (like Edson own), it seem not enough. Because it also need to know the column index too for their internal work (such differentiate coloring per block-fold according to it's fold-dept).
This is about moving the CatchNodeInfo into the TSynCustomFoldHighlighter?
Because all x pos can be gotten with CatchNodeInfo, only currently each HL needs to implement from scratch.
Sorry that is my fault. It's not happen in original/lazarus SynLFM. It occured only in my SynLFM2.pas- SingleLine still appears as foldable everywhere, but when is clicked the folded area is wrong (see LFM tab)still happening?QuoteFor example: FoldHL.pas (shipped with lazarus) doesn't work; see my FoldHL.pas for what is difference.ups, then it needs to be fixed. Do you have a patch.
Please report a bug. (with or without patch)
Keep old interface, and define a getter.Nice idea!
Eventually a patch will be needed.I forgot how to create a Svn patch?
I think No, its not about the normal way of how to get x pos.
(normal = sample code = finding X,Y as such in TSynEditMarkupWordGroup.FindMatchingWords() ).
Rather, there is a need to find the pair of opening~closing fold tag in simplest way!
You know, in FindMatchingWords: FindEndNode / FindStartNode methods are too complicated for such this easy task: get opening~closing fold tag.
(simplest = storing that (opening+closing fold tag) info into whether a CodeFoldBlock or into each FoldRange?).
So, normal way is bad (or at least : not optimized) because this way is finding through too many lines (assumed 10000 lines) until it match.
Storing that info will save time and battery. :)p
The very bad code in #19..23now we have to differentiate several things
Even if there was no alternative way, we still need to do that searching/calculation easier, by such wrap it in a function maybe enough.
I forgot how to create a Svn patch?We can create it from git.
But you can see all changes separated from original code here: https://github.com/x2nie/syneditmarkupnestedcolors/commit/2438a0298c73942e6545bc29be1bbeb194db4c7c
Or when you are visiting github, click the "38 commits', click a commit node (most top) and there will be file per file, changes are green.
Hey, I currently face a real problem ... >:D !So, normal way is bad (or at least : not optimized) because this way is finding through too many lines (assumed 10000 lines) until it match....
Storing that info will save time and battery. :)p
I see no need to store for example that a fold starting in line 190 has a length of 75 lines. (store 75)
c) if you store it in the range, or code foldblock (part of the range) then they become nearly impossible to re-use.
As for long folds (10000 lines), that is no problem. Well if you scan a 1000 folds of 10k lines then maybe it will be.
Ranges (if they are an object) are re-used. And with that the same FoldBlock object is used to represent the state on many lines.What do you think ? 8)
begin // range at the end of line has foldblock 0x0001 if a then begin // range at the end of line has foldblock 0x0002 end; // range at the end of line has foldblock 0x0001 since the inner fold is closed if longer_condition then begin // range at the end of line has foldblock 0x0002 again (re-used), even the X pos differs. end; end;
But...
LogX2 := LogX1 + L -1;
the MarkupWordGroup seem as wrong on Node.LogXEnd, mean conflict with my idea.
See the "food"~"/food" vs "repeat"~"until" screengrab.
please ignore my previous questiontoo late
Now, the problem is when text/line trailing with tab. The vertical lines drawn improperly.Either:
Hey, I currently face a real problem ... >:D !
This messy visual effect, can be solved by drawing all vertical lines, straight to "end;" keyword vertically.
We can see how CnPack solves the same problem. (see attachment II )
... plus : if it still in 3th depth of folds, comparison will be repeated 3 times again.That is why this code (searching end line) should be added to nested fold list. there known info can be cached and reused.
For honest, the longest procedure AFAIK would be only 200 lines including off-of-screen lines. (sorry for 10K abuse).Yes, but not by storing on the range.
But, couldn't we make this real solution more efficient?
But, because MarkupColorFold will operate per sourcecode's line basis,Not if cached in the nested list. Also I have to look at your code again. I need to see if the invalidate issue was truly fixed.
the next sourcecode-line will repeat comparing the left most pair of that begin~end (that have already proir calculated)
... plus : if it still in 3th depth of folds, comparison will be repeated 3 times again.
Assumed that System.pas has 10K lines, it should have 10K states stored, right? because 1line = 1 states.As I wrote a few posts back: No.
Hence, on file loading, when a 'begin' of procedure block founded, I can store that begin postion.See above, you need a lot more range objects (and folding blocks, the 2 go together)
when 'end' of that procedure founded, I stored that in the shared-folding-block (whatever Range / CodeFoldBLock?).
If this concept is negative, how can an HL rescan (reparsing) a line randomly?For rescanning the x pos is not needed. The xpos is only needed outside the HL.
But it most probably impossible, when range (or foldblock?) is reused rewriten unpredicably.it is re-used (not unpredictable though).
Hence, on file loading, when a 'begin' of procedure block founded, I can store that begin postion.
when 'end' of that procedure founded, I stored that in the shared-folding-block (whatever Range / CodeFoldBLock?).
Also, do you still change the "exit(-1)" to 0 in the nodelist (if not valid).This is weekend, I am at home with my LiteZarus (compatible = Lazarus 1.5).
Some code currently relies on -1 (the example foldhl will not fold, if it is 0)
I agree the -1 is bad. So it is also possible to add a property IsValid and change all code that depends on it.
So one of 2 choices:
1) Make your code work with -1
2) add IsValid. This must be a standalone patch (or one separate commit of all involved changes in your git). Then it can be merged as a standalone svn commit.
2) sorry, I can provide files *.pas, no patches. If you really want to see diff, just replace / copy-paste them into your lazarus codes.
Oh? :o okay, I will. I just didn't think that it was important for people (other than me).Quote2) sorry, I can provide files *.pas, no patches. If you really want to see diff, just replace / copy-paste them into your lazarus codes.
I can work from your git.
But if you add modification to more files, please commit the unmodified copy first, then I can diff against that.
Revision: 50779
Message:
SynEdit: add FoldNodeInfo to TSynCustomFoldHighlighter / prepare new markups. Patch by x2nie
Author: martin
Date: 13 December 2015 20:36:29
----
Modified : /trunk/components/synedit/synedithighlighterfoldbase.pas
OK, 2 bits of info.Okay, I learn by doing. I am trying to understand what you said.
1) foldconfig.
currently length is set in .Create. not good, maybe move.
GetFoldConfigInstance
gets the initial setting.
for each foldtype (cfbtBeginEnd,...) it indicates, if it can- fold
TSynCustomFoldConfigMode = (fmFold, fmHide, fmMarkup);
- hide
- markup (word triplet at caret)
you could add fmMarkupOutline then this can be configured, and the HL can indicate which nodes are for your module.
the HL can set 2 flags (maybe more)
- sfaMarkupOutline
visible outline
- sfaMarkupOutlineIndend
not outlined, but increase color (e.g. a nested procedure block)
those would then be set in HL.InitNode() according to the language rules.
As for the "level" of intend. The HL levels are not correct, but when you go through Nest : TLazSynEditNestedFoldsList; you could keep count of nodes that have the flag (you need to start left to right then).
Or simply set them on the result FHighlights. But then you need to add dummy entries for the invisible sfaMarkupOutlineIndend. So that is not desirable.
....
Are you pointing to bugfix I show above,
or are you answering my question,
or you show me another bug?
1) "If" No increase in color wanted
add an action (to the inner) sfaOutlineMergeParent.
2) extra increase ("begin" in nested proc)
add an action sfaOutlineExtraLevel
If you make procedure and begin merge colors, so you have one long line from procedure, over begin to end, in one color, ...Excellent. That is what I really wanted.
I said it before: the level is not intended for what you do. It only server finding opening and closing lines.Okay, deal. It's clear that I was in wrong direction when depends on fold-level for nest-coloring, it should depends on other counter.
When you prepare FHighlights, then you know how many outer lines there are. So you can set the colors from the content of FHighlights.:o You have said it several time, but sorry for my stupid, I don't understand yet either. Maybe, I should just go forward, forgeting it for now.
As for your colors. You do not highlight the case labels 1: or 2: so the "else" of the case should also be ignored.
However you have special cases.
1) "If" No increase in color wanted
add an action (to the inner) sfaOutlineMergeParent.
QuoteWhen you prepare FHighlights, then you know how many outer lines there are. So you can set the colors from the content of FHighlights.:o You have said it several time, but sorry for my stupid, I don't understand yet either. Maybe, I should just go forward, forgeting it for now.
I also would reapply that rule into the if-then make if the longest line, over the then,begin and else,begin. whooaa..... :-[
The reason is: I don't really want to fold/unfold at "then" (current rule), instead I want at "if". I believe other people want too.
* Was it a mistake to fold/unfold at "then". ?
StartPascalCodeFoldBlock(cfbtCaseElse); FTokenIsCaseLabel := True; //<-------------------------------bug
StartPascalCodeFoldBlock(cfbtCaseElse); FTokenIsCaseLabel := True; //<-------------------------------bug
Why is it a bug, where does it go wrong?
QuoteWhen you prepare FHighlights, then you know how many outer lines there are. So you can set the colors from the content of FHighlights.:o You have said it several time, but sorry for my stupid, I don't understand yet either. Maybe, I should just go forward, forgeting it for now.
Lets start with this one.
If I read your code correctly:
Step 1:
you add all nodes to FHighlights.
First from the current line, then from TLazSynEditNestedFoldsList. As a result the innermost node is at index 0.
You will have to add duplicates at the same x pos, so you can NOT
if FHighlights.X = x then exit;
but instead add a flag to this node.
Then before you sort them (SortLeftMostFI), do something like
a := 0 for i = high(FHighlights) downto 0 do begin// start at the outermost) FHighlights[i].color = a mod lenght(colors). inc(a) end
And you got your colors. Of course if you want to join 2 lines, do not inc the color.
And that way you do not need any level from the HL at all.
It's only can be done if closing tag has indentical BlockType with opening one.
So let make a deal, PAS (and any other) hl should be redesigned (a little bit modification),
because PAS hl seem as sometime closes fold block with unknown block type.
It's only can be done if closing tag has indentical BlockType with opening one.This shouldn't be possible. (not if there is valid code).
So let make a deal, PAS (and any other) hl should be redesigned (a little bit modification),
because PAS hl seem as sometime closes fold block with unknown block type.
However closing tags may have 0 size, or NO location:The var block just ends. there is no keyword for that.
procedure foo, var a: boolean; begin
The original PAS HL (I didn't touch) implements it's own version of InitNode,StartPascalBlock,EndPascalBlock...
anyway, my markup slowdown when scroll at about 4000 lines.
I test using GR32.pas ( has 6500 lines).
But it fast enough at earlier lines ( < 1000 ) using the same file
anyway, my markup slowdown when scroll at about 4000 lines.
I test using GR32.pas ( has 6500 lines).
But it fast enough at earlier lines ( < 1000 ) using the same file
I got it!
Finding parent + grandpa in TLazSynEditNestedFoldsList, will always continue until the most top the "unit" keyword (root of folds, at first line ) founded !
QuoteI got it!
Finding parent + grandpa in TLazSynEditNestedFoldsList, will always continue until the most top the "unit" keyword (root of folds, at first line ) founded !
I have several ideas how to fix that. But dont have the time now, so later.
I have a quick solution: stop search the grand parent when the "procedure" keyword is just reached.
This way, will make my markup only work in Pascal Highlighter, or may need some complex configuration for other HL.
Anther reason is I also hate the ReleaseReference + AddReference (aliens) things. >:Dcompile with assert enabled.
It's far better to use Create + Free. Which are common and predicable behavior.
Yes it was, but Now I changed my mind.QuoteThe original PAS HL (I didn't touch) implements it's own version of InitNode,StartPascalBlock,EndPascalBlock...
Then for now it keeps its own. That can be independently reviewed later.
Well, although it was almost complete, there are known bug:This problem is now solved.
It can't resolve this real code:
{$IFDEF BCB} procedure TBitmap32.Draw(const DstRect, SrcRect: TRect; hSrc: Cardinal); {$ELSE} procedure TBitmap32.Draw(const DstRect, SrcRect: TRect; hSrc: HDC); {$ENDIF} begin (FBackend as IDeviceContextSupport).Draw(DstRect, SrcRect, hSrc); end;
Any clue?
... But if you use different foldgroups, test with {%region} too.I have no problem with {%region }..{%endregion}, and nested one.
I dont know how you did it.It's easy in theory. (evil is in detail >:D ) :D
Also you can not assume any knowledge with foldgroup is what, as that is internal to each HL and can change@Martin, in english, please?
Also you can not assume any knowledge with foldgroup is what, as that is internal to each HL and can changeI try my best to reparse, your intonation might be:
I havent sees your code, only will in 2 weeks...
so just generic comments, that may or may not apply.
IMHO, it is better: if drawing any markup (such as MarkupWordGroup / paired open~close fold) independently, to being not interfered by code-folding.
in other word (not english) : [sfaFoldFold] <> [sfaMarkup] <> [sfaOutline].
Well, now we can make different between FoldConfig[].Enabled := False; vs FoldConfig[].Modes := -[sfaFold].
This way, "Enabled" will completely affect the whole Modes.
when "sfaFold" will only apply to foldable code, and other modes still active.
I also deprecating PasFoldLevelEnd, PasMinLevel. >:D Reusing (and bugfixing) the base class. :-*that can also be patched on its own.
absolutely correct. that is how it should be. (And the HL can force some nodes to be enabled, if it needs them for context. e.g. keywords that exists only in certain context)Yeah,
I think the modified pascal HL is now stable. And it's baseclass too.QuoteI also deprecating PasFoldLevelEnd, PasMinLevel. >:D Reusing (and bugfixing) the base class. :-*that can also be patched on its own.
if there are fixes needed, they should be applied independent of the markup you work on.Actually my Markup is also stable enough,
FYI, using my markup class, a 'procedure' can be configured by setting 4 new configuration.
* procedure can completely ignored = exclude(sfaOutline)
* can has/ has not coloring, but maybe has a vertical guideline
* can has / has not vertical outline, but maybe itself has not be colored (like current "then" behavior)
* can visually ignored but has some affect to it's children.
Whatever, how can it be easy to configured?
sure by redesign / redefine the 'fold'. at least I need to redesign the IDE Options's frame.
Thanks you! 8-)QuoteFYI, ...
Whatever, how can it be easy to configured?
sure by redesign / redefine the 'fold'. at least I need to redesign the IDE Options's frame.
redesign of the ide frame should be no problem. Open to any change needed. But imho thats last on the list.
foldconfig:I keep it simple in mind:
I am still thinking about it, there are various options and considerations. Most important, what needs to be known by the HL, and what is markup.
Since the HL needs to deliver the base info for the markup, it also needs some config for this. This means certain config will have to be present, even if the markup is not used at all. (this conflicts with keeping everything modular, but it can't be avoided)well it could have partly been avoided by ...Hey, there are no conflicting. When HL providing nodes/infos, it is not a big job for HL, because everything has been done once foldconfig changed. And as usually, foldconfigs are not changed during user work with synedit.
But that would cause far to much overhead, so imho not an option.I agree, there are no benefit of additional-config-properties of current foldconfig.
We first need to look at how many modes are needed.If Merging not applicable for few keyword, let's make it disabled (not configurable) for them.
That means do decide if EVERY configurable keyword can have all options:
- colored and/or lined
- ignored/merged parent or child
merging does not make sense for all the keywords (there in no point in merge "repeat" with parent or child ever).
If it was only your markup, then we could add 2 modes (outlineColor, outlineLine) and maybe that is the better idea for now.Great! adding fmOutlineColor,fmOutlineLine is the best we can do for now.
2) the difference between color and line is really none of the HLs business.even HL will never care about these, but the HL is the only correct place to put them.
Maybe you have more ideas?I don't understand what is problem with current foldconfig?
foldconfig:
I am still thinking about it, there are various options and considerations. Most important, what needs to be known by the HL, and what is markup.
Since the HL needs to deliver the base info for the markup, it also needs some config for this. This means certain config will have to be present, even if the markup is not used at all. (this conflicts with keeping everything modular, but it can't be avoided)
well it could have partly been avoided by adding a collection to foldconfig, and the markup could add a sub classed collection item. But that would cause far to much overhead, so imho not an option.
QuoteIf it was only your markup, then we could add 2 modes (outlineColor, outlineLine) and maybe that is the better idea for now.Great! adding fmOutlineColor,fmOutlineLine is the best we can do for now.
These 2 modes, together with fmOutline, can easily be implemented into IDE options. see attachment of how to.
markup.drawLines[ord(cfbtForDO)] := true;You know, such configurations is needed to be stored some where.
markup.drawColor[ord(cfbtForDO)] := true;
of course if you change the HL to xml, then you need to change settings.
on top of that 2 settings
markup.DrawLinesForAll := false // if true then do not check the above is ignored
markup.DrawColorForAll :=
You are keeping HL classes to be not be changed when a new markup class introduced. aren't you?Ideally yes-ish. though its not entirely possible. (at reasonable cost.
And what are NOT be constraints:foldconf class is always defined by the HL (HL subclass)
- FoldConfigs may/ may not attached to HL. it can be independent class, can be looked up using TypeInfo.
which part to extract?
So, from above thought this is my idea:
+ Extract the markup config into a new class/record.
Note:
1) I don't know whether FoldConfig will be completely detached from HL, because fmFold may still needed by other module (such gutter, TextBuffer, etc.).
2) when a "new" config created, it will not require the markup instance exist,
but we only need the count of them for displaying / playing with them.
Even there are one global config for all instance (multiple instance same class),
each instance can has a copy of that config.
Meaning no mater inside or ouside HL the config attached, each instance can has individual config for further make different with other intance (same class).
It also possible to make the config shared among instance or independently, just like your ".AddReference" things !
wow. What's your opinion?
markup.drawLines[ord(cfbtForDO)] := true;
markup.drawColor[ord(cfbtForDO)] := true;
1) I assume you agree: we put Markup classes in general purpose level (not for specific programming language) as possible./quote]
MarkupIfDef is excepted. But other markup class should be independent.
2) I can implement what you wanted, so I accepted above code is a concept, it may different in real code.
.. please write a brief description on what did you till now and what i can do (i tried reading last pages but it was not clear for me because i wasn't involved)
a) let we remove the hard to understand "CountPascalCodeFoldBlockOffset ",
since its not portable/ not reusable, and is actually has another meaning: hidden sfaFold.
b) let remove InitNode in pas HL, replace with DoInitNode (override virtual).You already had that done, and I just merged it.
Let's add more happiness to any new HL development, by enabling sfaOutline by-default.Let me first finish merging. And see if other bugs turn up that need fixing.
If I am right, the only outstanding change for merging from your git, are in SynPasSyn?Apparently (yes).
QuoteLet's add more happiness to any new HL development, by enabling sfaOutline by-default.Let me first finish merging. And see if other bugs turn up that need fixing.
Better to make it avail via Object inspector.
There still is the issue of missing invalidation.
- go to a begin/end block of at least 10 lines.
- position the caret in the line with the "begin",
but not touching the begin itself.
Position the caret in the leading spaces of the line.
- now add some spaces.
The begin will indent, so will the horizontal line, but only on the top 3 text lines of the block. The rest will not be redrawn.
Martin_fr,Ups forgot that file in the commit, fixed
your last commit (52184) breaks build. Additional parameter in TSynCustomFoldHighlighter.StartCodeFoldBlock is not added in TIDESynPasSyn.
No, but OI should be able to show and configure FoldConfig, and markups should be components that can be added like a HL. But that will be some work to get there.QuoteLet's add more happiness to any new HL development, by enabling sfaOutline by-default.Better to make it avail via Object inspector.
Wait ! do you mean that in future, Object Inspector can be used as Code Explorer ? :o
if yes, OI would be very useful for both form and code exploration.
I love this idea :-*
I had a long look at the remaining changes. (InProcLevel, ...)@Martin:
As far as I can see, they are dealing with {$ELSE} (and only $ELSE)?
If they have any other effects, without $ELSE (or even without any $IFDEF) then let me know.
Instead of storing data for each screen line, you could also store a list of all the nodes found for the current screen.The obstacle of previous progress of fixing the vertical line is the speed will be amazingly very slow.
...
That has the line, and the x pos.
The list includes the nodes that start off screen, but paint into the screen.
@martin:
your last changes can not deal with this:The visual problem: second "procedure" should be drawn as black not as red.
{$IFDEF BCB} procedure TBitmap32.Draw(const DstRect, SrcRect: TRect; hSrc: Cardinal); {$ELSE} procedure TBitmap32.Draw(const DstRect, SrcRect: TRect; hSrc: HDC); {$ENDIF} begin (FBackend as IDeviceContextSupport).Draw(DstRect, SrcRect, hSrc); end;
reddish "procedure" indicates that she is sub-procedure, black is first level proc.
Wait, above code is not imaginated, its real code taken from famous gr32.pas.
meaning the code is valid, and there are many similar style of {$ifdef} usage like this.
meaning : you should support the style.
So as I said, you fix one issue by introducing another....It sad to say, but :-[ apparently (yes) I support a block of pascal syntax and go (I hit and run).
That said, of course there are many thinks that only work with your fix.
But it only proofs you cant highlight all ifdefs correct.
There may also be a way to solve this outside the HL. But I haven't given it much thought yet.Yes
Better approach, would be to use info from the lowlighting. That comes from codetools,
so it only works in the IDE.No. It should be also work outside IDE, somehow.
And that means it needs a subclassed HL, special for that.I think not the HL, but the codetools itself must be subclassed into smaller, which only support $Ifdef.
But even that couldn't fix everything.No worry for now, we fix only the known bugs.
Here is another example. All levels out correct in the current HL.Yes, I can reproduce that bug.
...
with your code added. it thinks the procedure ends at the line I commented.
the "try" could also be any other block.
---------------I am trying that approach,
About nested procs. you can identify them by checking the TopPascalCodeFoldBlockType, or not? So that needs no extra data to be stored.
So, sorry. but let me know whenever you passed the unit-test for your codeIt passed for all that has been merged.
Quote from: Martin_fr on Today at 04:24:26 amPlease use a branch in your git, based on the pas hl, that is now in svn.QuoteAbout nested procs. you can identify them by checking the TopPascalCodeFoldBlockType, or not? So that needs no extra data to be stored.I am trying that approach,
This is probably a bug in the current HL. forward should cancel the fold block.
Procedure First (n : longint); forward;
Procedure First (n : longint); forward;
from your codeThat happened when loading included file {$I including.pas} that has no unit, no program, just direct functions.
//avoid bug of IncludeOpeningOnLine := False; not ((sfaOpen in TmpNode.FoldAction) and (TmpNode.LineIndex + 1 = aRow)) then
How to reproduce this bug?
from your codeThat happened when loading included file {$I including.pas} that has no unit, no program, just direct functions.
//avoid bug of IncludeOpeningOnLine := False; not ((sfaOpen in TmpNode.FoldAction) and (TmpNode.LineIndex + 1 = aRow)) then
How to reproduce this bug?
But I tried using your last changes, its not a bug.
You mean the procedure(s) at the top, do not get outlined (nor folded)?Even worst, there were fatal error such "could not read memory address nyah nyah nya.."
This is intention. And important.put the situation in unit test maybe enough.
sfaOutlineNoColor, // Node will not painted by nested-coloring, but may increase color (e.g. any "procedure") sfaOutlineNoLine, // Node doesn't want to have vertical line. (e.g. "then")
Should those be in the node?
I would imagine it is better ifwould *instead* of fmOutline have
TSynCustomFoldConfigMode = (fmFold, fmHide, fmMarkup, fmOutline);
fmOutlineLine
fmOutlineColor
Then it can be userconfigured for each node.
How can user configure OutlineLine/OutlineColor if there are no fmOutlineLine/fmOutlineColor?
I do understand the reason, but I can't understand the technical implementation.
... OI should be able to show and configure FoldConfig, and markups should be components that can be added like a HL. But that will be some work to get there.I try this : componentize the markup
Shouldnt the markup first be finished, before starting to add new features?
There may be several ways to make the Markup designer ready.
is it possible to add the line index of the sfaClose-node to TSynFoldNodeInfo of the sfaOpen-nodes?
This would make invalidation much easier.
This would mean that when scanning for sfaOpen node, the HL always must search for the close line. Even if calling code does not need this. (if there are 10 open nodes on a line, and only one is needed....)
So it should not be added.
TLazSynEditNestedFoldsList should provide this already (and more efficient)
Probably you should get the nodes ONLY via TLazSynEditNestedFoldsList. And then you have all info you need.
is it possible to add the line index of the sfaClose-node to TSynFoldNodeInfo of the sfaOpen-nodes?
This would make invalidation much easier.
See latest patch on mantis.
FYI: I'll be away the next couple of weeks.Holidays or business travel? Anyway, have a good journey!
x2nie,
did you test the patch?
I have had big problem running my app demo that uses synedit+SynEditMarkupFoldColoring.pas, something has been changed somewhere in months.
But I found the bugfix. The problem is in my demo app (that earlier works):
procedure TForm1.FormCreate(Sender: TObject); var M : TSynEditMarkupFoldColors; S : TSynEdit; begin s := SynEdit1; s.Highlighter := nil; // my original markupcoloring unit doesn't require this M := TSynEditMarkupFoldColors.Create(s); s.MarkupManager.AddMarkUp(M); s.Highlighter := SynFreePascalSyn1; // so this reseting doesn't required by my original markupcoloring unit. end;
Thanks @Pascal !
I will investigate this. Should work without setting highlighter to nil and back to SynFreePascalSyn1.I found the problem: SetLines does not recreate FNestList.
I have had big problem running my app demo that uses synedit+SynEditMarkupFoldColoring.pas, something has been changed somewhere in months.
But I found the bugfix. The problem is in my demo app (that earlier works):
procedure TForm1.FormCreate(Sender: TObject); var M : TSynEditMarkupFoldColors; S : TSynEdit; begin s := SynEdit1; s.Highlighter := nil; // my original markupcoloring unit doesn't require this M := TSynEditMarkupFoldColors.Create(s); s.MarkupManager.AddMarkUp(M); s.Highlighter := SynFreePascalSyn1; // so this reseting doesn't required by my original markupcoloring unit. end;
Interesting to see in SynEditMarkupFoldColoring, that the error occurred from this line:
NestCount := FNestList.Count;
Do you test withNo, i didn't before. But with this options i do not get any errors, neither in my testprogramm nor in the ide.
-Criot -gt -gh -dSynAssert -dSynAssertFold
If i disable some keyword for outlining i also get errors.
How can i configure some keywords to not be outlined in a test programm?
Why does FEndLine has the same length as SynEdit.Lines (If I read that correct)?Yes the array is as long as the SynEdit.Lines. It's a chache for the endline for that line. The same with FFirstCharacterColumn as cache for the
You only need to know about current displayed lines. (then the above problem would not exist).
If I am correct you keep an array with length of 10000 (for some units) where you may need only 50 (on screen).
TApplication.HandleException Arithmetic overflow
Stack trace:
$00B2DAE3 TLAZSYNTEXTAREA__INVALIDATELINES, line 1299 of lazsyntextarea.pp
$00B2CF3E TLAZSYNSURFACEMANAGER__INVALIDATETEXTLINES, line 1117 of lazsyntextarea.pp
$00C3609C TSOURCELAZSYNSURFACEMANAGER__INVALIDATETEXTLINES, line 1366 of sourcesyneditor.pas
$007325F2 TCUSTOMSYNEDIT__INVALIDATELINES, line 2819 of synedit.pp
$00AE346C TSYNEDITMARKUP__INVALIDATESYNLINES, line 337 of syneditmarkup.pp
$00BD1E03 TSYNEDITMARKUPFOLDCOLORS__DOTEXTCHANGED, line 706 of syneditmarkupfoldcoloring.pas
$00AE3B1E TSYNEDITMARKUP__TEXTCHANGED, line 442 of syneditmarkup.pp
$00AE46D9 TSYNEDITMARKUPMANAGER__TEXTCHANGED, line 614 of syneditmarkup.pp
$00739F11 TCUSTOMSYNEDIT__SCANRANGES, line 5080 of synedit.pp
$007311DD TCUSTOMSYNEDIT__DODECPAINTLOCK, line 2381 of synedit.pp
$004D606D TMETHODLIST__CALLNOTIFYEVENTS, line 307 of lazmethodlist.pas
$00AFE97D TSYNEDITSTRINGLIST__SENDNOTIFICATION, line 1545 of synedittextbuffer.pp
$00AFCAAF TSYNEDITSTRINGLIST__SETUPDATESTATE, line 1248 of synedittextbuffer.pp
$00AF64BE TSYNEDITSTRINGS__ENDUPDATE, line 973 of lazsynedittext.pas
$00730CF7 TCUSTOMSYNEDIT__DECPAINTLOCK, line 2305 of synedit.pp
$00742213 TCUSTOMSYNEDIT__INTERNALENDUNDOBLOCK, line 7148 of synedit.pp
$00738577 TCUSTOMSYNEDIT__SETSELTEXTEXTERNAL, line 4586 of synedit.pp
I will investigate this later.
Yes the array is as long as the SynEdit.Lines. It's a chache for the endline for that line. The same with FFirstCharacterColumn as cache for the
column of the first character in the line (this is the position of the vertical line).
I can not limit this to the lines in the window as i have to at least keep track of FFirstCharacterColumn as i have to draw vertical lines for nodes
that where opened before Topline or i have to refind the positions for every invalidation but we would like to save cpu/energy if i got you right.
By the way: If i disable "Outline (global)" i can not set "Outline" for the individual keyword as it is disabled.Why is it disabled?
if outlining is disabled, then there is no point in being able to change its config.Ah, i see. So you enable outlining with "Outline (global)" and then disable individual folds.
It would not change anything.
Fixed! Added new patch which also uses NestList.NodeEndLine.There are still various FHighlighter.FoldEndLine ?
procedure TSynEditMarkupFoldColors.InitArrays; .... Lines.AddChangeHandler(senrLineCount, @LinesChanged); Lines.AddChangeHandler(senrHighlightChanged, @HighlightChanged);
This is called from TSynEditMarkupFoldColors.SetLines (where it is matched by remove calls.)
But also from other locations. (Afaik duplicates are ignored, but still....)
AlsoIt can never be assigned.
constructor TSynEditMarkupFoldColors.Create(ASynEdit: TSynEditBase); begin inherited Create(ASynEdit); if Assigned(Lines) then begin Lines.AddChangeHandler(senrLineCount, @LinesChanged); ....
This code should only be in SetLines and Destroy (a remove call is ignored, if the handler wasn't added.
InitArrays is also the wrong place for
if Assigned(FHighlighter) then begin FNestList := TLazSynEditNestedFoldsList.Create(Lines, FHighlighter);
That code belongs ONLY into TSynEditMarkupFoldColors.HighlightChanged
There is a copy in Create, but I wonder, can create just call HighlightChanged?
--------------
Why does SetLines do FreeAndNil(FNestList); ?
FNestList.Lines needs to be updated in SetLines
You are using logical positions http://wiki.lazarus.freepascal.org/SynEdit#Logical.2FPhysical_caret_position
That causes the tab misalign.....
You should use Phys x pos.
You need to convert the first none space, into phys, and then on each line use that.
(for keywords (frame) you can use either log, or pos. Since they are for one line only, so the pos can not shift)
(It will not fix all problems, but it is a start)
Better to set the lenght to the full needed lengh at start.
procedure AddVerticalLine( .... z := Length(FFoldColorInfos); SetLength(FFoldColorInfos, z+1);
That should be "NestCount := FNestList.Count;"?
Well I guess up to NestCount. It can be less.
But then set it to maximum, and reduce at the end.
Or better yet, keep it at maximum (and also keep for next line) and store the real used len in a separate variable.
InitArray is only called once It's the fix for cloned Buffers as SetLines isn't called for the clone SynEditWhen the buffer was replaced from a shared editor, then there is no need for AddChangeHandler (handlers are copied to the new buffer)
It is there because TLazSynEditNestedFoldsList.Create(Lines, FHighlighter) depends on both Lines and Highlighter!
It has to be called when one of them changes.
QuoteInitArray is only called once It's the fix for cloned Buffers as SetLines isn't called for the clone SynEditWhen the buffer was replaced from a shared editor, then there is no need for AddChangeHandler (handlers are copied to the new buffer)
So i only have to init the arrays, right? Is there a way to store them with the lines, so that the clone can also use it?
...I think the ScanRange(False); will solve the potentially a problem.
procedure TCustomSynEdit.SetHighlighter(const Value: TSynCustomHighlighter); begin ... ScanRanges; // Todo: Skip if paintlocked <------------------- ScanRanges calls TextChanged for all Markups (see below) end;
procedure TCustomSynEdit.ScanRanges(ATextChanged: Boolean = True); begin if ATextChanged then fMarkupManager.TextChanged(FChangedLinesStart, FChangedLinesEnd, FChangedLinesDiff); <------- notify Markups of changed end;
So maybe the notify should be done before ScanRange !? Or call ScanRange(false).
So maybe the notify should be done before ScanRange !? Or call ScanRange(false).Looking at the code....
It seems thatmay never have been meant as "the highlighter was replaced"
FLines.SendNotification(senrHighlightChanged, FLines, -1, -1);
Something else:SynEdit shouldnt need to know individual markups.
fMarkupHighCaret.Highlighter := Value; fMarkupWordGroup.Highlighter := Value;
One way would be to introduce senrHighlighterClassChanged.
But easier will be to put HighlighterChanged() the base class, and call MarkupManager.HighlighterChanged, that will call all Markups.
Any Markup in need of a HL, can react (the 2 existing Markup can retrieve the HL)
But -1, -1 is the only way to identify a highlighter change as senrHighlightChanged is also called many times for other reasons.Is called to indicate for which lines the highlighting has changed.
Which Highlighter did you use?
there is an other bug remainig: array FFoldColorInfos in SynEditMarkupFoldColoring is too small/not dynamic
I put a patch on Mantis: http://bugs.freepascal.org/view.php?id=31049 (http://bugs.freepascal.org/view.php?id=31049)
Something I noted:
Markups are supposed to do NO work, if they are not enabled (or if there colors are ALL off)
For most markups that is covered by the manager, because the GetNext.... functions are not called.
But TextChanged is still called. Many markup set only a flag in there "FFirstLineChanged:=.... FlastLine.....".
That is ok (not much work)
But yours does a lot of work in
procedure TextBufferChanged(Sender: TSynEditStrings; aIndex, aCount: Integer procedure DoTextChanged({%H-}StartLine, EndLine, {%H-}ACountDiff: Integer); override; // 1 based procedure SetLines(const AValue: TSynEditStrings); override; procedure LinesChanged(Sender: TSynEditStrings; aIndex, aCount: Integer); procedure HighlightChanged(Sender: TSynEditStrings; aIndex, aCount: Integer);
So they should start with
if not ( Enabled and MarkupInfo.IsEnabled) then exit;
Of course then you need to listen to
DoEnabledChanged
MarkupChanged
and if you get enabled, then initialize.
I found an other bug and i have no clue what went wrong in my code.I have a look in a bit...
I attached a sample prog. Start it, place the cursor after {$ifdef DEBUG} and press ENTER.
Maybe you did not call "clear" on the nested fold list.
Calling Clear before setting Line did it. I thought setting Line would also clear the NestList.
You should clear, ONCE, at the first line that is painted. Following lines should only set the number (or it can get a lot slower).
Maybe best to introduce "BeginMarkup" same as "FinishMArkup" but called before the very first line.
Something I noted:
Markups are supposed to do NO work, if they are not enabled (or if there colors are ALL off)
For most markups that is covered by the manager, because the GetNext.... functions are not called.
But TextChanged is still called. Many markup set only a flag in there "FFirstLineChanged:=.... FlastLine.....".
That is ok (not much work)
But yours does a lot of work in
procedure TextBufferChanged(Sender: TSynEditStrings; aIndex, aCount: Integer procedure DoTextChanged({%H-}StartLine, EndLine, {%H-}ACountDiff: Integer); override; // 1 based procedure SetLines(const AValue: TSynEditStrings); override; procedure LinesChanged(Sender: TSynEditStrings; aIndex, aCount: Integer); procedure HighlightChanged(Sender: TSynEditStrings; aIndex, aCount: Integer);
So they should start with
if not ( Enabled and MarkupInfo.IsEnabled) then exit;
Of course then you need to listen to
DoEnabledChanged
MarkupChanged
and if you get enabled, then initialize.
Strange! DoMarkupChanged toggles very often between enabled and disabled.Stupid me!
Why is the MarkupInfo of the OutlineMarkup changed by the highlighter?
#0 TSYNEDITMARKUPFOLDCOLORS__CHECKENABLED(<error reading variable>) at C:\freepascal\laz\components\synedit\syneditmarkupfoldcoloring.pas:683 #1 TSYNEDITMARKUPFOLDCOLORS__MYMARKUPINFOCHANGED(0x36af890, <error reading variable>) at C:\freepascal\laz\components\synedit\syneditmarkupfoldcoloring.pas:956 #2 TSYNHIGHLIGHTERATTRIBUTES__DOCHANGE(<error reading variable>) at C:\freepascal\laz\components\synedit\synedithighlighter.pp:1241 #3 TLAZSYNCUSTOMTEXTATTRIBUTES__CHANGED(<error reading variable>) at C:\freepascal\laz\components\synedit\synedithighlighter.pp:789 #4 TLAZSYNCUSTOMTEXTATTRIBUTES__SETFOREGROUND(536870911, <error reading variable>) at C:\freepascal\laz\components\synedit\synedithighlighter.pp:745 #5 TSYNEDITMARKUPFOLDCOLORS__GETMARKUPATTRIBUTEATROWCOL(5, {PHYSICAL = 1, LOGICAL = 1, OFFSET = 0}, {ISRTL = false, PHYSLEFT = 1431655765, PHYSRIGHT = 1431655765, LOGFIRST = 1431655765, LOGLAST = 1431655765}, <error reading variable>) at C:\freepascal\laz\components\synedit\syneditmarkupfoldcoloring.pas:230 #6 TSYNEDITMARKUP__MERGEMARKUPATTRIBUTEATROWCOL(5, {PHYSICAL = 1, LOGICAL = 1, OFFSET = 0}, {PHYSICAL = 5, LOGICAL = 5, OFFSET = 0}, {ISRTL = false, PHYSLEFT = 1431655765, PHYSRIGHT = 1431655765, LOGFIRST = 1431655765, LOGLAST = 1431655765}, 0x802f6e8, <error reading variable>) at C:\freepascal\laz\components\synedit\syneditmarkup.pp:435 #7 TSYNEDITMARKUPMANAGER__MERGEMARKUPATTRIBUTEATROWCOL(5, {PHYSICAL = 1, LOGICAL = 1, OFFSET = 0}, {PHYSICAL = 5, LOGICAL = 5, OFFSET = 0}, {ISRTL = false, PHYSLEFT = 1431655765, PHYSRIGHT = 1431655765, LOGFIRST = 1431655765, LOGLAST = 1431655765}, 0x802f6e8, <error reading variable>) at C:\freepascal\laz\components\synedit\syneditmarkup.pp:579 #8 TLAZSYNPAINTTOKENBREAKER__GETNEXTHIGHLIGHTERTOKENEX({TK = {TOKENSTART = 0x8022464 'uses', TOKENLENGTH = 4, TOKENATTR = 0x36d5958}, ATTR = 0x802f528, STARTPOS = {PHYSICAL = 1, LOGICAL = 1, OFFSET = 0}, ENDPOS = {PHYSICAL = 5, LOGICAL = 5, OFFSET = 0}, PHYSICALCHARSTART = 1, PHYSICALCLIPSTART = 1, PHYSICALCHAREND = 5, PHYSICALCLIPEND = 5, RTLINFO = {ISRTL = false, PHYSLEFT = 1431655765, PHYSRIGHT = 1431655765, LOGFIRST = 1431655765, LOGLAST = 1431655765}, RTLEXPANDEDEXTRABYTES = 1431655765, RTLHASTABS = 85, RTLHASDOUBLEWIDTH = 85, EXPANDEDEXTRABYTES = 0, HASTABS = false, HASDOUBLEWIDTH = false, NEXTPOS = {PHYSICAL = 5, LOGICAL = 5, OFFSET = 0}, NEXTRTLINFO = {ISRTL = false, PHYSLEFT = 0, PHYSRIGHT = 0, LOGFIRST = 1, LOGLAST = 1}}, <error reading variable>) at C:\freepascal\laz\components\synedit\lazsyntextarea.pp:417 #9 PAINTLINES(0x358f658) at C:\freepascal\laz\components\synedit\lazsyntextarea.pp:1721 ...
Calling Clear before setting Line did it. I thought setting Line would also clear the NestList.
"if" should not keep, not even on the same line...But here "else if" should keep the same color (if starts on same column):
"if" should not keep, not even on the same line...This looks ugly %)
what happens when
if foo then if bar then x else y
"if" is on a new line, but it is the *same" statement, as "else if" on one line. Should it not have the same color?
By the way I would not say the last example looks ugly.
The diff color is helpful. You can immediately see which "if" the "else" belongs too.
If it where all yellow, then it would be unclear.
You will be amazed how many different people will want many different colors....
For example, I would like colors by keyword.
That is
- try except: always red, nested in a different shade of red (1 or 2 shades, then repeat)
- "if" always green (nested with shades of green)
- "else" yellow
- loops: blue
Also I would have the keyword underlined in color, but not change the font color. Or maybe have they keyword not changed at all.
Also maybe only outline if/while , if they have a begin / or if they have nested if/while ....
There are millions of ways.....
Next step is to publish MarkupInfo for each color. So you can decide the colors (frame, text, background), underline, style.
If you whant to have colors by keyord we need multiple arrays of MarkuInfo and we have to store the desired color array in the FoldAction
and we also have to have levels per color array.
Can someone, plz, lpz PLEASE create a 'tip of the day' on front lazarus-ide page so people can find the checkbox to tuen on this amazing functionnality ??