Difference between revisions of "Fully automatic indentation"

From Free Pascal wiki
Jump to navigationJump to search
m (Fixed syntax highlighting)
 
(7 intermediate revisions by 3 users not shown)
Line 1: Line 1:
 
=Overview=
 
=Overview=
  
Since 0.9.29 the lazarus source editor has a new algorithm for automatic indentation on pressing Enter or on pasting code from clipboard. This new algorithm works only for pascal and works different to many other editors you may know. Most other editors use either a fixed set of rules or a set of options to configure the rules. This is ''semi automatic'' indentation. These options are either too simple or too complex. And the other editors only allow one set of rules, so editing sources with different policies is difficult.
+
Since 0.9.29 the lazarus source editor has a new algorithm for automatic indentation on pressing Enter or on pasting code from clipboard. This new algorithm works only for pascal and does not use static rules, but parses the sources to imitate the indentation. Most other editors use either a fixed set of rules or a set of options to configure the rules. This is ''semi automatic'' indentation. These options are either too simple or too complex. And the other editors only allow one set of rules, so editing sources with different policies is difficult.
  
 
'''Fully automatic indentation''' tries to guess the rules from the surrounding code. It does that by searching for similar code and copying the indent.
 
'''Fully automatic indentation''' tries to guess the rules from the surrounding code. It does that by searching for similar code and copying the indent.
Line 8: Line 8:
  
 
=How it works=
 
=How it works=
 +
 +
==Indent on paste from clipboard==
  
 
For example you have the code:
 
For example you have the code:
<Delphi>
+
<syntaxhighlight lang=pascal>
 
procedure TMainForm.Button1Click(Sender: TObject);
 
procedure TMainForm.Button1Click(Sender: TObject);
 
begin
 
begin
 
|
 
|
 
end;
 
end;
</Delphi>
+
</syntaxhighlight>
  
 
You place the cursor at column 1 and paste the code
 
You place the cursor at column 1 and paste the code
<Delphi>
+
 
 +
<syntaxhighlight lang=pascal>
 
     if Visible then
 
     if Visible then
 
     begin
 
     begin
 
     end;
 
     end;
</Delphi>
+
</syntaxhighlight>
  
 
Note that the code on the clipboard is already indented by 4.
 
Note that the code on the clipboard is already indented by 4.
Line 28: Line 31:
 
The indenter first scans the code, finds out, that the insertion position is in a ''begin..end'' block of a ''procedure''. With the standard options it searches the code in front for other ''procedure begin..end'' blocks. If it does not find the code it searches behind the code, then in all units of the project (or package) and finally in the example code of the options. For instance it finds:
 
The indenter first scans the code, finds out, that the insertion position is in a ''begin..end'' block of a ''procedure''. With the standard options it searches the code in front for other ''procedure begin..end'' blocks. If it does not find the code it searches behind the code, then in all units of the project (or package) and finally in the example code of the options. For instance it finds:
  
<Delphi>
+
<syntaxhighlight lang=pascal>
 
procedure TMainForm.FormPaint(Sender: TObject);
 
procedure TMainForm.FormPaint(Sender: TObject);
 
begin
 
begin
 
   with Canvas do FillRect(0,0,Width,Height);
 
   with Canvas do FillRect(0,0,Width,Height);
 
end;
 
end;
</Delphi>
+
</syntaxhighlight>
  
 
The indentation of the first token ''with'' is 2. The indentation of the first token of clipboard code is 4, so the indenter unindents the code by 2, resulting in:
 
The indentation of the first token ''with'' is 2. The indentation of the first token of clipboard code is 4, so the indenter unindents the code by 2, resulting in:
  
<Delphi>
+
<syntaxhighlight lang=pascal>
 
procedure TMainForm.Button1Click(Sender: TObject);
 
procedure TMainForm.Button1Click(Sender: TObject);
 
begin
 
begin
Line 44: Line 47:
 
   end;|
 
   end;|
 
end;
 
end;
</Delphi>
+
</syntaxhighlight>
 +
 
 +
The indenter always indents the whole block, not every line. For example pasting
 +
 
 +
<syntaxhighlight lang=pascal>
 +
DoSomething(Param1,
 +
            Param2);
 +
</syntaxhighlight>
 +
 
 +
will keep the indent both lines by the same amount, keeping the two ''Params'' aligned.
 +
 
 +
The indenter scans what is inserted. For instance pasting
 +
 
 +
<syntaxhighlight lang=pascal>
 +
end;
 +
</syntaxhighlight>
 +
 
 +
will end the the begin block, resulting in:
 +
 
 +
<syntaxhighlight lang=pascal>
 +
procedure TMainForm.Button1Click(Sender: TObject);
 +
begin
 +
end;|
 +
end;
 +
</syntaxhighlight>
  
 
=Examples=
 
