Recent

Author Topic: Folding in SynEdit  (Read 25728 times)

Edson

  • Hero Member
  • *****
  • Posts: 1057
Re: Folding in SynEdit
« Reply #30 on: April 07, 2014, 02:17:03 am »
Hi Martin,

I was trying to get the information of ranges from a Highlighter, for any line (specifically the current line).

I read a pointer, and then I apply the Typecast:

Code: [Select]
var
  hlr: TSynCustomHighlighterRange;
  p: Pointer;
  ...
  p := CurrentRanges[SynEdit1.CaretY-1]
  hlr := TSynCustomHighlighterRange(p);

I think this is OK until now.

I can read the RangeType (at the end of the line) like pointer in: hlr.RangeType

But, how can I get the Fold Block information?

I see the properties: 'hlr.MinimumCodeFoldBlockLevel' and 'hlr.CodeFoldStackSize', that I guess, are the mentioned here:
http://wiki.lazarus.freepascal.org/SynEdit_Highlighter#Folding

But I don't know how to obtain the 'ABlockType' used with StartCodeFoldBlock(). Is this captured at the end of the line, too? And the nested levels?

Probably I'm on the wrong way. My objective is to obtain all the Fold information of the current Caret position.

Thanks.
Lazarus 1.6 - FPC 3.0.0 - x86_64-win64 on  Windows 7

Martin_fr

  • Administrator
  • Hero Member
  • *
  • Posts: 5800
    • wiki
Re: Folding in SynEdit
« Reply #31 on: April 07, 2014, 03:16:51 am »
It depends what fold info you exactly need.....


1) Yes your code sample is right. That is, if the HL inherit from TSynCustomFoldHighlighter.
Other HL may have data of different type in the Range. But TSynCustomFoldHighlighter does TSynCustomHighlighterRange

2)
Look at
function TSynCustomFoldHighlighter.TopCodeFoldBlockType(DownIndex: Integer = 0): Pointer;

It shows how to get "ABlockType"


However:
Code: [Select]
Procedure a;               1 -      0
 Begin                      2 --     1 -
   b:= 1;                   2 --     2 --
   if c > b then begin      3 ---    2 --
     c:=b;                  3 ---    3 ---   // <<<<< If you query this line
   end else begin           3 ---    2 --
     b:=c;                  3 ---    3 ---
   end;                     2 --     2 --
 end;                       0        0 

You will be able to know:
- there are 3 folds open
- they have the "ABlockType" cfbtProcedure, cfbtTobBegin, cfbtBegin
 Or whatever the blocktypes are in your code.

You can find the line at which they start/end, by looking at MinLevel. That is explained on the wiki. Or look at
function TSynCustomFoldHighlighter.FoldLineLength(ALineIndex, FoldIndex: Integer): integer;


You can *NOT* find out, at which column the fold started/ended.

Well I know it is done for Highlighting begin/end pairs. But it is not documented, and probably will change, as it needs clean up.
Also this is not done via the ranges. The pas highlighter supports a special way of scanning a single line. In this scan it will record the column info.
So to find anything within a line, special scans are needed.
(search for "FCatchNodeInfo", but as I said, It is intentionally not documented)
« Last Edit: April 07, 2014, 03:20:49 am by Martin_fr »

Edson

  • Hero Member
  • *****
  • Posts: 1057
Re: Folding in SynEdit
« Reply #32 on: April 07, 2014, 06:08:17 am »
I need the Start and the End of the Fold Block, where the Cursor is. I need this information for to highlight the Current Block.

I think TSynCustomFoldHighlighter.TopCodeFoldBlockType() is not working for me, because it is for Read the Top Fold at the current time, when it is scanned.

I need to read information about block in ANY time. It's, every time the cursor is moved, or some text is typed. I'm trying to avoid re-scan the text wtih the HL, every time the cursor is moved.

TSynCustomFoldHighlighter.FoldLineLength()? How does this function work?

I have found that:

TSynCustomHighlighterRange.Top.BlockType

gives the Fold Block, at the end of the line. Is't true?

It could be useful.

Lazarus 1.6 - FPC 3.0.0 - x86_64-win64 on  Windows 7

Martin_fr

  • Administrator
  • Hero Member
  • *
  • Posts: 5800
    • wiki
Re: Folding in SynEdit
« Reply #33 on: April 07, 2014, 03:39:42 pm »
What stops you from getting the range of any line you want?

Look at TSynCustomFoldHighlighter.FoldBlockEndLevel (It is designed to be called *outside* scanning). It gets the FoldEndLevel for any line.

However, it assumes that all scanning is done. So if you modify the textbuffer, you must first call scan, then you can call this. (SynEdit does this in the last EndUpdate)

You can write yourself a method that gets the blocktype in that way.

---
There also is TLazSynEditNestedFoldsList which is a helper for all this.

----
TSynCustomFoldHighlighter.FoldLineLength()

