@@ -954,6 +954,175 @@ func (f *File) SetRequire(req []*Require) {
954
954
f .SortBlocks ()
955
955
}
956
956
957
+ // SetRequireSeparateIndirect updates the requirements of f to contain the given
958
+ // requirements. Comment contents (except for 'indirect' markings) are retained
959
+ // from the first existing requirement for each module path, and block structure
960
+ // is maintained as long as the indirect markings match.
961
+ //
962
+ // Any requirements on paths not already present in the file are added. Direct
963
+ // requirements are added to the last block containing *any* other direct
964
+ // requirement. Indirect requirements are added to the last block containing
965
+ // *only* other indirect requirements. If no suitable block exists, a new one is
966
+ // added, with the last block containing a direct dependency (if any)
967
+ // immediately before the first block containing only indirect dependencies.
968
+ //
969
+ // The Syntax field is ignored for requirements in the given blocks.
970
+ func (f * File ) SetRequireSeparateIndirect (req []* Require ) {
971
+ type modKey struct {
972
+ path string
973
+ indirect bool
974
+ }
975
+ need := make (map [modKey ]string )
976
+ for _ , r := range req {
977
+ need [modKey {r .Mod .Path , r .Indirect }] = r .Mod .Version
978
+ }
979
+
980
+ comments := make (map [string ]Comments )
981
+ for _ , r := range f .Require {
982
+ v , ok := need [modKey {r .Mod .Path , r .Indirect }]
983
+ if ! ok {
984
+ if _ , ok := need [modKey {r .Mod .Path , ! r .Indirect }]; ok {
985
+ if _ , dup := comments [r .Mod .Path ]; ! dup {
986
+ comments [r .Mod .Path ] = r .Syntax .Comments
987
+ }
988
+ }
989
+ r .markRemoved ()
990
+ continue
991
+ }
992
+ r .setVersion (v )
993
+ delete (need , modKey {r .Mod .Path , r .Indirect })
994
+ }
995
+
996
+ var (
997
+ lastDirectOrMixedBlock Expr
998
+ firstIndirectOnlyBlock Expr
999
+ lastIndirectOnlyBlock Expr
1000
+ )
1001
+ for _ , stmt := range f .Syntax .Stmt {
1002
+ switch stmt := stmt .(type ) {
1003
+ case * Line :
1004
+ if len (stmt .Token ) == 0 || stmt .Token [0 ] != "require" {
1005
+ continue
1006
+ }
1007
+ if isIndirect (stmt ) {
1008
+ lastIndirectOnlyBlock = stmt
1009
+ } else {
1010
+ lastDirectOrMixedBlock = stmt
1011
+ }
1012
+ case * LineBlock :
1013
+ if len (stmt .Token ) == 0 || stmt .Token [0 ] != "require" {
1014
+ continue
1015
+ }
1016
+ indirectOnly := true
1017
+ for _ , line := range stmt .Line {
1018
+ if len (line .Token ) == 0 {
1019
+ continue
1020
+ }
1021
+ if ! isIndirect (line ) {
1022
+ indirectOnly = false
1023
+ break
1024
+ }
1025
+ }
1026
+ if indirectOnly {
1027
+ lastIndirectOnlyBlock = stmt
1028
+ if firstIndirectOnlyBlock == nil {
1029
+ firstIndirectOnlyBlock = stmt
1030
+ }
1031
+ } else {
1032
+ lastDirectOrMixedBlock = stmt
1033
+ }
1034
+ }
1035
+ }
1036
+
1037
+ isOrContainsStmt := func (stmt Expr , target Expr ) bool {
1038
+ if stmt == target {
1039
+ return true
1040
+ }
1041
+ if stmt , ok := stmt .(* LineBlock ); ok {
1042
+ if target , ok := target .(* Line ); ok {
1043
+ for _ , line := range stmt .Line {
1044
+ if line == target {
1045
+ return true
1046
+ }
1047
+ }
1048
+ }
1049
+ }
1050
+ return false
1051
+ }
1052
+
1053
+ addRequire := func (path , vers string , indirect bool , comments Comments ) {
1054
+ var line * Line
1055
+ if indirect {
1056
+ if lastIndirectOnlyBlock != nil {
1057
+ line = f .Syntax .addLine (lastIndirectOnlyBlock , "require" , path , vers )
1058
+ } else {
1059
+ // Add a new require block after the last direct-only or mixed "require"
1060
+ // block (if any).
1061
+ //
1062
+ // (f.Syntax.addLine would add the line to an existing "require" block if
1063
+ // present, but here the existing "require" blocks are all direct-only, so
1064
+ // we know we need to add a new block instead.)
1065
+ line = & Line {Token : []string {"require" , path , vers }}
1066
+ lastIndirectOnlyBlock = line
1067
+ firstIndirectOnlyBlock = line // only block implies first block
1068
+ if lastDirectOrMixedBlock == nil {
1069
+ f .Syntax .Stmt = append (f .Syntax .Stmt , line )
1070
+ } else {
1071
+ for i , stmt := range f .Syntax .Stmt {
1072
+ if isOrContainsStmt (stmt , lastDirectOrMixedBlock ) {
1073
+ f .Syntax .Stmt = append (f .Syntax .Stmt , nil ) // increase size
1074
+ copy (f .Syntax .Stmt [i + 2 :], f .Syntax .Stmt [i + 1 :]) // shuffle elements up
1075
+ f .Syntax .Stmt [i + 1 ] = line
1076
+ break
1077
+ }
1078
+ }
1079
+ }
1080
+ }
1081
+ } else {
1082
+ if lastDirectOrMixedBlock != nil {
1083
+ line = f .Syntax .addLine (lastDirectOrMixedBlock , "require" , path , vers )
1084
+ } else {
1085
+ // Add a new require block before the first indirect block (if any).
1086
+ //
1087
+ // That way if the file initially contains only indirect lines,
1088
+ // the direct lines still appear before it: we preserve existing
1089
+ // structure, but only to the extent that that structure already
1090
+ // reflects the direct/indirect split.
1091
+ line = & Line {Token : []string {"require" , path , vers }}
1092
+ lastDirectOrMixedBlock = line
1093
+ if firstIndirectOnlyBlock == nil {
1094
+ f .Syntax .Stmt = append (f .Syntax .Stmt , line )
1095
+ } else {
1096
+ for i , stmt := range f .Syntax .Stmt {
1097
+ if isOrContainsStmt (stmt , firstIndirectOnlyBlock ) {
1098
+ f .Syntax .Stmt = append (f .Syntax .Stmt , nil ) // increase size
1099
+ copy (f .Syntax .Stmt [i + 1 :], f .Syntax .Stmt [i :]) // shuffle elements up
1100
+ f .Syntax .Stmt [i ] = line
1101
+ break
1102
+ }
1103
+ }
1104
+ }
1105
+ }
1106
+ }
1107
+
1108
+ line .Comments .Before = commentsAdd (line .Comments .Before , comments .Before )
1109
+ line .Comments .Suffix = commentsAdd (line .Comments .Suffix , comments .Suffix )
1110
+
1111
+ r := & Require {
1112
+ Mod : module.Version {Path : path , Version : vers },
1113
+ Indirect : indirect ,
1114
+ Syntax : line ,
1115
+ }
1116
+ r .setIndirect (indirect )
1117
+ f .Require = append (f .Require , r )
1118
+ }
1119
+
1120
+ for k , vers := range need {
1121
+ addRequire (k .path , vers , k .indirect , comments [k .path ])
1122
+ }
1123
+ f .SortBlocks ()
1124
+ }
1125
+
957
1126
func (f * File ) DropRequire (path string ) error {
958
1127
for _ , r := range f .Require {
959
1128
if r .Mod .Path == path {
0 commit comments