=Examples=
Line 50: Line 77:
 
Sometimes record and classes are aligned to the keywords instead of start of last line.
 
Sometimes record and classes are aligned to the keywords instead of start of last line.
  
<Delphi>
+
<syntaxhighlight lang=pascal>
 
type TMyRecord = record
 
type TMyRecord = record
 
                 i: integer;
 
                 i: integer;
 
                 end;
 
                 end;
</Delphi>
+
</syntaxhighlight>
 +
 
 +
At the moment the indenter supports only amount of spaces, not aligning to keywords or symbols.
  
 
==begin end==
 
==begin end==
  
<Delphi>
+
<syntaxhighlight lang=pascal>
 
procedure Do;
 
procedure Do;
 
var
 
var
Line 68: Line 97:
 
   end;        // unindent before end
 
   end;        // unindent before end
 
end;
 
end;
</Delphi>
+
</syntaxhighlight>
  
<Delphi>
+
<syntaxhighlight lang=pascal>
 
procedure Do;
 
procedure Do;
 
begin
 
begin
Line 77: Line 106:
 
   end;                // unindent before end
 
   end;                // unindent before end
 
end;
 
end;
</Delphi>
+
</syntaxhighlight>
  
<Delphi>
+
<syntaxhighlight lang=pascal>
 
procedure Do;
 
procedure Do;
 
begin
 
begin
Line 87: Line 116:
 
     end;    // no unindent before end
 
     end;    // no unindent before end
 
end;
 
end;
</Delphi>
+
</syntaxhighlight>
  
<Delphi>
+
<syntaxhighlight lang=pascal>
 
procedure Do;
 
procedure Do;
 
begin
 
begin
Line 97: Line 126:
 
     end;      // unindent before end
 
     end;      // unindent before end
 
end;           
 
end;           
</Delphi>
+
</syntaxhighlight>
  
 
==repeat until==
 
==repeat until==
Line 103: Line 132:
 
I never saw code other than this:
 
I never saw code other than this:
  
<Delphi>
+
<syntaxhighlight lang=pascal>
 
repeat
 
repeat
 
   Code; // indent after repeat
 
   Code; // indent after repeat
 
until ; // unindent before until
 
until ; // unindent before until
</Delphi>
+
</syntaxhighlight>
  
 
==try finally==
 
==try finally==
Line 113: Line 142:
 
I never saw code other than this:
 
I never saw code other than this:
  
<Delphi>
+
<syntaxhighlight lang=pascal>
 
try
 
try
 
   Code;    // indent after try
 
   Code;    // indent after try
Line 119: Line 148:
 
   on e do ; // indent after finally
 
   on e do ; // indent after finally
 
end;        // unindent before finally-end
 
end;        // unindent before finally-end
</Delphi>
+
</syntaxhighlight>
  
 
==case of end==
 
==case of end==
  
<Delphi>
+
<syntaxhighlight lang=pascal>
 
case expr of
 
case expr of
 
1: ;  // no indent after case-of
 
1: ;  // no indent after case-of
 
2:  
 
2:  
 
   Code;    // indent after case-colon
 
   Code;    // indent after case-colon
const:  
+
3:  
 
   begin    // indent after case-colon
 
   begin    // indent after case-colon
 
     Code;  // indent after case-colon-begin
 
     Code;  // indent after case-colon-begin
Line 135: Line 164:
 
   Code;    // indent after case-else
 
   Code;    // indent after case-else
 
end;      // unindent after case-else statements
 
end;      // unindent after case-else statements
</Delphi>
+
</syntaxhighlight>
  
<Delphi>
+
<syntaxhighlight lang=pascal>
 
case expr of
 
case expr of
           // empty lines => not supported
+
            
 
   1: ; // indent after case-of
 
   1: ; // indent after case-of
  
Line 147: Line 176:
 
else      // no indent before case-else
 
else      // no indent before case-else
 
end;      // no indent before case-end
 
end;      // no indent before case-end
</Delphi>
+
</syntaxhighlight>
  
 
==if then else==
 
==if then else==
  
<Delphi>
+
<syntaxhighlight lang=pascal>
 
if expr then
 
if expr then
 
   Code
 
   Code
 
else
 
else
 
   Code;
 
   Code;
</delphi>
+
</syntaxhighlight>
  
<Delphi>
+
<syntaxhighlight lang=pascal>
 
if expr or
 
if expr or
 
   expr or
 
   expr or
Line 165: Line 194:
 
else
 
else
 
   Code;
 
   Code;
</delphi>
+
</syntaxhighlight>
  
<Delphi>
+
<syntaxhighlight lang=pascal>
 
if expr  
 
if expr  
 
   or expr
 
   or expr
Line 174: Line 203:
 
else
 
else
 
   Code;
 
   Code;
