{"id":14035,"date":"2026-07-03T11:55:17","date_gmt":"2026-07-03T09:55:17","guid":{"rendered":"https:\/\/maenken.systems\/de\/?post_type=wpdmpro&#038;p=14035"},"modified":"2026-09-18T13:37:01","modified_gmt":"2026-09-18T11:37:01","slug":"msys_delphi_formatter","status":"publish","type":"wpdmpro","link":"https:\/\/maenken.systems\/de\/download\/msys_delphi_formatter\/","title":{"rendered":"MSYS_Delphi_Formatter_1.2.0.24"},"content":{"rendered":"<p>MSYS Delphi Formatter Installer<\/p>\n","protected":false},"excerpt":{"rendered":"<p>MSYS Delphi Formatter Installer<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","template":"page.php","meta":{"__wpdm_changelog":[{"id":"cl_new_1789731347107","version":"1.2.0.24","date":"2026-09-18","changes":"<p>=======================================================================<\/br>\r\n MSys Delphi Formatter 1.2.0.24 \u2014 What's New<\/br>\r\n Maenken Systems \u00b7 https:\/\/maenken.systems<\/br>\r\n Release 1.2.0.24 (from release 1.0.0.23)<\/br>\r\n=======================================================================<\/p>\r\n\r\n\r\n<p>-----------------------------------------------------------------------<\/br>\r\n 1. Delphi 13.2: the formatter runs on the IDE's own formatter interface<\/br>\r\n-----------------------------------------------------------------------<\/br><\/p>\r\n<p>Delphi 13.2 opens the code formatter to third parties, and this release\r\nis the first to use it. On 13.2 the IDE itself owns the command, the\r\nEdit-menu entry, the editor context menu, the <code>Ctrl+D<\/code> shortcut\r\nand the confirmation prompt \u2014 the formatter is a formatter, not a program\r\nthat reaches into menus to get called.<\/p>\r\n<p>What that changes for you is not the look of the menu but what the\r\nformatter is finally allowed to do:<\/p>\r\n<ul>\r\n<li><strong>Format a selection.<\/strong> Mark a few lines, format only\r\n  those. The selection is snapped to whole lines and formatted with the\r\n  whole file as context, so the result is exactly what a full run would\r\n  have produced for those lines \u2014 and everything outside stays byte for\r\n  byte as it was.<\/li>\r\n<li><strong>Format when the file is saved.<\/strong> New in this release,\r\n  off by default. Every save the IDE performs runs through it, including\r\n  the ones it does on your behalf before a build.<\/li>\r\n<li><strong>Format a line while typing.<\/strong> Reformats the line you\r\n  leave with Enter, in complete silence. Still a pilot, off by default.<\/li>\r\n<li><strong>Breakpoints and bookmarks keep their place.<\/strong>\r\n  Formatting moves code between lines; the marks in the gutter now move\r\n  with the code they were set on instead of staying on a line number.<\/li>\r\n<\/ul>\r\n<p>All of it sits behind the same safety gates as before: a file that does\r\nnot lex, or whose blocks do not balance, is left exactly as it is.<\/p>\r\n<p>On earlier Delphi versions nothing changes \u2014 the formatter keeps using\r\nthe way it always has, and the options that only work through the new\r\ninterface are not shown there at all rather than offered in vain.<\/p>\r\n\r\n<p>-----------------------------------------------------------------------<\/br>\r\n 2. Fixed<\/br>\r\n-----------------------------------------------------------------------<\/br><\/p>\r\n<ul>\r\n<li><strong>The new interface is actually used, not just\r\n  supported.<\/strong> The IDE publishes its formatter services under a\r\n  versioned name, and asking for the plain one is not enough: the request\r\n  came back empty, and the plugin quietly fell back to the old menu-based\r\n  route although the interface was right there. The effect was invisible\r\n  from the outside \u2014 same menu entry, same shortcut \u2014 but everything the\r\n  interface offers stayed out of reach. Both names are now asked for.<\/li>\r\n<li><strong>Formatting no longer adds a blank line at the end of the\r\n  file.<\/strong> When the IDE writes the formatted text back, a trailing\r\n  line break counts as a separator, while loading a file counts it as a\r\n  terminator. Handing the break back therefore grew the file by one empty\r\n  line after <code>end.<\/code>. The same asymmetry had already been fixed\r\n  for the older route; both now share one treatment. The growth happened\r\n  once, not on every run, which is what made it easy to overlook.<\/li>\r\n<\/ul>\r\n\r\n<p>-----------------------------------------------------------------------<\/br>\r\n 3. New<\/br>\r\n-----------------------------------------------------------------------<\/br><\/p>\r\n<ul>\r\n<li><p><strong>Format when the file is saved<\/strong> (<em>General \u2192\r\n  Format when the file is saved<\/em>, default off). Saving runs the\r\n  formatter over the file \u2014 <code>Ctrl+S<\/code>, <em>Save all<\/em>, and\r\n  the saves the IDE performs on its own.<\/p>\r\n  <p>Two things make this safe to leave on. A file that does not lex or\r\n  does not balance is <strong>saved unchanged<\/strong>, and the save path\r\n  is <strong>completely silent<\/strong>: no confirmation, no notice, ever.\r\n  Saving must not wait for a click, and a file being written is unfinished\r\n  more often than not. And files excluded through\r\n  <code>delphi_format = false<\/code> are not touched here either, so\r\n  vendor and generated code stays out of it.<\/p>\r\n  <p>The option is a setting of the IDE integration, not part of a\r\n  profile, and it deliberately has no <code>.editorconfig<\/code> key: the\r\n  IDE asks for it without reference to any one file, so a per-directory\r\n  value could not be attributed to anything.<\/p><\/li>\r\n<\/ul>\r\n\r\n<p>-----------------------------------------------------------------------<\/br>\r\n 4. By design<\/br>\r\n-----------------------------------------------------------------------<\/br><\/p>\r\n<ul>\r\n<li><strong>No <code>.editorconfig<\/code> key for\r\n  format-on-save.<\/strong> See above: the question the IDE asks carries no\r\n  file, so there is nothing to resolve a per-directory setting against.\r\n  Guessing one from the current editor window would be wrong the moment\r\n  <em>Save all<\/em> writes several files at once.<\/li>\r\n<li><strong>Options that need the new interface are hidden where it is\r\n  missing<\/strong>, rather than shown and doing nothing. A profile carries\r\n  their values regardless, so moving a profile between Delphi versions\r\n  leaves them intact.<\/li>\r\n<\/ul>\r\n\r\n<p>-----------------------------------------------------------------------<\/br>\r\n 5. Under the hood<\/br>\r\n-----------------------------------------------------------------------<\/br><\/p>\r\n<ul>\r\n<li>Built and verified against Delphi 13.2. The test suite covers 1477\r\n  checks; the full corpus run over a large open-source code base reports\r\n  no file the formatter refuses and no unbalanced result.<\/li>\r\n<\/ul>\r\n","timestamp":1789731347},{"id":"cl_new_1789384595127","version":"1.0.0.23","date":"2026-09-14","changes":"<p>=======================================================================<\/br>\r\n MSys Delphi Formatter 1.0.0.23 \u2014 What's New<\/br>\r\n Maenken Systems \u00b7 https:\/\/maenken.systems<\/br>\r\n Release 1.0.0.23 (from release 0.7.0.22)<\/br>\r\n=======================================================================<\/p>\r\n\r\n\r\n<p>-----------------------------------------------------------------------<\/br>\r\n 1. Fixed<\/br>\r\n-----------------------------------------------------------------------<\/br><\/p>\r\n<ul>\r\n<li><strong>A string of apostrophes ending a line no longer derails the\r\n  formatter.<\/strong> A literal like <code>''''<\/code> \u2014 a regular string\r\n  holding a single quote \u2014 may legally stand at the end of a line, for\r\n  example in a wrapped concatenation. The formatter misread such a literal\r\n  as the start of a multiline string: a file the compiler accepts was\r\n  refused with a lexical error, and when the same shape appeared twice,\r\n  everything between the two occurrences was silently treated as string\r\n  content. Only an odd number of quotes opens a multiline string \u2014 the\r\n  formatter now reads it that way too.<\/li>\r\n<li><strong>An <code>.editorconfig<\/code> section listing several masks in\r\n  braces works.<\/strong> A section such as <code>[{*.pas,*.dpr}]<\/code> \u2014\r\n  a standard-conform way to address multiple file types \u2014 broke the\r\n  settings resolution outright: on the command line every file under that\r\n  <code>.editorconfig<\/code> was skipped with an error, and in the IDE the\r\n  format command went silently dead. Wildcards inside the braces are now\r\n  understood exactly like outside them.<\/li>\r\n<li><strong>Alignment no longer pads statements after a <code>case<\/code>\r\n  or <code>try<\/code> block.<\/strong> The column aligner lost track of\r\n  where executable code begins once a <code>case<\/code>, <code>try<\/code>\r\n  or <code>repeat<\/code> block closed, and from there on treated\r\n  statements as declarations: with the initialization alignment switched\r\n  on, a comparison like <code>Ok := A = 1;<\/code> gained stray spaces\r\n  before its <code>=<\/code>. Declarations still align; statements are\r\n  left alone again.<\/li>\r\n<li><strong><code>array of record ... end<\/code> keeps its\r\n  shape.<\/strong> The anonymous record body in a type like\r\n  <code>TItems = array of record N: Integer; end;<\/code> was not\r\n  recognized: its fields fell out of the record, its <code>end<\/code>\r\n  closed the surrounding type section instead, and every declaration\r\n  after it dropped to the left margin \u2014 legal code, visibly torn apart.\r\n  The same applies to <code>file of record<\/code>. Both now open a proper\r\n  body, and the metaclass form <code>class of X<\/code> stays untouched.<\/li>\r\n<li><strong>A <code>{$REGION}<\/code> pair only pairs when it really is\r\n  one.<\/strong> The column pairing introduced for region directives\r\n  assumed every opener and closer stands alone on its line; an\r\n  <code>{$ENDREGION}<\/code> written behind code, or a region inside an\r\n  <code>asm<\/code> body, silently derailed the bookkeeping and a later,\r\n  unrelated closer inherited the wrong column. Unclean pairs are now left\r\n  alone entirely \u2014 they simply follow the surrounding code, as they did\r\n  before the pairing existed.<\/li>\r\n<li><strong>Alignment options work again after a\r\n  <code>procedure ... of object;<\/code> type.<\/strong> Such a\r\n  procedure-type reference (and a generic constraint like\r\n  <code><\/code>) was mistaken for the start of a record\r\n  body that never ends, so from there to the end of the unit the constant\r\n  and type alignments went silent. Only real bodies count now.<\/li>\r\n<li><strong><code>threadvar<\/code> sections wrap like <code>var<\/code>\r\n  sections.<\/strong> The one-name-per-line option split <code>var<\/code>\r\n  and <code>const<\/code> name lists but silently skipped\r\n  <code>threadvar<\/code>, which declares exactly the same lists.<\/li>\r\n<li><strong>A stray <code>$<\/code> with no digits stops the\r\n  formatter.<\/strong> A bare dollar sign is not a hex literal and does not\r\n  compile; the formatter used to format the file regardless. It now\r\n  refuses it untouched, exactly as it does for an unterminated string.<\/li>\r\n<li><strong>The command line warns when the mirror folder overwrites\r\n  itself.<\/strong> With <code>--out<\/code> and <code>-r<\/code> all results\r\n  land flat in one folder; two same-named units from different subfolders\r\n  used to overwrite each other without a word, so the mirror looked\r\n  complete when it was not. Each collision now prints a warning naming\r\n  both source files; the behaviour itself is unchanged.<\/li>\r\n<li><strong>The options preview spaces its tabs the way you set\r\n  them.<\/strong> The two panes used a fixed tab width of their own, so a\r\n  sample containing tabs was laid out differently from the code the\r\n  formatter would actually produce \u2014 the preview contradicted the very\r\n  setting it was there to demonstrate. It now follows the tab width\r\n  currently being edited, so a freshly typed value takes effect at once,\r\n  like every other option on those pages.<\/li>\r\n<li><strong>A <code>{$REGION}<\/code> and its <code>{$ENDREGION}<\/code>\r\n  stay in the same column.<\/strong> The closer used to follow whatever the\r\n  last line of code did, so a region wrapped around a routine whose body\r\n  was indented ended up with its two halves two columns apart. A closer\r\n  now follows its own opener \u2014 including the other way round, where a\r\n  region inside a routine body moves along with it. Nested regions pair\r\n  correctly, and a region written inside one branch of a conditional\r\n  stays there.<\/li>\r\n<li><strong>The \"indent else line\" setting is remembered again.<\/strong>\r\n  It was written to the settings store but never read back, so switching\r\n  it off held until the next IDE start and then quietly returned. Anyone\r\n  who preferred the RTL-style <code>else<\/code> placement had to set it\r\n  again after every restart. Two settings that could not be carried in an\r\n  exported profile at all \u2014 format on typing, and the alignment at the\r\n  opening bracket \u2014 now travel with it, alongside the option that joins a\r\n  wrapped assignment.<\/li>\r\n<li><strong>An inline variable written across two lines keeps its\r\n  indentation.<\/strong> With <code>var<\/code> alone on one line and the\r\n  declaration under it, the declaration was pulled back onto the\r\n  <code>var<\/code> column \u2014 while the very same declaration under a\r\n  routine's <code>var<\/code> section indents. Two spellings of one thing\r\n  came out differently, and the one line the author had indented was the\r\n  one that moved. The declaration now sits one level under its keyword,\r\n  exactly where the routine section would put it; everything after the\r\n  closing semicolon stays where it was.<\/li>\r\n<li><strong>Repeated formatting in the editor no longer adds a line to\r\n  the end of the file.<\/strong> Every run appended one empty line after\r\n  <code>end.<\/code>, so a file that was already formatted still came back\r\n  changed, and its end drifted further down with each pass. The cause sat\r\n  at the boundary to the editor buffer rather than in the formatter\r\n  itself: the editor reads the last line break of a file as a terminator\r\n  when it loads the file, but as a separator when text is written back\r\n  into it, so handing a buffer back unchanged still grew it by one line.\r\n  The round trip is exact now \u2014 a file whose end the formatter does not\r\n  touch comes back untouched, and a file that genuinely ends in a blank\r\n  line keeps it.<\/li>\r\n<li>A keyword used as a <strong>member name after a dot<\/strong> is no\r\n  longer treated as an operator. <code>class operator\r\n  Vector.In(...)<\/code> came out as\r\n  <code>class operator Vector. in (...)<\/code> \u2014 the qualified\r\n  name pulled apart and the member lowercased. The same happened to\r\n  <code>.And<\/code>, <code>.Div<\/code>, <code>.Is<\/code>, <code>.As<\/code>,\r\n  <code>.Or<\/code> and <code>.Not<\/code>. Operator overloads are exactly\r\n  the code where such names have to appear, so this hit real libraries.<\/li>\r\n<li>A <strong>case label following an arm that ends in a nested\r\n  <code>case<\/code><\/strong> kept the inner case's indentation instead of\r\n  returning to the label column. Only the first label after the nested\r\n  block was affected, which is why it read as a one-off rather than a\r\n  pattern.<\/li>\r\n<li><strong>Nested inline <code>if<\/code> expressions<\/strong> (Delphi 13)\r\n  lost every level of indentation: each <code>then<\/code>\/<code>else<\/code>\r\n  line fell back to the statement column, so a four-level expression came\r\n  out as one flat block. Each level now carries one continuation step,\r\n  with <code>then<\/code> and <code>else<\/code> of the same <code>if<\/code>\r\n  in one column.<\/li>\r\n<li>A <strong>stray blank line was no longer left before the closing\r\n  keyword<\/strong> of an <code>initialization<\/code> or\r\n  <code>finalization<\/code> section: with the corresponding option off \u2014\r\n  the default \u2014 the statements of such a section were re-indented onto\r\n  the keyword's column instead of being left alone. The option is\r\n  documented as \"keep as is\", and now it keeps.<\/li>\r\n<li>A <strong>unary sign no longer fuses with the keyword in front of\r\n  it<\/strong>. <code>then -1<\/code>, <code>else -2<\/code>,\r\n  <code>not -Y<\/code> and <code>to -1<\/code> came out as\r\n  <code>then-1<\/code>, <code>else-2<\/code>, <code>not-Y<\/code> and\r\n  <code>to-1<\/code> \u2014 legal, but they read as a single token.<\/li>\r\n<li><strong>A <code>class procedure<\/code> or <code>class function<\/code>\r\n  body directly after a <code>uses<\/code>, <code>const<\/code> or\r\n  <code>var<\/code> section is no longer indented like a section\r\n  entry.<\/strong> Only the <code>class<\/code> prefix made the difference:\r\n  a plain <code>procedure<\/code> in the same spot closed the section and\r\n  landed on the routine column, while a class method header was taken for\r\n  one more entry of the section and pushed to the entries' column \u2014 even\r\n  when it already stood at column 0, with the standard settings, and\r\n  stable across runs, so reformatting never repaired it. A comment\r\n  standing above such a header shared its fate. The section now ends at\r\n  any routine header, prefixed or not. Reported by a user against a unit\r\n  whose class-method implementations followed the implementation\r\n  <code>uses<\/code> clause; measured against a large open source library,\r\n  this moves the headers in twelve units back to the column their authors\r\n  wrote.<\/li>\r\n<li><strong>A routine body that slipped a level comes back.<\/strong>\r\n  Comment out the <code>end;<\/code> of a procedure, format, and everything\r\n  below it moves one level in \u2014 as it should. Restore the <code>end;<\/code>\r\n  and format again, and the routine headers came back to the margin, but\r\n  their <code>begin<\/code> and <code>end<\/code> lines stayed where the\r\n  slip had put them, stable across runs, so reformatting never repaired\r\n  it. The rule that protects a nested routine's own column had been\r\n  keeping the outer routine's frame too. It now keeps only what it was\r\n  made for; the <code>begin<\/code>, the <code>end<\/code> and a local\r\n  <code>var<\/code>, <code>const<\/code> or <code>type<\/code> line are\r\n  pulled back under their header again. Reported from the field with\r\n  exactly that three-step sequence.<\/li>\r\n<\/ul>\r\n\r\n<p>-----------------------------------------------------------------------<\/br>\r\n 2. New<\/br>\r\n-----------------------------------------------------------------------<\/br><\/p>\r\n<ul>\r\n<li><strong>A blank-line separator can be told not to split what belongs\r\n  together.<\/strong> A separator guarantees a blank line in front of a\r\n  keyword without asking whether the line above is part of the same\r\n  thought. Two cases, and they point in opposite directions: a ruler\r\n  comment written directly over <code>implementation<\/code> introduces\r\n  that section, and a local <code>type<\/code>\/<code>var<\/code>\/<code>const<\/code>\r\n  belongs to the routine header above it \u2014 both got a blank line pushed\r\n  into them. The new option leaves both alone. It is off by default, so\r\n  nothing changes unless you ask for it, and a unit-level <code>type<\/code>\r\n  following a forward declaration keeps its blank line either way. Only\r\n  profiles that have the separators running were ever affected.<\/li>\r\n<\/ul>\r\n\r\n<p>-----------------------------------------------------------------------<\/br>\r\n 3. Clarified and pinned<\/br>\r\n-----------------------------------------------------------------------<\/br><\/p>\r\n<ul>\r\n<li><strong>The statements of a <code>case<\/code>-<code>else<\/code>\r\n  branch now sit one level under their own <code>else<\/code> by\r\n  default.<\/strong> Previously they stayed on the column of the other\r\n  alternatives' statements, which left two levels between <code>else<\/code>\r\n  and the code belonging to it. The setting has existed for a while; only\r\n  its default changed. It has an effect solely in combination with the\r\n  RTL-style <code>else<\/code> placement (the else line on the\r\n  <code>case<\/code> column) \u2014 with the else line indented, both readings\r\n  coincide and nothing moves.<\/li>\r\n<li>The option for that indent has <strong>moved into the \"Case\r\n  statements\" group<\/strong> of the options dialog, where the rest of the\r\n  case settings live. It used to sit under \"Declarations\", three rows\r\n  away from anything to do with <code>case<\/code>.<\/li>\r\n<li>The <strong>help texts in the options dialog appear after one\r\n  second<\/strong> instead of half a second. Crossing the page with the\r\n  mouse no longer throws up help nobody asked for; a deliberate pause\r\n  still shows it.<\/li>\r\n<\/ul>\r\n\r\n<p>-----------------------------------------------------------------------<\/br>\r\n 4. Under the hood<\/br>\r\n-----------------------------------------------------------------------<\/br><\/p>\r\n<ul>\r\n<li>A deep pass over the engine consolidated duplicated internals: the\r\n  property specifier words, the operator list that recognizes\r\n  <code>if<\/code>-expressions and the synthesized-space factory each live\r\n  in one shared place now, the re-indenter works on its own copy of the\r\n  token stream instead of the caller's, and the command-line program calls\r\n  the encoding round-trip unit directly. No behaviour change; the test\r\n  suite grew by over a hundred checks pinning these and the fixes above.<\/li>\r\n<\/ul>\r\n","timestamp":1789384595},{"id":"cl_new_1786966681443","version":"0.7.0.22","date":"2026-08-17","changes":"<p>=======================================================================<\/br>\r\n MSys Delphi Formatter 0.7.0.22 \u2014 What's New<\/br>\r\n Maenken Systems \u00b7 https:\/\/maenken.systems<\/br>\r\n Release 0.7.0.22 (from release 0.6.0.21)<\/br>\r\n=======================================================================<\/p>\r\n\r\n\r\n<p>-----------------------------------------------------------------------<\/br>\r\n 1. New<\/br>\r\n-----------------------------------------------------------------------<\/br><\/p>\r\n<ul>\r\n<li><strong>Every option explains itself.<\/strong> Hovering an option\r\n  anywhere in the settings dialog brings up its help: what the option\r\n  does, a short example and the default \u2014 laid out for reading instead of\r\n  squeezed into a single line of tooltip. All 90 options are covered, in\r\n  English and German. It runs through the IDE's own hint mechanism, so it\r\n  can never end up as a second popup competing with the usual one.<\/li>\r\n<li><strong>The preview panes are syntax coloured and numbered.<\/strong>\r\n  The Before and After panes used to paint their sample in one single\r\n  colour. They now colour it per token \u2014 reserved words, strings,\r\n  comments, numbers, directives \u2014 and the colours come from your own\r\n  editor colour scheme, so the preview looks like the editor you actually\r\n  work in. Both panes carry line numbers in a quiet gutter, which is what\r\n  makes the two halves comparable: the same number on the left and on the\r\n  right answers what the formatter did to that line. A scheme that cannot\r\n  be read falls back to plain text on the correct background rather than\r\n  to a guess.<\/li>\r\n<li><strong>Collapse a line break after an assignment when the statement\r\n  fits.<\/strong> A new option on the Line Breaks page \u2014 off by default \u2014\r\n  removes the break directly after an assignment's <code>:=<\/code> if the\r\n  whole statement then still fits within the right margin, so\r\n  <code>Result :=<\/code> followed by a short expression on the next line\r\n  becomes one line again. Like the end\/else option it only ever merges: it\r\n  never breaks a line, it only takes a break away. It stays out of the way\r\n  where the layout was intentional \u2014 if the statement carries any further\r\n  break, or the right-hand side is an anonymous method, it is left\r\n  untouched. The statement is measured as though all its breaks were\r\n  already gone, so the result does not depend on the current layout and\r\n  reformatting again changes nothing. Requested by a user.<\/li>\r\n<\/ul>\r\n\r\n<p>-----------------------------------------------------------------------<\/br>\r\n 2. Fixed<\/br>\r\n-----------------------------------------------------------------------<\/br><\/p>\r\n<p>Five defects in this release share one trigger: they only appear once\r\nthe reflow of line breaks is switched on, and the standard Delphi 12\r\nconfiguration switches it on. Anyone who imported their existing settings\r\nmet several of them at once, which is why the import had a reputation for\r\nproducing odd results. The import itself turned out to be faithful \u2014 it\r\nwas applying the options exactly as given, and the engine mishandled\r\nthem.<\/p>\r\n<ul>\r\n<li><strong>An inline variable is no longer mistaken for a goto\r\n  label.<\/strong> With <em>Line break after a label<\/em> active,\r\n  <code>var a: Integer := 1;<\/code> was torn in two after the colon,\r\n  leaving <code>var a:<\/code> on one line and <code>Integer := 1;<\/code>\r\n  on the next. The rule recognised any identifier followed by a colon; it\r\n  now requires what a label actually is \u2014 the start of a statement. Real\r\n  labels still get their line break, and <code>for I: Integer := 0 to 3\r\n  do<\/code> stays in one piece.<\/li>\r\n<li><strong>No more blank line before every inline variable.<\/strong>\r\n  With <em>Blank lines before a subsection<\/em> set, a blank line was\r\n  inserted ahead of any line starting with <code>var<\/code>,\r\n  <code>const<\/code> or <code>type<\/code> \u2014 including an inline variable\r\n  in the middle of a routine. In code written for Delphi 10.3 and later\r\n  that put a blank line between nearly every statement. The separator now\r\n  fires only where a declaration section can actually begin; genuine\r\n  sections keep their blank line.<\/li>\r\n<li><strong>Local declarations keep their indentation.<\/strong> Switching\r\n  on either of the two routine-body indentation options pushed a\r\n  routine's own <code>var<\/code>, <code>const<\/code> and <code>type<\/code>\r\n  declarations to column 0 \u2014 actively, even when they had been correctly\r\n  indented beforehand, while the <code>begin\u2026end<\/code> body kept\r\n  indenting normally. Both options now leave the declarations one step\r\n  under their keyword, for top-level and nested routines alike.<\/li>\r\n<li><strong>Conditional branch-swap tails survive reflow.<\/strong> A tail\r\n  of the form <code>else{$ELSE}begin{$ENDIF}<\/code> has to stay on one\r\n  line \u2014 both passes of the engine depend on it. With <em>Line break after\r\n  begin<\/em> active, the <code>begin<\/code> was separated from the\r\n  directive behind it, the structural bookkeeping went astray, and the\r\n  file ended up with unbalanced blocks. The formatter then refused it\r\n  outright, so the affected units silently stayed unformatted. Measured\r\n  against a large open source library, six units were affected; now none\r\n  are.<\/li>\r\n<li><strong>Variant records after a nested section no longer shift the\r\n  rest of the file.<\/strong> When <em>Indent type\/const sections inside a\r\n  class or record body<\/em> was on, a record variant part\r\n  (<code>case Integer of<\/code> without an <code>end<\/code> of its own)\r\n  following a nested <code>var<\/code>, <code>const<\/code> or\r\n  <code>type<\/code> section left an open block behind. Every declaration\r\n  after that record \u2014 up to and including the final <code>end.<\/code> \u2014\r\n  stayed one level too deep and never returned to column 0. Because the\r\n  result was stable across runs, reformatting never repaired it.<\/li>\r\n<li><strong>Inline variables in initialization and finalization are\r\n  statements again.<\/strong> Those sections carry statements but open no\r\n  block, so everything that asks \"are we in executable code?\" read them as\r\n  declaration territory. An inline <code>var<\/code> there was taken for a\r\n  declaration section: it pushed every following statement one level\r\n  deeper, attracted the blank-line separator meant for real sections, and\r\n  had the name list of <code>var a, b: Integer;<\/code> split at its comma.\r\n  All three are gone, in both passes. A related case: a declaration\r\n  written across two lines \u2014 <code>var<\/code> alone, the name below it \u2014\r\n  was read as a goto label and pulled to the label column.<\/li>\r\n<li><strong>Directive lines follow their code instead of\r\n  freezing.<\/strong> With directive indentation switched off, a\r\n  <code>{$IFDEF}<\/code> line kept its original column literally. That was\r\n  invisible until the block settings moved the code around it \u2014 then the\r\n  directive stayed behind and ended up stranded between lines it no longer\r\n  lined up with, which reads as if the conditional had not been\r\n  understood at all. It now moves with the code, keeping exactly the\r\n  distance the author gave it. Where nothing moves, nothing changes.<\/li>\r\n<li><strong>Anonymous methods get their own indentation level\r\n  again.<\/strong> An anonymous method passed as a call argument \u2014\r\n  <code>TThread.Queue(nil, procedure begin \u2026 end)<\/code> and every shape\r\n  like it \u2014 had its whole contents pulled onto the column of its own\r\n  <code>begin<\/code>. A <code>for<\/code> or <code>if<\/code> inside it lost\r\n  its level as well, and its local <code>var<\/code>, <code>const<\/code>\r\n  and <code>type<\/code> declarations sat on the same column as the\r\n  keyword introducing them. The body now sits one step under its\r\n  <code>begin<\/code>, and the declarations one step under their section\r\n  keyword, while the <code>begin<\/code> itself stays where it belongs \u2014\r\n  attached to the call it is an argument of. This is not tied to an\r\n  option: it happens with the standard settings, which is why it was\r\n  reported from ordinary use. Measured against a large open source\r\n  library, every one of the affected places now matches the column its\r\n  authors had written by hand.<\/li>\r\n<li><strong>A unit named Inline keeps its name.<\/strong>\r\n  <code>inline<\/code> is a directive in Delphi, not a reserved word \u2014 but\r\n  the formatter had it listed among the reserved words, so every\r\n  identifier of that name was treated as a keyword and re-cased:\r\n  <code>unit Inline;<\/code> came out as <code>unit inline;<\/code>, and the\r\n  same happened in a uses clause or a qualified reference. Nothing stopped\r\n  compiling, since Delphi does not distinguish case in identifiers, but\r\n  the formatter was changing a name that belongs to the author. It is now\r\n  handled like every other directive \u2014 <code>overload<\/code>,\r\n  <code>stdcall<\/code>, <code>register<\/code>, <code>deprecated<\/code>\r\n  were all already treated correctly, so this removes an inconsistency\r\n  rather than adding a rule.<\/li>\r\n<li><strong>One-line anonymous methods no longer make a whole unit\r\n  unformattable.<\/strong> Passing an anonymous method as a call argument on\r\n  a single line \u2014 <code>Foo(procedure begin X := 1; end)<\/code> \u2014 left a\r\n  stray internal region behind whenever one of the two routine-body\r\n  indentation options was on. A single occurrence still balanced out by\r\n  chance; from the second one in a file the bookkeeping came apart, the\r\n  safety net declared the file unsound, and the formatter refused it. The\r\n  visible symptom was the worst kind: nothing happened at all, with no\r\n  explanation. Measured against a large open source library, this was the\r\n  last file in 366 that could not be formatted under any of six\r\n  configuration profiles; all of them are now clean.<\/li>\r\n<li><strong>A wrapped assignment keeps its indentation level.<\/strong>\r\n  When an assignment is broken across lines without brackets, everything\r\n  below the first line used to fall back to the column of the statement\r\n  itself, so <code>Result :=<\/code> and the expression belonging to it\r\n  ended up side by side and the statement read as two. The continuation\r\n  now sits one step in, anchored on the line that opened the assignment \u2014\r\n  so a right-hand side spanning three or four lines stays at one step\r\n  instead of drifting further right with every line. Where the right-hand\r\n  side is an anonymous method, its header, its local declarations, its\r\n  body and its end each land on the level they belong to. Brackets keep\r\n  priority, so a wrapped argument list is unaffected. Measured against a\r\n  large open source library, this moves the formatter closer to what the\r\n  authors wrote by several hundred lines.<\/li>\r\n<li><strong>A for loop is no longer aligned like an assignment.<\/strong>\r\n  With <em>Align assignments<\/em> switched on, a loop header standing\r\n  between two assignments was padded into their column, so\r\n  <code>for I := 0 to 9 do<\/code> came out as\r\n  <code>for I\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0:=\r\n  0 to 9 do<\/code>. The alignment looks for any <code>:=<\/code> outside\r\n  brackets, and a loop header's is spelled exactly like an assignment's \u2014\r\n  only the leading keyword tells them apart. It does now.<\/li>\r\n<li><strong>Comments no longer drift when a section ends.<\/strong> A\r\n  <code>uses<\/code>, <code>var<\/code> or <code>const<\/code> section ends\r\n  at its semicolon \u2014 but the formatter kept treating the space after it as\r\n  part of the section, so a comment standing between the section and the\r\n  next routine was pulled in one level. That hit <code>{ TMyClass }<\/code>,\r\n  the comment the IDE itself writes above the first method body: the\r\n  formatter moved a line nobody had touched. Such a comment now takes the\r\n  level of the code it introduces. A comment inside a section still moves\r\n  with its declarations, and one above a nested routine follows that\r\n  routine rather than dropping to the margin. Measured on a large code\r\n  base, this restores the author's own column in the great majority of\r\n  cases. Compiler directives on the same spot follow the same rule, and a\r\n  closing <code>{$ENDIF}<\/code> moves together with the <code>{$IF}<\/code>\r\n  it belongs to.<\/li>\r\n<li><strong>A note above a routine directive moves with it.<\/strong> The\r\n  option that indents a stand-alone <code>inline;<\/code> or\r\n  <code>overload;<\/code> line under its routine header moved the directive\r\n  alone. In the very shape the option was built for \u2014 a note explaining\r\n  why the directive is wrapped in a condition \u2014 the comment stayed behind\r\n  on the old column, so a pair that had been written on one column came\r\n  apart, and the result looked less like the original than before the\r\n  option existed. The comment now takes the column of the directive it\r\n  introduces, the same rule a comment above an <code>else<\/code> already\r\n  followed. A comment that introduces an ordinary declaration keeps its\r\n  place, and <em>Indent comments: No<\/em> still means every comment stays\r\n  exactly where it is. Measured against a large open source library, this\r\n  moves one single line in 366 units \u2014 the one from the report.<\/li>\r\n<li><strong>The align page's preview demonstrates its own option\r\n  again.<\/strong> The sample there had no hand-set columns in it, so\r\n  switching <em>Keep hand-set columns<\/em> on changed nothing visible \u2014\r\n  the one thing the preview exists for. The sample now carries a\r\n  hand-aligned <code>=<\/code> column and hand-aligned trailing comments,\r\n  which shows all three behaviours in one snippet: the default collapses\r\n  them, the preserve option keeps them, and the align options re-compute\r\n  them.<\/li>\r\n<\/ul>\r\n\r\n<p>-----------------------------------------------------------------------<\/br>\r\n 3. Under the hood<\/br>\r\n-----------------------------------------------------------------------<\/br><\/p>\r\n<ul>\r\n<li><strong>Every defect above is pinned by regression checks that fail\r\n  without their respective fix<\/strong> \u2014 including the column drift,\r\n  which no balance check could catch.<\/li>\r\n<li><strong>Verified against a large open source code base \u2014 366 units \u2014\r\n  under twelve different configuration profiles, not just the default\r\n  one<\/strong>: no unbalanced unit, no file the formatter had to refuse,\r\n  and under every one of the twelve a second pass over the formatted\r\n  output changes nothing.<\/li>\r\n<li><strong>The alignment options were measured against inline variables\r\n  and leave them alone in every combination, at every nesting\r\n  depth<\/strong> \u2014 that immunity is now pinned by a test as well.<\/li>\r\n<\/ul>\r\n","timestamp":1786966681},{"id":"cl_new_1786533860564","version":"0.6.0.21","date":"2026-08-12","changes":"<p>=======================================================================<\/br>\r\n MSys Delphi Formatter 0.6.0.21 \u2014 What's New<\/br>\r\n Maenken Systems \u00b7 https:\/\/maenken.systems<\/br>\r\n Release 0.6.0.21 (from release 0.5.0.17)<\/br>\r\n=======================================================================<\/p>\r\n\r\n\r\n<p>-----------------------------------------------------------------------<\/br>\r\n 1. New<\/br>\r\n-----------------------------------------------------------------------<\/br><\/p>\r\n<ul>\r\n<li><strong>Indent <code>type<\/code>\/<code>const<\/code> sections inside a\r\n  class body.<\/strong> Many libraries \u2014 and the RTL itself \u2014 put a\r\n  <code>type<\/code> or <code>const<\/code> section inside a class or record\r\n  body and indent its declarations one level under the keyword. The\r\n  formatter used to pull them back to the level of the section keyword,\r\n  and the existing \"indent after sections\" option did not help: that one\r\n  only ever covered the unit sections <code>interface<\/code> and\r\n  <code>implementation<\/code>. The new option <em>Indent type\/const\r\n  sections inside a class or record body<\/em> (Indentation page, or the\r\n  <code>.editorconfig<\/code> key\r\n  <code>delphi_indent_nested_sections<\/code>) covers the class-internal\r\n  case. Default off, so nothing changes unless you ask for it; the\r\n  indented region ends at the next visibility specifier, method\r\n  declaration, further section keyword or the type's <code>end<\/code>, so\r\n  it cannot spill into the rest of the file.<\/li>\r\n<li><strong>Indent case-else statements under their own else.<\/strong>\r\n  With the case-else placed on the <code>case<\/code> column, its\r\n  statements used to stay on the arm column \u2014 two levels away from the\r\n  <code>else<\/code> they belong to. The new option <em>Indent case-else\r\n  statements under their own else<\/em> (Indentation page,\r\n  <code>.editorconfig<\/code> key\r\n  <code>delphi_indent_case_else_contents<\/code>) closes that gap. It only\r\n  differs in exactly that pairing; with the default case-else placement\r\n  both readings coincide.<\/li>\r\n<li><strong>Indent directive lines under their routine header.<\/strong> A\r\n  stand-alone <code>inline;<\/code>, <code>overload;<\/code> or\r\n  <code>stdcall;<\/code> line \u2014 including the conditional form\r\n  <code>{$IF \u2026}inline;{$IFEND}<\/code> \u2014 can now be indented one\r\n  continuation step under the header it belongs to, via <em>Indent\r\n  directive lines under their routine header<\/em> (Indentation page,\r\n  <code>.editorconfig<\/code> key\r\n  <code>delphi_indent_routine_directives<\/code>). This is a matter of\r\n  style rather than a defect: the <code>;<\/code> after the parameter list\r\n  ends the declaration, so the directive really is a line of its own and\r\n  the flat reading is just as defensible \u2014 which is why the default is\r\n  off. A comment between the header and the directive does not break the\r\n  association.<\/li>\r\n<li><strong>Keep hand-set columns.<\/strong> Manually aligned trailing\r\n  comments and <code>=<\/code> columns in const blocks used to be collapsed\r\n  to a single space \u2014 the formatter threw away work someone had done on\r\n  purpose. The align options were no answer: they compute the minimal\r\n  column and so replace one normalisation with another. The new option\r\n  <em>Keep hand-set columns<\/em> (Align page, <code>.editorconfig<\/code>\r\n  key <code>delphi_preserve_manual_columns<\/code>) simply leaves such a\r\n  run alone. It never widens a single space, so it cannot create\r\n  alignment \u2014 it only keeps what is there. Default off.<\/li>\r\n<\/ul>\r\n","timestamp":1786533860},{"id":"cl_new_1786361217072","version":"0.5.0.17","date":"2026-08-10","changes":"<p>=======================================================================<\/br>\r\n MSys Delphi Formatter 0.5.0.17 \u2014 What's New<\/br>\r\n Maenken Systems \u00b7 https:\/\/maenken.systems<\/br>\r\n Release 0.5.0.17 (from release 0.4.0.10)<\/br>\r\n=======================================================================<\/p>\r\n\r\n<p>All changes in this release were driven by community feedback from the\r\nDelphi-PRAXiS beta threads \u2014 thank you to everyone who tested and\r\nreported.<\/p>\r\n\r\n\r\n<p>-----------------------------------------------------------------------<\/br>\r\n 1. Fixed<\/br>\r\n-----------------------------------------------------------------------<\/br><\/p>\r\n<ul>\r\n<li><strong>The branch-swap conditional idiom no longer blocks\r\n  formatting.<\/strong> The pattern where one preprocessor branch opens a\r\n  <code>case \u2026 of<\/code> and the other contributes a <code>begin<\/code>\r\n  instead \u2014 both sharing the closing <code>end<\/code>s, with\r\n  <code>{$ELSE}<\/code> and <code>{$ENDIF}<\/code> sitting in the middle of\r\n  one code line (<code>else{$ELSE}begin{$ENDIF}<\/code>) \u2014 used to make the\r\n  whole file count as structurally unbalanced: with several occurrences\r\n  the formatter refused the file (this is what made Spring4D's\r\n  <code>Spring.pas<\/code> appear \"unformattable\"), and with exactly one\r\n  occurrence everything after the pattern silently drifted two columns to\r\n  the right. The formatter now recognizes this shape, follows the first\r\n  branch \u2014 consistent with how conditional regions are handled everywhere\r\n  else \u2014 and keeps the indentation stable. Other mid-line directive\r\n  regions remain deliberately untouched, as documented under Known\r\n  limits.<\/li>\r\n<li><strong>The options dialog is readable again under a light IDE\r\n  theme.<\/strong> The pane holding the options took its background from\r\n  the <em>editor<\/em> colour scheme instead of the IDE theme. Since that\r\n  scheme is chosen independently of the theme, anyone running a light IDE\r\n  with a dark editor scheme \u2014 a very common combination \u2014 got a black\r\n  options pane while the captions stayed dark, which left whole pages\r\n  unreadable. Editor colours now dress the two preview panes only, where\r\n  they belong; everything else follows the IDE theme, in both the light\r\n  and the dark variant.<\/li>\r\n<li><strong>The command-line formatter carries version\r\n  information.<\/strong> <code>Formatter.exe<\/code> shipped without any\r\n  version resource at all: neither Windows' file properties nor a\r\n  deployment script could tell which build was in use. It now reports its\r\n  version and vendor like the plugin does, and both are built from the\r\n  same version number.<\/li>\r\n<\/ul>\r\n\r\n<p>-----------------------------------------------------------------------<\/br>\r\n 2. New<\/br>\r\n-----------------------------------------------------------------------<\/br><\/p>\r\n<ul>\r\n<li><strong>Blank-line separators without switching on reflow.<\/strong>\r\n  The separators that put blank lines at structural boundaries \u2014 between\r\n  methods, before the final <code>end.<\/code>, around sections \u2014 used to\r\n  require the reflow master to be off, which meant that anyone who only\r\n  wanted a blank line between methods had to hand the formatter their\r\n  whole line layout as well. Inserting a blank line moves no code, so it\r\n  never needed that: the new option <em>Apply the separators even when\r\n  user line breaks are kept<\/em> (Blank Lines page, or the\r\n  <code>.editorconfig<\/code> key\r\n  <code>delphi_apply_blank_line_separators<\/code>) releases them on their\r\n  own. The default is off, so nothing changes unless you ask for it.\r\n  Verified against a large real-world code base: with the option on, the\r\n  only difference to a normal run is blank lines \u2014 not a single token\r\n  moves.<\/li>\r\n<li><strong>Product page, version and contact are now in plain\r\n  sight.<\/strong> The options dialog carries a slim header above every\r\n  page: the product name with a link straight to the product page \u2014\r\n  opening the English or the German edition to match the IDE language \u2014\r\n  and the installed version on the right. Beta testers told us the\r\n  previous placement was easy to miss, which made it unnecessarily hard to\r\n  find out where to send feedback.<\/li>\r\n<li><strong>Format while typing (pilot, opt-in).<\/strong> On IDEs that\r\n  offer the native formatter interface \u2014 prepared for future RAD Studio\r\n  versions \u2014 the formatter can now re-format the line the caret leaves\r\n  with Enter \u2014 same rules and safety gates as Ctrl+D, but completely\r\n  silent: an incomplete or unbalanced buffer (the normal state while\r\n  typing) or an already-formatted line is a quiet no-op, and no dialog\r\n  ever appears. Caret, breakpoints and bookmarks follow exactly. Strictly\r\n  opt-in (default: off) via the new General-page option <em>Format line on\r\n  Enter<\/em> or per project via the new <code>.editorconfig<\/code> key\r\n  <code>delphi_format_on_type<\/code>; semicolons, pasting and navigation\r\n  never trigger it.<\/li>\r\n<li><strong>The command-line formatter no longer overwrites files it\r\n  could not read properly.<\/strong> When the formatter cannot make sense\r\n  of a file's structure \u2014 it ends up with more block openers than closers\r\n  \u2014 its indentation is a guess. The IDE has always refused such a buffer\r\n  and left it alone; the command-line tool used to mention the fact in\r\n  passing and rewrite the file anyway, with a success exit code. A batch\r\n  run over a repository therefore wrote guessed indentation into exactly\r\n  the files the IDE protects, and <code>--check<\/code> reported them as an\r\n  ordinary formatting difference. Both now give the same answer: the file\r\n  is reported as <code>KEEP<\/code> and left untouched, the run ends with\r\n  its own exit code 3 so a build pipeline can tell \"needs formatting\" from\r\n  \"cannot be formatted safely\", and <code>--out<\/code> still writes the\r\n  file into the mirror directory, where it overwrites nothing. The new\r\n  switch <code>--best-effort<\/code> restores the previous behaviour for\r\n  anyone who wants it, exit code included.<\/li>\r\n<\/ul>\r\n\r\n<p>-----------------------------------------------------------------------<\/br>\r\n 3. Under the hood<\/br>\r\n-----------------------------------------------------------------------<\/br><\/p>\r\n<ul>\r\n<li><strong>Conditional branches no longer add up in the line-break\r\n  pass.<\/strong> The reflow stage kept one structural tally across all\r\n  branches of a <code>{$IFDEF}<\/code> region instead of following the\r\n  first one, the way the indentation stage already did. A branch that\r\n  opened or closed a block without closing or opening it again shifted\r\n  that tally for the rest of the unit, after which whole families of\r\n  line-break rules quietly stopped applying \u2014 or started applying \u2014 far\r\n  away from the directive that caused it. The effect was invisible from\r\n  the outside: the output stayed structurally sound and stable under\r\n  repeated formatting, only the line breaks were placed as if a different\r\n  file had been read. Both stages now follow the same rule. This only ever\r\n  affected profiles with reflow enabled.<\/li>\r\n<li><strong>A <code>var<\/code> or <code>const<\/code> parameter is no\r\n  longer mistaken for a declaration section.<\/strong> With per-element\r\n  wrapping of <code>var<\/code>\/<code>const<\/code> lists enabled, a routine\r\n  taking a <code>var<\/code> parameter had the comma in a generic return\r\n  type (<code>IDictionary<\/code>) broken as if it were\r\n  a name-list separator, leaving the continuation dangling. The two\r\n  meanings of the keyword are now kept apart.<\/li>\r\n<\/ul>\r\n","timestamp":1786361217},{"id":"cl_new_1784806349476","version":"0.4.0.10","date":"2026-07-23","changes":"<p>=======================================================================<\/br>\r\n MSys Delphi Formatter \u2014 History 0.4.0.10<\/br>\r\n=======================================================================<\/p>\r\n\r\n<p>Release 0.4.0.10 (from beta build 3.0.1 \/ GetIt). All changes below were driven\r\nby community feedback from the Delphi-PRAXiS beta threads \u2014 thank you to\r\neveryone who tested and reported.<\/p>\r\n\r\n\r\n<p>-----------------------------------------------------------------------<\/br>\r\n 1. Fixed<\/br>\r\n-----------------------------------------------------------------------<\/br><\/p>\r\n\r\n<p><b>Sources the formatter refused, or changed in ways it must not<\/b><\/p>\r\n\r\n<p><b>Conditional compilation ({$IFDEF} \/ {$ELSE} \/ {$ENDIF}) \u2014 the big one<\/b><\/p>\r\n<p>Sources whose conditional branches open or close blocks (<code>begin<\/code>, <code>try<\/code>,\r\n<code>class<\/code>, <code>asm<\/code> \u2026) could not be formatted at all: the formatter saw the code of\r\n<b>every<\/b> branch at once, counted openers and closers across branches and\r\nrefused the whole file as \"unbalanced block structure\". The mirror case \u2014 a\r\ncloser duplicated across branches \u2014 silently produced drifted indentation.<\/p>\r\n\r\n<p>The structural bookkeeping is now branch-aware: at <code>{$IF*}<\/code> the formatter\r\nsnapshots its state, walks every <code>{$ELSE}<\/code> \/ <code>{$ELSEIF}<\/code> branch from that same\r\nentry state and continues after <code>{$ENDIF}<\/code> \/ <code>{$IFEND}<\/code> with the first\r\nbranch's exit state. Both directive families are supported, nested regions\r\nwork, and column alignment no longer bridges across branch boundaries. The\r\nsafety guarantee is unchanged: nothing is ever written when the source truly\r\nis unbalanced.<\/p>\r\n\r\n<p><i>Known limits:<\/i> include files (<code>{$I}<\/code>) that contribute block edges and\r\ndirectives sharing a line with code are by-design limits \u2014 now documented\r\nin the manual's \"Known limits\" section; some reflow-mode edge cases\r\nremain tracked.<\/p>\r\n\r\n<p><b>Variant records refused every unit<\/b><\/p>\r\n<p>Any unit containing a variant record (<code>case Integer of<\/code> inside a <code>record<\/code>)\r\nwas refused with the same \"unbalanced\" message \u2014 without a single compiler\r\ndirective involved. Fixed.<\/p>\r\n\r\n<p><b>A closing <code>end<\/code> at the end of a statement line refused the whole file<\/b><\/p>\r\n<p>A line that both finishes a statement and closes its block \u2014 <code>B; end;<\/code>, or the\r\n<code>end);<\/code> a compact one-line anonymous method leaves behind \u2014 made the formatter\r\nreport the source as unbalanced and refuse it entirely, although the code is\r\nperfectly valid. The closer was consuming the pending continuation of an\r\nenclosing <code>if \u2026 then<\/code> \/ <code>while \u2026 do<\/code> instead of the block itself, so the block\r\nstayed open until the end of the file. A closer now always ends a block, wherever\r\nit sits on the line. This affected the default profile too, not just reflow \u2014\r\nand in the cases that were <i>not<\/i> refused outright it silently pushed everything\r\nafter that line further and further right.<\/p>\r\n\r\n<p><b>asm blocks that end mid-line refused the file<\/b><\/p>\r\n<p>The same class of defect as the previous fix, one code path over: after the <code>end<\/code> of an\r\n<code>asm<\/code> block the formatter stopped looking at the rest of that line, so a\r\nroutine written as <code>asm \u2026 end; end;<\/code> lost its outer block and the file was\r\nrefused. The remaining closers on the line are now counted.<\/p>\r\n\r\n<p><b>Reflow could silently change what a string literal means<\/b><\/p>\r\n<p>With line-break rules active, <code>if C = '''' then Q;<\/code> was reflowed so that the\r\napostrophe literal ended up as the last token on its line. Delphi 12 reads a\r\nrun of three or more apostrophes followed only by whitespace as the <i>opening<\/i>\r\ndelimiter of a multiline string \u2014 so on the next run the formatter (correctly)\r\nread its own output as a multiline string and swallowed the following code. The\r\nresult was a program with different meaning, and in some shapes one that no\r\nlonger even parses. Such a literal is now never left at the end of a line.\r\nThis also removes the last known case where formatting twice gave a different\r\nresult than formatting once.<\/p>\r\n\r\n<p><b>An unrelated <code>.editorconfig<\/code> could be applied to your file<\/b><\/p>\r\n<p>The settings cascade walks from the file's own directory upwards \u2014 but one step\r\npast the drive root it produced the <i>drive-relative<\/i> form (<code>C:<\/code>), and asking\r\nWindows for <code>C:.editorconfig<\/code> means \"<code>.editorconfig<\/code> in the current directory of\r\nthat drive\". Files that had no <code>.editorconfig<\/code> of their own therefore silently\r\npicked up the one belonging to whatever directory the formatter happened to be\r\nstarted from \u2014 in the IDE that is not even a directory you chose. The cascade\r\nnow stops at the drive root, so only the file's own path decides its settings.<\/p>\r\n\r\n<p><b>Broken parentheses no longer produce file-wide indent drift<\/b><\/p>\r\n<p>A source with a missing <code>)<\/code> or <code>]<\/code> used to pass the safety check silently \u2014\r\nit only measured <code>begin<\/code>\/<code>end<\/code> blocks \u2014 and the continuation indent then\r\ndrifted through the rest of the file. Unbalanced parentheses or brackets now\r\nshare the same protection as unbalanced blocks: the IDE refuses with a\r\nprecise message (\"unbalanced parentheses or brackets\") and leaves the buffer\r\nuntouched. Such files cannot compile anyway; once the syntax is fixed, a\r\nsingle format run restores canonical indentation everywhere.<\/p>\r\n\r\n<p><b>Layout<\/b><\/p>\r\n\r\n<p><b>Alignment stopped working after branch-asymmetric conditionals<\/b><\/p>\r\n<p>The aligner's nesting counters read every <code>{$IFDEF}<\/code> branch as if all of\r\nthem were compiled at once \u2014 a <code>begin<\/code> in each branch counted twice, so\r\nbehind such a conditional the aligner believed it was still inside\r\nexecutable code and quietly stopped aligning <code>const<\/code>\/<code>var<\/code>\/field blocks.\r\nThe counters now follow the <i>first<\/i> branch only (the same\r\nfirst-branch-wins rule the conditional-fork indenter uses); behind the\r\nregion the alignment is identical to a file containing just that branch.<\/p>\r\n\r\n<p><b>Generics with conditional type arguments were spaced as comparisons<\/b><\/p>\r\n<p><code>TList<\/code> came out as\r\n<code>TList  ;<\/code> \u2014 the generic matcher silently swallowed the directives\r\nand read both branches as one invalid token stream. The scan now follows\r\nthe <i>first<\/i> branch and skips <code>{$ELSE}\u2026{$ENDIF}<\/code> whole (the same\r\nfirst-branch-wins rule the conditional-fork indenter uses), so the\r\nangle brackets stay generic-tight in every branch. Real comparisons\r\nnext to directives keep their operator spacing.<\/p>\r\n\r\n<p><b>Box-art comment headers were gutted<\/b><\/p>\r\n<p>Under the default profile, decorative comment blocks such as the classic\r\n<code>{  Framework Name  }<\/code> unit headers lost their interior padding, and\r\nwhitespace-only filler lines collapsed to <code>{}<\/code>. The comment-spacing option now\r\ndoes exactly what it says \u2014 it normalizes the <i>single<\/i> space inside the\r\ndelimiters (<code>{x}<\/code> \u2194 <code>{ x }<\/code>) \u2014 and treats everything beyond that (padding\r\nruns, frame lines like <code>{******}<\/code>, multi-line comments) as deliberate layout\r\nthat survives byte-for-byte.<\/p>\r\n\r\n<p><b>Nested if\/else indented misleadingly<\/b><\/p>\r\n<p>In <code>if A then if B then \u2026 else \u2026<\/code> chains the <code>else<\/code> branch was visually\r\nre-bound to the <i>outer<\/i> <code>if<\/code>, although Delphi binds it to the nearest one.\r\nThe indentation now follows the language's actual binding \u2014 including through\r\n<code>while \u2026 do<\/code> in between, and without disturbing <code>case<\/code>-<code>else<\/code> handling.<\/p>\r\n\r\n<p><b>Multi-line signatures and calls lost their continuation indent<\/b><\/p>\r\n<p>Continuation lines of a multi-line parameter list or call were flattened to\r\nthe same column as their head line. They now indent one continuation step past\r\nthe line that opened the bracket; a closing-bracket line aligns with that\r\nopener line.<\/p>\r\n\r\n<p><b>Statements after a mid-line <code>finally<\/code> \/ <code>except<\/code> were mis-indented<\/b><\/p>\r\n<p><code>try<\/code> \/ <code>if A then<\/code> \/ <code>B; finally<\/code> left the pending <code>then<\/code> continuation\r\nstanding, so the statements of the <code>finally<\/code> part sat one step too deep.<\/p>\r\n\r\n<p><b>The <code>else<\/code> of an unparenthesized if-expression looked like a second else<\/b><\/p>\r\n<p>In <code>if C then<\/code> \/ <code>X := if A<\/code> \/ <code>then 1<\/code> \/ <code>else 2<\/code> \/ <code>else<\/code> \/ <code>Y := 3;<\/code> the\r\nfirst <code>else<\/code> belongs to the <i>expression<\/i>, the second to the statement \u2014 but the\r\nformatter pulled both onto the same column, so the code read as if one <code>if<\/code> had\r\ntwo <code>else<\/code> branches. The expression's <code>else<\/code> now stays at the expression's\r\nlevel. (The branch lines themselves keep their layout: an unparenthesized\r\ncontinuation is never indented an extra step \u2014 the same as <code>X := A +<\/code> \/ <code>B;<\/code>.)<\/p>\r\n\r\n<p><b>Generic constraints were spaced as comparisons<\/b><\/p>\r\n<p><code>TList<\/code> came out as <code>TList <\/code>.\r\nConstraint lists (<code>class<\/code>, <code>record<\/code>, <code>constructor<\/code>, interface types, multiple\r\nparameter groups) are now recognized as generics.<\/p>\r\n\r\n<p><b>Generics in parameter defaults were torn apart<\/b><\/p>\r\n<p>A default value glued to a generic parameter type \u2014 <code>Symbols: TArray= []<\/code>\r\n(no space before the <code>=<\/code>) \u2014 made the scanner read <code>&gt;=<\/code> as a single\r\ngreater-or-equal operator. The generic then failed to be recognized and came\r\nout spaced as a comparison: <code>TArray = []<\/code>. Both the tight original\r\nand the previously damaged form now heal to <code>TArray = []<\/code>; a generic\r\n<code>&lt;<\/code> also glues back to its type name. Real comparisons like <code>a <b>= c<\/code>\r\nare untouched.<\/p>\r\n\r\n<p><b>Delphi 13 if-expressions were staircased<\/b><\/p>\r\n<p>A multi-line if-expression in parentheses \u2014 <code>(if cond then '-' else '')<\/code>\r\nspread over lines \u2014 had its branch lines indented like a statement-if, one\r\nextra level per <code>then<\/code>\/<code>else<\/code>. Combined with the bracket-indent option this\r\nproduced a staircase drifting right with every branch. Inside parentheses an\r\n<code>if<\/code> is always an expression (only an anonymous method's <code>begin<\/code> re-enters\r\nstatement territory \u2014 that case is recognized and unchanged), so the branch\r\nlines now get plain continuation indent. Statement-<code>if<\/code> formatting, dangling-\r\n<code>else<\/code> binding and <code>case<\/code>-<code>else<\/code> handling are untouched.<\/p>\r\n\r\n<p><b>\"else if\" line breaks left the else hanging<\/b><\/p>\r\n<p>With line-break rules active, <code>if A then B else if C then D<\/code> reflowed to\r\n<code>then B else<\/code> \u23ce <code>if C<\/code>. A statement-level <code>else<\/code> now always starts its own\r\nline; the existing option keeps controlling the gap <i>between<\/i> <code>else<\/code> and <code>if<\/code>\r\n(both settings of it now produce clean shapes). Delphi 13 <code>if<\/code>-expressions\r\n(<code>x := if a then b else c<\/code>) are never broken apart.<\/p>\r\n\r\n<p><b>Line-break rules tore if-expressions apart<\/b><\/p>\r\n<p>With reflow enabled, a Delphi 13 if-expression was laid out like a statement:\r\n<code>S := (if A then 'yes' else 'no');<\/code> came back split across four lines. The\r\nfirst round of protection covered only the unparenthesized form and only two of\r\nthe four rules involved, so both the parenthesized form and \u2014 with the <i>then<\/i>\r\nand <i>simple instruction<\/i> rules armed \u2014 even the bare one were still broken up.\r\nInside parentheses an <code>if<\/code> is always an expression (an anonymous method's\r\n<code>begin<\/code> re-enters statement territory), and those rules now leave both forms\r\nalone. Statement <code>if<\/code>s keep breaking exactly as configured.<\/p>\r\n\r\n<p><b>Command line<\/b><\/p>\r\n\r\n<p><b>One bad file no longer aborts the whole run<\/b><\/p>\r\n<p>A source that cannot be decoded (a stray byte in a file claiming UTF-8) made\r\n<code>Formatter.exe<\/code> stop right there \u2014 silently truncating a recursive sweep, even\r\nunder <code>--check<\/code> where nothing is written at all. Such a file is now reported,\r\ncounted as skipped and left untouched; the run continues and the exit code\r\nstill reports the error.<\/p>\r\n\r\n\r\n<p>-----------------------------------------------------------------------<\/br>\r\n 2. New<\/br>\r\n-----------------------------------------------------------------------<\/br><\/p>\r\n\r\n<p><b>Blank lines around initialization\/finalization, and before the final end.<\/b><\/p>\r\n<p>Two new structure separators and a new indent option close the gap the\r\nsection separators left: <code>delphi_blank_lines_around_initialization<\/code>\r\nguarantees blank lines before and after the <code>initialization<\/code>\/\r\n<code>finalization<\/code> keywords, <code>delphi_blank_lines_before_final_end<\/code> puts\r\nthem before the closing <code>end.<\/code>, and <i>Indent initialization\/finalization\r\ncontents<\/i> indents the sections' statements one level like a begin..end\r\nblock. All three are off by default \u2014 existing output stays\r\nbyte-identical.<\/p>\r\n\r\n<p><b>Unusable options are now hidden<\/b><\/p>\r\n<p>When a master option turns a group of settings off, those settings used to\r\nstay visible \u2014 at best with a grayed group title, while the controls below\r\nstill looked active (\"It's disturbing\"). Dependent options are now hidden\r\noutright: with \"Keep user line breaks\" on (the default), the line-break\r\nrules, the whole Reflow page and the blank-line separators disappear (an\r\nexplanatory note stays behind); the alignment tuning numbers hide while no\r\nalignment case is active; coupled or inert switches (colons-before-types\r\ncoupling, case options without block indent, bracket indent under reflow)\r\nvanish with their masters. Toggling the master brings everything back \u2014\r\nlive, across pages, without reopening the dialog.<\/p>\r\n\r\n<p><b>Readable, complete option texts<\/b><\/p>\r\n<p>Longer labels were cut off \u2014 most visibly the note under <i>Blank Lines<\/i> and the\r\n\"colons before type names\" switch under <i>Alignment<\/i>, and more often in German,\r\nwhere the translations are longer than the English originals. Every page was\r\nmeasured against both languages: notes have room for their full text, long\r\nswitch captions wrap instead of being clipped, and the drop-downs sit far\r\nenough right that no label can run into them. The explanatory notes are now\r\nlaid out in paragraphs instead of one dense block.<\/p>\r\n\r\n<p><b>The option previews now show real cases<\/b><\/p>\r\n<p>The live before\/after preview on each options page demonstrates the\r\nconstellations this release fixed: conditional branches carrying a <code>try<\/code>,\r\n<code>case<\/code> with <code>else<\/code>, box-art comment headers, generic constraints and\r\ndefaults, multi-line call continuations, dangling <code>else<\/code> binding,\r\n<code>{$REGION}<\/code> blank lines and alignment groups broken by a directive \u2014 so\r\nevery page shows its own options working on real code.<\/p>\r\n\r\n<p><b>The line-break master is now a regular <code>.editorconfig<\/code> key<\/b><\/p>\r\n<p>\"Keep user line breaks\" can be set from a project's <code>.editorconfig<\/code>\r\n(<code>delphi_keep_user_linebreaks<\/code>) like every other option, so a team can pin its\r\nline-break policy in the repository and an exported profile round-trips\r\ncompletely. This deliberately reverses the earlier safety stance that a\r\nrepository file must never enable reflow.<\/p>\r\n\r\n<p><b>Export writes what you see<\/b><\/p>\r\n<p>Both export buttons (.config and .editorconfig) now write the values currently\r\nset in the options dialog, including pages you have edited but not yet applied\r\n\u2014 previously they silently wrote the last saved state, and the note explaining\r\nthat is gone along with the behaviour.<\/p>\r\n\r\n<p><b>The installer now serves both IDEs<\/b><\/p>\r\n<p>The setup installs the 32-bit and the 64-bit design package side by side\r\nand registers each with its IDE (Known Packages \/ Known Packages x64) \u2014\r\nCtrl+D works in whichever RAD Studio 13 you launch. Both packages always\r\ncarry the same version number; the installer build refuses to package\r\nmismatched builds.<\/p>\r\n\r\n<p><b>Option: placement of the case-else line<\/b><\/p>\r\n<p>A new switch <b>\"Indent else line\"<\/b> (<code>delphi_indent_case_else<\/code>) controls where\r\nthe <code>else<\/code> of a <code>case<\/code> statement sits: at the level of the alternative labels\r\n(default, unchanged behaviour) or at the <code>case<\/code> keyword \u2014 the classic RTL\r\nstyle. The statements below it remain governed by the existing case options.<\/p>\r\n\r\n\r\n<p>-----------------------------------------------------------------------<\/br>\r\n 3. Clarified and pinned<\/br>\r\n-----------------------------------------------------------------------<\/br><\/p>\r\n<ul>\r\n<li><b>Blank lines around <code>{$REGION}<\/code>:<\/b> both halves of the request are\r\n  covered by existing options \u2014 set <i>Max. consecutive blank lines<\/i> to 2 to\r\n  preserve a two-blank region spacing, and <i>Blank lines around compiler\r\n  directives<\/i> (reflow tier) to insert them. Documented in the manual and\r\n  locked in by tests.<\/li>\r\n<li><b>\"Ensure final newline\"<\/b> and <b>\"Enable formatting\"<\/b>: both\r\n  reported against beta build 3.0.1; the current code base behaves correctly\r\n  (the disable switch is honoured end to end, including the <code>.editorconfig<\/code>\r\n  cascade, and the final-newline option is exact in both directions). Both\r\n  behaviours are now locked in by tests. The 3.0.1 symptoms came from that\r\n  build's Revive-based IDE integration, which the native RAD Studio 13.2\r\n  formatter interface has since replaced.<\/li>\r\n<li><b>Options pages opening slowly:<\/b> verified not to be the plugin \u2014 even\r\n  empty pages build up slowly; the cost sits in RAD Studio's own\r\n  Tools\/Options page embedding and would affect any add-in equally.<\/li>\r\n<\/ul>\r\n\r\n<p><b>The limits of conditional handling are documented<\/b><\/p>\r\n<p>Two constellations the conditional-fork model cannot cover by principle\r\nnow have their own \"Known limits\" manual section (German and English):\r\ninclude files that contribute block structure (the formatter refuses\r\nhonestly or, for an included <code>begin<\/code>, may indent differently after the\r\nspot - never damaging anything), and directive regions starting or\r\nending next to code on the same line (left untouched). Each comes with\r\na minimal example, the observable behavior and an <code>.editorconfig<\/code>\r\nopt-out recipe.<\/p>\r\n\r\n\r\n<p>-----------------------------------------------------------------------<\/br>\r\n 4. By design<\/br>\r\n-----------------------------------------------------------------------<\/br><\/p>\r\n<ul>\r\n<li><b>The options tree stays under the MSys node:<\/b> requested twice from\r\n  the community (\"Third Party\" placement); declined deliberately \u2014 the MSys\r\n  node is the shared home for current and future MSys settings.<\/li>\r\n<li><b>Ctrl+D stays Ctrl+D:<\/b> the format shortcut is not configurable \u2014\r\n  Ctrl+D has been the Delphi formatter's key since the built-in one, and on\r\n  RAD Studio 13.2 the IDE owns the command anyway. Plugins that also claim\r\n  Ctrl+D (e.g. GExperts) offer configurable shortcuts on their side.<\/li>\r\n<\/ul>\r\n\r\n\r\n<p>-----------------------------------------------------------------------<\/br>\r\n 5. Under the hood<\/br>\r\n-----------------------------------------------------------------------<\/br><\/p>\r\n<ul>\r\n<li>The regression suite grew from 744 to 970 checks; every repository fixture\r\n  is now also verified to format <i>balanced<\/i>, and formatting twice is now\r\n  pinned for reflow profiles too, not just the default one.<\/li>\r\n<li>New shared conditional-directive classifier used by the re-indenter and the\r\n  aligner; community-provided reproduction sources joined the fixture set.<\/li>\r\n<li>Several of the defects above were found by the fix for another one: the\r\n  mid-line closer and the multiline-string cases came out of reviewing the\r\n  if-expression reflow, and the <code>.editorconfig<\/code> cascade bug out of measuring\r\n  the multiline-string fix.<\/li>\r\n<\/ul>\r\n","timestamp":1784806349}]},"wpdmcategory":[62],"wpdmtag":[],"class_list":["post-14035","wpdmpro","type-wpdmpro","status-publish","hentry","wpdmcategory-developer-tools"],"_links":{"self":[{"href":"https:\/\/maenken.systems\/de\/wp-json\/wp\/v2\/wpdmpro\/14035","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/maenken.systems\/de\/wp-json\/wp\/v2\/wpdmpro"}],"about":[{"href":"https:\/\/maenken.systems\/de\/wp-json\/wp\/v2\/types\/wpdmpro"}],"author":[{"embeddable":true,"href":"https:\/\/maenken.systems\/de\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/maenken.systems\/de\/wp-json\/wp\/v2\/comments?post=14035"}],"wp:attachment":[{"href":"https:\/\/maenken.systems\/de\/wp-json\/wp\/v2\/media?parent=14035"}],"wp:term":[{"taxonomy":"wpdmcategory","embeddable":true,"href":"https:\/\/maenken.systems\/de\/wp-json\/wp\/v2\/wpdmcategory?post=14035"},{"taxonomy":"wpdmtag","embeddable":true,"href":"https:\/\/maenken.systems\/de\/wp-json\/wp\/v2\/wpdmtag?post=14035"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}