func computeSplitRoot(fs map[string]bool) string {
	if len(fs) == 0 {
		return ""
	}
	pkgPath := func(fp string) []string {
		return strings.Split(filepath.Dir(fp), "/")
	}
	var root []string
	for f := range fs {
		if root == nil {
			root = pkgPath(f)
			continue
		}
		next := pkgPath(f)
		if len(root) > len(next) {
			root = root[:len(next)]
		} else {
			next = next[:len(root)]
		}
		var j int
		for j < len(root) {
			if root[j] != next[j] {
				break
			}
			j++
		}
		root = root[:j]
	}
	return filepath.Join(root...)
}
computeSplitRoot will find the longest common prefix path of the supplied set of paths. Paths
must be filepaths and not directory paths as the last element of each path is stripped of before
compute the prefix.
package chopper
import (
	"bytes"
	"fmt"
	"go/ast"
	"go/parser"
	"go/printer"
	"io/ioutil"
	"os"
	"path/filepath"
	"regexp"
	"strings"
	"go.uber.org/zap"
	"github.com/modularise/modularise/cmd/config"
	"github.com/modularise/modularise/internal/filecache"
)
func CleaveSplits(log *zap.Logger, fc filecache.FileCache, sp *config.Splits) error {
	for _, s := range sp.Splits {
		s.Root = computeSplitRoot(s.Files)
		log.Debug("Computed root.", zap.String("split", s.Name), zap.String("root", s.Root), zap.Any("files", s.Files))
		if len(s.Residuals) > 0 {
			f := filepath.Join(s.Root, "modularise_dummy.go")
			s.ResidualFiles[f] = true
			s.ResidualsRoot = computeSplitRoot(s.ResidualFiles)
			log.Debug("Computed residuals root.", zap.String("split", s.Name), zap.String("root", s.ResidualsRoot), zap.Any("files", s.ResidualFiles))
			delete(s.ResidualFiles, f)
		}
	}
	for _, s := range sp.Splits {
		c := cleaver{log: log.With(zap.String("split", s.Name)), fc: fc, s: s, sp: sp}
		if err := c.cleaveSplit(); err != nil {
			return err
		}
	}
	return nil
}