func Rename(filename string, line int, column int, newName string) (ok bool, err *errors.GoRefactorError) { if ok, err = CheckRenameParameters(filename, line, column, newName); !ok { return } programTree := parseProgram(filename) var sym st.Symbol if sym, err = programTree.FindSymbolByPosition(filename, line, column); err == nil { if _, ok := sym.(*st.PointerTypeSymbol); ok { panic("find by position returned pointer type!!!") } if st.IsPredeclaredIdentifier(sym.Name()) { return false, errors.UnrenamableIdentifierError(sym.Name(), " It's a basic language symbol") } if sym.PackageFrom().IsGoPackage { return false, errors.UnrenamableIdentifierError(sym.Name(), " It's a symbol,imported from go library") } if unicode.IsUpper(int(sym.Name()[0])) && !unicode.IsUpper(int(newName[0])) || unicode.IsLower(int(sym.Name()[0])) && !unicode.IsLower(int(newName[0])) { return false, &errors.GoRefactorError{ErrorType: "Can't rename identifier", Message: "can't change access modifier. Changing first letter's case can cause errors."} } if _, ok := sym.Scope().LookUp(newName, filename); ok { return false, errors.IdentifierAlreadyExistsError(newName) } if meth, ok := sym.(*st.FunctionSymbol); ok { if meth.IsInterfaceMethod { return false, errors.UnrenamableIdentifierError(sym.Name(), " It's an interface method") } } if ps, ok := sym.(*st.PackageSymbol); ok { pack, file := programTree.FindPackageAndFileByFilename(filename) impDecl := findImportDecl(pack, file, ps) if impDecl == nil { panic("couldn't find import decl") } if impDecl.Name == nil || impDecl.Name.Name == "" { impDecl.Name = ast.NewIdent(newName) } } fnames, fsets, files, err := renameSymbol(sym, newName, programTree) if err == nil { for i, f := range fnames { programTree.SaveFileExplicit(f, fsets[i], files[i]) } } return err == nil, err } else { return false, err } panic("unreachable code") }
func extractInterface(programTree *program.Program, filename string, line int, column int, interfaceName string) (bool, *errors.GoRefactorError) { if ok, err := CheckExtractInterfaceParameters(filename, line, column, interfaceName); !ok { return false, err } pack, file := programTree.FindPackageAndFileByFilename(filename) if pack == nil { return false, errors.ArgumentError("filename", "Program packages don't contain file '"+filename+"'") } fset := pack.FileSet mod := 20000 fset, file = printerUtil.ReparseFile(file, filename, mod, programTree.IdentMap) tokFile := printerUtil.GetFileFromFileSet(fset, filename) lines := printerUtil.GetLines(tokFile) tokFile.SetLines(lines[:len(lines)-(mod)]) if _, ok := pack.Symbols.LookUp(interfaceName, filename); ok { return false, errors.IdentifierAlreadyExistsError(interfaceName) } field, nameNum, fdecl, ok := getSelectedArgument(file, token.Position{filename, 0, line, column}, fset) if !ok { return false, &errors.GoRefactorError{ErrorType: "extract interface error", Message: "couldn't find function argument to extract interface"} } if field.Names == nil { return false, &errors.GoRefactorError{ErrorType: "extract interface error", Message: "argument has no name, so empty interface"} } varS, ok := programTree.IdentMap.GetSymbol(field.Names[nameNum]).(*st.VariableSymbol) if !ok { panic("symbol supposed to be a variable, but it's not") } meths, errs := getUsedMethods(programTree, fset, fdecl, varS) if len(errs) > 0 { println("some errors don't allow to extract interface:") for e, _ := range errs { println(e.String()) } return false, &errors.GoRefactorError{ErrorType: "extract interface error", Message: "some errors occured, stopped extracting"} } methList, i := make([]*ast.Field, len(meths)), 0 for m, _ := range meths { methList[i] = &ast.Field{nil, []*ast.Ident{ast.NewIdent(m.Name())}, m.FunctionType.ToAstExpr(pack, filename), nil, nil} i++ } interfaceMethods := &ast.FieldList{token.NoPos, methList, token.NoPos} interfaceType := &ast.InterfaceType{token.NoPos, interfaceMethods, false} interfaceDecl := &ast.TypeSpec{nil, ast.NewIdent(interfaceName), interfaceType, nil} genDecl := &ast.GenDecl{nil, token.NoPos, token.TYPE, token.NoPos, []ast.Spec{interfaceDecl}, token.NoPos} fieldNum := 0 for i := 0; i < len(fdecl.Type.Params.List); i++ { if field == fdecl.Type.Params.List[i] { fieldNum = i break } } oldFTypeLen := int(fdecl.Type.End() - fdecl.Type.Pos()) var lField, mField, rField *ast.Field mField = &ast.Field{nil, []*ast.Ident{field.Names[nameNum]}, ast.NewIdent(interfaceName), nil, nil} if nameNum > 0 { lField = &ast.Field{nil, field.Names[:nameNum], field.Type, nil, nil} } if nameNum < len(field.Names)-1 { rField = &ast.Field{nil, field.Names[(nameNum + 1):], field.Type, nil, nil} } newList := make([]*ast.Field, fieldNum) copy(newList, fdecl.Type.Params.List[:fieldNum]) if lField != nil { newList = append(newList, lField) } newList = append(newList, mField) if rField != nil { newList = append(newList, rField) } newList = append(newList, fdecl.Type.Params.List[(fieldNum+1):]...) fdecl.Type.Params.List = newList newFTypeLen, _ := utils.GetNodeLength(fdecl.Type) newFTypeLen += len(fdecl.Name.Name) + 1 fmt.Printf("old %d new %d\n", oldFTypeLen, newFTypeLen) allMod := newFTypeLen - oldFTypeLen if allMod != 0 { printerUtil.FixPositions(fdecl.Type.End(), allMod, file, true) lines = printerUtil.GetLines(tokFile) fmt.Printf("before last (mod = %d) : %v\n", allMod, lines) for i, offset := range lines { if offset > tokFile.Offset(fdecl.Type.Pos()) { for j := i; j < len(lines); j++ { lines[j] += allMod } break } } fmt.Printf("after last (mod = %d) : %v\n", allMod, lines) if !tokFile.SetLines(lines) { panic("couldn't set lines for file " + tokFile.Name()) } } file.Decls = append(file.Decls, genDecl) programTree.SaveFileExplicit(filename, fset, file) return true, nil }