Recent

Author Topic: SynEdit TSynEditMarkupHighlightAll: highlight text spread thought multiple lines  (Read 15219 times)

Henppy

  • Jr. Member
  • **
  • Posts: 60
This comes from this thread http://forum.lazarus.freepascal.org/index.php/topic,23160.msg138331.html#msg138331, if you see it and have something to add, please do it here. (I messed up big time with the thread cohesion algorithm. :D)

So I'm using a TSynEditMarkupHighlightAll to mark syntax error on code. It works but only if the text I want to highlight is in one single line. Any idea how can I make it work with text spread across multiple lines?

What I have so far:

- My own TMySyn class to be able to access the markup manager.
- Add a markup with red wavy underline to said manager.
- Set the SearchString property to what I want highlighted.

It works with single lines, but not if the text spreads through more than one line. Just in case, I tried SearchString := "this" + LineEnding + "that" and #13 and #10 combinations but obliviously it's not that simple.   

Any ideas?

Martin_fr

  • Administrator
  • Hero Member
  • *
  • Posts: 12171
  • Debugger - SynEdit - and more
    • wiki
Not exactly sure what the problem is (I can imagine many for what you describe)

I guess: you struggle with the highlighter doing one line of a time?

There are 2 ways. Proper and hack:

The hack: While scanning one line the highlighter can access the TextBuffer, and read other lines. I guess that would help?
But: it can add huge processing cost, scanning each line many extra times....

The proper way. After each token the Scanner is in a specific state. At the end of line, store this state as "range" then pick it up at the start of the next line.
States in bracket:

[begin_of_statement] SearchString [after_identifier_at_begin_of_stmt] := [at_start_of_expression]

Now you must remember that you are in a expression, so you need a 2nd flag

{in_expression}
 "this" [after_value]{in_expression} + [after_operator]{in_expression} LineEnding + "that"


Henppy

  • Jr. Member
  • **
  • Posts: 60
Hey Martin.

Let's see if I can explain my self a little better:

I have a custom highlighter. It works, keywords are green, identifiers are white, and so on. Not a problem there.

But now I'd like to mark errors on the code. So I wrote a syntax checking code. No problem there neither. The HL does its thing and feeds the syntax checker and it works just fine.

Now, how do I mark code to indicate an error? You know, like a red underline. For what I can gather on the Internet I need to use a TSynEditMarkupHighlightAll object to do that.

Again, no problem there, I have my Markup object all set up, I assign it the text I want marked and it marks it.

BUT, it only works when the text that I want marked is in one single line. If the text extends over more than one line, then I don't know what to do.

So this will be marked with a red underline:

Code: [Select]
thisvar := 44 + ;

But what to do if in this situation?

Code: [Select]
thisvar := 33 +
+ 32;

Where I would like to mark "33 + enter + +32;" because you have two "+".
« Last Edit: January 12, 2014, 02:58:47 am by Henppy »

Edson

  • Hero Member
  • *****
  • Posts: 1327
Now it's clear for me.

If for your language, the expresión:

Code: [Select]
thisvar := 33 +
+ 32;

is an error. Then, the language is "line oriented".

So, as I can see, there is not ONE syntax error. There is TWO:

*The first would be:  "Syntax Error 666 in line 1: Expected Something"
*The second would be: "Syntax  Error 667 in line 2: Unexpected Symbol"

In this case you don't have to deal with a multiline text. Just highlight two simple parts of text. It will be easy.

Well, this is just an suggestion. But if you still want to mark a multiline text. I would suggest to scan all the code since the current line ("x" lines forward), every time a key is pressed, and then get the "Begin" and "End" coordinates of the Syntax Error. Then, use this information for highlight with the Highlighter or probably with the Markup. But I see this some kind of complicated.
Lazarus 2.2.6 - FPC 3.2.2 - x86_64-win64 on Windows 10

Henppy

  • Jr. Member
  • **
  • Posts: 60
Hmm... this is very strange. Apparently adding a LineEnding works. But not always, please see the attached picture.

I have set up SearchString := 'this' + LineEnding + 'that';

First I write both words, nothing happens. Then I add space + "d" and it works, then I backspace to delete the space and letter I just typed and in keeps working... ?

BTW, the language is multi-line, my example was not great. The error on the example is that you have two "+", it just happens to be spread in two lines instead of one.

Edson

  • Hero Member
  • *****
  • Posts: 1327
It looks like a bug of the Markup. I have observed that SynEdit, doesn't highlight the last word of the text when using TSynEditMarkupHighlightAll. Probably Martin have some extra information.

Maybe you should think on using the Highlighter instead of TSynEditMarkupHighlightAll.

BTW, I see that the text of the "syntax error", you want to highlight is more a text of a "semantical error", because I suppose you make first some extra analysis for detecting the multiline text of the error.
Lazarus 2.2.6 - FPC 3.2.2 - x86_64-win64 on Windows 10

Martin_fr

  • Administrator
  • Hero Member
  • *
  • Posts: 12171
  • Debugger - SynEdit - and more
    • wiki
I see. You use one of the existing Markup. I thought you wrote your own.

If you look at all the ones that can highlight text by matching a search string, they all have a list of start/end points.
In your own class, you could directly set those points.

If you use the existing search markup, then
Code: [Select]
// comment a::=b;
a::=b
would highlight bot occurrences as error. but the comment is not.

The simple search markup (IIRC) has SearchFlags (set of enum), and there is a value for multi line.

As for the picture: maybe an issue of the module not calling invalidate? But just a guess. I will have to test this, and see if there is a bug or not.
If your findings are accurate, then this is a bug in SynEdit

