TurboPack / SynEdit

SynEdit is a syntax highlighting edit control, not based on the Windows common controls.
216 stars 72 forks source link

Lines.Add / Lines.Insert do not respect CRLF in a string #235

Closed code-kungfu closed 1 year ago

code-kungfu commented 1 year ago

Digging through the SynEdit code, it seems the underlying logic responsible for inserting content into the edit control, are not respecting a string with CRLF in it and shifts to a new line.

Given the follwing example:

procedure TForm1.Button1Click(Sender: TObject);
begin
  SynEdit1.Lines.Add('This is a ' + sLineBreak + 'two line ' + sLineBreak + #32 + #32 + 'indented line' + sLineBreak);
end;

This will result in only the first line with This is a added and the rest of the content is left out.

In another practical example, if you have a string containing prettified JSON you need to display with highlighter, it will not insert this correctly at all.

I suspect it is the logic in TSynEditStringList.InsertStrings in unit SynEditTextBuffer that needs to be looked at.

A crude way I solve it for now, is with a class helper which loops through the string, splits it up every time a LineFeed control character is hit and inserts it into the SynEdit control, however this shouldn't be necessary in the first place.

chapmanworld commented 1 year ago

Another work around...

SynEdit1.Lines.Text := SynEdit1.Lines.Text + 'This is a ' + sLineBreak + 'two line ' + sLineBreak + #32 + #32 + 'indented line' + sLineBreak;

pyscripter commented 1 year ago

SynEdit.Lines inherits from TStrings and behaves the same.

Try this

  var SL := TStringList.Create;
  SL.Add('This is a ' + sLineBreak + 'two line ' + sLineBreak + #32 + #32 + 'indented line' + sLineBreak);
  ShowMessage(SL.Text);
  ShowMessage(SL.Count.ToString);
  SL.Free;

You get one line with the text which includes line breaks.

Now try with SynEdit assuming you have SynEdit1:

  SynEdit1.Lines.Clear;
  SynEdit1.Lines.Add('This is a ' + sLineBreak + 'two line ' + sLineBreak + #32 + #32 + 'indented line' + sLineBreak);
  ShowMessage(SynEdit1.Text);
  ShowMessage(SynEdit1.Lines.Count.ToString);

You get exactly the same as with TStrings.Add. Your full line is there (!) but only the first part is displayed. This is because each visually displayed line corresponds to line of the underlying TStrings descendent and cannot take more vertical space.

One way to add text that may contain line breaks is:

  var S := 'This is a ' + sLineBreak + 'two line ' + sLineBreak + #32 + #32 + 'indented line' + sLineBreak;
  SynEdit1.Lines.AddStrings(S.Split([sLineBreak]));

Another way is to use the method InsertText e.g.

  var S := 'This is a ' + sLineBreak + 'two line ' + sLineBreak + #32 + #32 + 'indented line' + sLineBreak;
  TSynEditStringList(SynEdit1.Lines).InsertText(SynEdit1.Lines.Count, S);
code-kungfu commented 1 year ago

With all due respect, but it is a design defect and it isn't working as designed when you look at every other edit control's behavior which are using the same Lines derivative approach.

I'm happy with either a) fix the behavior of Lines.Add() or b) add a method to the SynEdit base class which is more intuitive to use, like SynEdit1.AddLines() which takes care of the housekeeping or something along those lines.

pyscripter commented 1 year ago

b) add a method to the SynEdit base class which is more intuitive to use, like SynEdit1.AddLines()

SynEdit.Lines.InsertText?

The SynEdit text buffer is manipulated through its Lines property which is a TStrings descendent.

code-kungfu commented 1 year ago

The SynEdit text buffer is manipulated through its Lines property which is a TStrings descendent.

Yes, but the behavior is not correct compared to any other editor control available, and that is a defect. Lines.Add() has always handled a string with multiple CRLF occurrences correctly.

SynEdit.Lines.InsertText?

It is not intuitive. And, it is Lines.Insert(), not InsertText. Lines.Insert() does not work either with strings containing CRLF

pyscripter commented 1 year ago

TStrings.Add adds a single line. TStrings.AddStrings overloads add many lines. If you want to add many lines just use TStrings.AddStrings. In SynEdit a single line should not contain line breaks.

TSynEditStringList provides a public InsertText method exactly for this reason. It splits the line and inserts the result into the string list. However to use it you need to typecast. BTW it also provides another public method InsertStrings which works like AddStrings.

TSynEditStringList(SynEdit1.Lines).InsertText(Index, NewText);

It can also be used for adding text.