Recent

Author Topic: SynBeautifier: Autoindent and tab settings  (Read 5132 times)

Martin_fr

  • Administrator
  • Hero Member
  • *
  • Posts: 9754
  • Debugger - SynEdit - and more
    • wiki
SynBeautifier: Autoindent and tab settings
« on: May 04, 2021, 03:10:03 pm »
From https://bugs.freepascal.org/view.php?id=38843
Quote from: martok
Quote from: martin_fr
AutoIndent is done by TSynBeautifier and that has it's own settings.

The default class (none language specific beautifier) has
SynEdit.Beautifier.IndentType
Another setting? Hm. Could this be documented somewhere?
Default Beautifier properties set in Object Inspector are not saved to LFM so this has to be set in code, is that intentional?

On a more general note, that means that, so far:
- block indent of selections uses BlockIndent/BlockTabIndent
- smart indent from [Tab] at line start uses CreateTabsAndSpaces(TabWidth)
- autoindent uses the Beautifier
All of these have completely different rules and depending on which one you happen to hit, the resulting text looks completely different. Is it really supposed to be that messy?

The short bits....

1) AutoIndent can require handling of situations (conflict/see below) that BlockIndent never has to deal with. So it needs additional settings.

2) There are a lot of ways those feature can be set. Many of them came from user-requests (or from the original SynEdit). That leads to a lot of settings, unfortunately...