It just accesses the Ranges for all the lines it want. Once scanned the ranges can be accessed at random. they are stored in a big list.

----

TSynCustomHighlighterRange( CurrentRanges[SynEdit1.CaretY-1] ) .Top.BlockType

that is the type of the last fold opened, before the end of the indicated line. That fo[d may have opened on that line, or it may have opened on a previous line.

- If MinLevel < EndLevel, then it opened on this line.
- If MinLevel = EndLevel, then it opened on a previous line

----
You should always cache the current begin/end pair.
If you are on line 200, and the nearest block starts at 190, and goes to 225, then you can cache his. If the cursor moves without editing, then the info is still valid.


----
SynEdit1.CaretY-1

Hint:

For readability use ToIdx(SynEdit1.CaretY)

Then it is clear you talk about the caret line, not the line befor the caret

Idx (Index) = 0 based
Pos (Position) = 1 based

and there are
  ToIdx(ALinePos)
  ToPos(ALineIndex)

Martin_fr

  • Administrator
  • Hero Member
  • *
  • Posts: 5800
    • wiki
Re: Folding in SynEdit
« Reply #34 on: April 07, 2014, 04:02:19 pm »
Almost forgot. Before any access to the highlighter
Code: [Select]
Highlighter.CurrentLines := SynEdit.FLines; //

Otherwise the HL does not know, which SynEdit...

Edson

  • Hero Member
  • *****
  • Posts: 1057
Re: Folding in SynEdit
« Reply #35 on: April 07, 2014, 10:29:09 pm »
OK, I can obtain the MinLevel and the EndLevel for each line, using:

TSynCustomFoldHighlighter.FoldBlockMinLevel()
TSynCustomFoldHighlighter.FoldBlockEndLevel()

And I can obtain the last BlockType opened, at the end of the line using:

TSynCustomHighlighterRange( CurrentRanges[SynEdit1.CaretY-1] ) .Top.BlockType (Is there a short form?)

But, How can I can obtain the other BlockType's (of the nested blocks) for every line? Is it saved in some place?

There also is TLazSynEditNestedFoldsList which is a helper for all this.

How can I use this class?
Lazarus 1.6 - FPC 3.0.0 - x86_64-win64 on  Windows 7

Martin_fr

  • Administrator
  • Hero Member
  • *
  • Posts: 5800
    • wiki
Re: Folding in SynEdit
« Reply #36 on: April 07, 2014, 10:42:07 pm »
TSynCustomHighlighterRange( CurrentRanges[SynEdit1.CaretY-1] ) .Top.BlockType (Is there a short form?)

But, How can I can obtain the other BlockType's (of the nested blocks) for every line? Is it saved in some place?
Look at
function TSynCustomFoldHighlighter.TopCodeFoldBlockType(DownIndex: Integer = 0): Pointer;

This is for the range during the scan. But if ou get a range outside the scan you can do the same

Quote

There also is TLazSynEditNestedFoldsList which is a helper for all this.

How can I use this class?

Look at \ide\sourcesyneditor.pas
TIDESynEditor.SrcSynCaretChanged(

      List := TextView.FoldProvider.NestedFoldsList;
      List.ResetFilter;
      List.Clear;
      List.Line := CaretY-1;
      List.FoldGroup := FOLDGROUP_PASCAL; // laways 0 (zero)
      List.FoldFlags := [sfbIncludeDisabled];  // only matters, if your HL has such config
      List.IncludeOpeningOnLine := False;

List.NodeLine[0]  // line on which the most inner fold starts
List.NodeLine[1]  // line on which the ONE outside the most inner fold starts

List.NodeFoldType  the type



Edson

  • Hero Member
  • *****
  • Posts: 1057
Re: Folding in SynEdit
« Reply #37 on: April 08, 2014, 10:57:06 pm »
Thanks Martin.

Finally, I have used TSynCustomHighlighter.StartAtLineIndex(), for set the position of the HL, then I explore the current line until raise the CaretX. Then I use TopCodeFoldBlockType() for get the Current Top Folding Block.

It seems to work by now.

I have some bug on detecting the Top block at the end of some blocks, but it must be problem of the block's manage of my HL.

PS: It should be very good if the Source Code of the IDE would have comments.
Lazarus 1.6 - FPC 3.0.0 - x86_64-win64 on  Windows 7

Edson

  • Hero Member
  • *****
  • Posts: 1057
Re: Folding in SynEdit
« Reply #38 on: April 12, 2014, 09:06:54 pm »
I have success on having the Current Fold Block. But I'm having problems obtaining the limits of that block. I manage several blocks on the same line. That's why I need to read the column information.

Some questions:

1. What's the meaning of the parameter "FoldIndex" on the function TSynCustomFoldHighlighter.FoldLineLength() ?
2. What's FoldEndLine() for?. I guess this gives the end line, of the last block opened at the end of the indicated Line. But I find some errors on his work.
3. Is there some direct way for to obtain the quantity of ALL levels of folding? Accessing to TSynCustomFoldHighlighter.CodeFoldRange.CodeFoldStackSize, give me only the blocks opened with IncreaseLevel=TRUE.

Thanks.
« Last Edit: April 12, 2014, 09:10:32 pm by Edson »
Lazarus 1.6 - FPC 3.0.0 - x86_64-win64 on  Windows 7

Martin_fr

  • Administrator
  • Hero Member
  • *
  • Posts: 5800
    • wiki
Re: Folding in SynEdit
« Reply #39 on: April 12, 2014, 09:44:22 pm »
1. What's the meaning of the parameter "FoldIndex" on the function TSynCustomFoldHighlighter.FoldLineLength() ?
IIRC (please test) the index of the fold. That is, if there are more than one fold opening in that line (e.g. in line 20) then
  FoldLineLength(20,0) returns the amount of lines to the end of the first fold (leftmost) opening on that line
  FoldLineLength(20,1)  returns for the 2nd fold opening in that line.

This only accounts for folds that do NOT close on the same line. Folds closing on the same line are ignored, as they can not be folded.

Quote
2. What's FoldEndLine() for?. I guess this gives the end line, of the last block opened at the end of the indicated Line. But I find some errors on his work.

Returns the line number (or index,  not sure 0 versus 1 based) of the line on which the closing token of the fold is.

It does so by using end- and min-levels.
Again it ignores anything that closes on the same line as it opens.

Quote
3. Is there some direct way for to obtain the quantity of ALL levels of folding? Accessing to TSynCustomFoldHighlighter.CodeFoldRange.CodeFoldStackSize, give me only the blocks opened with IncreaseLevel=TRUE.

Not sure what you mean?

If you mean that you open/close some folds with In-/De-creaseLevel, and you want
- the total of al that had In-/De-creaseLevel
- AND the total of all, including the ones without In-/De-creaseLevel

Then look at  TSynPasSynRange
It adds the 2nd counter.
Also search for sfbIncludeDisabled (must be implemented by the highlighter)



Quote
I manage several blocks on the same line. That's why I need to read the column information.

Then you need to scan the line, when you need the info, and capture the info.
Pas HL does that. But it needs a lot of clean up, and the structures involved in that may change in future (Well maybe / maybe not so much...). But it should be in such way, that your sources can then be updated too. (I do recommend basic unit testing on that stuff)

Search for FCatchNodeInfo in the pas hl.

The info can be queried using       
List := TextView.FoldProvider.NestedFoldsList; (see  foldactions and stuff)

Unfortunately I havent worked on it in a while, so you need to search the sources.

Edson

  • Hero Member
  • *****
  • Posts: 1057
Re: Folding in SynEdit
« Reply #40 on: April 14, 2014, 01:32:20 am »
Thanks Martin for the responses.

Yes. "FoldIndex" is the Nesting Level of Folding. I haven't tested at all, but it seems to be that.

I think that Folding in TSynCustomFoldHighlighter, have been designed just for support the Folding marks on the gutter. It's oriented to lines.

The MinLevel and EndLevel, just consider the visible blocks. That's not enough for my HL.

I see Pascal HL, use another counters. But it's difficult for me to follow undocumented source. I'm not good on reading other's mind.

Anyway I need to scan lines, and need some extra counters. On the other hand, I don't want to loose speed on extra processing

I'm going to check and document some methods of the unit SynEditHighlighterFoldBase. It will take me some time.
Lazarus 1.6 - FPC 3.0.0 - x86_64-win64 on  Windows 7

Martin_fr

  • Administrator
  • Hero Member
  • *
  • Posts: 5800
    • wiki
Re: Folding in SynEdit
« Reply #41 on: April 14, 2014, 02:12:45 am »
With maybe 50 or 100 lines visible, that may need extra scanning, speed is not an issue.
Storing the extra info for 10000 lines, would probably be more of an issue.

Also, you can store a flag, if there is anything or nothing on that line. Then it will be even less of an effort.

The pas HL does not even do that flag, and yet it is super fast.

It only needs the extra scan for very few lines, such as the line at caret., So doing an extra scan does not matter. If it needs more, a flag may be added.

---
Adding the extra counter for with/without In-/De-creaseLevel should be easy.
I can get you more on that, if you need.

---
But the column finding code is another matter. It is a lot of code, and to document, or explain from scratch would take a lot of time.

Just basic notes:
1) During normal scanning, columns are not recorded.
2) When needed a flag (FCatchNodeInfo ) is set, and then a single line will be scanned again (or several)
3) When the flag is set, for each keyword information is added to a list.

You can see where the flag is tested "if FCatchNodeInfo " in open/close-fold procedures.
Ignore that there are different procedures for open/close, that has nothing to do with FCatchNodeInfo . Just look at one open and one close-fold method.

You can see the record where the info is stored, as well the list to which it is added.

The rest, sorry, is up to you.