Martin_fr

  • Administrator
  • Hero Member
  • *
  • Posts: 12171
  • Debugger - SynEdit - and more
    • wiki
It looks like a bug of the Markup. I have observed that SynEdit, doesn't highlight the last word of the text when using TSynEditMarkupHighlightAll. Probably Martin have some extra information
Then please report

Martin_fr

  • Administrator
  • Hero Member
  • *
  • Posts: 12171
  • Debugger - SynEdit - and more
    • wiki
Is that last word highlight issue present in 1.2?

I can reproduce it in 1.0.14, but it appears to be fixed in trunk.

Edson

  • Hero Member
  • *****
  • Posts: 1327
I can see it's solved in 1.2.  :)

But, I have seen some cases when the word under the cursor is not highlighted. I have seen this when pointing some word, and the match word, is one thousand lines before. Is that normal?
Lazarus 2.2.6 - FPC 3.2.2 - x86_64-win64 on Windows 10

Martin_fr

  • Administrator
  • Hero Member
  • *
  • Posts: 12171
  • Debugger - SynEdit - and more
    • wiki
Yes it only looks 100 lines out of screen .

Edson

  • Hero Member
  • *****
  • Posts: 1327
It is not configurable?
Lazarus 2.2.6 - FPC 3.2.2 - x86_64-win64 on Windows 10

Martin_fr

  • Administrator
  • Hero Member
  • *
  • Posts: 12171
  • Debugger - SynEdit - and more
    • wiki
NO, but could be added. yet it will be slow. Big searches must go into OnIdle.

look at unit SynEditMarkupHighAll

there are several classes:

* TSynEditMarkupHighlightAll
Which has a SEarchTerm property.

It also has HideSingleMatch
- If that is false, then it highlight every match, so there is no need for extra search.
- if it is true, then it searches for a 2nd match outside the visible area, but only 100 lines.

* TSynEditMarkupHighlightAllMulti  which can have many search terms

* TSynEditMarkupHighlightAllCaret which uses the word under caret

------
For writing any such markup, just base it on TSynEditMarkupHighlightMatches

and add start/end points to
    property  Matches: TSynMarkupHighAllMatchList read FMatches;

(the ifdef lowlight uses it too)


Henppy

  • Jr. Member
  • **
  • Posts: 60
I've been trying to make this work. Following the advice of using my own descendant class so I can set the start and end point for the matches.

So far I have this (highly in "just to see" coding style):

Code: [Select]
type fEditor = class(TMySyn)
  Markup: TMarkup;
[...]
  Markup := TMarkup.Create(fEditor);
  Markup.MarkupInfo.FrameEdges := sfeBottom;
  // more style related lines
  fEditor.MarkupMgr.AddMarkUp(Markup);       

With TMarkup being:

Code: [Select]
type  TMarkup = class(TSynEditMarkupHighlightAll)
  public
    procedure AddMark(Start, Finish: TPoint);
end;

procedure TMarkup.AddMark(Start, Finish: TPoint);
var
    p: __TSynEditMarkupHighlightAll; //This class exposes the fMatches field.
begin
  p := __TSynEditMarkupHighlightAll(self);

  p.fMatches.StartPoint[0] := LogicalToPhysicalPos(Point(1,1));
  p.fMatches.EndPoint[0]:= LogicalToPhysicalPos(Point(5,1));
  Invalidate;
end


The idea being that I artificially inject a match in the match list instead of relaying in the InvalidateMacthes method that gets called when SearchString is set.

Now, if I just do Markup.SearchString := 'something', it will mark that code, it works. So I assume the markup is properly set in the manager.

But if I use my AddMark method to add a particular point, it doesn't mark anything and when the program ends I get a SIGSEGV in TSynEditMarkupManager.Destroy -> fMarkUpList[idx].Destroy -> FreeAndNil(fMarkupInfo);.

So obviously my markup is not properly set.

I tried looking how to use the TSynMarkup on the net but my searches came pretty much empty. So if you have any suggestions or know of any place where I could look for examples please let me know.

Thanks.



Martin_fr

  • Administrator
  • Hero Member
  • *
  • Posts: 12171
  • Debugger - SynEdit - and more
    • wiki
Code: [Select]
type fEditor = class(TMySyn)
  Markup: TMarkup;
[...]
  Markup := TMarkup.Create(fEditor);
  Markup.MarkupInfo.FrameEdges := sfeBottom;
  // more style related lines
  fEditor.MarkupMgr.AddMarkUp(Markup);       
Looks correct.

Note, the markupmgr will take ownership, and destroy your markup when SynEdit is freed. So you do not need to do that yourself.

Quote
With TMarkup being:
Code: [Select]
type  TMarkup = class(TSynEditMarkupHighlightAll)
  public
    procedure AddMark(Start, Finish: TPoint);
end;

procedure TMarkup.AddMark(Start, Finish: TPoint);
var
    p: __TSynEditMarkupHighlightAll; //This class exposes the fMatches field.
begin
  p := __TSynEditMarkupHighlightAll(self);

  p.fMatches.StartPoint[0] := LogicalToPhysicalPos(Point(1,1));
  p.fMatches.EndPoint[0]:= LogicalToPhysicalPos(Point(5,1));
  Invalidate;
end


Try inheriting from TSynEditMarkupHighlightMatches (needs Lazarus 1.2 or 1.3) / same unit.
Then the build-in search will not invalidate your entries.

"  Invalidate;" removes all entries.

You need to call
    procedure InvalidateSynLines(FirstLine, LastLine: integer); // Call Synedt to invalidate lines
That will trigger the repaint in SynEdit.

Points are logical-x, so no need for LogicalToPhysicalPos


 

TinyPortal © 2005-2018