3) Grouping of settings / finding them all over the place => see "modularity" below.
  (Also note, that some of those settings will also be influenced by "trimming trailing spaces", which are on a module of their own.

So between all of that, it's not trivial to find one good way to have all the settings accessible.
(For the OI, a special property editor with a wizard style configurator might be an idea / but who would write that?)




Why the extra setting.

If you look at the options
Code: Pascal  [Select][+][-]
  1.  sbitConvertToTabSpace, // convert to tabs, fill with spcaces if needed
  2.  sbitConvertToTabOnly // convert to tabs, even if shorter
You will see that the Beautifier can not follow the "BlockIndent" settings, because they do not contain info how to resolve conflicts. So additional settings are needed.

Yet see the proposal on the mantis issue, about how to at least make it follow for none conflict cases.

Given that new setting, it would be nice, if this was not crammed into a single property.
But if that is to be changed, special care needs to be taken, so the old property can still be read (IIRC possible with "DefineProperty")




Why the "invisible" default Beautifier

Due to historical reasons SynEdit has a lot of add-ons pre-added. (I.e. SynEdit used to be a big block of "all or nothing" code.).
The overall goal is to make it more modular.

Many features have been extracted (and more should be), so they can be added as needed.

The problem is, they can not simply be removed from the drag-and-drop SynEdit, because any project having a SynEdit on a form would loose all the features.
So instead a new lean SynEdit that does not have them pre-added needs to be created.
However creating that now, also recreates the above problem, if it is used and more features become add-ons... (And I don't want to end with half a dozen different LeanSynEdit.)

Off topic, this is also why I try to publish as few properties as possible. Once in OI, and lfm files, it becomes hell to change them.
If they are in code only, they get deprecated with a note how to replace them.
Code is checked when you compile, users will get a note or error. LFM are not check, they crash and maybe only sometimes.


The beautifier is one of the things SynEdit should only have, if added by a user. (And then the user would configure it).
But the problem is as told, existing projects expect it (or it's behaviour) to be present.

So that behaviour needed to be kept. Therefore there is now a default Beautifier as a fallback.
IIRC its a global object shared by all edits (needs to be checked).

The default can not work in the OI  (It would take quite some dirty workarounds....)., and it shouldn't even be shown (Not sure why it does, or since when / I almost overlooked that part of your comment completely).
So the idea is to add your own, if you want to configure it.


Hope I did not miss anything else.

Martin_fr

  • Administrator
  • Hero Member
  • *
  • Posts: 9754
  • Debugger - SynEdit - and more
    • wiki
Re: SynBeautifier: Autoindent and tab settings
« Reply #1 on: May 04, 2021, 07:45:03 pm »
Just adding, the implementation of the default beautifier, suggests it was meant to be avail in OI, and maybe save to lfm... Need to dig deeper...

And it is not global, it is one per each SynEdit.

Martok

  • New Member
  • *
  • Posts: 20
Re: SynBeautifier: Autoindent and tab settings
« Reply #2 on: May 08, 2021, 11:14:43 pm »
Hi Martin,

first of all, thank you for the long explanation. I didn't even know what the goal architecture for the refactoring was, quite a few things make a lot more sense now.

Do you have plans to at some point cut compatibility and use the new modular system to assemble a SynEdit that doesn't have to deal with compatibility requirements? Not as a "LeanSynEdit", more like a proper rebuild of a "baseline" feature set on the new architecture.
That could be useful as a general text editor and have a "simpler" external interface that controls the individual plugin parts, even if it doesn't expose all of them.


Why the extra setting.

If you look at the options
Code: Pascal  [Select][+][-]
  1.  sbitConvertToTabSpace, // convert to tabs, fill with spcaces if needed
  2.  sbitConvertToTabOnly // convert to tabs, even if shorter
You will see that the Beautifier can not follow the "BlockIndent" settings, because they do not contain info how to resolve conflicts. So additional settings are needed.

Yet see the proposal on the mantis issue, about how to at least make it follow for none conflict cases.
Right. The thing that bugs me is that the action "indent the following line up to character N" is fundamentally different from "indent the current line up to character N" (I can see why SmartIndent is different). In terms of the redesign, should that be a function of the TextBuffer "insert padding according to the current rules" that both features call to?

Many features have been extracted (and more should be), so they can be added as needed.

The problem is, they can not simply be removed from the drag-and-drop SynEdit, because any project having a SynEdit on a form would loose all the features.
So instead a new lean SynEdit that does not have them pre-added needs to be created.
However creating that now, also recreates the above problem, if it is used and more features become add-ons... (And I don't want to end with half a dozen different LeanSynEdit.)
True, having to keep the same interface of the component means that someone will try to use them.

Although I wonder... both FPC and Lazarus have made pretty heavy breaking changes in the past, replacing a few properties with linked components seems managable?


Off topic, this is also why I try to publish as few properties as possible. Once in OI, and lfm files, it becomes hell to change them.
If they are in code only, they get deprecated with a note how to replace them.
Code is checked when you compile, users will get a note or error. LFM are not check, they crash and maybe only sometimes.
Markups are one of these new properties, right? Took me a while to figure out how to make the SpecialChars a different color, since the comment on the flag that says the're grayed out is not true anymore.

So that behaviour needed to be kept. Therefore there is now a default Beautifier as a fallback.
Just adding, the implementation of the default beautifier, suggests it was meant to be avail in OI, and maybe save to lfm... Need to dig deeper...

And it is not global, it is one per each SynEdit.
The thing is, I for one hadn't even noticed that it is even possible to assign a different Beautifier. It presents very similar to properties like Font in the OI...
And since the field is published as TSynCustomBeautifier, setting IndentType only works with a typecast... come to think of it, maybe that is why it doesn't save correctly?


Martin_fr

  • Administrator
  • Hero Member
  • *
  • Posts: 9754
  • Debugger - SynEdit - and more
    • wiki
Re: SynBeautifier: Autoindent and tab settings
« Reply #3 on: May 09, 2021, 12:11:55 am »
Do you have plans to at some point cut compatibility and use the new modular system to assemble a SynEdit that doesn't have to deal with compatibility requirements? Not as a "LeanSynEdit", more like a proper rebuild of a "baseline" feature set on the new architecture.
That could be useful as a general text editor and have a "simpler" external interface that controls the individual plugin parts, even if it doesn't expose all of them.

First of all, "plans" is currently something that can be reaching far into the future. With all the other stuff on my list....

The idea is to maybe create a TLaz[Syn]Edit, which would be the core only.
TSynEdit itself would stay as it is, and inherit from the new LazEdit. TSynEdit might then disappear from the component palette, to encourage using the new one.

Quote
Right. The thing that bugs me is that the action "indent the following line up to character N" is fundamentally different from "indent the current line up to character N" (I can see why SmartIndent is different).

First of all, they are always different. The "beautify indent" is more like the "smart tab" (and smart tab, can be indent, but also be in the middle of the line).

- Normal Indent always goes to a "reachable location". Indent by "one tab" will never need to indent to a position that can't be reached by tabs.
- "Block indent" adds to existing indent, and needs not to worry if there are spaces (block un-indent might).

- "beauty indent" (like smart indent) does not go to a position given by tabs. It goes to an x given by the line(s) above. (the line above can contain spaces).
  "beauty indent" therefore might need spaces, even if normal indent will work with tabs only.

Normal indent => calculate X from the tabs
beauty indent => calculate tabs from X

So by their own nature they are different.

Quote
In terms of the redesign, should that be a function of the TextBuffer "insert padding according to the current rules" that both features call to?
That would not solve the issue. There still remain cases that will need spaces, when normal indent is tab only.

It does not need to go to the TextBuffer, see my proposal of adding more enum to the option.

It is even more complex. If 2 SynEdits share a text buffer, the 2 SynEdit can have different config. So indent would (and should) act different depending which SynEdit you type in.
It's up to the user code to sync any options that should be the same.
For example, if the editor had a keycombo to toggle trimming spaces, then that could very well be per editor (and not per textbuffer). That way a user could temp disable it in one of the editors.


Quote
Markups are one of these new properties, right?
Yes as well as the shared textbuffer, and the TextViews (some of them contain functionality that should not be in textviews, like the CharWidths stuff)

Markups are not published, because there are some pre-installed. And having some auto created elements in the list, makes streaming a hell.
Gutter has such hell-ish code....

Quote
I for one hadn't even noticed that it is even possible to assign a different Beautifier.
That is a problem....

I can either hide the default in OI (by means of a special property editor).
But then you can't edit in OI...

Or I can make it stream to lfm. But then it should show in OI.

And in either case, there is no way to set it to "nil"

Quote
come to think of it, maybe that is why it doesn't save correctly
Not checked 100%, but I believe it is because it is an inlined component. And SynEdit as owner does not return it, when asked by the streaming system.


Martin_fr

  • Administrator
  • Hero Member
  • *
  • Posts: 9754
  • Debugger - SynEdit - and more
    • wiki
Re: SynBeautifier: Autoindent and tab settings
« Reply #4 on: May 09, 2021, 12:24:04 am »
Couldn't think of a good word for it before, now I got it: align.

Smart indent and beautifier are not so much indent functions. They are align functions.

Martok

  • New Member
  • *
  • Posts: 20
Re: SynBeautifier: Autoindent and tab settings
« Reply #5 on: May 11, 2021, 07:51:45 pm »
First of all, "plans" is currently something that can be reaching far into the future. With all the other stuff on my list....
We'll call them goals then. "Wenn man mal Zeit hat"  :)

It does not need to go to the TextBuffer, see my proposal of adding more enum to the option.

It is even more complex. If 2 SynEdits share a text buffer, the 2 SynEdit can have different config. So indent would (and should) act different depending which SynEdit you type in.
Interestingly, I was thinking about just that configuration and came to the opposite conclusion... to me, indentation (not align) is a property of the text itself, not the input method. Like for example Python code is always supposed to have 4 spaces, regardless of what it is written with.

For example, if the editor had a keycombo to toggle trimming spaces, then that could very well be per editor (and not per textbuffer). That way a user could temp disable it in one of the editors.
That, on the other hand, clearly is part of the input method, yes.


Couldn't think of a good word for it before, now I got it: align.

Smart indent and beautifier are not so much indent functions. They are align functions.
That makes so much more sense and is actually sort-of self-documenting!
The new option you proposed would "align as if it was indented", but would still have to pad with spaces when some columns are not reachable by full tabs.


Quote
I for one hadn't even noticed that it is even possible to assign a different Beautifier.
That is a problem....
That might just have been me being blind.
The OI shows a dropdown, properties are green, just like for all other linked component properties. It's just a bit unexpected, AFAIK no other component provides its own instance, they just have default behaviour if the property is nil.
Update: actually, that's a lie. TAChart Series's Source property does the same. I also never really noticed that:o


Martin_fr

  • Administrator
  • Hero Member
  • *
  • Posts: 9754
  • Debugger - SynEdit - and more
    • wiki
Re: SynBeautifier: Autoindent and tab settings
« Reply #6 on: May 11, 2021, 09:15:52 pm »
It is even more complex. If 2 SynEdits share a text buffer, the 2 SynEdit can have different config. So indent would (and should) act different depending which SynEdit you type in.
Interestingly, I was thinking about just that configuration and came to the opposite conclusion... to me, indentation (not align) is a property of the text itself, not the input method. Like for example Python code is always supposed to have 4 spaces, regardless of what it is written with.
Well SynEdit is not to instruct the user (who edits the text) what to do. The user is to instruct SynEdit.

For a particular application, that application can setup SynEdit to enforce whatever is needed.

Lets say the 2nd editor is currently on a line with embedded sql, which may need a different method of indent => then it could need a diff config.
If it should have the same config, then the App should make sure of that.


Also, as for the original issue:
Moving the code and/or the settings to the textbuffer would not fix it. There are 2 distinct settings, because there are 2 distinct actions. Though the 2nd action can/could to some degree derive its settings from the first:
see my proposal of adding more enum to the option.





Quote
Couldn't think of a good word for it before, now I got it: align.

Smart indent and beautifier are not so much indent functions. They are align functions.
That makes so much more sense and is actually sort-of self-documenting!
I am reluctant to rename them though. It is a breaking change for no functional benefit.
Maybe just take it as a good reason to add the fpdoc for those properties and document the as alignment.
Quote
The new option you proposed would "align as if it was indented", but would still have to pad with spaces when some columns are not reachable by full tabs.
Or under-align.

Code: Pascal  [Select][+][-]
  1. --->--->if foo and
  2. --->--->   bar // here are 3 spaces but I do not want them to be seen as indent
  3. --->--->
  4.  
So when hitting return, the beautifier aligns with the "if"

And that is, why the option can not just follow the "tab key" or "block" indent.
Because if "tab key" or "block" indent are "tab" => then beauty align can be 
- tab + space
- tab, cut off


Quote


Quote
I for one hadn't even noticed that it is even possible to assign a different Beautifier.
That is a problem....
That might just have been me being blind.
The OI shows a dropdown, properties are green, just like for all other linked component properties. It's just a bit unexpected, AFAIK no other component provides its own instance, they just have default behaviour if the property is nil.
Update: actually, that's a lie. TAChart Series's Source property does the same. I also never really noticed that:o

Well from an OI view, it would best be nil, and have (optional) default behaviour.

But that breaks (runtime) all code that does
  SynEdit.Beautifier.foobar

I am currently undecided how to fix it.
- Keep as it is, but make sure it saves.
   Downside: you can not set it to nil. (and the code is going to be ugly)
- Hide the default in OI.
   Upside: no messing with the lfm.

As I am writing this...
Maybe I unpublish this field.
Then
  public Beautifier
is accessible by code only, and can return the default.

New published
  SynBeautifier = nil // can be set by user (must be dropped onto the form)
  UseDefaultBeautifier = True // if set to false then the old "Beautifier" becomes nil. (unless SynBeautifier is set)

This would not break anything, and get a cleaner solution.





 

TinyPortal © 2005-2018