diff --git a/internal/apply/apply.go b/internal/apply/apply.go index 63ba93e6..effbfacf 100644 --- a/internal/apply/apply.go +++ b/internal/apply/apply.go @@ -8,6 +8,7 @@ import ( "github.com/rectorphp/argtyper/internal/aggregate" "github.com/rectorphp/argtyper/internal/phpast" "github.com/rectorphp/php-parser-in-go/pkg/ast" + "github.com/rectorphp/php-parser-in-go/pkg/token" ) // Source adds parameter types to a single PHP source file. It returns the new @@ -95,14 +96,48 @@ func (a *applier) setType(param *ast.Parameter, resolved aggregate.Resolved) { nullable := resolved.Nullable || hasNullDefault(param) typeText := typeText(resolved.Type) + // Take the whitespace that sat before the variable (indentation, or the + // space after a comma or modifier) and put it in front of the new type, so + // the type slots into the variable's old position. A single space then + // separates the type from the variable. + leading := takeVarLeading(param) + identifier := &ast.Identifier{IdentifierTkn: &token.Token{Value: []byte(typeText)}} + if nullable { - param.Type = &ast.Nullable{Expr: &ast.Identifier{Value: []byte(typeText + " ")}} + param.Type = &ast.Nullable{ + QuestionTkn: &token.Token{Value: []byte("?"), FreeFloating: leading}, + Expr: identifier, + } } else { - param.Type = &ast.Identifier{Value: []byte(typeText + " ")} + identifier.IdentifierTkn.FreeFloating = leading + param.Type = identifier } a.added++ } +// takeVarLeading returns the leading whitespace tokens of the parameter's +// variable and replaces them with a single space. +func takeVarLeading(param *ast.Parameter) []*token.Token { + variable, ok := param.Var.(*ast.ExprVariable) + if !ok { + return nil + } + + leadingToken := variable.DollarTkn + if leadingToken == nil { + if name, ok := variable.Name.(*ast.Identifier); ok { + leadingToken = name.IdentifierTkn + } + } + if leadingToken == nil { + return nil + } + + leading := leadingToken.FreeFloating + leadingToken.FreeFloating = []*token.Token{{Value: []byte(" ")}} + return leading +} + // typeText turns a resolved type into the text written into source. The trailing // space that separates the type from the variable is added by the caller. func typeText(resolved string) string { diff --git a/internal/apply/apply_test.go b/internal/apply/apply_test.go index 8d1f0fd9..1cb8fffd 100644 --- a/internal/apply/apply_test.go +++ b/internal/apply/apply_test.go @@ -90,6 +90,27 @@ func TestApply(t *testing.T) { target: "set(1); }\n}", want: "set(1); }\n}", }, + { + name: "keeps comma spacing when typing a later parameter", + target: "set(\"x\", 1); }\n}", + want: "set(\"x\", 1); }\n}", + }, + { + name: "types promoted constructor parameter with modifier", + target: "