Author Topic: Synedit wordwrap  (Read 6952 times)

Tommi

  • Sr. Member
  • ****
  • Posts: 258
Synedit wordwrap
« on: November 17, 2022, 11:50:00 pm »
Hi,
is there in synedit any wordwrap option ? I cannot see it but it seems very strange that a so big and complex library has no word wrap.

Thank you

Martin_fr

  • Administrator
  • Hero Member
  • *
  • Posts: 12641
  • Debugger - SynEdit - and more
    • wiki
Re: Synedit wordwrap
« Reply #1 on: November 18, 2022, 12:40:47 am »
Yes, there is. But it's kind of beta... in progress.

Code: Pascal  [Select][+][-]
  1. uses SynEditWrappedView;
  2. begin
  3.   TLazSynEditLineWrapPlugin.Create(ASynEdit);

Tommi

  • Sr. Member
  • ****
  • Posts: 258
Re: Synedit wordwrap
« Reply #2 on: November 18, 2022, 01:10:16 pm »
Ok, thank you. And how to disable it after it has been enabled?

Martin_fr

  • Administrator
  • Hero Member
  • *
  • Posts: 12641
  • Debugger - SynEdit - and more
    • wiki
Re: Synedit wordwrap
« Reply #3 on: November 18, 2022, 01:43:09 pm »
That isn't implemented yet....

You may be able to do something like
Code: Pascal  [Select][+][-]
  1. FLineMapView := aSynEdit.TextViewsManager.SynTextViewByClass[TSynEditLineMappingView];
  2. aSynEdit.TextViewsManager.RemoveSynTextView(FLineMapView);
and add it back later...
Mind that is only the visualization... So the rest is still running in the background.

I have not tested this.

Martin_fr

  • Administrator
  • Hero Member
  • *
  • Posts: 12641
  • Debugger - SynEdit - and more
    • wiki
Re: Synedit wordwrap
« Reply #4 on: November 18, 2022, 01:44:48 pm »
Depending on what you need you may want to checkout AtSynEdit.
It has had wrapping for much longer...

ChristianH

  • New Member
  • *
  • Posts: 49
Re: Synedit wordwrap
« Reply #5 on: December 18, 2024, 02:09:44 pm »
The thread is quite old but the issue still exists. To fix this you should add the following code to the TLazSynEditLineWrapPlugin:

Code: Pascal  [Select][+][-]
  1. destructor TLazSynEditLineWrapPlugin.Destroy;
  2. begin
  3.  TSynEdit(Editor).UnRegisterStatusChangedHandler(@DoWidthChanged);
  4.  TSynEdit(Editor).TextViewsManager.RemoveSynTextView(FLineMapView, true);
  5.  TSynEdit(Editor).Invalidate;
  6.  
  7.   inherited;
  8. end;  


I do not know if this is missing on purpose, but I guess nobody tried to enable and disable the wrapper.

Christian

Martin_fr

  • Administrator
  • Hero Member
  • *
  • Posts: 12641
  • Debugger - SynEdit - and more
    • wiki
Re: Synedit wordwrap
« Reply #6 on: January 24, 2025, 09:54:08 pm »
The thread is quite old but the issue still exists. To fix this you should add the following code to the TLazSynEditLineWrapPlugin:

Thanks, sorry for the delay. Added now. Will merge to fixes-4 asap. (the fix(es) only, not the new feature that were added around it)

Removing the view crashed, when the editor itself was already in destruction. So I added guards for that.


The reason it wasn't there is, that the view may not have been owned by the plugin.

The plugin originally was thought to be able to share that. (see the constructor, there is a search for an existing view).
This will however be removed, it wont be needed any more.

Martin_fr

  • Administrator
  • Hero Member
  • *
  • Posts: 12641
  • Debugger - SynEdit - and more
    • wiki
