@@ -1034,170 +1034,190 @@ func (f *File) SetRequire(req []*Require) {
1034
1034
1035
1035
// SetRequireSeparateIndirect updates the requirements of f to contain the given
1036
1036
// requirements. Comment contents (except for 'indirect' markings) are retained
1037
- // from the first existing requirement for each module path, and block structure
1038
- // is maintained as long as the indirect markings match.
1037
+ // from the first existing requirement for each module path. Like SetRequire,
1038
+ // SetRequireSeparateIndirect adds requirements for new paths in req,
1039
+ // updates the version and "// indirect" comment on existing requirements,
1040
+ // and deletes requirements on paths not in req. Existing duplicate requirements
1041
+ // are deleted.
1039
1042
//
1040
- // Any requirements on paths not already present in the file are added. Direct
1041
- // requirements are added to the last block containing *any* other direct
1042
- // requirement. Indirect requirements are added to the last block containing
1043
- // *only* other indirect requirements. If no suitable block exists, a new one is
1044
- // added, with the last block containing a direct dependency (if any)
1045
- // immediately before the first block containing only indirect dependencies .
1043
+ // As its name suggests, SetRequireSeparateIndirect puts direct and indirect
1044
+ // requirements into two separate blocks, one containing only direct
1045
+ // requirements, and the other containing only indirect requirements.
1046
+ // SetRequireSeparateIndirect may move requirements between these two blocks
1047
+ // when their indirect markings change. However, SetRequireSeparateIndirect
1048
+ // won't move requirements from other blocks, especially blocks with comments .
1046
1049
//
1047
- // The Syntax field is ignored for requirements in the given blocks.
1050
+ // If the file initially has one uncommented block of requirements,
1051
+ // SetRequireSeparateIndirect will split it into a direct-only and indirect-only
1052
+ // block. This aids in the transition to separate blocks.
1048
1053
func (f * File ) SetRequireSeparateIndirect (req []* Require ) {
1049
- type modKey struct {
1050
- path string
1051
- indirect bool
1052
- }
1053
- need := make (map [modKey ]string )
1054
- for _ , r := range req {
1055
- need [modKey {r .Mod .Path , r .Indirect }] = r .Mod .Version
1054
+ // hasComments returns whether a line or block has comments
1055
+ // other than "indirect".
1056
+ hasComments := func (c Comments ) bool {
1057
+ return len (c .Before ) > 0 || len (c .After ) > 0 || len (c .Suffix ) > 1 ||
1058
+ (len (c .Suffix ) == 1 &&
1059
+ strings .TrimSpace (strings .TrimPrefix (c .Suffix [0 ].Token , string (slashSlash ))) != "indirect" )
1056
1060
}
1057
1061
1058
- comments := make (map [string ]Comments )
1059
- for _ , r := range f .Require {
1060
- v , ok := need [modKey {r .Mod .Path , r .Indirect }]
1061
- if ! ok {
1062
- if _ , ok := need [modKey {r .Mod .Path , ! r .Indirect }]; ok {
1063
- if _ , dup := comments [r .Mod .Path ]; ! dup {
1064
- comments [r .Mod .Path ] = r .Syntax .Comments
1065
- }
1062
+ // moveReq adds r to block. If r was in another block, moveReq deletes
1063
+ // it from that block and transfers its comments.
1064
+ moveReq := func (r * Require , block * LineBlock ) {
1065
+ var line * Line
1066
+ if r .Syntax == nil {
1067
+ line = & Line {Token : []string {AutoQuote (r .Mod .Path ), r .Mod .Version }}
1068
+ r .Syntax = line
1069
+ if r .Indirect {
1070
+ r .setIndirect (true )
1066
1071
}
1067
- r .markRemoved ()
1068
- continue
1072
+ } else {
1073
+ line = new (Line )
1074
+ * line = * r .Syntax
1075
+ if ! line .InBlock && len (line .Token ) > 0 && line .Token [0 ] == "require" {
1076
+ line .Token = line .Token [1 :]
1077
+ }
1078
+ r .Syntax .Token = nil // Cleanup will delete the old line.
1079
+ r .Syntax = line
1069
1080
}
1070
- r . setVersion ( v )
1071
- delete ( need , modKey { r . Mod . Path , r . Indirect } )
1081
+ line . InBlock = true
1082
+ block . Line = append ( block . Line , line )
1072
1083
}
1073
1084
1085
+ // Examine existing require lines and blocks.
1074
1086
var (
1075
- lastDirectOrMixedBlock Expr
1076
- firstIndirectOnlyBlock Expr
1077
- lastIndirectOnlyBlock Expr
1087
+ // We may insert new requirements into the last uncommented
1088
+ // direct-only and indirect-only blocks. We may also move requirements
1089
+ // to the opposite block if their indirect markings change.
1090
+ lastDirectBlock , lastIndirectBlock * LineBlock
1091
+ lastDirectBlockIndex = - 1
1092
+ lastIndirectBlockIndex = - 1
1093
+
1094
+ // If there are no direct-only or indirect-only blocks, a new block may
1095
+ // be inserted after the last require line or block.
1096
+ lastRequireIndex = - 1
1097
+
1098
+ // If there's only one require line or block, and it's uncommented,
1099
+ // we'll move its requirements to the direct-only or indirect-only blocks.
1100
+ requireLineOrBlockCount = 0
1101
+
1102
+ // Track the block each requirement belongs to (if any) so we can
1103
+ // move them later.
1104
+ lineToBlock = make (map [* Line ]* LineBlock )
1078
1105
)
1079
- for _ , stmt := range f .Syntax .Stmt {
1106
+ for i , stmt := range f .Syntax .Stmt {
1080
1107
switch stmt := stmt .(type ) {
1081
1108
case * Line :
1082
1109
if len (stmt .Token ) == 0 || stmt .Token [0 ] != "require" {
1083
1110
continue
1084
1111
}
1085
- if isIndirect (stmt ) {
1086
- lastIndirectOnlyBlock = stmt
1087
- } else {
1088
- lastDirectOrMixedBlock = stmt
1089
- }
1112
+ lastRequireIndex = i
1113
+ requireLineOrBlockCount ++
1114
+
1090
1115
case * LineBlock :
1091
1116
if len (stmt .Token ) == 0 || stmt .Token [0 ] != "require" {
1092
1117
continue
1093
1118
}
1094
- indirectOnly := true
1119
+ lastRequireIndex = i
1120
+ requireLineOrBlockCount ++
1121
+ allDirect := len (stmt .Line ) > 0 && ! hasComments (stmt .Comments )
1122
+ allIndirect := len (stmt .Line ) > 0 && ! hasComments (stmt .Comments )
1095
1123
for _ , line := range stmt .Line {
1096
- if len (line .Token ) == 0 {
1097
- continue
1098
- }
1099
- if ! isIndirect (line ) {
1100
- indirectOnly = false
1101
- break
1124
+ lineToBlock [line ] = stmt
1125
+ if hasComments (line .Comments ) {
1126
+ allDirect = false
1127
+ allIndirect = false
1128
+ } else if isIndirect (line ) {
1129
+ allDirect = false
1130
+ } else {
1131
+ allIndirect = false
1102
1132
}
1103
1133
}
1104
- if indirectOnly {
1105
- lastIndirectOnlyBlock = stmt
1106
- if firstIndirectOnlyBlock == nil {
1107
- firstIndirectOnlyBlock = stmt
1108
- }
1109
- } else {
1110
- lastDirectOrMixedBlock = stmt
1134
+ if allDirect {
1135
+ lastDirectBlock = stmt
1136
+ lastDirectBlockIndex = i
1137
+ }
1138
+ if allIndirect {
1139
+ lastIndirectBlock = stmt
1140
+ lastIndirectBlockIndex = i
1111
1141
}
1112
1142
}
1113
1143
}
1114
1144
1115
- isOrContainsStmt := func (stmt Expr , target Expr ) bool {
1116
- if stmt == target {
1117
- return true
1118
- }
1119
- if stmt , ok := stmt .(* LineBlock ); ok {
1120
- if target , ok := target .(* Line ); ok {
1121
- for _ , line := range stmt .Line {
1122
- if line == target {
1123
- return true
1124
- }
1125
- }
1126
- }
1145
+ oneFlatUncommentedBlock := requireLineOrBlockCount == 1 &&
1146
+ ! hasComments (* f .Syntax .Stmt [lastRequireIndex ].Comment ())
1147
+
1148
+ // Create direct and indirect blocks if needed. It's okay if they're empty.
1149
+ // Cleanup will remove them and convert one-line blocks to lines.
1150
+ if lastDirectBlock == nil {
1151
+ lastDirectBlock = & LineBlock {Token : []string {"require" }}
1152
+ i := len (f .Syntax .Stmt )
1153
+ if lastIndirectBlockIndex >= 0 {
1154
+ i = lastIndirectBlockIndex
1155
+ lastIndirectBlockIndex ++
1156
+ } else if lastRequireIndex >= 0 {
1157
+ i = lastRequireIndex + 1
1127
1158
}
1128
- return false
1159
+ f .Syntax .Stmt = append (f .Syntax .Stmt , nil )
1160
+ copy (f .Syntax .Stmt [i + 1 :], f .Syntax .Stmt [i :])
1161
+ f .Syntax .Stmt [i ] = lastDirectBlock
1162
+ lastDirectBlockIndex = i
1129
1163
}
1130
1164
1131
- addRequire := func (path , vers string , indirect bool , comments Comments ) {
1132
- var line * Line
1133
- if indirect {
1134
- if lastIndirectOnlyBlock != nil {
1135
- line = f .Syntax .addLine (lastIndirectOnlyBlock , "require" , path , vers )
1136
- } else {
1137
- // Add a new require block after the last direct-only or mixed "require"
1138
- // block (if any).
1139
- //
1140
- // (f.Syntax.addLine would add the line to an existing "require" block if
1141
- // present, but here the existing "require" blocks are all direct-only, so
1142
- // we know we need to add a new block instead.)
1143
- line = & Line {Token : []string {"require" , path , vers }}
1144
- lastIndirectOnlyBlock = line
1145
- firstIndirectOnlyBlock = line // only block implies first block
1146
- if lastDirectOrMixedBlock == nil {
1147
- f .Syntax .Stmt = append (f .Syntax .Stmt , line )
1148
- } else {
1149
- for i , stmt := range f .Syntax .Stmt {
1150
- if isOrContainsStmt (stmt , lastDirectOrMixedBlock ) {
1151
- f .Syntax .Stmt = append (f .Syntax .Stmt , nil ) // increase size
1152
- copy (f .Syntax .Stmt [i + 2 :], f .Syntax .Stmt [i + 1 :]) // shuffle elements up
1153
- f .Syntax .Stmt [i + 1 ] = line
1154
- break
1155
- }
1156
- }
1157
- }
1158
- }
1159
- } else {
1160
- if lastDirectOrMixedBlock != nil {
1161
- line = f .Syntax .addLine (lastDirectOrMixedBlock , "require" , path , vers )
1162
- } else {
1163
- // Add a new require block before the first indirect block (if any).
1164
- //
1165
- // That way if the file initially contains only indirect lines,
1166
- // the direct lines still appear before it: we preserve existing
1167
- // structure, but only to the extent that that structure already
1168
- // reflects the direct/indirect split.
1169
- line = & Line {Token : []string {"require" , path , vers }}
1170
- lastDirectOrMixedBlock = line
1171
- if firstIndirectOnlyBlock == nil {
1172
- f .Syntax .Stmt = append (f .Syntax .Stmt , line )
1173
- } else {
1174
- for i , stmt := range f .Syntax .Stmt {
1175
- if isOrContainsStmt (stmt , firstIndirectOnlyBlock ) {
1176
- f .Syntax .Stmt = append (f .Syntax .Stmt , nil ) // increase size
1177
- copy (f .Syntax .Stmt [i + 1 :], f .Syntax .Stmt [i :]) // shuffle elements up
1178
- f .Syntax .Stmt [i ] = line
1179
- break
1180
- }
1181
- }
1182
- }
1183
- }
1165
+ if lastIndirectBlock == nil {
1166
+ lastIndirectBlock = & LineBlock {Token : []string {"require" }}
1167
+ i := len (f .Syntax .Stmt )
1168
+ if lastDirectBlockIndex >= 0 {
1169
+ i = lastDirectBlockIndex + 1
1184
1170
}
1171
+ f .Syntax .Stmt = append (f .Syntax .Stmt , nil )
1172
+ copy (f .Syntax .Stmt [i + 1 :], f .Syntax .Stmt [i :])
1173
+ f .Syntax .Stmt [i ] = lastIndirectBlock
1174
+ lastIndirectBlockIndex = i
1175
+ }
1185
1176
1186
- line .Comments .Before = commentsAdd (line .Comments .Before , comments .Before )
1187
- line .Comments .Suffix = commentsAdd (line .Comments .Suffix , comments .Suffix )
1188
-
1189
- r := & Require {
1190
- Mod : module.Version {Path : path , Version : vers },
1191
- Indirect : indirect ,
1192
- Syntax : line ,
1177
+ // Delete requirements we don't want anymore.
1178
+ // Update versions and indirect comments on requirements we want to keep.
1179
+ // If a requirement is in last{Direct,Indirect}Block with the wrong
1180
+ // indirect marking after this, or if the requirement is in an single
1181
+ // uncommented mixed block (oneFlatUncommentedBlock), move it to the
1182
+ // correct block.
1183
+ //
1184
+ // Some blocks may be empty after this. Cleanup will remove them.
1185
+ need := make (map [string ]* Require )
1186
+ for _ , r := range req {
1187
+ need [r .Mod .Path ] = r
1188
+ }
1189
+ have := make (map [string ]* Require )
1190
+ for _ , r := range f .Require {
1191
+ path := r .Mod .Path
1192
+ if need [path ] == nil || have [path ] != nil {
1193
+ // Requirement not needed, or duplicate requirement. Delete.
1194
+ r .markRemoved ()
1195
+ continue
1196
+ }
1197
+ have [r .Mod .Path ] = r
1198
+ r .setVersion (need [path ].Mod .Version )
1199
+ r .setIndirect (need [path ].Indirect )
1200
+ if need [path ].Indirect &&
1201
+ (oneFlatUncommentedBlock || lineToBlock [r .Syntax ] == lastDirectBlock ) {
1202
+ moveReq (r , lastIndirectBlock )
1203
+ } else if ! need [path ].Indirect &&
1204
+ (oneFlatUncommentedBlock || lineToBlock [r .Syntax ] == lastIndirectBlock ) {
1205
+ moveReq (r , lastDirectBlock )
1193
1206
}
1194
- r .setIndirect (indirect )
1195
- f .Require = append (f .Require , r )
1196
1207
}
1197
1208
1198
- for k , vers := range need {
1199
- addRequire (k .path , vers , k .indirect , comments [k .path ])
1209
+ // Add new requirements.
1210
+ for path , r := range need {
1211
+ if have [path ] == nil {
1212
+ if r .Indirect {
1213
+ moveReq (r , lastIndirectBlock )
1214
+ } else {
1215
+ moveReq (r , lastDirectBlock )
1216
+ }
1217
+ f .Require = append (f .Require , r )
1218
+ }
1200
1219
}
1220
+
1201
1221
f .SortBlocks ()
1202
1222
}
1203
1223
0 commit comments