hunk.code_window: 		newPluginV2 := &model.MarketplacePlugin{
   			BaseMarketplacePlugin: &model.BaseMarketplacePlugin{
   				HomepageURL: "",
   				IconData:    "",
   				DownloadURL: "",
   				Manifest:    manifest,
   			},
   			InstalledVersion: manifest.Version,
   		}
  
hunk.labels: keep keep keep keep keep replace keep keep keep
hunk.after_edit: 				Labels: []model.MarketplaceLabel{{
   					Name:        "Local",
   					Description: "This plugin is not listed in the marketplace but was installed manually",
   				}},
   				Manifest: manifest,
  
hunk.file_path: api4/plugin_test.go
hunk.type: replace
file: package printer
  
  import (
  	"bytes"
  	"fmt"
  	"sort"
  
  	"github.com/hashicorp/hcl/hcl/ast"
  	"github.com/hashicorp/hcl/hcl/token"
  )
  
  const (
  	blank    = byte(' ')
  	newline  = byte('\n')
  	tab      = byte('\t')
  	infinity = 1 << 30 // offset or line
  )
  
  var (
  	unindent = []byte("\uE123") // in the private use space
  )
  
  type printer struct {
  	cfg  Config
  	prev token.Pos
  
  	comments           []*ast.CommentGroup // may be nil, contains all comments
  	standaloneComments []*ast.CommentGroup // contains all standalone comments (not assigned to any node)
  
  	enableTrace bool
  	indentTrace int
  }
  
  type ByPosition []*ast.CommentGroup
  
  func (b ByPosition) Len() int           { return len(b) }
  func (b ByPosition) Swap(i, j int)      { b[i], b[j] = b[j], b[i] }
  func (b ByPosition) Less(i, j int) bool { return b[i].Pos().Before(b[j].Pos()) }
  
  // collectComments comments all standalone comments which are not lead or line
  // comment
  func (p *printer) collectComments(node ast.Node) {
  	// first collect all comments. This is already stored in
  	// ast.File.(comments)
  	ast.Walk(node, func(nn ast.Node) (ast.Node, bool) {
  		switch t := nn.(type) {
  		case *ast.File:
  			p.comments = t.Comments
  			return nn, false
  		}
  		return nn, true
  	})
  
  	standaloneComments := make(map[token.Pos]*ast.CommentGroup, 0)
  	for _, c := range p.comments {
  		standaloneComments[c.Pos()] = c
  	}
  
  	// next remove all lead and line comments from the overall comment map.
  	// This will give us comments which are standalone, comments which are not
  	// assigned to any kind of node.
  	ast.Walk(node, func(nn ast.Node) (ast.Node, bool) {
  		switch t := nn.(type) {
  		case *ast.LiteralType:
  			if t.LeadComment != nil {
  				for _, comment := range t.LeadComment.List {
  					if _, ok := standaloneComments[comment.Pos()]; ok {
  						delete(standaloneComments, comment.Pos())
  					}
  				}
  			}
  
  			if t.LineComment != nil {
  				for _, comment := range t.LineComment.List {
  					if _, ok := standaloneComments[comment.Pos()]; ok {
  						delete(standaloneComments, comment.Pos())
  					}
  				}
  			}
  		case *ast.ObjectItem:
  			if t.LeadComment != nil {
  				for _, comment := range t.LeadComment.List {
  					if _, ok := standaloneComments[comment.Pos()]; ok {
  						delete(standaloneComments, comment.Pos())
  					}
  				}
  			}
  
  			if t.LineComment != nil {
  				for _, comment := range t.LineComment.List {
  					if _, ok := standaloneComments[comment.Pos()]; ok {
  						delete(standaloneComments, comment.Pos())
  					}
  				}
  			}
  		}
  
  		return nn, true
  	})
  
  	for _, c := range standaloneComments {
  		p.standaloneComments = append(p.standaloneComments, c)
  	}
  
  	sort.Sort(ByPosition(p.standaloneComments))
  }
  
  // output prints creates b printable HCL output and returns it.
  func (p *printer) output(n interface{}) []byte {
  	var buf bytes.Buffer
  
  	switch t := n.(type) {
  	case *ast.File:
  		// File doesn't trace so we add the tracing here
  		defer un(trace(p, "File"))
  		return p.output(t.Node)
  	case *ast.ObjectList:
  		defer un(trace(p, "ObjectList"))
  
  		var index int
  		for {
  			// Determine the location of the next actual non-comment
  			// item. If we're at the end, the next item is at "infinity"
  			var nextItem token.Pos
  			if index != len(t.Items) {
  				nextItem = t.Items[index].Pos()
  			} else {
  				nextItem = token.Pos{Offset: infinity, Line: infinity}
  			}
  
  			// Go through the standalone comments in the file and print out
  			// the comments that we should be for this object item.
  			for _, c := range p.standaloneComments {
  				// Go through all the comments in the group. The group
  				// should be printed together, not separated by double newlines.
  				printed := false
  				newlinePrinted := false
  				for _, comment := range c.List {
  					// We only care about comments after the previous item
  					// we've printed so that comments are printed in the
  					// correct locations (between two objects for example).
  					// And before the next item.
  					if comment.Pos().After(p.prev) && comment.Pos().Before(nextItem) {
  						// if we hit the end add newlines so we can print the comment
  						// we don't do this if prev is invalid which means the
  						// beginning of the file since the first comment should
  						// be at the first line.
  						if !newlinePrinted && p.prev.IsValid() && index == len(t.Items) {
  							buf.Write([]byte{newline, newline})
  							newlinePrinted = true
  						}
  
  						// Write the actual comment.
  						buf.WriteString(comment.Text)
  						buf.WriteByte(newline)
  
  						// Set printed to true to note that we printed something
  						printed = true
  					}
  				}
  
  				// If we're not at the last item, write a new line so
  				// that there is a newline separating this comment from
  				// the next object.
  				if printed && index != len(t.Items) {
  					buf.WriteByte(newline)
  				}
  			}
  
  			if index == len(t.Items) {
  				break
  			}
  
  			buf.Write(p.output(t.Items[index]))
  			if index != len(t.Items)-1 {
  				// Always write a newline to separate us from the next item
  				buf.WriteByte(newline)
  
  				// Need to determine if we're going to separate the next item
  				// with a blank line. The logic here is simple, though there
  				// are a few conditions:
  				//
  				//   1. The next object is more than one line away anyways,
  				//      so we need an empty line.
  				//
  				//   2. The next object is not a "single line" object, so
  				//      we need an empty line.
  				//
  				//   3. This current object is not a single line object,
  				//      so we need an empty line.
  				current := t.Items[index]
  				next := t.Items[index+1]
  				if next.Pos().Line != t.Items[index].Pos().Line+1 ||
  					!p.isSingleLineObject(next) ||
  					!p.isSingleLineObject(current) {
  					buf.WriteByte(newline)
  				}
  			}
  			index++
  		}
  	case *ast.ObjectKey:
  		buf.WriteString(t.Token.Text)
  	case *ast.ObjectItem:
  		p.prev = t.Pos()
  		buf.Write(p.objectItem(t))
  	case *ast.LiteralType:
  		buf.Write(p.literalType(t))
  	case *ast.ListType:
  		buf.Write(p.list(t))
  	case *ast.ObjectType:
  		buf.Write(p.objectType(t))
  	default:
  		fmt.Printf(" unknown type: %T\n", n)
  	}
  
  	return buf.Bytes()
  }
  
  func (p *printer) literalType(lit *ast.LiteralType) []byte {
  	result := []byte(lit.Token.Text)
  	switch lit.Token.Type {
  	case token.HEREDOC:
  		// Clear the trailing newline from heredocs
  		if result[len(result)-1] == '\n' {
  			result = result[:len(result)-1]
  		}
  
  		// Poison lines 2+ so that we don't indent them
  		result = p.heredocIndent(result)
  	case token.STRING:
  		// If this is a multiline string, poison lines 2+ so we don't
  		// indent them.
  		if bytes.IndexRune(result, '\n') >= 0 {
  			result = p.heredocIndent(result)
  		}
  	}
  
  	return result
  }
  
  // objectItem returns the printable HCL form of an object item. An object type
  // starts with one/multiple keys and has a value. The value might be of any
  // type.
  func (p *printer) objectItem(o *ast.ObjectItem) []byte {
  	defer un(trace(p, fmt.Sprintf("ObjectItem: %s", o.Keys[0].Token.Text)))
  	var buf bytes.Buffer
  
  	if o.LeadComment != nil {
  		for _, comment := range o.LeadComment.List {
  			buf.WriteString(comment.Text)
  			buf.WriteByte(newline)
  		}
  	}
  
  	// If key and val are on different lines, treat line comments like lead comments.
  	if o.LineComment != nil && o.Val.Pos().Line != o.Keys[0].Pos().Line {
  		for _, comment := range o.LineComment.List {
  			buf.WriteString(comment.Text)
  			buf.WriteByte(newline)
  		}
  	}
  
  	for i, k := range o.Keys {
  		buf.WriteString(k.Token.Text)
  		buf.WriteByte(blank)
  
  		// reach end of key
  		if o.Assign.IsValid() && i == len(o.Keys)-1 && len(o.Keys) == 1 {
  			buf.WriteString("=")
  			buf.WriteByte(blank)
  		}
  	}
  
  	buf.Write(p.output(o.Val))
  
  	if o.LineComment != nil && o.Val.Pos().Line == o.Keys[0].Pos().Line {
  		buf.WriteByte(blank)
  		for _, comment := range o.LineComment.List {
  			buf.WriteString(comment.Text)
  		}
  	}
  
  	return buf.Bytes()
  }
  
  // objectType returns the printable HCL form of an object type. An object type
  // begins with a brace and ends with a brace.
  func (p *printer) objectType(o *ast.ObjectType) []byte {
  	defer un(trace(p, "ObjectType"))
  	var buf bytes.Buffer
  	buf.WriteString("{")
  
  	var index int
  	var nextItem token.Pos
  	var commented, newlinePrinted bool
  	for {
  		// Determine the location of the next actual non-comment
  		// item. If we're at the end, the next item is the closing brace
  		if index != len(o.List.Items) {
  			nextItem = o.List.Items[index].Pos()
  		} else {
  			nextItem = o.Rbrace
  		}
  
  		// Go through the standalone comments in the file and print out
  		// the comments that we should be for this object item.
  		for _, c := range p.standaloneComments {
  			printed := false
  			var lastCommentPos token.Pos
  			for _, comment := range c.List {
  				// We only care about comments after the previous item
  				// we've printed so that comments are printed in the
  				// correct locations (between two objects for example).
  				// And before the next item.
  				if comment.Pos().After(p.prev) && comment.Pos().Before(nextItem) {
  					// If there are standalone comments and the initial newline has not
  					// been printed yet, do it now.
  					if !newlinePrinted {
  						newlinePrinted = true
  						buf.WriteByte(newline)
  					}
  
  					// add newline if it's between other printed nodes
  					if index > 0 {
  						commented = true
  						buf.WriteByte(newline)
  					}
  
  					// Store this position
  					lastCommentPos = comment.Pos()
  
  					// output the comment itself
  					buf.Write(p.indent(p.heredocIndent([]byte(comment.Text))))
  
  					// Set printed to true to note that we printed something
  					printed = true
  
  					/*
  						if index != len(o.List.Items) {
  							buf.WriteByte(newline) // do not print on the end
  						}
  					*/
  				}
  			}
  
  			// Stuff to do if we had comments
  			if printed {
  				// Always write a newline
  				buf.WriteByte(newline)
  
  				// If there is another item in the object and our comment
  				// didn't hug it directly, then make sure there is a blank
  				// line separating them.
  				if nextItem != o.Rbrace && nextItem.Line != lastCommentPos.Line+1 {
  					buf.WriteByte(newline)
  				}
  			}
  		}
  
  		if index == len(o.List.Items) {
  			p.prev = o.Rbrace
  			break
  		}
  
  		// At this point we are sure that it's not a totally empty block: print
  		// the initial newline if it hasn't been printed yet by the previous
  		// block about standalone comments.
  		if !newlinePrinted {
  			buf.WriteByte(newline)
  			newlinePrinted = true
  		}
  
  		// check if we have adjacent one liner items. If yes we'll going to align
  		// the comments.
  		var aligned []*ast.ObjectItem
  		for _, item := range o.List.Items[index:] {
  			// we don't group one line lists
  			if len(o.List.Items) == 1 {
  				break
  			}
  
  			// one means a oneliner with out any lead comment
  			// two means a oneliner with lead comment
  			// anything else might be something else
  			cur := lines(string(p.objectItem(item)))
  			if cur > 2 {
  				break
  			}
  
  			curPos := item.Pos()
  
  			nextPos := token.Pos{}
  			if index != len(o.List.Items)-1 {
  				nextPos = o.List.Items[index+1].Pos()
  			}
  
  			prevPos := token.Pos{}
  			if index != 0 {
  				prevPos = o.List.Items[index-1].Pos()
  			}
  
  			// fmt.Println("DEBUG ----------------")
  			// fmt.Printf("prev = %+v prevPos: %s\n", prev, prevPos)
  			// fmt.Printf("cur = %+v curPos: %s\n", cur, curPos)
  			// fmt.Printf("next = %+v nextPos: %s\n", next, nextPos)
  
  			if curPos.Line+1 == nextPos.Line {
  				aligned = append(aligned, item)
  				index++
  				continue
  			}
  
  			if curPos.Line-1 == prevPos.Line {
  				aligned = append(aligned, item)
  				index++
  
  				// finish if we have a new line or comment next. This happens
  				// if the next item is not adjacent
  				if curPos.Line+1 != nextPos.Line {
  					break
  				}
  				continue
  			}
  
  			break
  		}
  
  		// put newlines if the items are between other non aligned items.
  		// newlines are also added if there is a standalone comment already, so
  		// check it too
  		if !commented && index != len(aligned) {
  			buf.WriteByte(newline)
  		}
  
  		if len(aligned) >= 1 {
  			p.prev = aligned[len(aligned)-1].Pos()
  
  			items := p.alignedItems(aligned)
  			buf.Write(p.indent(items))
  		} else {
  			p.prev = o.List.Items[index].Pos()
  
  			buf.Write(p.indent(p.objectItem(o.List.Items[index])))
  			index++
  		}
  
  		buf.WriteByte(newline)
  	}
  
  	buf.WriteString("}")
  	return buf.Bytes()
  }
  
  func (p *printer) alignedItems(items []*ast.ObjectItem) []byte {
  	var buf bytes.Buffer
  
  	// find the longest key and value length, needed for alignment
  	var longestKeyLen int // longest key length
  	var longestValLen int // longest value length
  	for _, item := range items {
  		key := len(item.Keys[0].Token.Text)
  		val := len(p.output(item.Val))
  
  		if key > longestKeyLen {
  			longestKeyLen = key
  		}
  
  		if val > longestValLen {
  			longestValLen = val
  		}
  	}
  
  	for i, item := range items {
  		if item.LeadComment != nil {
  			for _, comment := range item.LeadComment.List {
  				buf.WriteString(comment.Text)
  				buf.WriteByte(newline)
  			}
  		}
  
  		for i, k := range item.Keys {
  			keyLen := len(k.Token.Text)
  			buf.WriteString(k.Token.Text)
  			for i := 0; i < longestKeyLen-keyLen+1; i++ {
  				buf.WriteByte(blank)
  			}
  
  			// reach end of key
  			if i == len(item.Keys)-1 && len(item.Keys) == 1 {
  				buf.WriteString("=")
  				buf.WriteByte(blank)
  			}
  		}
  
  		val := p.output(item.Val)
  		valLen := len(val)
  		buf.Write(val)
  
  		if item.Val.Pos().Line == item.Keys[0].Pos().Line && item.LineComment != nil {
  			for i := 0; i < longestValLen-valLen+1; i++ {
  				buf.WriteByte(blank)
  			}
  
  			for _, comment := range item.LineComment.List {
  				buf.WriteString(comment.Text)
  			}
  		}
  
  		// do not print for the last item
  		if i != len(items)-1 {
  			buf.WriteByte(newline)
  		}
  	}
  
  	return buf.Bytes()
  }
  
  // list returns the printable HCL form of an list type.
  func (p *printer) list(l *ast.ListType) []byte {
  	if p.isSingleLineList(l) {
  		return p.singleLineList(l)
  	}
  
  	var buf bytes.Buffer
  	buf.WriteString("[")
  	buf.WriteByte(newline)
  
  	var longestLine int
  	for _, item := range l.List {
  		// for now we assume that the list only contains literal types
  		if lit, ok := item.(*ast.LiteralType); ok {
  			lineLen := len(lit.Token.Text)
  			if lineLen > longestLine {
  				longestLine = lineLen
  			}
  		}
  	}
  
  	haveEmptyLine := false
  	for i, item := range l.List {
  		// If we have a lead comment, then we want to write that first
  		leadComment := false
  		if lit, ok := item.(*ast.LiteralType); ok && lit.LeadComment != nil {
  			leadComment = true
  
  			// Ensure an empty line before every element with a
  			// lead comment (except the first item in a list).
  			if !haveEmptyLine && i != 0 {
  				buf.WriteByte(newline)
  			}
  
  			for _, comment := range lit.LeadComment.List {
  				buf.Write(p.indent([]byte(comment.Text)))
  				buf.WriteByte(newline)
  			}
  		}
  
  		// also indent each line
  		val := p.output(item)
  		curLen := len(val)
  		buf.Write(p.indent(val))
  
  		// if this item is a heredoc, then we output the comma on
  		// the next line. This is the only case this happens.
  		comma := []byte{','}
  		if lit, ok := item.(*ast.LiteralType); ok && lit.Token.Type == token.HEREDOC {
  			buf.WriteByte(newline)
  			comma = p.indent(comma)
  		}
  
  		buf.Write(comma)
  
  		if lit, ok := item.(*ast.LiteralType); ok && lit.LineComment != nil {
  			// if the next item doesn't have any comments, do not align
  			buf.WriteByte(blank) // align one space
  			for i := 0; i < longestLine-curLen; i++ {
  				buf.WriteByte(blank)
  			}
  
  			for _, comment := range lit.LineComment.List {
  				buf.WriteString(comment.Text)
  			}
  		}
  
  		buf.WriteByte(newline)
  
  		// Ensure an empty line after every element with a
  		// lead comment (except the first item in a list).
  		haveEmptyLine = leadComment && i != len(l.List)-1
  		if haveEmptyLine {
  			buf.WriteByte(newline)
  		}
  	}
  
  	buf.WriteString("]")
  	return buf.Bytes()
  }
  
  // isSingleLineList returns true if:
  // * they were previously formatted entirely on one line
  // * they consist entirely of literals
  // * there are either no heredoc strings or the list has exactly one element
  // * there are no line comments
  func (printer) isSingleLineList(l *ast.ListType) bool {
  	for _, item := range l.List {
  		if item.Pos().Line != l.Lbrack.Line {
  			return false
  		}
  
  		lit, ok := item.(*ast.LiteralType)
  		if !ok {
  			return false
  		}
  
  		if lit.Token.Type == token.HEREDOC && len(l.List) != 1 {
  			return false
  		}
  
  		if lit.LineComment != nil {
  			return false
  		}
  	}
  
  	return true
  }
  
  // singleLineList prints a simple single line list.
  // For a definition of "simple", see isSingleLineList above.
  func (p *printer) singleLineList(l *ast.ListType) []byte {
  	buf := &bytes.Buffer{}
  
  	buf.WriteString("[")
  	for i, item := range l.List {
  		if i != 0 {
  			buf.WriteString(", ")
  		}
  
  		// Output the item itself
  		buf.Write(p.output(item))
  
  		// The heredoc marker needs to be at the end of line.
  		if lit, ok := item.(*ast.LiteralType); ok && lit.Token.Type == token.HEREDOC {
  			buf.WriteByte(newline)
  		}
  	}
  
  	buf.WriteString("]")
  	return buf.Bytes()
  }
  
  // indent indents the lines of the given buffer for each non-empty line
  func (p *printer) indent(buf []byte) []byte {
  	var prefix []byte
  	if p.cfg.SpacesWidth != 0 {
  		for i := 0; i < p.cfg.SpacesWidth; i++ {
  			prefix = append(prefix, blank)
  		}
  	} else {
  		prefix = []byte{tab}
  	}
  
  	var res []byte
  	bol := true
  	for _, c := range buf {
  		if bol && c != '\n' {
  			res = append(res, prefix...)
  		}
  
  		res = append(res, c)
  		bol = c == '\n'
  	}
  	return res
  }
  
  // unindent removes all the indentation from the tombstoned lines
  func (p *printer) unindent(buf []byte) []byte {
  	var res []byte
  	for i := 0; i < len(buf); i++ {
  		skip := len(buf)-i <= len(unindent)
  		if !skip {
  			skip = !bytes.Equal(unindent, buf[i:i+len(unindent)])
  		}
  		if skip {
  			res = append(res, buf[i])
  			continue
  		}
  
  		// We have a marker. we have to backtrace here and clean out
  		// any whitespace ahead of our tombstone up to a \n
  		for j := len(res) - 1; j >= 0; j-- {
  			if res[j] == '\n' {
  				break
  			}
  
  			res = res[:j]
  		}
  
  		// Skip the entire unindent marker
  		i += len(unindent) - 1
  	}
  
  	return res
  }
  
  // heredocIndent marks all the 2nd and further lines as unindentable
  func (p *printer) heredocIndent(buf []byte) []byte {
  	var res []byte
  	bol := false
  	for _, c := range buf {
  		if bol && c != '\n' {
  			res = append(res, unindent...)
  		}
  		res = append(res, c)
  		bol = c == '\n'
  	}
  	return res
  }
  
  // isSingleLineObject tells whether the given object item is a single
  // line object such as "obj {}".
  //
  // A single line object:
  //
  //   * has no lead comments (hence multi-line)
  //   * has no assignment
  //   * has no values in the stanza (within {})
  //
  func (p *printer) isSingleLineObject(val *ast.ObjectItem) bool {
  	// If there is a lead comment, can't be one line
  	if val.LeadComment != nil {
  		return false
  	}
  
  	// If there is assignment, we always break by line
  	if val.Assign.IsValid() {
  		return false
  	}
  
  	// If it isn't an object type, then its not a single line object
  	ot, ok := val.Val.(*ast.ObjectType)
  	if !ok {
  		return false
  	}
  
  	// If the object has no items, it is single line!
  	return len(ot.List.Items) == 0
  }
  
  func lines(txt string) int {
  	endline := 1
  	for i := 0; i < len(txt); i++ {
  		if txt[i] == '\n' {
  			endline++
  		}
  	}
  	return endline
  }
  
  // ----------------------------------------------------------------------------
  // Tracing support
  
  func (p *printer) printTrace(a ...interface{}) {
  	if !p.enableTrace {
  		return
  	}
  
  	const dots = ". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . "
  	const n = len(dots)
  	i := 2 * p.indentTrace
  	for i > n {
  		fmt.Print(dots)
  		i -= n
  	}
  	// i <= n
  	fmt.Print(dots[0:i])
  	fmt.Println(a...)
  }
  
  func trace(p *printer, msg string) *printer {
  	p.printTrace(msg, "(")
  	p.indentTrace++
  	return p
  }
  
  // Usage pattern: defer un(trace(p, "..."))
  func un(p *printer) {
  	p.indentTrace--
  	p.printTrace(")")
  }
  