</delphi>
+
</syntaxhighlight>
  
<Delphi>
+
<syntaxhighlight lang=pascal>
 
if SrcEdit.SelectionAvailable
 
if SrcEdit.SelectionAvailable
 
and SrcEdit.CaretInSelection(CaretPos)
 
and SrcEdit.CaretInSelection(CaretPos)
Line 184: Line 213:
 
or (DebugEval = '')
 
or (DebugEval = '')
 
then DebugEval := '???';
 
then DebugEval := '???';
</delphi>
+
</syntaxhighlight>
 +
 
 +
[[Category:Lazarus]]

Latest revision as of 05:11, 16 February 2020

Overview

Since 0.9.29 the lazarus source editor has a new algorithm for automatic indentation on pressing Enter or on pasting code from clipboard. This new algorithm works only for pascal and does not use static rules, but parses the sources to imitate the indentation. Most other editors use either a fixed set of rules or a set of options to configure the rules. This is semi automatic indentation. These options are either too simple or too complex. And the other editors only allow one set of rules, so editing sources with different policies is difficult.

Fully automatic indentation tries to guess the rules from the surrounding code. It does that by searching for similar code and copying the indent.

Because it works fully automatic there are only a few options. You can disable it, you can setup the search scope and you can provide an example code: Codetools Options: Indentation. You do not need to learn rules, just write pascal.

How it works

Indent on paste from clipboard

For example you have the code:

procedure TMainForm.Button1Click(Sender: TObject);
begin
|
end;

You place the cursor at column 1 and paste the code

    if Visible then
    begin
    end;

Note that the code on the clipboard is already indented by 4.

The indenter first scans the code, finds out, that the insertion position is in a begin..end block of a procedure. With the standard options it searches the code in front for other procedure begin..end blocks. If it does not find the code it searches behind the code, then in all units of the project (or package) and finally in the example code of the options. For instance it finds:

procedure TMainForm.FormPaint(Sender: TObject);
begin
  with Canvas do FillRect(0,0,Width,Height);
end;

The indentation of the first token with is 2. The indentation of the first token of clipboard code is 4, so the indenter unindents the code by 2, resulting in:

procedure TMainForm.Button1Click(Sender: TObject);
begin
  if Visible then
  begin
  end;|
end;

The indenter always indents the whole block, not every line. For example pasting

DoSomething(Param1,
            Param2);

will keep the indent both lines by the same amount, keeping the two Params aligned.

The indenter scans what is inserted. For instance pasting

end;

will end the the begin block, resulting in:

procedure TMainForm.Button1Click(Sender: TObject);
begin
end;|
end;

Examples

Sometimes record and classes are aligned to the keywords instead of start of last line.

type TMyRecord = record
                 i: integer;
                 end;

At the moment the indenter supports only amount of spaces, not aligning to keywords or symbols.

begin end

procedure Do;
var
  i: integer; // indent after var
begin         // no indent after procedure, no indent before procedure-begin, unindent after var section
  if expr then
  begin       // no indent after then, no indent before then-begin
    Code;     // indent after begin
  end;        // unindent before end
end;
procedure Do;
begin
  if expr then begin  
    Code;             // indent after begin
  end;                // unindent before end
end;
procedure Do;
begin
  if expr then
    begin   // indent after then and unindent after then-statement
    Code;   // no indent after begin
    end;    // no unindent before end
end;
procedure Do;
begin
  if expr then
    begin     // indent after then and unindent after then-statement
      Code;   // indent after begin
    end;      // unindent before end
end;

repeat until

I never saw code other than this:

repeat
  Code; // indent after repeat
until ; // unindent before until

try finally

I never saw code other than this:

try
  Code;     // indent after try
finally     // unindent before finally
  on e do ; // indent after finally
end;        // unindent before finally-end

case of end

case expr of
1: ;   // no indent after case-of
2: 
  Code;    // indent after case-colon
3: 
  begin    // indent after case-colon
    Code;  // indent after case-colon-begin
  end;     // unindent before case-colon-end, unindent after case-colon-end
else       // no indent case-else 
  Code;    // indent after case-else
end;       // unindent after case-else statements
case expr of
           
  1: ; // indent after case-of

  2: 
  begin    // no indent after case-colon
  end;
else       // no indent before case-else
end;       // no indent before case-end

if then else

if expr then
  Code
else
  Code;
if expr or
  expr or
  expr then
  Code
else
  Code;
if expr 
  or expr
then
  Code
else
  Code;
if SrcEdit.SelectionAvailable
and SrcEdit.CaretInSelection(CaretPos)
then Expression := SrcEdit.GetText(True)
else Expression := Identifier;
if not DebugBoss.Evaluate(Expression, DebugEval)
or (DebugEval = '')
then DebugEval := '???';