unit NoSpaceAfter;

{ AFS 9 Dec 1999
  no space after  certain tokens }

{(*}
(*------------------------------------------------------------------------------
 Delphi Code formatter source code 

The Original Code is NoSpaceAfter, released May 2003.
The Initial Developer of the Original Code is Anthony Steele. 
Portions created by Anthony Steele are Copyright (C) 1999-2008 Anthony Steele.
All Rights Reserved. 
Contributor(s): Anthony Steele. 

The contents of this file are subject to the Mozilla Public License Version 1.1
(the "License"). you may not use this file except in compliance with the License.
You may obtain a copy of the License at http://www.mozilla.org/NPL/

Software distributed under the License is distributed on an "AS IS" basis,
WITHOUT WARRANTY OF ANY KIND, either express or implied.
See the License for the specific language governing rights and limitations 
under the License.

Alternatively, the contents of this file may be used under the terms of
the GNU General Public License Version 2 or later (the "GPL") 
See http://www.gnu.org/licenses/gpl.html
------------------------------------------------------------------------------*)
{*)}

{$I JcfGlobal.inc}

interface

uses SwitchableVisitor, SourceToken;

type
  TNoSpaceAfter = class(TSwitchableVisitor)
  private
    fcLastSolidToken: TSourceToken;
    fbSafeToRemoveReturn: boolean;  // this taken from NoReturnBefore
  protected
    function EnabledVisitSourceToken(const pcNode: TObject): boolean; override;
  public
    constructor Create; override;

    function IsIncludedInSettings: boolean; override;
  end;


implementation

uses
  JcfStringUtils,
  Tokens, ParseTreeNodeType, JcfSettings, FormatFlags,
  TokenUtils, SettingsTypes;

{ TNoSpaceAfter }


function NeedsNoSpace(const pt, ptNext: TSourceToken): boolean;
const
  NoSpaceAnywhere: TTokenTypeSet = [ttOpenBracket, ttOpenSquareBracket, ttDot];
begin
  Result := False;

  if pt = nil then
    exit;

  { if the next thing is a comment, leave well enough alone }
  if ptNext.TokenType = ttComment then
    exit;

  if pt.TokenType in NoSpaceAnywhere then
  begin
    Result := True;
    exit;
  end;

  if (FormattingSettings.Spaces.SpaceForOperator = eNever) then
  begin
    if IsSymbolOperator(pt) then
    begin
      Result := True;
      exit;
    end;
  end;


  { no space between method name and open bracket for param list
    no space between type & bracket for cast
    no space between fn name & params for procedure call }
  if pt.HasParentNode([nProcedureDecl, nFunctionDecl, nConstructorDecl,
    nDestructorDecl, nStatementList]) and
    (IsIdentifier(pt, idAllowDirectives) or (pt.TokenType in BuiltInTypes)) then
  begin
    if (ptNext.TokenType in OpenBrackets) and (not IsInsideAsm(ptNext)) then
    begin
      Result := True;
      exit;
    end;
  end;

  { the above takes care of procedure headers but not procedure type defs
   eg type TFred = procedure(i: integer) of object;
    note no space before the open bracket }
  if pt.HasParentNode(nTypeDecl) and (pt.IsOnRightOf(nTypeDecl, ttEquals)) and
    (pt.TokenType in ProcedureWords) then
  begin
    if (ptNext.TokenType in OpenBrackets) then
    begin
      Result := True;
      exit;
    end;
  end;

  { no space after unary operator in expression }
  if pt.HasParentNode(nExpression) and IsUnaryOperator(pt) and
    ( not StrHasAlpha(pt.SourceCode)) then
  begin
    Result := True;
    exit;
  end;

  { no space before class heritage ? could be one of 3 things
    TFoo = class; - no space, but "No space before semicolon" should take care of that
    TBar = class(TBaz) - no space unless you are Marcel van Brakel
    TWibble = class of TFish - has space

    see SingleSpaceAfter

    also applies to type TFoo = interface(IDispatch) }
  if (pt.HasParentNode(nRestrictedType)) and (pt.TokenType in ObjectTypeWords) and
    ( not (FormattingSettings.Spaces.SpaceBeforeClassHeritage)) then
  begin
    if (ptNext.TokenType in [ttOpenBracket, ttSemiColon]) then
    begin
      Result := True;
      exit;
    end;
  end;
end;


constructor TNoSpaceAfter.Create;
begin
  inherited;
  fbSafeToRemoveReturn := True;
  FormatFlags := FormatFlags + [eRemoveSpace];
end;

function TNoSpaceAfter.EnabledVisitSourceToken(const pcNode: TObject): boolean;
var
  lcSourceToken: TSourceToken;
  lcNextSolid:   TSourceToken;
begin
  Result := False;
  lcSourceToken := TSourceToken(pcNode);

  if lcSourceToken.TokenType = ttWhiteSpace then
  begin
    lcNextSolid := lcSourceToken.NextTokenWithExclusions([ttWhiteSpace, ttReturn]);
    if lcNextSolid <> nil then
    begin
      if NeedsNoSpace(fcLastSolidToken, lcNextSolid) then
        BlankToken(lcSourceToken);
    end;
  end
  else
  begin
    { store for next time }
    if not (lcSourceToken.TokenType in [ttWhiteSpace, ttReturn]) then
      fcLastSolidToken := lcSourceToken;
  end;
end;

function TNoSpaceAfter.IsIncludedInSettings: boolean;
begin
  Result := FormattingSettings.Spaces.FixSpacing;
end;

end.
