// Copyright 2014 Google Inc. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package parser import ( "sort" "text/scanner" ) func SortLists(file *File) { for _, def := range file.Defs { if assignment, ok := def.(*Assignment); ok { sortListsInValue(assignment.Value, file) } else if module, ok := def.(*Module); ok { for _, prop := range module.Properties { sortListsInValue(prop.Value, file) } } } sort.Sort(commentsByOffset(file.Comments)) } func SortList(file *File, value Value) { for i := 0; i < len(value.ListValue); i++ { // Find a set of values on contiguous lines line := value.ListValue[i].Pos.Line var j int for j = i + 1; j < len(value.ListValue); j++ { if value.ListValue[j].Pos.Line > line+1 { break } line = value.ListValue[j].Pos.Line } nextPos := value.EndPos if j < len(value.ListValue) { nextPos = value.ListValue[j].Pos } sortSubList(value.ListValue[i:j], nextPos, file) i = j - 1 } } func ListIsSorted(value Value) bool { for i := 0; i < len(value.ListValue); i++ { // Find a set of values on contiguous lines line := value.ListValue[i].Pos.Line var j int for j = i + 1; j < len(value.ListValue); j++ { if value.ListValue[j].Pos.Line > line+1 { break } line = value.ListValue[j].Pos.Line } if !subListIsSorted(value.ListValue[i:j]) { return false } i = j - 1 } return true } func sortListsInValue(value Value, file *File) { if value.Variable != "" { return } if value.Expression != nil { sortListsInValue(value.Expression.Args[0], file) sortListsInValue(value.Expression.Args[1], file) return } if value.Type == Map { for _, p := range value.MapValue { sortListsInValue(p.Value, file) } return } else if value.Type != List { return } SortList(file, value) } func sortSubList(values []Value, nextPos scanner.Position, file *File) { l := make(elemList, len(values)) for i, v := range values { if v.Type != String { panic("list contains non-string element") } n := nextPos if i < len(values)-1 { n = values[i+1].Pos } l[i] = elem{v.StringValue, i, v.Pos, n} } sort.Sort(l) copyValues := append([]Value{}, values...) copyComments := append([]Comment{}, file.Comments...) curPos := values[0].Pos for i, e := range l { values[i] = copyValues[e.i] values[i].Pos = curPos for j, c := range copyComments { if c.Pos.Offset > e.pos.Offset && c.Pos.Offset < e.nextPos.Offset { file.Comments[j].Pos.Line = curPos.Line file.Comments[j].Pos.Offset += values[i].Pos.Offset - e.pos.Offset } } curPos.Offset += e.nextPos.Offset - e.pos.Offset curPos.Line++ } } func subListIsSorted(values []Value) bool { prev := "" for _, v := range values { if v.Type != String { panic("list contains non-string element") } if prev > v.StringValue { return false } prev = v.StringValue } return true } type elem struct { s string i int pos scanner.Position nextPos scanner.Position } type elemList []elem func (l elemList) Len() int { return len(l) } func (l elemList) Swap(i, j int) { l[i], l[j] = l[j], l[i] } func (l elemList) Less(i, j int) bool { return l[i].s < l[j].s } type commentsByOffset []Comment func (l commentsByOffset) Len() int { return len(l) } func (l commentsByOffset) Less(i, j int) bool { return l[i].Pos.Offset < l[j].Pos.Offset } func (l commentsByOffset) Swap(i, j int) { l[i], l[j] = l[j], l[i] }