You are an expert in writing code in many different languages. Your goal is to perform code completion for the following code, keeping in mind the rest of the code and the file meta data.
    Metadata details:
    filename: template.go
    path of file: ./repos/gomplate
    
    the code of the file until where you have to start completion: package gomplate

import (
	"context"
	"errors"
	"fmt"
	"io"
	"io/fs"
	"os"
	"path"
	"path/filepath"
	"slices"
	"strings"
	"text/template"

	"github.com/hack-pad/hackpadfs"
	"github.com/hairyhenderson/go-fsimpl"
	"github.com/hairyhenderson/gomplate/v4/internal/config"
	"github.com/hairyhenderson/gomplate/v4/internal/datafs"
	"github.com/hairyhenderson/gomplate/v4/internal/iohelpers"
	"github.com/hairyhenderson/gomplate/v4/tmpl"

	// TODO: switch back if/when fs.FS support gets merged upstream
	"github.com/hairyhenderson/xignore"
)

// ignorefile name, like .gitignore
const gomplateignore = ".gomplateignore"

func addTmplFuncs(f template.FuncMap, root *template.Template, tctx interface{}, path string) {
	t := tmpl.New(root, tctx, path)
	tns := func() *tmpl.Template { return t }
	f["tmpl"] = tns
	f["tpl"] = t.Inline
}

// copyFuncMap - copies the template.FuncMap into a new map so we can modify it
// without affecting the original
func copyFuncMap(funcMap template.FuncMap) template.FuncMap {
	if funcMap == nil {
		return nil
	}

	newFuncMap := make(template.FuncMap, len(funcMap))
	for k, v := range funcMap {
		newFuncMap[k] = v
	}
	return newFuncMap
}

// parseTemplate - parses text as a Go template with the given name and options
func parseTemplate(ctx context.Context, name, text string, funcs template.FuncMap, tmplctx interface{}, nested config.Templates, leftDelim, rightDelim string, missingKey string) (tmpl *template.Template, err error) {
	tmpl = template.New(name)
	if missingKey == "" {
		missingKey = "error"
	}

	missingKeyValues := []string{"error", "zero", "default", "invalid"}
	if !slices.Contains(missingKeyValues, missingKey) {
		return nil, fmt.Errorf("not allowed value for the 'missing-key' flag: %s. Allowed values: %s", missingKey, strings.Join(missingKeyValues, ","))
	}

	tmpl.Option("missingkey=" + missingKey)

	funcMap := copyFuncMap(funcs)

	// the "tmpl" funcs get added here because they need access to the root template and context
	addTmplFuncs(funcMap, tmpl, tmplctx, name)
	tm
    
func walkDir(ctx context.Context, cfg *config.Config, dir string, outFileNamer func(context.Context, string) (string, error), excludeGlob []string, excludeProcessingGlob []string, mode os.FileMode, modeOverride bool) ([]Template, error) {
	dir = filepath.ToSlash(filepath.Clean(dir))

	// get a filesystem rooted in the same volume as dir (or / on non-Windows)
	fsys, err := datafs.FSysForPath(ctx, dir)
	if err != nil {
		return nil, err
	}

	// we need dir to be relative to the root of fsys
	// TODO: maybe need to do something with root here?
	_, resolvedDir, err := datafs.ResolveLocalPath(fsys, dir)
	if err != nil {
		return nil, fmt.Errorf("resolveLocalPath: %w", err)
	}

	// we need to sub the filesystem to the dir
	subfsys, err := fs.Sub(fsys, resolvedDir)
	if err != nil {
		return nil, fmt.Errorf("sub: %w", err)
	}

	// just check . because fsys is subbed to dir already
	dirStat, err := fs.Stat(subfsys, ".")
	if err != nil {
		return nil, fmt.Errorf("stat %q (%q): %w", dir, resolvedDir, err)
	}
	dirMode := dirStat.Mode()

	templates := make([]Template, 0)
	matcher := xignore.NewMatcher(subfsys)

	excludeMatches, err := matcher.Matches(".", &xignore.MatchesOptions{
		Ignorefile:    gomplateignore,
		Nested:        true, // allow nested ignorefile
		AfterPatterns: excludeGlob,
	})
	if err != nil {
		return nil, fmt.Errorf("ignore matching failed for %s: %w", dir, err)
	}

	excludeProcessingMatches, err := matcher.Matches(".", &xignore.MatchesOptions{
		// TODO: fix or replace xignore module so we can avoid attempting to read the .gomplateignore file for both exclude and excludeProcessing patterns
		Ignorefile:    gomplateignore,
		Nested:        true, // allow nested ignorefile
		AfterPatterns: excludeProcessingGlob,
	})
	if err != nil {
		return nil, fmt.Errorf("passthough matching failed for %s: %w", dir, err)
	}

	passthroughFiles := make(map[string]bool)

	for _, file := range excludeProcessingMatches.MatchedFiles {
		// files that need to be directly copied
		passthroughFiles[file] = true
	}

	// Unmatched ignorefile rules's files
	for _, file := range excludeMatches.UnmatchedFiles {
		// we want to pass an absolute (as much as possible) path to fileToTemplate
		inPath := filepath.Join(dir, file)
		inPath = filepath.ToSlash(inPath)

		// but outFileNamer expects only the filename itself
		outFile, err := outFileNamer(ctx, file)
		if err != nil {
			return nil, fmt.Errorf("outFileNamer: %w", err)
		}

		_, ok := passthroughFiles[file]
		if ok {
			err = copyFileToOutDir(ctx, cfg, inPath, outFile, mode, modeOverride)
			if err != nil {
				return nil, fmt.Errorf("copyFileToOutDir: %w", err)
			}

			continue
		}

		tpl, err := fileToTemplate(ctx, cfg, inPath, outFile, mode, modeOverride)
		if err != nil {
			return nil, fmt.Errorf("fileToTemplate: %w", err)
		}

		// Ensure file parent dirs - use separate fsys for output file
		outfsys, err := datafs.FSysForPath(ctx, outFile)
		if err != nil {
			return nil, fmt.Errorf("fsysForPath: %w", err)
		}
		if err = hackpadfs.MkdirAll(outfsys, filepath.Dir(outFile), dirMode); err != nil {
			return nil, fmt.Errorf("mkdirAll %q: %w", outFile, err)
		}

		templates = append(templates, tpl)
	}

	return templates, nil
}