diff --git a/components/codetools/codeatom.pas b/components/codetools/codeatom.pas index b9ddaccf1e..2fcaae505e 100644 --- a/components/codetools/codeatom.pas +++ b/components/codetools/codeatom.pas @@ -100,7 +100,7 @@ type procedure Add(NewAtom: TAtomPosition); inline; procedure UndoLastAdd; inline; function GetValueAt(RelativePos:integer): TAtomPosition; inline; - // 0=current 1=prior current ... + // 0=current last 1=prior current ... // for LastAtoms: 0 is the last atom function Count: integer; inline; property Size: integer read FSize write SetSize; diff --git a/components/codetools/customcodetool.pas b/components/codetools/customcodetool.pas index 65ddb31a66..36fddb9e27 100644 --- a/components/codetools/customcodetool.pas +++ b/components/codetools/customcodetool.pas @@ -250,6 +250,7 @@ type // read atoms function AtomIs(const AnAtom: shortstring): boolean; function UpAtomIs(const AnAtom: shortstring): boolean; + function UpAtomIs(const AtomPos: TAtomPosition; const AnAtom: shortstring): boolean; overload; function ReadNextAtomIs(const AnAtom: shortstring): boolean; {$IFDEF UseInline}inline;{$ENDIF} function ReadNextUpAtomIs(const AnAtom: shortstring): boolean; {$IFDEF UseInline}inline;{$ENDIF} function ReadNextAtomIsChar(const c: char): boolean; {$IFDEF UseInline}inline;{$ENDIF} @@ -583,6 +584,25 @@ begin end; end; +function TCustomCodeTool.UpAtomIs(const AtomPos: TAtomPosition; + const AnAtom: shortstring): boolean; +var + AnAtomLen, i: integer; + p: PChar; +begin + Result:=false; + AnAtomLen:=length(AnAtom); + if AnAtomLen<>AtomPos.EndPos-AtomPos.StartPos then exit; + if (AtomPos.EndPos<=SrcLen+1) and (AtomPos.StartPos>=1) then begin + p:=@Src[AtomPos.StartPos]; + for i:=1 to AnAtomLen do begin + if AnAtom[i]<>UpChars[p^] then exit; + inc(p); + end; + Result:=true; + end; +end; + function TCustomCodeTool.ReadNextAtomIs(const AnAtom: shortstring): boolean; begin ReadNextAtom; diff --git a/components/codetools/stdcodetools.pas b/components/codetools/stdcodetools.pas index 002e8f2716..fd1bd2fc42 100644 --- a/components/codetools/stdcodetools.pas +++ b/components/codetools/stdcodetools.pas @@ -5591,6 +5591,7 @@ var Indent: Integer; CursorBlockInnerIndent, CursorBlockOuterIndent: LongInt; CursorBlock: TBlock; + AtomInFrontOfCursor: TAtomPosition; BehindCursorBlock: Boolean; // atom is behind cursor block InCursorBlock: Boolean; NeedCompletion: Boolean; @@ -5600,6 +5601,9 @@ var FrontGap: TGapTyp; BeautifyFlags: TBeautifyCodeFlags; BehindPos: LongInt; + CursorInEmptyStatement: Boolean; + FromPos: LongInt; + ToPos: LongInt; function EndBlockIsOk: boolean; begin @@ -5621,6 +5625,8 @@ var CursorBlock.StartPos:=0; BehindCursorBlock:=false; NeedCompletion:=false; + AtomInFrontOfCursor.StartPos:=0; + CursorInEmptyStatement:=false; repeat ReadNextAtom; @@ -5628,7 +5634,7 @@ var if (Stack.Top>=0) and (Stack.Stack[Stack.Top].InnerIndent<0) and (not PositionsInSameLine(Src,Stack.Stack[Stack.Top].StartPos,CurPos.StartPos)) then begin - // the first atom of this block on a new line + // the first atom of this block is on a new line Stack.Stack[Stack.Top].InnerIndent:=GetLineIndent(Src,CurPos.StartPos); end; @@ -5643,6 +5649,15 @@ var CursorBlock:=Stack.Stack[CursorBlockLvl]; CursorBlockOuterIndent:=GetLineIndent(Src,CursorBlock.StartPos); CursorBlockInnerIndent:=Stack.Stack[Stack.Top].InnerIndent; + AtomInFrontOfCursor:=LastAtoms.GetValueAt(0); + if (CurPos.Flag=cafSemicolon) + and ((AtomInFrontOfCursor.Flag=cafSemicolon) + or (CursorBlock.StartPos=AtomInFrontOfCursor.StartPos)) + and (FindNextNonSpace(Src,AtomInFrontOfCursor.EndPos)=CurPos.StartPos) + then begin + // cursor in empty statement + CursorInEmptyStatement:=true; + end; end; //DebugLn(['ReadStatements CursorBlockLvl=',CursorBlockLvl,' Indent=',CursorBlockIndent]); end; @@ -5758,6 +5773,7 @@ var if TopBlockType(Stack)=btCaseOf then BeginBlock(Stack,btCaseColon,CurPos.StartPos); cafSemicolon: + // todo: close btRoundBracket, btEdgedBracket while TopBlockType(Stack) in [btCaseColon,btIf,btIfElse] do begin if not EndBlockIsOk then exit; end; @@ -5791,14 +5807,17 @@ var end else if UpAtomIs('IF') then begin BeginBlock(Stack,btIf,CurPos.StartPos); end else if UpAtomIs('THEN') then begin + // todo: close brackets if TopBlockType(Stack)=btIf then Stack.Stack[Stack.Top].InnerIndent:=-1; end else if UpAtomIs('CASE') then begin BeginBlock(Stack,btCase,CurPos.StartPos) end else if UpAtomIs('OF') then begin + // todo: close brackets if TopBlockType(Stack)=btCase then BeginBlock(Stack,btCaseOf,CurPos.StartPos); end else if UpAtomIs('ELSE') then begin + // todo: close brackets case TopBlockType(Stack) of btIf: begin @@ -5836,6 +5855,8 @@ var end else if UpAtomIs('PROCEDURE') or UpAtomIs('FUNCTION') or UpAtomIs('CONSTRUCTOR') or UpAtomIs('DESTRUCTOR') or UpAtomIs('VAR') or UpAtomIs('TYPE') or UpAtomIs('CONST') + or UpAtomIs('RESOURCESTRING') or UpAtomIs('LABEL') or UpAtomIs('CLASS') + or UpAtomIs('INITIALIZATION') or UpAtomIs('FINALIZATION') then begin // unexpected keyword => block not closed {$IFDEF ShowCompleteBlock} @@ -5896,7 +5917,8 @@ var // check code behind BehindPos:=FindNextNonSpace(Src,InsertPos); if BehindPos<=SrcLen then begin - if PositionsInSameLine(Src,InsertPos,BehindPos) then begin + if (not CursorInEmptyStatement) + and PositionsInSameLine(Src,InsertPos,BehindPos) then begin // target line not empty {$IFDEF ShowCompleteBlock} DebugLn(['CompleteStatements target line not empty => skip']); @@ -5916,30 +5938,38 @@ var end; end; - NewCode:=''; + NewCode:=';'; FrontGap:=gtEmptyLine; AfterGap:=gtNewLine; + FromPos:=InsertPos; + ToPos:=InsertPos; BeautifyFlags:=[bcfIndentExistingLineBreaks]; + if CursorInEmptyStatement and (BehindPos<=SrcLen) then begin + // replace the empty statement + FrontGap:=gtNewLine; + ToPos:=BehindPos; + end; case CursorBlock.Typ of btBegin,btFinally,btExcept,btAsm,btCaseOf,btCaseElse: - NewCode:='end;'; + NewCode:='end'+NewCode; btRepeat: - NewCode:='until ;'; + NewCode:='until '+NewCode; btTry: NewCode:='finally'+SourceChangeCache.BeautifyCodeOptions.LineEnd - +'end;'; + +'end'+NewCode; btCaseColon: begin - NewCode:=';'; FrontGap:=gtNone; AfterGap:=gtNone; end; else exit; end; - if (CursorBlockLvl=0) and (AfterGap=gtNewLine) then + if (CursorBlockLvl=0) and (AfterGap=gtNewLine) then begin + // top level => insert empty lines between top level structures AfterGap:=gtEmptyLine; - if not Replace(NewCode,InsertPos,InsertPos,Indent,FrontGap,AfterGap, + end; + if not Replace(NewCode,FromPos,ToPos,Indent,FrontGap,AfterGap, BeautifyFlags) then exit; end; Result:=true;