Re: Synedit wordwrap
« Reply #7 on: January 24, 2025, 10:26:36 pm »
I merged any fixes to the fixes-4 branch. If you are using git (e.g. fpcUpDeluxe), then you can pull them and test them. (Or you can view them online and patch your SynEdit).  RC2 does NOT contain them.

gompertz

  • Newbie
  • Posts: 5
Re: Synedit wordwrap
« Reply #8 on: December 06, 2025, 05:17:46 am »
Hi I'm trying to toggle wrapping in SynEdit and have confirmed my component library has the changes merged by Martin_fr, however I'm getting a crash in Linux when I trigger my callback.

Relevant code for removing wrapping:

Code: Pascal  [Select][+][-]
  1. var
  2. FLineMapView: TSynEditStringsLinked;
  3. begin
  4.  FLineMapView := ASynEdit.TextViewsManager.SynTextViewByClass[TSynEditLineMappingView];
  5.  ASynEdit.TextViewsManager.RemoveSynTextView(FLineMapView);
  6. ...
  7.  

Any ideas? Thanks



Martin_fr

  • Administrator
  • Hero Member
  • *
  • Posts: 12641
  • Debugger - SynEdit - and more
    • wiki
Re: Synedit wordwrap
« Reply #9 on: December 06, 2025, 09:42:04 am »
component library has the changes merged by Martin_fr, however I'm getting a crash in Linux when I trigger my callback.

Deos that mean you use 4.x with some code merged from git main branch?
Or 4.99 aka the git main branch?

I did a lot of work on SynEdit in 4.99. And afaik some that was related to how such addons (like wordwrap) are handled. But I can't recall all the details.
So, if in doubt, make sure  you use 4.99.

With 4.99, I checked what the IDE (in ide/SourceSynEditor) does.
And it simple destroys the line wrap, and that will take care of its own removal.

Add:
  FWrapView := TLazSynEditLineWrapPlugin.Create(Self);

Remove:
  FreeAndNil(FWrapView);

If you want to temp remove it, then you need to check what all happens in destroy.

aotan

  • Newbie
  • Posts: 2
Re: Synedit wordwrap
« Reply #10 on: August 15, 2026, 10:33:52 am »
Re: Synedit wordwrap

I followed the approach discussed in this thread (TLazSynEditLineWrapPlugin via SynEditWrappedView) and found a crash when toggling WordWrap off at runtime (i.e. destroying the plugin while the editor is still alive).

Environment: Lazarus 4.8, FPC 3.2.2, Windows (x86_64-win64).

Symptom: Access Violation reading address ~0x50, in TSynTextViewsManager.RemoveGenericHandler (lazsynedittext.pas line 1946), called from TSynEditCaret.SetLines via TCustomSynEdit.DoTopViewChanged.

Root cause: In TSynTextViewsManager.RemoveSynTextView (lazsynedittext.pas), the outgoing view is detached from the Manager (SetManager(nil)) before ReconnectViews notifies dependents (e.g. the Caret) of the new top view. When the Caret tries to unregister its handler from the outgoing view during that reconnect step, the view's Manager reference is already nil, causing the AV.

Fix: reorder so dependents are reconnected before detaching/freeing the view:

Code: Pascal  [Select][+][-]
  1. procedure TSynTextViewsManager.RemoveSynTextView(aTextView: TSynEditStringsLinked;
  2.   aDestroy: Boolean);
  3. var
  4.   i: Integer;
  5. begin
  6.   i := FTextViewsList.IndexOf(aTextView);
  7.   if i >= 0 then begin
  8.     FTextViewsList.Delete(i);
  9.     ReconnectViews;
  10.     aTextView.SetManager(nil);
  11.     if aDestroy then
  12.       aTextView.Free;
  13.   end;
  14. end;
« Last Edit: August 15, 2026, 10:41:57 am by aotan »

aotan

  • Newbie
  • Posts: 2
