func extractMultipleStatementsAsFunc( astFile *ast.File, fileSet *token.FileSet, stmtsToExtract []ast.Node, parentNode ast.Node, extractedFuncName string) { params := varIdentsUsedIn(stmtsToExtract) varsDeclaredWithinStmtsToExtract := varIdentsDeclaredWithin(stmtsToExtract) util.MapStringAstIdentRemoveKeys(params, namesOf(varsDeclaredWithinStmtsToExtract)) util.MapStringAstIdentRemoveKeys(params, namesOf(globalVarIdents(astFile))) allStmts := stmtsFromBlockStmt(parentNode) indexOfExtractedStmt := indexOf(stmtsToExtract[0].(ast.Stmt), *allStmts) varsUsedAfterwards := overlappingVarsIdentsUsedIn((*allStmts)[indexOfExtractedStmt+len(stmtsToExtract):], varsDeclaredWithinStmtsToExtract) newStmt := funcCallStmt(varsUsedAfterwards, extractedFuncName, params, (*allStmts)[indexOfExtractedStmt].Pos()) replaceStmtsWithFuncCallStmt(newStmt, allStmts, indexOfExtractedStmt, len(stmtsToExtract)) areaRemoved := areaRemoved(fileSet, (stmtsToExtract)[0].Pos(), (stmtsToExtract)[len(stmtsToExtract)-1].End()) lineLengths := lineLengthsFrom(fileSet) lineNum, numLinesToCut, newLineLength := replacementModifications(fileSet, (stmtsToExtract)[0].Pos(), (stmtsToExtract)[len(stmtsToExtract)-1].End(), newStmt.End(), lineLengths, areaRemoved) shiftPosesAfterPos(astFile, newStmt, (stmtsToExtract)[len(stmtsToExtract)-1].End(), newStmt.End()-stmtsToExtract[len(stmtsToExtract)-1].End()) multipleStmtFuncDecl := CopyNode(multipleStmtFuncDeclWith( extractedFuncName, fieldsFrom(params), stmtsFromNodes(stmtsToExtract), exprsFrom(varsUsedAfterwards), )).(*ast.FuncDecl) var moveOffset token.Pos RecalcPoses(multipleStmtFuncDecl, astFile.End()+2, &moveOffset, 0) astFile.Decls = append(astFile.Decls, multipleStmtFuncDecl) areaToBeAppended := insertionModificationsForStmts(astFile, multipleStmtFuncDecl, areaRemoved, exprsFrom(varsUsedAfterwards)) lineLengths = append( lineLengths[:lineNum+1], lineLengths[lineNum+1+numLinesToCut:]...) lineLengths[lineNum] = newLineLength lineLengths = append(lineLengths, areaToBeAppended...) newFileSet := token.NewFileSet() newFileSet.AddFile(fileSet.File(1).Name(), 1, int(astFile.End())) newFileSet.File(1).SetLines(ConvertLineLengthsToLineOffsets(lineLengths)) *fileSet = *newFileSet moveComments(astFile, moveOffset /*, needs a range to restict which comments to move*/) }
func extractExpressionAsFunc( astFile *ast.File, fileSet *token.FileSet, expr ast.Expr, parent ast.Node, extractedFuncName string) { params := varIdentsUsedIn([]ast.Node{expr}) util.MapStringAstIdentRemoveKeys(params, namesOf(globalVarIdents(astFile))) newExpr := CopyNode(callExprWith(extractedFuncName, params)).(ast.Expr) RecalcPoses(newExpr, expr.Pos(), nil, 0) switch typedNode := parent.(type) { case *ast.AssignStmt: for i, rhs := range typedNode.Rhs { if rhs == expr { typedNode.Rhs[i] = newExpr } } for i, lhs := range typedNode.Lhs { if lhs == expr { typedNode.Lhs[i] = newExpr } } case *ast.CallExpr: for i, arg := range typedNode.Args { if arg == expr { typedNode.Args[i] = newExpr } } case *ast.ExprStmt: typedNode.X = newExpr case *ast.ReturnStmt: for i, result := range typedNode.Results { if result == expr { typedNode.Results[i] = newExpr } } case *ast.IfStmt: if typedNode.Cond == expr { typedNode.Cond = newExpr } case *ast.CaseClause: for i, caseExpr := range typedNode.List { if caseExpr == expr { typedNode.List[i] = newExpr } } case *ast.SwitchStmt: if typedNode.Tag == expr { typedNode.Tag = newExpr } case *ast.ForStmt: if typedNode.Cond == expr { typedNode.Cond = newExpr } case *ast.RangeStmt: if typedNode.Key == expr { typedNode.Key = newExpr } else if typedNode.Value == expr { typedNode.Value = newExpr } else if typedNode.X == expr { typedNode.X = newExpr } case *ast.SendStmt: if typedNode.Chan == expr { typedNode.Chan = newExpr } else if typedNode.Value == expr { typedNode.Value = newExpr } case *ast.IncDecStmt: if typedNode.X == expr { typedNode.X = newExpr } case *ast.ValueSpec: for i, value := range typedNode.Values { if value == expr { typedNode.Values[i] = newExpr } } default: panic(fmt.Sprintf("Type %v not supported yet", reflect.TypeOf(parent))) } areaRemoved := areaRemoved(fileSet, expr.Pos(), expr.End()) lineLengths := lineLengthsFrom(fileSet) lineNum, numLinesToCut, newLineLength := replacementModifications(fileSet, expr.Pos(), expr.End(), newExpr.End(), lineLengths, areaRemoved) shiftPosesAfterPos(astFile, newExpr, expr.End(), newExpr.End()-expr.End()) singleExprStmtFuncDeclWith := CopyNode(singleExprStmtFuncDeclWith(extractedFuncName, fieldsFrom(params), expr)).(*ast.FuncDecl) var moveOffset token.Pos RecalcPoses(singleExprStmtFuncDeclWith, astFile.End()+2, &moveOffset, 0) astFile.Decls = append(astFile.Decls, singleExprStmtFuncDeclWith) areaToBeAppended := insertionModifications(astFile, singleExprStmtFuncDeclWith, areaRemoved) lineLengths = append( lineLengths[:lineNum+1], lineLengths[lineNum+1+numLinesToCut:]...) lineLengths[lineNum] = newLineLength lineLengths = append(lineLengths, areaToBeAppended...) newFileSet := token.NewFileSet() newFileSet.AddFile(fileSet.File(1).Name(), 1, int(astFile.End())) success := newFileSet.File(1).SetLines(ConvertLineLengthsToLineOffsets(lineLengths)) if !success { panic("Could not SetLines on File.") } *fileSet = *newFileSet moveComments(astFile, moveOffset /*, needs a range to restict which comments to move*/) }