Re: Synedit wordwrap
« Reply #11 on: August 15, 2026, 10:59:38 am »
Re: Synedit wordwrap

Follow-up: found two more bugs in TLazSynEditLineWrapPlugin.CalculateNextBreak (syneditwrappedview.pp) that corrupt wrapped lines containing multi-byte UTF-8 characters (e.g. Japanese text, or wide symbols like ● / ■). Both are in the single-character fallback loop used when a "word" doesn't fit the available width (which is basically every line break in CJK text, since there are no ASCII break chars).

Bug 1 – negative remaining width splits a UTF-8 sequence:

When the first (too-wide) character is force-included via the Result = ALogStartFrom escape hatch, AMaxWidth can go negative. On the next byte (a UTF-8 continuation byte, width 0), the check CurCharPhysWidth <= AMaxWidth becomes 0 <= (negative) = False, so the loop stops mid-character, leaving an orphan continuation byte at the start of the next subline (renders as "?").

Bug 2 – wrong array index on subsequent wrapped lines:

Code: Pascal  [Select][+][-]
  1. PhysWidthPtr := @PhysCharWidths[0];  // should be [ALogStartFrom]

This resets the width lookup to the start of the whole line instead of the current subline's position, so wrap decisions for every subline after the first are made using the widths of unrelated characters earlier in the line. Since CJK text has almost no ASCII break chars, this fires on nearly every wrapped line, causing widespread garbling.

Fix (both in the same function):

Code: Pascal  [Select][+][-]
  1. if Result = ALogStartFrom then begin
  2.   PhysWidthPtr := @PhysCharWidths[ALogStartFrom]; // was [0]
  3.   ALine := LastGoodPos;
  4.   while ALine^ <> #0 do begin
  5.     CurCharPhysWidth := PhysWidthPtr^ and PCWMask;
  6.     if (CurCharPhysWidth <= AMaxWidth) or (Result = ALogStartFrom)
  7.        or (CurCharPhysWidth = 0) then begin  // always sweep in continuation bytes
  8.       inc(ALine);
  9.       inc(PhysWidthPtr);
  10.       inc(Result);
  11.       dec(AMaxWidth, CurCharPhysWidth);
  12.     end
  13.     else
  14.       break;
  15.   end;
  16. end;

Screenshots attached: before the fix (widespread "?" corruption) and after (bug 2 fixed — the "?" garbling is gone; a separate, unrelated rendering artifact remains where the character immediately after a full-width symbol like ●/■ loses its left half — this looks like a pre-existing SynEdit character-painting issue unrelated to wrapping, reproducible even with WordWrap off).

Martin_fr

  • Administrator
  • Hero Member
  • *
  • Posts: 12641
  • Debugger - SynEdit - and more
    • wiki
Re: Synedit wordwrap
« Reply #12 on: August 15, 2026, 12:38:44 pm »
Thanks, will check asap

Martin_fr

  • Administrator
  • Hero Member
  • *
  • Posts: 12641
  • Debugger - SynEdit - and more
    • wiki
Re: Synedit wordwrap
« Reply #13 on: August 15, 2026, 05:37:57 pm »
Symptom: Access Violation reading address ~0x50, in TSynTextViewsManager.RemoveGenericHandler (lazsynedittext.pas line 1946), called from TSynEditCaret.SetLines via TCustomSynEdit.DoTopViewChanged.

For now I have applied the new order. Decision on merging to fixes branch is still pending.

Btw, in 4.99 WordWrap is handled by a plugin, so all adding/removing should be done via that.

---
2nd issue still todo...

Martin_fr

  • Administrator
  • Hero Member
  • *
  • Posts: 12641
  • Debugger - SynEdit - and more
    • wiki
Re: Synedit wordwrap
« Reply #14 on: August 17, 2026, 11:53:26 am »
Fixed and added testcase.

Thanks for the report, and finding the fixes.

 

TinyPortal © 2005-2018