<commit_before>// Copyright (c) 2017 Intel Corporation
//
// 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 main

import (
	"fmt"
	"os"
	"path/filepath"
	"reflect"
	"strings"
	"sync"
	"time"

	"github.com/Sirupsen/logrus"
)

// Repo represents the repository under test
// For more information about this structure take a look to the README
type Repo struct {
	// URL is the url of the repository
	URL string

	// MasterBranch is the master branch of this repository
	MasterBranch string

	// PR is the pull request number
	PR int

	// RefreshTime is the time to wait for checking if a pull request needs to be tested
	RefreshTime string

	// Toke is the repository access token
	Token string

	// Setup contains the conmmands needed to setup the environment
	Setup []string

	// Run contains the commands to run the test
	Run []string

	// Teardown contains the commands to be executed once Run ends
	Teardown []string

	// OnSuccess contains the commands to be executed if Setup, Run and Teardown finished correctly
	OnSuccess []string

	// OnFailure contains the commands to be executed if any of Setup, Run or Teardown fail
	OnFailure []string

	// TTY specify whether a tty must be allocate to run the stages
	TTY bool

	// PostOnSuccess is the comment to be posted if the test finished correctly
	PostOnSuccess string

	// PostOnFailure is the comment to be posted if the test fails
	PostOnFailure string

	// LogDir is the logs directory
	LogDir string

	// Language is the language of the repository
	Language RepoLanguage

	// CommentTrigger is the comment that must be present to trigger the test
	CommentTrigger RepoComment

	// LogServer contains the information of the server where the logs must be placed
	LogServer LogServer

	// Whitelist is the list of users whose pull request can be tested
	Whitelist string

	// cvr control version repository
	cvr CVR

	// refresh is RefreshTime once parsed
	refresh time.Duration

	// env contains the environment variables to be used in each stage
	env []string

	// whitelistUsers is the whitelist once parsed
	whitelistUsers []string

	// logger of the repository
	logger *logrus.Entry

	// prConfig is the configuration used to create pull request objects
	prConfig pullRequestConfig
}

const (
	logDirMode    = 0755
	logFileMode   = 0664
	logServerUser = "root"
)

var defaultEnv = []string{"CI=true", "LOCALCI=true"}

var runTestsInParallel bool

var testLock sync.Mutex

func (r *Repo) setupCvr() error {
	var err error

	// validate url
	r.URL = strings.TrimSpace(r.URL)
	if len(r.URL) == 0 {
		return fmt.Errorf("missing repository url")
	}

	// set repository logger
	r.logger = ciLog.WithFields(logrus.Fields{
		"Repo": r.URL,
	})

	// get the control version repository
	r.cvr, err = newCVR(r.URL, r.Token)
	r.logger.Debugf("control version repository: %#v", r.cvr)

	return err
}

func (r *Repo) setupLogServer() error {
	if reflect.DeepEqual(r.LogServer, LogServer{}) {
		return nil
	}

	if len(r.LogServer.IP) == 0 {
		return fmt.Errorf("missing server ip")
	}

	if len(r.LogServer.User) == 0 {
		r.LogServer.User = logServerUser
	}

	if len(r.LogServer.Dir) == 0 {
		r.LogServer.Dir = defaultLogDir
	}

	return nil
}

func (r *Repo) setupLogDir() error {
	// create log directory
	if err := os.MkdirAll(r.LogDir, logDirMode); err != nil {
		return err
	}

	return nil
}

func (r *Repo) setupRefreshTime() error {
	var err error

	// validate refresh time
	r.refresh, err = time.ParseDuration(r.RefreshTime)
	if err != nil {
		return fmt.Errorf("failed to parse refresh time '%s' %s", r.RefreshTime, err)
	}

	return nil
}

func (r *Repo) setupCommentTrigger() error {
	if reflect.DeepEqual(r.CommentTrigger, RepoComment{}) {
		return nil
	}

	if len(r.CommentTrigger.Comment) == 0 {
		return fmt.Errorf("missing comment trigger")
	}

	return nil
}

func (r *Repo) setupLanguage() error {
	return r.Language.setup()
}

func (r *Repo) setupStages() error {
	if len(r.Run) == 0 {
		return fmt.Errorf("missing run commands")
	}

	return nil
}

func (r *Repo) setupWhitelist() error {
	// get the list of users
	r.whitelistUsers = strings.Split(r.Whitelist, ",")
	return nil
}

func (r *Repo) setupEnvars() error {
	// add environment variables
	r.env = os.Environ()
	r.env = append(r.env, defaultEnv...)
	repoSlug := fmt.Sprintf("LOCALCI_REPO_SLUG=%s", r.cvr.getProjectSlug())
	r.env = append(r.env, repoSlug)

	return nil
}

// setup the repository. This method MUST BE called before use any other
func (r *Repo) setup() error {
	var err error

	setupFuncs := []func() error{
		r.setupCvr,
		r.setupRefreshTime,
		r.setupLogDir,
		r.setupLogServer,
		r.setupCommentTrigger,
		r.setupLanguage,
		r.setupStages,
		r.setupWhitelist,
		r.setupEnvars,
	}

	for _, setupFunc := range setupFuncs {
		if err = setupFunc(); err != nil {
			return err
		}
	}

	r.prConfig = pullRequestConfig{
		cvr:            r.cvr,
		logger:         r.logger,
		commentTrigger: r.CommentTrigger,
		postOnFailure:  r.PostOnFailure,
		postOnSuccess:  r.PostOnSuccess,
	}

	r.logger.Debugf("control version repository: %#v", r.cvr)

	return nil
}

// loop to monitor the repository
func (r *Repo) loop() {
	revisionsTested := make(map[string]revision)

	r.logger.Debugf("monitoring in a loop the repository: %+v", *r)

	appendPullRequests := func(revisions *[]revision, prs []int) error {
		for _, pr := range prs {
			r.logger.Debugf("requesting pull request %d", pr)
			pr, err := newPullRequest(pr, r.prConfig)
			if err != nil {
				return fmt.Errorf("failed to get pull request '%d' %s", pr, err)
			}
			*revisions = append(*revisions, pr)
		}
		return nil
	}

	for {
		var revisionsToTest []revision

		// append master branch
		r.logger.Debugf("requesting master branch: %s", r.MasterBranch)
		branch, err := newRepoBranch(r.MasterBranch, r.cvr, r.logger)
		if err != nil {
			r.logger.Warnf("failed to get master branch %s: %s", r.MasterBranch, err)
		} else {
			revisionsToTest = append(revisionsToTest, branch)
		}

		// append pull requests
		if r.PR != 0 {
			// if PR is not 0 then we have to monitor just one PR
			if err = appendPullRequests(&revisionsToTest, []int{r.PR}); err != nil {
				r.logger.Warnf("failed to append pull request %d", r.PR, err)
			}
		} else {
			// append open pull request
			r.logger.Debugf("requesting open pull requests")
			prs, err := r.cvr.getOpenPullRequests()
			if err != nil {
				r.logger.Warnf("failed to get open pull requests: %s", err)
			} else if err = appendPullRequests(&revisionsToTest, prs); err != nil {
				r.logger.Warnf("failed to append pull requests %+v: %s", prs, err)
			}
		}

		r.logger.Debugf("testing revisions: %#v", revisionsToTest)
		r.testRevisions(revisionsToTest, &revisionsTested)

		r.logger.Debugf("going to sleep: %s", r.RefreshTime)
		time.Sleep(r.refresh)
	}
}

func (r *Repo) testRevisions(revisions []revision, revisionsTested *map[string]revision) {
	for _, revision := range revisions {
		tested, ok := (*revisionsTested)[revision.id()]
		if ok {
			// checking if the old version of the PR is being tested
			if tested.isBeingTested() {
				r.logger.Debugf("revision is being tested: %#v", tested)
				continue
			}
			if revision.equal(tested) {
				r.logger.Debugf("revision was already tested: %#v", revision)
				continue
			}
		}

		if err := r.testRevision(revision); err != nil {
			r.logger.Errorf("failed to test revision '%+v' %s", revision, err)
			continue
		}

		// copy the PR that was tested
		// FIXME: remove the PR's that were closed or merged
		(*revisionsTested)[revision.id()] = revision
	}
}

// test the pull request specified in the configuration file
// if pr does not exist an error is returned
func (r *Repo) test() error {
	if r.PR == 0 {
		return fmt.Errorf("Missing pull request number in configuration file")
	}

	rev, err := newPullRequest(r.PR, r.prConfig)
	if err != nil {
		return fmt.Errorf("failed to get pull request %d %s", r.PR, err)
	}

	// run tests in parallel does not make sense when
	// we are just testing one pull request
	runTestsInParallel = false

	return r.testRevision(rev)
}

// testRevision tests a specific revision
// returns an error if the test fail
func (r *Repo) testRevision(rev revision) error {
	if !runTestsInParallel {
		testLock.Lock()
		defer testLock.Unlock()
	}

	if !runTestsInParallel {
		return r.runTest(rev)
	}

	go func() {
		err := r.runTest(rev)
		if err != nil {
			r.logger.Errorf("failed to test revision %#v: %s", rev, err)
		}
	}()

	return nil
}

// runTest generate an environment and run the stages
func (r *Repo) runTest(rev revision) error {
	langEnv, err := r.Language.generateEnvironment(r.cvr.getProjectSlug())
	if err != nil {
		return err
	}

	// cleanup task
	defer func() {
		err = os.RemoveAll(langEnv.workingDir)
		if err != nil {
			r.logger.Errorf("failed to remove the working directory '%s': %s", langEnv.workingDir, err)
		}

		err = os.RemoveAll(langEnv.tempDir)
		if err != nil {
			r.logger.Errorf("failed to remove the temporal directory '%s': %s", langEnv.tempDir, err)
		}
	}()

	// download the revision
	if err = rev.download(langEnv.workingDir); err != nil {
		return err
	}

	config := stageConfig{
		logger:     r.logger,
		workingDir: langEnv.workingDir,
		tty:        r.TTY,
	}

	// cleanup and set the log directory of the pull request
	config.logDir = filepath.Join(r.LogDir, rev.logDirName())
	_ = os.RemoveAll(config.logDir)
	if err = os.MkdirAll(config.logDir, logDirMode); err != nil {
		return err
	}

	// set environment variables
	config.env = r.env

	// appends language environment variables
	if len(langEnv.env) > 0 {
		config.env = append(config.env, langEnv.env...)
	}

	// copy logs to server if we have an IP address
	if len(r.LogServer.IP) != 0 {
		defer func() {
			err = r.LogServer.copy(config.logDir)
			if err != nil {
				r.logger.Errorf("failed to copy log dir %s to server %+v", config.logDir, r.LogServer)
			}
		}()
	}

	r.logger.Debugf("stage config: %+v", config)

	stages := map[string]stage{
		"setup":     stage{name: "setup", commands: r.Setup},
		"run":       stage{name: "run", commands: r.Run},
		"teardown":  stage{name: "teardown", commands: r.Teardown},
		"onSuccess": stage{name: "onSuccess", commands: r.OnSuccess},
		"onFailure": stage{name: "onFailure", commands: r.OnFailure},
	}

	// run test
	return rev.test(config, stages)
}
<commit_msg>localCI: remove PRs already closed or merged<commit_after>// Copyright (c) 2017 Intel Corporation
//
// 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 main

import (
	"fmt"
	"os"
	"path/filepath"
	"reflect"
	"strings"
	"sync"
	"time"

	"github.com/Sirupsen/logrus"
)

// Repo represents the repository under test
// For more information about this structure take a look to the README
type Repo struct {
	// URL is the url of the repository
	URL string

	// MasterBranch is the master branch of this repository
	MasterBranch string

	// PR is the pull request number
	PR int

	// RefreshTime is the time to wait for checking if a pull request needs to be tested
	RefreshTime string

	// Toke is the repository access token
	Token string

	// Setup contains the conmmands needed to setup the environment
	Setup []string

	// Run contains the commands to run the test
	Run []string

	// Teardown contains the commands to be executed once Run ends
	Teardown []string

	// OnSuccess contains the commands to be executed if Setup, Run and Teardown finished correctly
	OnSuccess []string

	// OnFailure contains the commands to be executed if any of Setup, Run or Teardown fail
	OnFailure []string

	// TTY specify whether a tty must be allocate to run the stages
	TTY bool

	// PostOnSuccess is the comment to be posted if the test finished correctly
	PostOnSuccess string

	// PostOnFailure is the comment to be posted if the test fails
	PostOnFailure string

	// LogDir is the logs directory
	LogDir string

	// Language is the language of the repository
	Language RepoLanguage

	// CommentTrigger is the comment that must be present to trigger the test
	CommentTrigger RepoComment

	// LogServer contains the information of the server where the logs must be placed
	LogServer LogServer

	// Whitelist is the list of users whose pull request can be tested
	Whitelist string

	// cvr control version repository
	cvr CVR

	// refresh is RefreshTime once parsed
	refresh time.Duration

	// env contains the environment variables to be used in each stage
	env []string

	// whitelistUsers is the whitelist once parsed
	whitelistUsers []string

	// logger of the repository
	logger *logrus.Entry

	// prConfig is the configuration used to create pull request objects
	prConfig pullRequestConfig
}

const (
	logDirMode    = 0755
	logFileMode   = 0664
	logServerUser = "root"
)

var defaultEnv = []string{"CI=true", "LOCALCI=true"}

var runTestsInParallel bool

var testLock sync.Mutex

func (r *Repo) setupCvr() error {
	var err error

	// validate url
	r.URL = strings.TrimSpace(r.URL)
	if len(r.URL) == 0 {
		return fmt.Errorf("missing repository url")
	}

	// set repository logger
	r.logger = ciLog.WithFields(logrus.Fields{
		"Repo": r.URL,
	})

	// get the control version repository
	r.cvr, err = newCVR(r.URL, r.Token)
	r.logger.Debugf("control version repository: %#v", r.cvr)

	return err
}

func (r *Repo) setupLogServer() error {
	if reflect.DeepEqual(r.LogServer, LogServer{}) {
		return nil
	}

	if len(r.LogServer.IP) == 0 {
		return fmt.Errorf("missing server ip")
	}

	if len(r.LogServer.User) == 0 {
		r.LogServer.User = logServerUser
	}

	if len(r.LogServer.Dir) == 0 {
		r.LogServer.Dir = defaultLogDir
	}

	return nil
}

func (r *Repo) setupLogDir() error {
	// create log directory
	if err := os.MkdirAll(r.LogDir, logDirMode); err != nil {
		return err
	}

	return nil
}

func (r *Repo) setupRefreshTime() error {
	var err error

	// validate refresh time
	r.refresh, err = time.ParseDuration(r.RefreshTime)
	if err != nil {
		return fmt.Errorf("failed to parse refresh time '%s' %s", r.RefreshTime, err)
	}

	return nil
}

func (r *Repo) setupCommentTrigger() error {
	if reflect.DeepEqual(r.CommentTrigger, RepoComment{}) {
		return nil
	}

	if len(r.CommentTrigger.Comment) == 0 {
		return fmt.Errorf("missing comment trigger")
	}

	return nil
}

func (r *Repo) setupLanguage() error {
	return r.Language.setup()
}

func (r *Repo) setupStages() error {
	if len(r.Run) == 0 {
		return fmt.Errorf("missing run commands")
	}

	return nil
}

func (r *Repo) setupWhitelist() error {
	// get the list of users
	r.whitelistUsers = strings.Split(r.Whitelist, ",")
	return nil
}

func (r *Repo) setupEnvars() error {
	// add environment variables
	r.env = os.Environ()
	r.env = append(r.env, defaultEnv...)
	repoSlug := fmt.Sprintf("LOCALCI_REPO_SLUG=%s", r.cvr.getProjectSlug())
	r.env = append(r.env, repoSlug)

	return nil
}

// setup the repository. This method MUST BE called before use any other
func (r *Repo) setup() error {
	var err error

	setupFuncs := []func() error{
		r.setupCvr,
		r.setupRefreshTime,
		r.setupLogDir,
		r.setupLogServer,
		r.setupCommentTrigger,
		r.setupLanguage,
		r.setupStages,
		r.setupWhitelist,
		r.setupEnvars,
	}

	for _, setupFunc := range setupFuncs {
		if err = setupFunc(); err != nil {
			return err
		}
	}

	r.prConfig = pullRequestConfig{
		cvr:            r.cvr,
		logger:         r.logger,
		commentTrigger: r.CommentTrigger,
		postOnFailure:  r.PostOnFailure,
		postOnSuccess:  r.PostOnSuccess,
	}

	r.logger.Debugf("control version repository: %#v", r.cvr)

	return nil
}

// loop to monitor the repository
func (r *Repo) loop() {
	revisionsTested := make(map[string]revision)

	r.logger.Debugf("monitoring in a loop the repository: %+v", *r)

	appendPullRequests := func(revisions *[]revision, prs []int) error {
		for _, pr := range prs {
			r.logger.Debugf("requesting pull request %d", pr)
			pr, err := newPullRequest(pr, r.prConfig)
			if err != nil {
				return fmt.Errorf("failed to get pull request '%d' %s", pr, err)
			}
			*revisions = append(*revisions, pr)
		}
		return nil
	}

	for {
		var revisionsToTest []revision

		// append master branch
		r.logger.Debugf("requesting master branch: %s", r.MasterBranch)
		branch, err := newRepoBranch(r.MasterBranch, r.cvr, r.logger)
		if err != nil {
			r.logger.Warnf("failed to get master branch %s: %s", r.MasterBranch, err)
		} else {
			revisionsToTest = append(revisionsToTest, branch)
		}

		// append pull requests
		if r.PR != 0 {
			// if PR is not 0 then we have to monitor just one PR
			if err = appendPullRequests(&revisionsToTest, []int{r.PR}); err != nil {
				r.logger.Warnf("failed to append pull request %d", r.PR, err)
			}
		} else {
			// append open pull request
			r.logger.Debugf("requesting open pull requests")
			prs, err := r.cvr.getOpenPullRequests()
			if err != nil {
				r.logger.Warnf("failed to get open pull requests: %s", err)
			} else if err = appendPullRequests(&revisionsToTest, prs); err != nil {
				r.logger.Warnf("failed to append pull requests %+v: %s", prs, err)
			}
		}

		r.logger.Debugf("testing revisions: %#v", revisionsToTest)
		r.testRevisions(revisionsToTest, &revisionsTested)

		r.logger.Debugf("going to sleep: %s", r.RefreshTime)
		time.Sleep(r.refresh)
	}
}

func (r *Repo) testRevisions(revisions []revision, revisionsTested *map[string]revision) {
	// remove revisions that are not in the list of open pull request and already tested
	for k, v := range *revisionsTested {
		found := false
		// iterate over open pull requests and master branch
		for _, r := range revisions {
			if r.id() == k {
				found = true
				break
			}
		}

		if !found && !v.isBeingTested() {
			delete((*revisionsTested), k)
		}
	}

	for _, revision := range revisions {
		tested, ok := (*revisionsTested)[revision.id()]
		if ok {
			// checking if the old version of the PR is being tested
			if tested.isBeingTested() {
				r.logger.Debugf("revision is being tested: %#v", tested)
				continue
			}
			if revision.equal(tested) {
				r.logger.Debugf("revision was already tested: %#v", revision)
				continue
			}
		}

		if err := r.testRevision(revision); err != nil {
			r.logger.Errorf("failed to test revision '%+v' %s", revision, err)
			continue
		}

		// copy the PR that was tested
		(*revisionsTested)[revision.id()] = revision
	}
}

// test the pull request specified in the configuration file
// if pr does not exist an error is returned
func (r *Repo) test() error {
	if r.PR == 0 {
		return fmt.Errorf("Missing pull request number in configuration file")
	}

	rev, err := newPullRequest(r.PR, r.prConfig)
	if err != nil {
		return fmt.Errorf("failed to get pull request %d %s", r.PR, err)
	}

	// run tests in parallel does not make sense when
	// we are just testing one pull request
	runTestsInParallel = false

	return r.testRevision(rev)
}

// testRevision tests a specific revision
// returns an error if the test fail
func (r *Repo) testRevision(rev revision) error {
	if !runTestsInParallel {
		testLock.Lock()
		defer testLock.Unlock()
	}

	if !runTestsInParallel {
		return r.runTest(rev)
	}

	go func() {
		err := r.runTest(rev)
		if err != nil {
			r.logger.Errorf("failed to test revision %#v: %s", rev, err)
		}
	}()

	return nil
}

// runTest generate an environment and run the stages
func (r *Repo) runTest(rev revision) error {
	langEnv, err := r.Language.generateEnvironment(r.cvr.getProjectSlug())
	if err != nil {
		return err
	}

	// cleanup task
	defer func() {
		err = os.RemoveAll(langEnv.workingDir)
		if err != nil {
			r.logger.Errorf("failed to remove the working directory '%s': %s", langEnv.workingDir, err)
		}

		err = os.RemoveAll(langEnv.tempDir)
		if err != nil {
			r.logger.Errorf("failed to remove the temporal directory '%s': %s", langEnv.tempDir, err)
		}
	}()

	// download the revision
	if err = rev.download(langEnv.workingDir); err != nil {
		return err
	}

	config := stageConfig{
		logger:     r.logger,
		workingDir: langEnv.workingDir,
		tty:        r.TTY,
	}

	// cleanup and set the log directory of the pull request
	config.logDir = filepath.Join(r.LogDir, rev.logDirName())
	_ = os.RemoveAll(config.logDir)
	if err = os.MkdirAll(config.logDir, logDirMode); err != nil {
		return err
	}

	// set environment variables
	config.env = r.env

	// appends language environment variables
	if len(langEnv.env) > 0 {
		config.env = append(config.env, langEnv.env...)
	}

	// copy logs to server if we have an IP address
	if len(r.LogServer.IP) != 0 {
		defer func() {
			err = r.LogServer.copy(config.logDir)
			if err != nil {
				r.logger.Errorf("failed to copy log dir %s to server %+v", config.logDir, r.LogServer)
			}
		}()
	}

	r.logger.Debugf("stage config: %+v", config)

	stages := map[string]stage{
		"setup":     stage{name: "setup", commands: r.Setup},
		"run":       stage{name: "run", commands: r.Run},
		"teardown":  stage{name: "teardown", commands: r.Teardown},
		"onSuccess": stage{name: "onSuccess", commands: r.OnSuccess},
		"onFailure": stage{name: "onFailure", commands: r.OnFailure},
	}

	// run test
	return rev.test(config, stages)
}
<|endoftext|>


<commit_before>// Copyright 2012, 2013 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package cmd_test

import (
	"io/ioutil"
	"path/filepath"

	"github.com/juju/loggo"
	"github.com/juju/testing"
	gc "launchpad.net/gocheck"

	"launchpad.net/juju-core/cmd"
	"launchpad.net/juju-core/juju/osenv"
	coretesting "launchpad.net/juju-core/testing"
)

var logger = loggo.GetLogger("juju.test")

type LogSuite struct {
	testing.CleanupSuite
}

var _ = gc.Suite(&LogSuite{})

func (s *LogSuite) SetUpTest(c *gc.C) {
	s.PatchEnvironment(osenv.JujuLoggingConfigEnvKey, "")
	s.AddCleanup(func(_ *gc.C) {
		loggo.ResetLoggers()
		loggo.ResetWriters()
	})
}

func newLogWithFlags(c *gc.C, flags []string) *cmd.Log {
	log := &cmd.Log{}
	flagSet := coretesting.NewFlagSet()
	log.AddFlags(flagSet)
	err := flagSet.Parse(false, flags)
	c.Assert(err, gc.IsNil)
	return log
}

func (s *LogSuite) TestNoFlags(c *gc.C) {
	log := newLogWithFlags(c, []string{})
	c.Assert(log.Path, gc.Equals, "")
	c.Assert(log.Verbose, gc.Equals, false)
	c.Assert(log.Debug, gc.Equals, false)
	c.Assert(log.Config, gc.Equals, "")
}

func (s *LogSuite) TestFlags(c *gc.C) {
	log := newLogWithFlags(c, []string{"--log-file", "foo", "--verbose", "--debug",
		"--logging-config=juju.cmd=INFO;juju.worker.deployer=DEBUG"})
	c.Assert(log.Path, gc.Equals, "foo")
	c.Assert(log.Verbose, gc.Equals, true)
	c.Assert(log.Debug, gc.Equals, true)
	c.Assert(log.Config, gc.Equals, "juju.cmd=INFO;juju.worker.deployer=DEBUG")
}

func (s *LogSuite) TestLogConfigFromEnvironment(c *gc.C) {
	config := "juju.cmd=INFO;juju.worker.deployer=DEBUG"
	testing.PatchEnvironment(osenv.JujuLoggingConfigEnvKey, config)
	log := newLogWithFlags(c, []string{})
	c.Assert(log.Path, gc.Equals, "")
	c.Assert(log.Verbose, gc.Equals, false)
	c.Assert(log.Debug, gc.Equals, false)
	c.Assert(log.Config, gc.Equals, config)
}

func (s *LogSuite) TestVerboseSetsLogLevel(c *gc.C) {
	l := &cmd.Log{Verbose: true}
	ctx := coretesting.Context(c)
	err := l.Start(ctx)
	c.Assert(err, gc.IsNil)

	c.Assert(loggo.GetLogger("").LogLevel(), gc.Equals, loggo.INFO)
	c.Assert(coretesting.Stderr(ctx), gc.Equals, "")
	c.Assert(coretesting.Stdout(ctx), gc.Equals, "Flag --verbose is deprecated with the current meaning, use --show-log\n")
}

func (s *LogSuite) TestDebugSetsLogLevel(c *gc.C) {
	l := &cmd.Log{Debug: true}
	ctx := coretesting.Context(c)
	err := l.Start(ctx)
	c.Assert(err, gc.IsNil)

	c.Assert(loggo.GetLogger("").LogLevel(), gc.Equals, loggo.DEBUG)
	c.Assert(coretesting.Stderr(ctx), gc.Equals, "")
	c.Assert(coretesting.Stdout(ctx), gc.Equals, "")
}

func (s *LogSuite) TestShowLogSetsLogLevel(c *gc.C) {
	l := &cmd.Log{ShowLog: true}
	ctx := coretesting.Context(c)
	err := l.Start(ctx)
	c.Assert(err, gc.IsNil)

	c.Assert(loggo.GetLogger("").LogLevel(), gc.Equals, loggo.INFO)
	c.Assert(coretesting.Stderr(ctx), gc.Equals, "")
	c.Assert(coretesting.Stdout(ctx), gc.Equals, "")
}

func (s *LogSuite) TestStderr(c *gc.C) {
	l := &cmd.Log{Verbose: true, Config: "<root>=INFO"}
	ctx := coretesting.Context(c)
	err := l.Start(ctx)
	c.Assert(err, gc.IsNil)
	logger.Infof("hello")
	c.Assert(coretesting.Stderr(ctx), gc.Matches, `^.* INFO .* hello\n`)
}

func (s *LogSuite) TestRelPathLog(c *gc.C) {
	l := &cmd.Log{Path: "foo.log", Config: "<root>=INFO"}
	ctx := coretesting.Context(c)
	err := l.Start(ctx)
	c.Assert(err, gc.IsNil)
	logger.Infof("hello")
	content, err := ioutil.ReadFile(filepath.Join(ctx.Dir, "foo.log"))
	c.Assert(err, gc.IsNil)
	c.Assert(string(content), gc.Matches, `^.* INFO .* hello\n`)
	c.Assert(coretesting.Stderr(ctx), gc.Equals, "")
	c.Assert(coretesting.Stdout(ctx), gc.Equals, "")
}

func (s *LogSuite) TestAbsPathLog(c *gc.C) {
	path := filepath.Join(c.MkDir(), "foo.log")
	l := &cmd.Log{Path: path, Config: "<root>=INFO"}
	ctx := coretesting.Context(c)
	err := l.Start(ctx)
	c.Assert(err, gc.IsNil)
	logger.Infof("hello")
	c.Assert(coretesting.Stderr(ctx), gc.Equals, "")
	content, err := ioutil.ReadFile(path)
	c.Assert(err, gc.IsNil)
	c.Assert(string(content), gc.Matches, `^.* INFO .* hello\n`)
}

func (s *LogSuite) TestLoggingToFileAndStderr(c *gc.C) {
	l := &cmd.Log{Path: "foo.log", Config: "<root>=INFO", ShowLog: true}
	ctx := coretesting.Context(c)
	err := l.Start(ctx)
	c.Assert(err, gc.IsNil)
	logger.Infof("hello")
	content, err := ioutil.ReadFile(filepath.Join(ctx.Dir, "foo.log"))
	c.Assert(err, gc.IsNil)
	c.Assert(string(content), gc.Matches, `^.* INFO .* hello\n`)
	c.Assert(coretesting.Stderr(ctx), gc.Matches, `^.* INFO .* hello\n`)
	c.Assert(coretesting.Stdout(ctx), gc.Equals, "")
}

func (s *LogSuite) TestErrorAndWarningLoggingToStderr(c *gc.C) {
	// Error and warning go to stderr even with ShowLog=false
	l := &cmd.Log{Config: "<root>=INFO", ShowLog: false}
	ctx := coretesting.Context(c)
	err := l.Start(ctx)
	c.Assert(err, gc.IsNil)
	logger.Warningf("a warning")
	logger.Errorf("an error")
	logger.Infof("an info")
	c.Assert(coretesting.Stderr(ctx), gc.Matches, `^.*WARNING a warning\n.*ERROR an error\n.*`)
	c.Assert(coretesting.Stdout(ctx), gc.Equals, "")
}
<commit_msg>embedded testing.LoggingSuite into cmd LogSuite<commit_after>// Copyright 2012, 2013 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package cmd_test

import (
	"io/ioutil"
	"path/filepath"

	"github.com/juju/loggo"
	"github.com/juju/testing"
	"github.com/juju/testing/logging"
	gc "launchpad.net/gocheck"

	"launchpad.net/juju-core/cmd"
	"launchpad.net/juju-core/juju/osenv"
	coretesting "launchpad.net/juju-core/testing"
)

var logger = loggo.GetLogger("juju.test")

type LogSuite struct {
	logging.LoggingSuite
}

var _ = gc.Suite(&LogSuite{})

func (s *LogSuite) SetUpTest(c *gc.C) {
	s.PatchEnvironment(osenv.JujuLoggingConfigEnvKey, "")
	s.AddCleanup(func(_ *gc.C) {
		loggo.ResetLoggers()
		loggo.ResetWriters()
	})
}

func newLogWithFlags(c *gc.C, flags []string) *cmd.Log {
	log := &cmd.Log{}
	flagSet := coretesting.NewFlagSet()
	log.AddFlags(flagSet)
	err := flagSet.Parse(false, flags)
	c.Assert(err, gc.IsNil)
	return log
}

func (s *LogSuite) TestNoFlags(c *gc.C) {
	log := newLogWithFlags(c, []string{})
	c.Assert(log.Path, gc.Equals, "")
	c.Assert(log.Verbose, gc.Equals, false)
	c.Assert(log.Debug, gc.Equals, false)
	c.Assert(log.Config, gc.Equals, "")
}

func (s *LogSuite) TestFlags(c *gc.C) {
	log := newLogWithFlags(c, []string{"--log-file", "foo", "--verbose", "--debug",
		"--logging-config=juju.cmd=INFO;juju.worker.deployer=DEBUG"})
	c.Assert(log.Path, gc.Equals, "foo")
	c.Assert(log.Verbose, gc.Equals, true)
	c.Assert(log.Debug, gc.Equals, true)
	c.Assert(log.Config, gc.Equals, "juju.cmd=INFO;juju.worker.deployer=DEBUG")
}

func (s *LogSuite) TestLogConfigFromEnvironment(c *gc.C) {
	config := "juju.cmd=INFO;juju.worker.deployer=DEBUG"
	testing.PatchEnvironment(osenv.JujuLoggingConfigEnvKey, config)
	log := newLogWithFlags(c, []string{})
	c.Assert(log.Path, gc.Equals, "")
	c.Assert(log.Verbose, gc.Equals, false)
	c.Assert(log.Debug, gc.Equals, false)
	c.Assert(log.Config, gc.Equals, config)
}

func (s *LogSuite) TestVerboseSetsLogLevel(c *gc.C) {
	l := &cmd.Log{Verbose: true}
	ctx := coretesting.Context(c)
	err := l.Start(ctx)
	c.Assert(err, gc.IsNil)

	c.Assert(loggo.GetLogger("").LogLevel(), gc.Equals, loggo.INFO)
	c.Assert(coretesting.Stderr(ctx), gc.Equals, "")
	c.Assert(coretesting.Stdout(ctx), gc.Equals, "Flag --verbose is deprecated with the current meaning, use --show-log\n")
}

func (s *LogSuite) TestDebugSetsLogLevel(c *gc.C) {
	l := &cmd.Log{Debug: true}
	ctx := coretesting.Context(c)
	err := l.Start(ctx)
	c.Assert(err, gc.IsNil)

	c.Assert(loggo.GetLogger("").LogLevel(), gc.Equals, loggo.DEBUG)
	c.Assert(coretesting.Stderr(ctx), gc.Equals, "")
	c.Assert(coretesting.Stdout(ctx), gc.Equals, "")
}

func (s *LogSuite) TestShowLogSetsLogLevel(c *gc.C) {
	l := &cmd.Log{ShowLog: true}
	ctx := coretesting.Context(c)
	err := l.Start(ctx)
	c.Assert(err, gc.IsNil)

	c.Assert(loggo.GetLogger("").LogLevel(), gc.Equals, loggo.INFO)
	c.Assert(coretesting.Stderr(ctx), gc.Equals, "")
	c.Assert(coretesting.Stdout(ctx), gc.Equals, "")
}

func (s *LogSuite) TestStderr(c *gc.C) {
	l := &cmd.Log{Verbose: true, Config: "<root>=INFO"}
	ctx := coretesting.Context(c)
	err := l.Start(ctx)
	c.Assert(err, gc.IsNil)
	logger.Infof("hello")
	c.Assert(coretesting.Stderr(ctx), gc.Matches, `^.* INFO .* hello\n`)
}

func (s *LogSuite) TestRelPathLog(c *gc.C) {
	l := &cmd.Log{Path: "foo.log", Config: "<root>=INFO"}
	ctx := coretesting.Context(c)
	err := l.Start(ctx)
	c.Assert(err, gc.IsNil)
	logger.Infof("hello")
	content, err := ioutil.ReadFile(filepath.Join(ctx.Dir, "foo.log"))
	c.Assert(err, gc.IsNil)
	c.Assert(string(content), gc.Matches, `^.* INFO .* hello\n`)
	c.Assert(coretesting.Stderr(ctx), gc.Equals, "")
	c.Assert(coretesting.Stdout(ctx), gc.Equals, "")
}

func (s *LogSuite) TestAbsPathLog(c *gc.C) {
	path := filepath.Join(c.MkDir(), "foo.log")
	l := &cmd.Log{Path: path, Config: "<root>=INFO"}
	ctx := coretesting.Context(c)
	err := l.Start(ctx)
	c.Assert(err, gc.IsNil)
	logger.Infof("hello")
	c.Assert(coretesting.Stderr(ctx), gc.Equals, "")
	content, err := ioutil.ReadFile(path)
	c.Assert(err, gc.IsNil)
	c.Assert(string(content), gc.Matches, `^.* INFO .* hello\n`)
}

func (s *LogSuite) TestLoggingToFileAndStderr(c *gc.C) {
	l := &cmd.Log{Path: "foo.log", Config: "<root>=INFO", ShowLog: true}
	ctx := coretesting.Context(c)
	err := l.Start(ctx)
	c.Assert(err, gc.IsNil)
	logger.Infof("hello")
	content, err := ioutil.ReadFile(filepath.Join(ctx.Dir, "foo.log"))
	c.Assert(err, gc.IsNil)
	c.Assert(string(content), gc.Matches, `^.* INFO .* hello\n`)
	c.Assert(coretesting.Stderr(ctx), gc.Matches, `^.* INFO .* hello\n`)
	c.Assert(coretesting.Stdout(ctx), gc.Equals, "")
}

func (s *LogSuite) TestErrorAndWarningLoggingToStderr(c *gc.C) {
	// Error and warning go to stderr even with ShowLog=false
	l := &cmd.Log{Config: "<root>=INFO", ShowLog: false}
	ctx := coretesting.Context(c)
	err := l.Start(ctx)
	c.Assert(err, gc.IsNil)
	logger.Warningf("a warning")
	logger.Errorf("an error")
	logger.Infof("an info")
	c.Assert(coretesting.Stderr(ctx), gc.Matches, `^.*WARNING a warning\n.*ERROR an error\n.*`)
	c.Assert(coretesting.Stdout(ctx), gc.Equals, "")
}
<|endoftext|>


<commit_before>package memo

import (
	"encoding/binary"
	"fmt"
	"os"
	"path"
	"sort"

	"github.com/phil-mansfield/shellfish/cmd/env"

	"github.com/phil-mansfield/shellfish/cmd/halo"
	"github.com/phil-mansfield/shellfish/io"
)

const (
	rockstarMemoDir = "rockstar"
	rockstarMemoFile = "halo_%d.dat"
	rockstarShortMemoFile = "halo_short_%d.dat"
	rockstarShortMemoNum = 10 * 1000

	headerMemoFile = "hd_snap%d.dat"
)

// ReadSortedRockstarIDs returns a slice of IDs corresponding to the highest
// values of some quantity in a particular snapshot. maxID is the number of
// halos to return.
func ReadSortedRockstarIDs(
	snap, maxID int, e *env.Environment, flag halo.Val,
) ([]int, error) {
	dir := path.Join(e.MemoDir, rockstarMemoDir)
	if err, _ := os.Stat(dir); err != nil { os.Mkdir(dir, 0777) }

	var (
		vals [][]float64
		err error
	)
	if maxID >= rockstarShortMemoNum || maxID == -1 {
		file := path.Join(dir, fmt.Sprintf(rockstarMemoFile, snap))
		vals, err = readRockstar(
			file, -1, snap, nil, e, halo.ID, flag,
		)
		if err != nil { return nil, err }
	} else {
		file := path.Join(dir, fmt.Sprintf(rockstarShortMemoFile, snap))
		vals, err = readRockstar(
			file, rockstarShortMemoNum, snap, nil, e, halo.ID, flag,
		)
		if err != nil { return nil, err }
	}

	fids, ms := vals[0], vals[1]
	ids := make([]int, len(fids))
	for i := range ids { ids[i] = int(fids[i]) }

	if len(ids) < maxID {
		return nil, fmt.Errorf(
			"ID %d too large for snapshot %d", maxID, snap,
		)
	}

	sortRockstar(ids, ms)
	if maxID == -1 { return ids, nil }
	return ids[:maxID+1], nil
}

type massSet struct {
	ids []int
	ms []float64
}

func (set massSet) Len() int { return len(set.ids) }
// We're reverse sorting.
func (set massSet) Less(i, j int) bool { return set.ms[i] > set.ms[j] }
func (set massSet) Swap(i, j int) {
	set.ms[i], set.ms[j] = set.ms[j], set.ms[i]
	set.ids[i], set.ids[j] = set.ids[j], set.ids[i]
}

func sortRockstar(ids []int, ms []float64) {
	set := massSet{ ids, ms }
	sort.Sort(set)
}

// This function does fairly large heap allocations even when it doesn't need
// to. Consider passing it a buffer.
func ReadRockstar(
	snap int, ids []int, e *env.Environment, valFlags ...halo.Val,
) ([][]float64, error) {
	// Find binFile.
	dir := path.Join(e.MemoDir, rockstarMemoDir)
	if err, _ := os.Stat(dir); err != nil { os.Mkdir(dir, 0777) }

	binFile := path.Join(dir, fmt.Sprintf(rockstarMemoFile, snap))
	shortBinFile := path.Join(dir, fmt.Sprintf(rockstarShortMemoFile, snap))

	// This wastes a read the first time it's called. You need to decide if you
	// care. (Answer: probably.)
	vals, err := readRockstar(
		shortBinFile, rockstarShortMemoNum, snap, ids, e, valFlags...,
	)
	if err == nil { return vals, err }
	return readRockstar(binFile, -1, snap, ids, e, valFlags...)
}

func readRockstar(
	binFile string, n, snap int, ids []int,
	e *env.Environment, valFlags ...halo.Val,
) ([][]float64, error) {
	// If binFile doesn't exist, create it.
	if _, err := os.Stat(binFile); err != nil {
		if n == -1 {
			err = halo.RockstarConvert(e.HaloCatalog(snap), binFile)
			if err != nil { return nil, err }
		} else {
			err = halo.RockstarConvertTopN(e.HaloCatalog(snap), binFile, n)
			if err != nil { return nil, err}
		}
	}

	hds, _, err := ReadHeaders(snap, e)
	if err != nil { return nil, err }
	hd := &hds[0]

	rids, rvals, err := halo.ReadBinaryRockstarVals(
		binFile, &hd.Cosmo, valFlags...,
	)
	if err != nil { return nil, err }

	// Select out only the IDs we want.
	if ids == nil { return rvals, nil }
	vals := make([][]float64, len(rvals))

	for i := range vals { vals[i] = make([]float64, len(ids)) }
	f := NewIntFinder(rids)
	for i, id := range ids {
		line, ok := f.Find(id)
		if !ok { return nil, fmt.Errorf("Could not find ID %d", id) }
		for vi := range vals { vals[vi][i] = rvals[vi][line] }
	}

	return vals, nil
}

// A quick generic wrapper for doing those one-to-one mappings I need to do so
// often. Written like this so the backend can be swapped out easily.
type IntFinder struct {
	m map[int]int
}

// NewIntFinder creates a new IntFinder struct for a given slice of Rockstar
// IDs.
func NewIntFinder(rids []int) IntFinder {
	f := IntFinder{}
	f.m = make(map[int]int)
	for i, rid := range rids { f.m[rid] = i }
	return f
}

// Find returns the index which the given ID corresponds to and true if the
// ID is in the finder. Otherwise, false is returned.
func (f IntFinder) Find(rid int) (int, bool) {
	line, ok := f.m[rid]
	return line, ok
}

func readHeadersFromSheet(
	snap int, e *env.Environment,
) ([]io.GotetraHeader, []string, error) {
	files := make([]string, e.Blocks())
	hds := make([]io.GotetraHeader, e.Blocks())
	for i := range files {
		files[i] = e.ParticleCatalog(snap, i)
		err := io.ReadSheetHeaderAt(files[i], &hds[i])
		if err != nil { return nil, nil, err }
	}
	return hds, files, nil
}

// ReadHeaders returns all the segment headers and segment file names for all
// the segments at a given snapshot.
func ReadHeaders(
	snap int, e *env.Environment,
) ([]io.GotetraHeader, []string, error) {
	if _, err := os.Stat(e.MemoDir); err != nil { return nil, nil, err }
	memoFile := path.Join(e.MemoDir, fmt.Sprintf(headerMemoFile, snap))

	if _, err := os.Stat(memoFile); err != nil {
		// File not written yet.
		hds, files, err := readHeadersFromSheet(snap, e)
		if err != nil { return nil, nil, err }

        f, err := os.Create(memoFile)
        if err != nil { return nil, nil, err }
        defer f.Close()

		raws := make([]io.RawGotetraHeader, len(hds))
		for i := range raws { raws[i] = hds[i].RawGotetraHeader
		}
        binary.Write(f, binary.LittleEndian, raws)

		return hds, files, nil
	} else {
		// File exists: read from it instead.

		f, err := os.Open(memoFile)
        if err != nil { return nil, nil, err }
        defer f.Close()

		hds := make([]io.GotetraHeader, e.Blocks())
		raws := make([]io.RawGotetraHeader, e.Blocks())
        binary.Read(f, binary.LittleEndian, raws)
		for i := range hds { raws[i].Postprocess(&hds[i]) }
		files := make([]string, e.Blocks())
		for i := range files { files[i] = e.ParticleCatalog(snap, i) }

		return hds, files, nil
	}
}
<commit_msg>Propagated changes in cmd/halo interfaces to cmd/memo<commit_after>package memo

import (
	"encoding/binary"
	"fmt"
	"os"
	"path"
	"sort"

	"github.com/phil-mansfield/shellfish/cmd/env"

	"github.com/phil-mansfield/shellfish/cmd/halo"
	"github.com/phil-mansfield/shellfish/io"
)

// TODO: rewrite the six return values as a alice of slices.

const (
	rockstarMemoDir = "rockstar"
	rockstarMemoFile = "halo_%d.dat"
	rockstarShortMemoFile = "halo_short_%d.dat"
	rockstarShortMemoNum = 10 * 1000

	headerMemoFile = "hd_snap%d.dat"
)

// ReadSortedRockstarIDs returns a slice of IDs corresponding to the highest
// values of some quantity in a particular snapshot. maxID is the number of
// halos to return.
func ReadSortedRockstarIDs(
	snap, maxID int, vars *halo.VarColumns, e *env.Environment,
) ([]int, error) {
	dir := path.Join(e.MemoDir, rockstarMemoDir)
	if err, _ := os.Stat(dir); err != nil { os.Mkdir(dir, 0777) }

	var (
		ids []int
		ms []float64
		err error
	)
	if maxID >= rockstarShortMemoNum || maxID == -1 {
		file := path.Join(dir, fmt.Sprintf(rockstarMemoFile, snap))
		ids, _, _, _, ms, _, err = readRockstar(
			file, -1, snap, nil, vars, e,
		)
		if err != nil { return nil, err }
	} else {
		file := path.Join(dir, fmt.Sprintf(rockstarShortMemoFile, snap))
		ids, _, _, _, ms, _, err = readRockstar(
			file, rockstarShortMemoNum, snap, nil, vars, e,
		)
		if err != nil { return nil, err }
	}

	if len(ids) < maxID {
		return nil, fmt.Errorf(
			"ID %d too large for snapshot %d", maxID, snap,
		)
	}

	sortRockstar(ids, ms)
	if maxID == -1 { return ids, nil }
	return ids[:maxID+1], nil
}

type massSet struct {
	ids []int
	ms []float64
}

func (set massSet) Len() int { return len(set.ids) }
// We're reverse sorting.
func (set massSet) Less(i, j int) bool { return set.ms[i] > set.ms[j] }
func (set massSet) Swap(i, j int) {
	set.ms[i], set.ms[j] = set.ms[j], set.ms[i]
	set.ids[i], set.ids[j] = set.ids[j], set.ids[i]
}

func sortRockstar(ids []int, ms []float64) {
	set := massSet{ ids, ms }
	sort.Sort(set)
}

// This function does fairly large heap allocations even when it doesn't need
// to. Consider passing it a buffer.
func ReadRockstar(
	snap int, ids []int, vars *halo.VarColumns, e *env.Environment,
) (outIDs []int, xs, ys, zs, ms, rs []float64, err error) {
	// Find binFile.
	dir := path.Join(e.MemoDir, rockstarMemoDir)
	if err, _ := os.Stat(dir); err != nil { os.Mkdir(dir, 0777) }

	binFile := path.Join(dir, fmt.Sprintf(rockstarMemoFile, snap))
	shortBinFile := path.Join(dir, fmt.Sprintf(rockstarShortMemoFile, snap))

	// This wastes a read the first time it's called. You need to decide if you
	// care. (Answer: probably.)
	outIDs, xs, ys, zs, ms, rs, err = readRockstar(
		shortBinFile, rockstarShortMemoNum, snap, ids, vars, e,
	)
	// TODO: Fix error handling here.
	if err == nil { return outIDs, xs, ys, zs, ms, rs, err }
	outIDs, xs, ys, zs, ms, rs, err = readRockstar(
		binFile, -1, snap, ids, vars, e,
	)
	return outIDs, xs, ys, zs, ms, rs, nil
}

func readRockstar(
	binFile string, n, snap int, ids []int,
	vars *halo.VarColumns, e *env.Environment,
) (outIDs []int, xs, ys, zs, ms, rs []float64, err error) {
	hds, _, err := ReadHeaders(snap, e)
	if err != nil { return nil, nil, nil, nil, nil, nil, err }
	hd := &hds[0]

	// If binFile doesn't exist, create it.
	if _, err := os.Stat(binFile); err != nil {
		if n == -1 {
			err = halo.RockstarConvert(
				e.HaloCatalog(snap), binFile, vars, &hd.Cosmo,
			)
			if err != nil { return nil, nil, nil, nil, nil, nil, err }
		} else {
			err = halo.RockstarConvertTopN(
				e.HaloCatalog(snap), binFile, n, vars, &hd.Cosmo,
			)
			if err != nil { return nil, nil, nil, nil, nil, nil, err }
		}
	}

	rids, xs, ys, zs, ms, rs, err := halo.ReadBinaryRockstar(binFile)
	if err != nil { return nil, nil, nil, nil, nil, nil, err }
	rvals := [][]float64{ xs, ys, zs, ms, rs }

	// Select out only the IDs we want.
	if ids == nil { return rids, xs, ys, zs, ms, rs, nil }
	vals := make([][]float64, len(rvals))

	for i := range vals { vals[i] = make([]float64, len(ids)) }
	f := NewIntFinder(rids)
	for i, id := range ids {
		line, ok := f.Find(id)
		err = fmt.Errorf("Could not find ID %d", id)
		if !ok { return nil, nil, nil, nil, nil, nil, err }
		for vi := range vals { vals[vi][i] = rvals[vi][line] }
	}
	xs, ys, zs, ms, rs = vals[0], vals[1], vals[2], vals[3], vals[4]

	return ids, xs, ys, zs, ms, rs, nil
}

// A quick generic wrapper for doing those one-to-one mappings I need to do so
// often. Written like this so the backend can be swapped out easily.
type IntFinder struct {
	m map[int]int
}

// NewIntFinder creates a new IntFinder struct for a given slice of Rockstar
// IDs.
func NewIntFinder(rids []int) IntFinder {
	f := IntFinder{}
	f.m = make(map[int]int)
	for i, rid := range rids { f.m[rid] = i }
	return f
}

// Find returns the index which the given ID corresponds to and true if the
// ID is in the finder. Otherwise, false is returned.
func (f IntFinder) Find(rid int) (int, bool) {
	line, ok := f.m[rid]
	return line, ok
}

func readHeadersFromSheet(
	snap int, e *env.Environment,
) ([]io.GotetraHeader, []string, error) {
	files := make([]string, e.Blocks())
	hds := make([]io.GotetraHeader, e.Blocks())
	for i := range files {
		files[i] = e.ParticleCatalog(snap, i)
		err := io.ReadSheetHeaderAt(files[i], &hds[i])
		if err != nil { return nil, nil, err }
	}
	return hds, files, nil
}

// ReadHeaders returns all the segment headers and segment file names for all
// the segments at a given snapshot.
func ReadHeaders(
	snap int, e *env.Environment,
) ([]io.GotetraHeader, []string, error) {
	if _, err := os.Stat(e.MemoDir); err != nil { return nil, nil, err }
	memoFile := path.Join(e.MemoDir, fmt.Sprintf(headerMemoFile, snap))

	if _, err := os.Stat(memoFile); err != nil {
		// File not written yet.
		hds, files, err := readHeadersFromSheet(snap, e)
		if err != nil { return nil, nil, err }

        f, err := os.Create(memoFile)
        if err != nil { return nil, nil, err }
        defer f.Close()

		raws := make([]io.RawGotetraHeader, len(hds))
		for i := range raws { raws[i] = hds[i].RawGotetraHeader
		}
        binary.Write(f, binary.LittleEndian, raws)

		return hds, files, nil
	} else {
		// File exists: read from it instead.

		f, err := os.Open(memoFile)
        if err != nil { return nil, nil, err }
        defer f.Close()

		hds := make([]io.GotetraHeader, e.Blocks())
		raws := make([]io.RawGotetraHeader, e.Blocks())
        binary.Read(f, binary.LittleEndian, raws)
		for i := range hds { raws[i].Postprocess(&hds[i]) }
		files := make([]string, e.Blocks())
		for i := range files { files[i] = e.ParticleCatalog(snap, i) }

		return hds, files, nil
	}
}
<|endoftext|>


<commit_before>package main

// The Cursor struct stores the location of the cursor in the view
// The complicated part about the cursor is storing its location.
// The cursor must be displayed at an x, y location, but since the buffer
// uses a rope to store text, to insert text we must have an index. It
// is also simpler to use character indicies for other tasks such as
// selection.
type Cursor struct {
	buf *Buffer
	Loc

	// Last cursor x position
	LastVisualX int

	// The current selection as a range of character numbers (inclusive)
	CurSelection [2]Loc
	// The original selection as a range of character numbers
	// This is used for line and word selection where it is necessary
	// to know what the original selection was
	OrigSelection [2]Loc
}

// Goto puts the cursor at the given cursor's location and gives the current cursor its selection too
func (c *Cursor) Goto(b Cursor) {
	c.X, c.Y, c.LastVisualX = b.X, b.Y, b.LastVisualX
	c.OrigSelection, c.CurSelection = b.OrigSelection, b.CurSelection
}

// ResetSelection resets the user's selection
func (c *Cursor) ResetSelection() {
	c.CurSelection[0] = c.buf.Start()
	c.CurSelection[1] = c.buf.Start()
}

// HasSelection returns whether or not the user has selected anything
func (c *Cursor) HasSelection() bool {
	return c.CurSelection[0] != c.CurSelection[1]
}

// DeleteSelection deletes the currently selected text
func (c *Cursor) DeleteSelection() {
	if c.CurSelection[0].GreaterThan(c.CurSelection[1]) {
		c.buf.Remove(c.CurSelection[1], c.CurSelection[0])
		c.Loc = c.CurSelection[1]
	} else if c.GetSelection() == "" {
		return
	} else {
		c.buf.Remove(c.CurSelection[0], c.CurSelection[1])
		c.Loc = c.CurSelection[0]
	}
}

// GetSelection returns the cursor's selection
func (c *Cursor) GetSelection() string {
	if c.CurSelection[0].GreaterThan(c.CurSelection[1]) {
		return c.buf.Substr(c.CurSelection[1], c.CurSelection[0])
	}
	return c.buf.Substr(c.CurSelection[0], c.CurSelection[1])
}

// SelectLine selects the current line
func (c *Cursor) SelectLine() {
	c.Start()
	c.CurSelection[0] = c.Loc
	c.End()
	if c.buf.NumLines-1 > c.Y {
		c.CurSelection[1] = c.Loc.Move(1, c.buf)
	} else {
		c.CurSelection[1] = c.Loc
	}

	c.OrigSelection = c.CurSelection
}

// AddLineToSelection adds the current line to the selection
func (c *Cursor) AddLineToSelection() {
	if c.Loc.LessThan(c.OrigSelection[0]) {
		c.Start()
		c.CurSelection[0] = c.Loc
		c.CurSelection[1] = c.OrigSelection[1]
	}
	if c.Loc.GreaterThan(c.OrigSelection[1]) {
		c.End()
		c.CurSelection[1] = c.Loc.Move(1, c.buf)
		c.CurSelection[0] = c.OrigSelection[0]
	}

	if c.Loc.LessThan(c.OrigSelection[1]) && c.Loc.GreaterThan(c.OrigSelection[0]) {
		c.CurSelection = c.OrigSelection
	}
}

// SelectWord selects the word the cursor is currently on
func (c *Cursor) SelectWord() {
	if len(c.buf.Line(c.Y)) == 0 {
		return
	}

	if !IsWordChar(string(c.RuneUnder(c.X))) {
		c.CurSelection[0] = c.Loc
		c.CurSelection[1] = c.Loc.Move(1, c.buf)
		c.OrigSelection = c.CurSelection
		return
	}

	forward, backward := c.X, c.X

	for backward > 0 && IsWordChar(string(c.RuneUnder(backward-1))) {
		backward--
	}

	c.CurSelection[0] = Loc{backward, c.Y}
	c.OrigSelection[0] = c.CurSelection[0]

	for forward < Count(c.buf.Line(c.Y))-1 && IsWordChar(string(c.RuneUnder(forward+1))) {
		forward++
	}

	c.CurSelection[1] = Loc{forward, c.Y}.Move(1, c.buf)
	c.OrigSelection[1] = c.CurSelection[1]
	c.Loc = c.CurSelection[1]
}

// AddWordToSelection adds the word the cursor is currently on to the selection
func (c *Cursor) AddWordToSelection() {
	if c.Loc.GreaterThan(c.OrigSelection[0]) && c.Loc.LessThan(c.OrigSelection[1]) {
		c.CurSelection = c.OrigSelection
		return
	}

	if c.Loc.LessThan(c.OrigSelection[0]) {
		backward := c.X

		for backward > 0 && IsWordChar(string(c.RuneUnder(backward-1))) {
			backward--
		}

		c.CurSelection[0] = Loc{backward, c.Y}
		c.CurSelection[1] = c.OrigSelection[1]
	}

	if c.Loc.GreaterThan(c.OrigSelection[1]) {
		forward := c.X

		for forward < Count(c.buf.Line(c.Y))-1 && IsWordChar(string(c.RuneUnder(forward+1))) {
			forward++
		}

		c.CurSelection[1] = Loc{forward, c.Y}.Move(1, c.buf)
		c.CurSelection[0] = c.OrigSelection[0]
	}

	c.Loc = c.CurSelection[1]
}

// SelectTo selects from the current cursor location to the given location
func (c *Cursor) SelectTo(loc Loc) {
	if loc.GreaterThan(c.OrigSelection[0]) {
		c.CurSelection[0] = c.OrigSelection[0]
		c.CurSelection[1] = loc
	} else {
		c.CurSelection[0] = loc
		c.CurSelection[1] = c.OrigSelection[0]
	}
}

// WordRight moves the cursor one word to the right
func (c *Cursor) WordRight() {
	c.Right()
	for IsWhitespace(c.RuneUnder(c.X)) {
		if c.X == Count(c.buf.Line(c.Y)) {
			return
		}
		c.Right()
	}
	for !IsWhitespace(c.RuneUnder(c.X)) {
		if c.X == Count(c.buf.Line(c.Y)) {
			return
		}
		c.Right()
	}
}

// WordLeft moves the cursor one word to the left
func (c *Cursor) WordLeft() {
	c.Left()
	for IsWhitespace(c.RuneUnder(c.X)) {
		if c.X == 0 {
			return
		}
		c.Left()
	}
	for !IsWhitespace(c.RuneUnder(c.X)) {
		if c.X == 0 {
			return
		}
		c.Left()
	}
	c.Right()
}

// RuneUnder returns the rune under the given x position
func (c *Cursor) RuneUnder(x int) rune {
	line := []rune(c.buf.Line(c.Y))
	if len(line) == 0 {
		return '\n'
	}
	if x >= len(line) {
		return '\n'
	} else if x < 0 {
		x = 0
	}
	return line[x]
}

// UpN moves the cursor up N lines (if possible)
func (c *Cursor) UpN(amount int) {
	proposedY := c.Y - amount
	if proposedY < 0 {
		proposedY = 0
	} else if proposedY >= c.buf.NumLines {
		proposedY = c.buf.NumLines - 1
	}
	if proposedY == c.Y {
		return
	}

	c.Y = proposedY
	runes := []rune(c.buf.Line(c.Y))
	c.X = c.GetCharPosInLine(c.Y, c.LastVisualX)
	if c.X > len(runes) {
		c.X = len(runes)
	}
}

// DownN moves the cursor down N lines (if possible)
func (c *Cursor) DownN(amount int) {
	c.UpN(-amount)
}

// Up moves the cursor up one line (if possible)
func (c *Cursor) Up() {
	c.UpN(1)
}

// Down moves the cursor down one line (if possible)
func (c *Cursor) Down() {
	c.DownN(1)
}

// Left moves the cursor left one cell (if possible) or to the last line if it is at the beginning
func (c *Cursor) Left() {
	if c.Loc == c.buf.Start() {
		return
	}
	if c.X > 0 {
		c.X--
	} else {
		c.Up()
		c.End()
	}
	c.LastVisualX = c.GetVisualX()
}

// Right moves the cursor right one cell (if possible) or to the next line if it is at the end
func (c *Cursor) Right() {
	if c.Loc == c.buf.End() {
		return
	}
	if c.X < Count(c.buf.Line(c.Y)) {
		c.X++
	} else {
		c.Down()
		c.Start()
	}
	c.LastVisualX = c.GetVisualX()
}

// End moves the cursor to the end of the line it is on
func (c *Cursor) End() {
	c.X = Count(c.buf.Line(c.Y))
	c.LastVisualX = c.GetVisualX()
}

// Start moves the cursor to the start of the line it is on
func (c *Cursor) Start() {
	c.X = 0
	c.LastVisualX = c.GetVisualX()
}

// GetCharPosInLine gets the char position of a visual x y coordinate (this is necessary because tabs are 1 char but 4 visual spaces)
func (c *Cursor) GetCharPosInLine(lineNum, visualPos int) int {
	// Get the tab size
	tabSize := int(settings["tabsize"].(float64))
	visualLineLen := StringWidth(c.buf.Line(lineNum))
	if visualPos > visualLineLen {
		visualPos = visualLineLen
	}
	width := WidthOfLargeRunes(c.buf.Line(lineNum))
	if visualPos >= width {
		return visualPos - width
	}
	return visualPos / tabSize
}

// GetVisualX returns the x value of the cursor in visual spaces
func (c *Cursor) GetVisualX() int {
	runes := []rune(c.buf.Line(c.Y))
	return StringWidth(string(runes[:c.X]))
}

// Relocate makes sure that the cursor is inside the bounds of the buffer
// If it isn't, it moves it to be within the buffer's lines
func (c *Cursor) Relocate() {
	if c.Y < 0 {
		c.Y = 0
	} else if c.Y >= c.buf.NumLines {
		c.Y = c.buf.NumLines - 1
	}

	if c.X < 0 {
		c.X = 0
	} else if c.X > Count(c.buf.Line(c.Y)) {
		c.X = Count(c.buf.Line(c.Y))
	}
}
<commit_msg>Improve cursor word movement<commit_after>package main

// The Cursor struct stores the location of the cursor in the view
// The complicated part about the cursor is storing its location.
// The cursor must be displayed at an x, y location, but since the buffer
// uses a rope to store text, to insert text we must have an index. It
// is also simpler to use character indicies for other tasks such as
// selection.
type Cursor struct {
	buf *Buffer
	Loc

	// Last cursor x position
	LastVisualX int

	// The current selection as a range of character numbers (inclusive)
	CurSelection [2]Loc
	// The original selection as a range of character numbers
	// This is used for line and word selection where it is necessary
	// to know what the original selection was
	OrigSelection [2]Loc
}

// Goto puts the cursor at the given cursor's location and gives the current cursor its selection too
func (c *Cursor) Goto(b Cursor) {
	c.X, c.Y, c.LastVisualX = b.X, b.Y, b.LastVisualX
	c.OrigSelection, c.CurSelection = b.OrigSelection, b.CurSelection
}

// ResetSelection resets the user's selection
func (c *Cursor) ResetSelection() {
	c.CurSelection[0] = c.buf.Start()
	c.CurSelection[1] = c.buf.Start()
}

// HasSelection returns whether or not the user has selected anything
func (c *Cursor) HasSelection() bool {
	return c.CurSelection[0] != c.CurSelection[1]
}

// DeleteSelection deletes the currently selected text
func (c *Cursor) DeleteSelection() {
	if c.CurSelection[0].GreaterThan(c.CurSelection[1]) {
		c.buf.Remove(c.CurSelection[1], c.CurSelection[0])
		c.Loc = c.CurSelection[1]
	} else if c.GetSelection() == "" {
		return
	} else {
		c.buf.Remove(c.CurSelection[0], c.CurSelection[1])
		c.Loc = c.CurSelection[0]
	}
}

// GetSelection returns the cursor's selection
func (c *Cursor) GetSelection() string {
	if c.CurSelection[0].GreaterThan(c.CurSelection[1]) {
		return c.buf.Substr(c.CurSelection[1], c.CurSelection[0])
	}
	return c.buf.Substr(c.CurSelection[0], c.CurSelection[1])
}

// SelectLine selects the current line
func (c *Cursor) SelectLine() {
	c.Start()
	c.CurSelection[0] = c.Loc
	c.End()
	if c.buf.NumLines-1 > c.Y {
		c.CurSelection[1] = c.Loc.Move(1, c.buf)
	} else {
		c.CurSelection[1] = c.Loc
	}

	c.OrigSelection = c.CurSelection
}

// AddLineToSelection adds the current line to the selection
func (c *Cursor) AddLineToSelection() {
	if c.Loc.LessThan(c.OrigSelection[0]) {
		c.Start()
		c.CurSelection[0] = c.Loc
		c.CurSelection[1] = c.OrigSelection[1]
	}
	if c.Loc.GreaterThan(c.OrigSelection[1]) {
		c.End()
		c.CurSelection[1] = c.Loc.Move(1, c.buf)
		c.CurSelection[0] = c.OrigSelection[0]
	}

	if c.Loc.LessThan(c.OrigSelection[1]) && c.Loc.GreaterThan(c.OrigSelection[0]) {
		c.CurSelection = c.OrigSelection
	}
}

// SelectWord selects the word the cursor is currently on
func (c *Cursor) SelectWord() {
	if len(c.buf.Line(c.Y)) == 0 {
		return
	}

	if !IsWordChar(string(c.RuneUnder(c.X))) {
		c.CurSelection[0] = c.Loc
		c.CurSelection[1] = c.Loc.Move(1, c.buf)
		c.OrigSelection = c.CurSelection
		return
	}

	forward, backward := c.X, c.X

	for backward > 0 && IsWordChar(string(c.RuneUnder(backward-1))) {
		backward--
	}

	c.CurSelection[0] = Loc{backward, c.Y}
	c.OrigSelection[0] = c.CurSelection[0]

	for forward < Count(c.buf.Line(c.Y))-1 && IsWordChar(string(c.RuneUnder(forward+1))) {
		forward++
	}

	c.CurSelection[1] = Loc{forward, c.Y}.Move(1, c.buf)
	c.OrigSelection[1] = c.CurSelection[1]
	c.Loc = c.CurSelection[1]
}

// AddWordToSelection adds the word the cursor is currently on to the selection
func (c *Cursor) AddWordToSelection() {
	if c.Loc.GreaterThan(c.OrigSelection[0]) && c.Loc.LessThan(c.OrigSelection[1]) {
		c.CurSelection = c.OrigSelection
		return
	}

	if c.Loc.LessThan(c.OrigSelection[0]) {
		backward := c.X

		for backward > 0 && IsWordChar(string(c.RuneUnder(backward-1))) {
			backward--
		}

		c.CurSelection[0] = Loc{backward, c.Y}
		c.CurSelection[1] = c.OrigSelection[1]
	}

	if c.Loc.GreaterThan(c.OrigSelection[1]) {
		forward := c.X

		for forward < Count(c.buf.Line(c.Y))-1 && IsWordChar(string(c.RuneUnder(forward+1))) {
			forward++
		}

		c.CurSelection[1] = Loc{forward, c.Y}.Move(1, c.buf)
		c.CurSelection[0] = c.OrigSelection[0]
	}

	c.Loc = c.CurSelection[1]
}

// SelectTo selects from the current cursor location to the given location
func (c *Cursor) SelectTo(loc Loc) {
	if loc.GreaterThan(c.OrigSelection[0]) {
		c.CurSelection[0] = c.OrigSelection[0]
		c.CurSelection[1] = loc
	} else {
		c.CurSelection[0] = loc
		c.CurSelection[1] = c.OrigSelection[0]
	}
}

// WordRight moves the cursor one word to the right
func (c *Cursor) WordRight() {
	for IsWhitespace(c.RuneUnder(c.X)) {
		if c.X == Count(c.buf.Line(c.Y)) {
			c.Right()
			return
		}
		c.Right()
	}
	c.Right()
	for IsWordChar(string(c.RuneUnder(c.X))) {
		if c.X == Count(c.buf.Line(c.Y)) {
			return
		}
		c.Right()
	}
}

// WordLeft moves the cursor one word to the left
func (c *Cursor) WordLeft() {
	for IsWhitespace(c.RuneUnder(c.X)) {
		if c.X == 0 {
			c.Left()
			return
		}
		c.Left()
	}
	c.Left()
	for IsWordChar(string(c.RuneUnder(c.X))) {
		if c.X == 0 {
			return
		}
		c.Left()
	}
}

// RuneUnder returns the rune under the given x position
func (c *Cursor) RuneUnder(x int) rune {
	line := []rune(c.buf.Line(c.Y))
	if len(line) == 0 {
		return '\n'
	}
	if x >= len(line) {
		return '\n'
	} else if x < 0 {
		x = 0
	}
	return line[x]
}

// UpN moves the cursor up N lines (if possible)
func (c *Cursor) UpN(amount int) {
	proposedY := c.Y - amount
	if proposedY < 0 {
		proposedY = 0
	} else if proposedY >= c.buf.NumLines {
		proposedY = c.buf.NumLines - 1
	}
	if proposedY == c.Y {
		return
	}

	c.Y = proposedY
	runes := []rune(c.buf.Line(c.Y))
	c.X = c.GetCharPosInLine(c.Y, c.LastVisualX)
	if c.X > len(runes) {
		c.X = len(runes)
	}
}

// DownN moves the cursor down N lines (if possible)
func (c *Cursor) DownN(amount int) {
	c.UpN(-amount)
}

// Up moves the cursor up one line (if possible)
func (c *Cursor) Up() {
	c.UpN(1)
}

// Down moves the cursor down one line (if possible)
func (c *Cursor) Down() {
	c.DownN(1)
}

// Left moves the cursor left one cell (if possible) or to the last line if it is at the beginning
func (c *Cursor) Left() {
	if c.Loc == c.buf.Start() {
		return
	}
	if c.X > 0 {
		c.X--
	} else {
		c.Up()
		c.End()
	}
	c.LastVisualX = c.GetVisualX()
}

// Right moves the cursor right one cell (if possible) or to the next line if it is at the end
func (c *Cursor) Right() {
	if c.Loc == c.buf.End() {
		return
	}
	if c.X < Count(c.buf.Line(c.Y)) {
		c.X++
	} else {
		c.Down()
		c.Start()
	}
	c.LastVisualX = c.GetVisualX()
}

// End moves the cursor to the end of the line it is on
func (c *Cursor) End() {
	c.X = Count(c.buf.Line(c.Y))
	c.LastVisualX = c.GetVisualX()
}

// Start moves the cursor to the start of the line it is on
func (c *Cursor) Start() {
	c.X = 0
	c.LastVisualX = c.GetVisualX()
}

// GetCharPosInLine gets the char position of a visual x y coordinate (this is necessary because tabs are 1 char but 4 visual spaces)
func (c *Cursor) GetCharPosInLine(lineNum, visualPos int) int {
	// Get the tab size
	tabSize := int(settings["tabsize"].(float64))
	visualLineLen := StringWidth(c.buf.Line(lineNum))
	if visualPos > visualLineLen {
		visualPos = visualLineLen
	}
	width := WidthOfLargeRunes(c.buf.Line(lineNum))
	if visualPos >= width {
		return visualPos - width
	}
	return visualPos / tabSize
}

// GetVisualX returns the x value of the cursor in visual spaces
func (c *Cursor) GetVisualX() int {
	runes := []rune(c.buf.Line(c.Y))
	return StringWidth(string(runes[:c.X]))
}

// Relocate makes sure that the cursor is inside the bounds of the buffer
// If it isn't, it moves it to be within the buffer's lines
func (c *Cursor) Relocate() {
	if c.Y < 0 {
		c.Y = 0
	} else if c.Y >= c.buf.NumLines {
		c.Y = c.buf.NumLines - 1
	}

	if c.X < 0 {
		c.X = 0
	} else if c.X > Count(c.buf.Line(c.Y)) {
		c.X = Count(c.buf.Line(c.Y))
	}
}
<|endoftext|>


<commit_before>package mods

import (
	"context"
	"fmt"

	"github.com/danielkrainas/gobag/cmd"
	"github.com/danielkrainas/gobag/context"

	"github.com/danielkrainas/shex/api/v1"
	"github.com/danielkrainas/shex/cmd/cmdutils"
	"github.com/danielkrainas/shex/manager"
	"github.com/danielkrainas/shex/mods"
)

func init() {
	cmd.Register("mods", Info)
}

var (
	Info = &cmd.Info{
		Use:   "mods",
		Short: "mods",
		Long:  "mods",
		SubCommands: []*cmd.Info{
			{
				Use:   "list",
				Short: "list",
				Long:  "list",
				Run:   cmd.ExecutorFunc(listMods),
				Flags: []*cmd.Flag{
					{
						Long:        "profile",
						Short:       "p",
						Description: "display mods installed in a profile",
					},
				},
			},
		},
	}
)

/* List Mods Command
type listModsCommand struct {
	Profile string `short:"p" long:"profile" description:"display mods installed in a profile"`
} */

func listMods(ctx context.Context, args []string) error {
	m, err := cmdutils.LoadManager(ctx)
	if err != nil {
		return err
	}

	profileName := acontext.GetStringValue(ctx, "flags.profile")
	useProfile := profileName != ""
	var list v1.ModList
	if useProfile {
		if len(profileName) > 0 {
			selectedProfile, ok := m.Profiles()[profileName]
			if !ok {
				return fmt.Errorf("profile not found: %q", profileName)
			}

			list = selectedProfile.Mods
		} else {
			profileName = m.Profile().Name
			list = m.Profile().Mods
		}
	} else if len(m.Config().Games) <= 0 {
		fmt.Println("no games attached")
		return nil
	} else {
		gameName := ""
		if len(args) > 0 {
			gameName = args[0]
		}

		game := manager.GetGameOrDefault(m.Config().Games, gameName)
		manifest, err := mods.LoadGameManifest(m.Fs(), game.String())
		if err != nil {
			fmt.Printf("error loading game manifest: %v", err)
			fmt.Println("game manifest not found or invalid")
			return nil
		}

		list = manifest.Mods
	}

	//fmt.Printf("%-30s   %s\n", "NAME", "VERSION")
	if len(list) > 0 {
		if useProfile {
			fmt.Printf("Mods installed in profile %s\n", profileName)
		}

		for name, version := range list {
			fmt.Printf("%15s@%s\n", name, version)
		}
	} else {
		fmt.Printf("no mods installed\n")
	}

	return nil
}
<commit_msg>trim dead stuff<commit_after>package mods

import (
	"context"
	"fmt"

	"github.com/danielkrainas/gobag/cmd"
	"github.com/danielkrainas/gobag/context"

	"github.com/danielkrainas/shex/api/v1"
	"github.com/danielkrainas/shex/cmd/cmdutils"
	"github.com/danielkrainas/shex/manager"
	"github.com/danielkrainas/shex/mods"
)

func init() {
	cmd.Register("mods", Info)
}

var (
	Info = &cmd.Info{
		Use:   "mods",
		Short: "mods",
		Long:  "mods",
		SubCommands: []*cmd.Info{
			{
				Use:   "list",
				Short: "list",
				Long:  "list",
				Run:   cmd.ExecutorFunc(listMods),
				Flags: []*cmd.Flag{
					{
						Long:        "profile",
						Short:       "p",
						Description: "display mods installed in a profile",
					},
				},
			},
		},
	}
)

/* List Mods Command */
func listMods(ctx context.Context, args []string) error {
	m, err := cmdutils.LoadManager(ctx)
	if err != nil {
		return err
	}

	profileName := acontext.GetStringValue(ctx, "flags.profile")
	useProfile := profileName != ""
	var list v1.ModList
	if useProfile {
		if len(profileName) > 0 {
			selectedProfile, ok := m.Profiles()[profileName]
			if !ok {
				return fmt.Errorf("profile not found: %q", profileName)
			}

			list = selectedProfile.Mods
		} else {
			profileName = m.Profile().Name
			list = m.Profile().Mods
		}
	} else if len(m.Config().Games) <= 0 {
		fmt.Println("no games attached")
		return nil
	} else {
		gameName := ""
		if len(args) > 0 {
			gameName = args[0]
		}

		game := manager.GetGameOrDefault(m.Config().Games, gameName)
		manifest, err := mods.LoadGameManifest(m.Fs(), game.String())
		if err != nil {
			fmt.Printf("error loading game manifest: %v", err)
			fmt.Println("game manifest not found or invalid")
			return nil
		}

		list = manifest.Mods
	}

	//fmt.Printf("%-30s   %s\n", "NAME", "VERSION")
	if len(list) > 0 {
		if useProfile {
			fmt.Printf("Mods installed in profile %s\n", profileName)
		}

		for name, version := range list {
			fmt.Printf("%15s@%s\n", name, version)
		}
	} else {
		fmt.Printf("no mods installed\n")
	}

	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"image"
	"image/jpeg"
	"log"
	"os"

	"github.com/donatj/mpo"
)

var (
	format = flag.String("format", "stereo", "Output format [stereo|red-cyan|cyan-red|red-green|green-red]")
	output = flag.String("outfile", "output.jpg", "Output filename")
	help   = flag.Bool("help", false, "Displays this text")
)

func init() {
	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, "Usage: %s <mpofile>\n", os.Args[0])
		flag.PrintDefaults()
	}

	flag.Parse()

	if flag.NArg() != 1 {
		flag.Usage()
		os.Exit(2)
	}
}

func main() {
	r, err := os.Open(flag.Arg(0))
	if err != nil {
		log.Fatalf("err on %v %s", err, flag.Arg(0))
	}

	m, err := mpo.DecodeAll(r)
	if err != nil {
		log.Fatalf("err on %v %s", err, flag.Arg(0))
	}

	var img image.Image
	switch *format {
	case "stereo":
		img = m.ConvertToStereo()
	case "red-cyan":
		img, err = m.ConvertToAnaglyph(mpo.RedCyan)
		if err != nil {
			log.Fatal(err)
		}
	case "cyan-red":
		img, err = m.ConvertToAnaglyph(mpo.CyanRed)
		if err != nil {
			log.Fatal(err)
		}
	case "red-green":
		img, err = m.ConvertToAnaglyph(mpo.RedGreen)
		if err != nil {
			log.Fatal(err)
		}
	case "green-red":
		img, err = m.ConvertToAnaglyph(mpo.GreenRed)
		if err != nil {
			log.Fatal(err)
		}
	default:
		log.Fatal("Unknown format:", *format)
	}

	f, err := os.OpenFile(*output, os.O_CREATE|os.O_WRONLY, 0666)
	if err != nil {
		log.Fatal(err)
	}

	if err = jpeg.Encode(f, img, nil); err != nil {
		log.Fatal(err)
	}
}
<commit_msg>add missing help usage<commit_after>package main

import (
	"flag"
	"fmt"
	"image"
	"image/jpeg"
	"log"
	"os"

	"github.com/donatj/mpo"
)

var (
	format = flag.String("format", "stereo", "Output format [stereo|red-cyan|cyan-red|red-green|green-red]")
	output = flag.String("outfile", "output.jpg", "Output filename")
	help   = flag.Bool("help", false, "Displays this text")
)

func init() {
	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, "Usage: %s <mpofile>\n", os.Args[0])
		flag.PrintDefaults()
	}

	flag.Parse()

	if flag.NArg() != 1 || *help {
		flag.Usage()
		os.Exit(2)
	}
}

func main() {
	r, err := os.Open(flag.Arg(0))
	if err != nil {
		log.Fatalf("err on %v %s", err, flag.Arg(0))
	}

	m, err := mpo.DecodeAll(r)
	if err != nil {
		log.Fatalf("err on %v %s", err, flag.Arg(0))
	}

	var img image.Image
	switch *format {
	case "stereo":
		img = m.ConvertToStereo()
	case "red-cyan":
		img, err = m.ConvertToAnaglyph(mpo.RedCyan)
		if err != nil {
			log.Fatal(err)
		}
	case "cyan-red":
		img, err = m.ConvertToAnaglyph(mpo.CyanRed)
		if err != nil {
			log.Fatal(err)
		}
	case "red-green":
		img, err = m.ConvertToAnaglyph(mpo.RedGreen)
		if err != nil {
			log.Fatal(err)
		}
	case "green-red":
		img, err = m.ConvertToAnaglyph(mpo.GreenRed)
		if err != nil {
			log.Fatal(err)
		}
	default:
		log.Fatal("Unknown format:", *format)
	}

	f, err := os.OpenFile(*output, os.O_CREATE|os.O_WRONLY, 0666)
	if err != nil {
		log.Fatal(err)
	}

	if err = jpeg.Encode(f, img, nil); err != nil {
		log.Fatal(err)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"os"

	"github.com/spf13/cobra"

	"github.com/fatih/color"

	"github.com/mutagen-io/mutagen/cmd"
	"github.com/mutagen-io/mutagen/cmd/mutagen/daemon"
	"github.com/mutagen-io/mutagen/cmd/mutagen/forward"
	"github.com/mutagen-io/mutagen/cmd/mutagen/project"
	"github.com/mutagen-io/mutagen/cmd/mutagen/sync"
	"github.com/mutagen-io/mutagen/cmd/mutagen/tunnel"
	"github.com/mutagen-io/mutagen/pkg/prompt"
)

func rootMain(command *cobra.Command, arguments []string) error {
	// If no commands were given, then print help information and bail. We don't
	// have to worry about warning about arguments being present here (which
	// would be incorrect usage) because arguments can't even reach this point
	// (they will be mistaken for subcommands and a error will be displayed).
	command.Help()

	// Success.
	return nil
}

var rootCommand = &cobra.Command{
	Use:          "mutagen",
	Short:        "Mutagen is a remote development tool built on high-performance synchronization",
	RunE:         rootMain,
	SilenceUsage: true,
}

var rootConfiguration struct {
	// help indicates whether or not help information should be shown for the
	// command.
	help bool
}

func init() {
	// Disable alphabetical sorting of commands in help output. This is a global
	// setting that affects all Cobra command instances.
	cobra.EnableCommandSorting = false

	// Disable Cobra's use of mousetrap. This breaks daemon registration on
	// Windows because it tries to enforce that the CLI only be launched from
	// a console, which it's not when running automatically.
	cobra.MousetrapHelpText = ""

	// Grab a handle for the command line flags.
	flags := rootCommand.Flags()

	// Disable alphabetical sorting of flags in help output.
	flags.SortFlags = false

	// Manually add a help flag to override the default message. Cobra will
	// still implement its logic automatically.
	flags.BoolVarP(&rootConfiguration.help, "help", "h", false, "Show help information")

	// Register commands.
	// HACK: Add the sync commands as direct subcommands of the root command for
	// temporary backward compatibility.
	commands := []*cobra.Command{
		sync.RootCommand,
		forward.RootCommand,
		project.RootCommand,
		tunnel.RootCommand,
		loginCommand,
		logoutCommand,
		daemon.RootCommand,
		versionCommand,
		legalCommand,
		generateCommand,
	}
	commands = append(commands, sync.Commands...)
	rootCommand.AddCommand(commands...)

	// HACK: Register the sync subcommands with the sync command after
	// registering them with the root command so that they have the correct
	// parent command and thus the correct help output.
	sync.RootCommand.AddCommand(sync.Commands...)

	// Enable color support for command usage and error output in the root
	// command and all of its child commands.
	enableColorForCommand(rootCommand)
}

// enableColorForCommand recursively enables colorized usage and error output
// for a command and all of its child commands.
func enableColorForCommand(command *cobra.Command) {
	// Enable color support for the command itself.
	command.SetOut(color.Output)
	command.SetErr(color.Error)

	// Recursively enable color support for child commands.
	for _, c := range command.Commands() {
		enableColorForCommand(c)
	}
}

func main() {
	// Check if a prompting environment is set. If so, treat this as a prompt
	// request. Prompting is sort of a special pseudo-command that's indicated
	// by the presence of an environment variable, and hence it has to be
	// handled in a bit of a special manner.
	if _, ok := os.LookupEnv(prompt.PrompterEnvironmentVariable); ok {
		if err := promptMain(os.Args[1:]); err != nil {
			cmd.Fatal(err)
		}
		return
	}

	// Handle terminal compatibility issues. If this call returns, it means that
	// we should proceed normally.
	cmd.HandleTerminalCompatibility()

	// HACK: Modify the root command's help function to hide sync commands.
	defaultHelpFunction := rootCommand.HelpFunc()
	rootCommand.SetHelpFunc(func(command *cobra.Command, arguments []string) {
		if command == rootCommand {
			for _, command := range sync.Commands {
				command.Hidden = true
			}
		}
		defaultHelpFunction(command, arguments)
	})

	// Execute the root command.
	if err := rootCommand.Execute(); err != nil {
		os.Exit(1)
	}
}
<commit_msg>Restricted Cobra command colorized output hack to Windows.<commit_after>package main

import (
	"os"
	"runtime"

	"github.com/spf13/cobra"

	"github.com/fatih/color"

	"github.com/mutagen-io/mutagen/cmd"
	"github.com/mutagen-io/mutagen/cmd/mutagen/daemon"
	"github.com/mutagen-io/mutagen/cmd/mutagen/forward"
	"github.com/mutagen-io/mutagen/cmd/mutagen/project"
	"github.com/mutagen-io/mutagen/cmd/mutagen/sync"
	"github.com/mutagen-io/mutagen/cmd/mutagen/tunnel"
	"github.com/mutagen-io/mutagen/pkg/prompt"
)

func rootMain(command *cobra.Command, arguments []string) error {
	// If no commands were given, then print help information and bail. We don't
	// have to worry about warning about arguments being present here (which
	// would be incorrect usage) because arguments can't even reach this point
	// (they will be mistaken for subcommands and a error will be displayed).
	command.Help()

	// Success.
	return nil
}

var rootCommand = &cobra.Command{
	Use:          "mutagen",
	Short:        "Mutagen is a remote development tool built on high-performance synchronization",
	RunE:         rootMain,
	SilenceUsage: true,
}

var rootConfiguration struct {
	// help indicates whether or not help information should be shown for the
	// command.
	help bool
}

func init() {
	// Disable alphabetical sorting of commands in help output. This is a global
	// setting that affects all Cobra command instances.
	cobra.EnableCommandSorting = false

	// Disable Cobra's use of mousetrap. This breaks daemon registration on
	// Windows because it tries to enforce that the CLI only be launched from
	// a console, which it's not when running automatically.
	cobra.MousetrapHelpText = ""

	// Grab a handle for the command line flags.
	flags := rootCommand.Flags()

	// Disable alphabetical sorting of flags in help output.
	flags.SortFlags = false

	// Manually add a help flag to override the default message. Cobra will
	// still implement its logic automatically.
	flags.BoolVarP(&rootConfiguration.help, "help", "h", false, "Show help information")

	// Register commands.
	// HACK: Add the sync commands as direct subcommands of the root command for
	// temporary backward compatibility.
	commands := []*cobra.Command{
		sync.RootCommand,
		forward.RootCommand,
		project.RootCommand,
		tunnel.RootCommand,
		loginCommand,
		logoutCommand,
		daemon.RootCommand,
		versionCommand,
		legalCommand,
		generateCommand,
	}
	commands = append(commands, sync.Commands...)
	rootCommand.AddCommand(commands...)

	// HACK: Register the sync subcommands with the sync command after
	// registering them with the root command so that they have the correct
	// parent command and thus the correct help output.
	sync.RootCommand.AddCommand(sync.Commands...)

	// HACK If we're on Windows, enable color support for command usage and
	// error output by recursively replacing the output streams for Cobra
	// commands.
	if runtime.GOOS == "windows" {
		enableColorForCommand(rootCommand)
	}
}

// enableColorForCommand recursively enables colorized usage and error output
// for a command and all of its child commands.
func enableColorForCommand(command *cobra.Command) {
	// Enable color support for the command itself.
	command.SetOut(color.Output)
	command.SetErr(color.Error)

	// Recursively enable color support for child commands.
	for _, c := range command.Commands() {
		enableColorForCommand(c)
	}
}

func main() {
	// Check if a prompting environment is set. If so, treat this as a prompt
	// request. Prompting is sort of a special pseudo-command that's indicated
	// by the presence of an environment variable, and hence it has to be
	// handled in a bit of a special manner.
	if _, ok := os.LookupEnv(prompt.PrompterEnvironmentVariable); ok {
		if err := promptMain(os.Args[1:]); err != nil {
			cmd.Fatal(err)
		}
		return
	}

	// Handle terminal compatibility issues. If this call returns, it means that
	// we should proceed normally.
	cmd.HandleTerminalCompatibility()

	// HACK: Modify the root command's help function to hide sync commands.
	defaultHelpFunction := rootCommand.HelpFunc()
	rootCommand.SetHelpFunc(func(command *cobra.Command, arguments []string) {
		if command == rootCommand {
			for _, command := range sync.Commands {
				command.Hidden = true
			}
		}
		defaultHelpFunction(command, arguments)
	})

	// Execute the root command.
	if err := rootCommand.Execute(); err != nil {
		os.Exit(1)
	}
}
<|endoftext|>


<commit_before>// Copyright 2016 The nvim-go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// nvim-go: a Go language development plugin for Neovim written in pure Go.
package main

import (
	"context"
	"flag"
	logpkg "log"
	"net/http"
	_ "net/http/pprof"
	"os"
	"os/signal"
	"runtime"
	"syscall"

	"github.com/google/gops/agent"
	"github.com/neovim/go-client/nvim/plugin"
	"github.com/pkg/errors"
	"github.com/zchee/nvim-go/src/autocmd"
	"github.com/zchee/nvim-go/src/buildctx"
	"github.com/zchee/nvim-go/src/command"
	"github.com/zchee/nvim-go/src/logger"
	"github.com/zchee/nvim-go/src/server"
	"go.uber.org/zap"
	"golang.org/x/sync/errgroup"
)

var (
	pluginHost  = flag.String("manifest", "", "Write plugin manifest for `host` to stdout")
	vimFilePath = flag.String("location", "", "Manifest is automatically written to `.vim file`")
)

func main() {
	flag.Parse()

	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	zapLogger, undo := logger.NewRedirectZapLogger()
	defer undo()
	ctx = logger.NewContext(ctx, zapLogger)

	if *pluginHost != "" {
		fn := func(p *plugin.Plugin) error {
			return Main(ctx, p)
		}
		Plugin(fn)
		return
	}

	var eg = &errgroup.Group{}
	eg, ctx = errgroup.WithContext(ctx)
	eg.Go(func() error {
		fn := func(p *plugin.Plugin) error {
			return Main(ctx, p)
		}
		Plugin(fn)
		return nil
	})
	eg.Go(func() error {
		return Child(ctx)
	})
	go func() {
		if err := eg.Wait(); err != nil {
			logger.FromContext(ctx).Fatal("eg.Wait", zap.Error(err))
		}
	}()

	sigc := make(chan os.Signal, 1)
	signal.Notify(sigc, syscall.SIGINT, syscall.SIGTERM)
	select {
	case sig := <-sigc:
		switch sig {
		case syscall.SIGINT, syscall.SIGTERM:
			logger.FromContext(ctx).Info("notify signal", zap.String("interrupted signal", sig.String()))
			cancel() // avoid goroutine leak
			return
		}
	}
}

func Main(ctx context.Context, p *plugin.Plugin) error {
	debug := os.Getenv("NVIM_GO_DEBUG") != ""
	pprof := os.Getenv("NVIM_GO_PPROF") != ""

	log := logger.FromContext(ctx).Named("main")
	ctx = logger.NewContext(ctx, log)

	buildctxt := buildctx.NewContext()
	c := command.Register(ctx, p, buildctxt)
	autocmd.Register(ctx, p, buildctxt, c)

	if debug {
		// starts the gops agent
		if err := agent.Listen(&agent.Options{NoShutdownCleanup: true}); err != nil {
			return err
		}

		if pprof {
			const addr = ":14715" // (n: 14)vim-(g: 7)(o: 15)
			log.Debug("start the pprof debugging", zap.String("listen at", addr))

			// enable the report of goroutine blocking events
			runtime.SetBlockProfileRate(1)
			go logpkg.Println(http.ListenAndServe(addr, nil))
		}
	}

	return nil
}

func Child(ctx context.Context) error {
	log := logger.FromContext(ctx).Named("child")
	ctx = logger.NewContext(ctx, log)

	s, err := server.NewServer(ctx)
	if err != nil {
		return errors.Wrap(err, "failed to create NewServer")
	}
	go s.Serve()
	defer func() {
		if err := s.Close(); err != nil {
			log.Fatal("Close", zap.Error(err))
		}
	}()

	bufs, err := s.Buffers()
	if err != nil {
		return errors.Wrap(err, "failed to get buffers")
	}
	// Get the names using a single atomic call to Nvim.
	names := make([]string, len(bufs))
	b := s.NewBatch()
	for i, buf := range bufs {
		b.BufferName(buf, &names[i])
	}

	if err := b.Execute(); err != nil {
		return errors.Wrap(err, "failed to execute batch")
	}

	for _, name := range names {
		log.Info("buffer", zap.String("name", name))
	}

	return nil
}
<commit_msg>cmd/nvim-go: fix signal handling zap logger message<commit_after>// Copyright 2016 The nvim-go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// nvim-go: a Go language development plugin for Neovim written in pure Go.
package main

import (
	"context"
	"flag"
	logpkg "log"
	"net/http"
	_ "net/http/pprof"
	"os"
	"os/signal"
	"runtime"
	"syscall"

	"github.com/google/gops/agent"
	"github.com/neovim/go-client/nvim/plugin"
	"github.com/pkg/errors"
	"github.com/zchee/nvim-go/src/autocmd"
	"github.com/zchee/nvim-go/src/buildctx"
	"github.com/zchee/nvim-go/src/command"
	"github.com/zchee/nvim-go/src/logger"
	"github.com/zchee/nvim-go/src/server"
	"go.uber.org/zap"
	"golang.org/x/sync/errgroup"
)

var (
	pluginHost  = flag.String("manifest", "", "Write plugin manifest for `host` to stdout")
	vimFilePath = flag.String("location", "", "Manifest is automatically written to `.vim file`")
)

func main() {
	flag.Parse()

	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	zapLogger, undo := logger.NewRedirectZapLogger()
	defer undo()
	ctx = logger.NewContext(ctx, zapLogger)

	if *pluginHost != "" {
		fn := func(p *plugin.Plugin) error {
			return Main(ctx, p)
		}
		Plugin(fn)
		return
	}

	var eg = &errgroup.Group{}
	eg, ctx = errgroup.WithContext(ctx)
	eg.Go(func() error {
		fn := func(p *plugin.Plugin) error {
			return Main(ctx, p)
		}
		Plugin(fn)
		return nil
	})
	eg.Go(func() error {
		return Child(ctx)
	})
	go func() {
		if err := eg.Wait(); err != nil {
			logger.FromContext(ctx).Fatal("eg.Wait", zap.Error(err))
		}
	}()

	sigc := make(chan os.Signal, 1)
	signal.Notify(sigc, syscall.SIGINT, syscall.SIGTERM)
	select {
	case sig := <-sigc:
		switch sig {
		case syscall.SIGINT, syscall.SIGTERM:
			logger.FromContext(ctx).Info("catch signal", zap.String("name", sig.String()))
			cancel() // avoid goroutine leak
			return
		}
	}
}

func Main(ctx context.Context, p *plugin.Plugin) error {
	debug := os.Getenv("NVIM_GO_DEBUG") != ""
	pprof := os.Getenv("NVIM_GO_PPROF") != ""

	log := logger.FromContext(ctx).Named("main")
	ctx = logger.NewContext(ctx, log)

	buildctxt := buildctx.NewContext()
	c := command.Register(ctx, p, buildctxt)
	autocmd.Register(ctx, p, buildctxt, c)

	if debug {
		// starts the gops agent
		if err := agent.Listen(&agent.Options{NoShutdownCleanup: true}); err != nil {
			return err
		}

		if pprof {
			const addr = ":14715" // (n: 14)vim-(g: 7)(o: 15)
			log.Debug("start the pprof debugging", zap.String("listen at", addr))

			// enable the report of goroutine blocking events
			runtime.SetBlockProfileRate(1)
			go logpkg.Println(http.ListenAndServe(addr, nil))
		}
	}

	return nil
}

func Child(ctx context.Context) error {
	log := logger.FromContext(ctx).Named("child")
	ctx = logger.NewContext(ctx, log)

	s, err := server.NewServer(ctx)
	if err != nil {
		return errors.Wrap(err, "failed to create NewServer")
	}
	go s.Serve()
	defer func() {
		if err := s.Close(); err != nil {
			log.Fatal("Close", zap.Error(err))
		}
	}()

	bufs, err := s.Buffers()
	if err != nil {
		return errors.Wrap(err, "failed to get buffers")
	}
	// Get the names using a single atomic call to Nvim.
	names := make([]string, len(bufs))
	b := s.NewBatch()
	for i, buf := range bufs {
		b.BufferName(buf, &names[i])
	}

	if err := b.Execute(); err != nil {
		return errors.Wrap(err, "failed to execute batch")
	}

	for _, name := range names {
		log.Info("buffer", zap.String("name", name))
	}

	return nil
}
<|endoftext|>


<commit_before>// Copyright 2016 The nvim-go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// nvim-go is a msgpack remote plugin for Neovim
package main

import (
	"context"
	logpkg "log"
	"net/http"
	_ "net/http/pprof"
	"os"
	"os/signal"
	"runtime"
	"syscall"

	"github.com/google/gops/agent"
	"github.com/neovim/go-client/nvim/plugin"
	"github.com/pkg/errors"
	"github.com/zchee/nvim-go/src/autocmd"
	"github.com/zchee/nvim-go/src/buildctx"
	"github.com/zchee/nvim-go/src/command"
	"github.com/zchee/nvim-go/src/logger"
	"github.com/zchee/nvim-go/src/server"
	"go.uber.org/zap"
	"golang.org/x/sync/errgroup"
)

func main() {
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	zapLogger, undo := logger.NewRedirectZapLogger()
	defer undo()
	ctx = logger.NewContext(ctx, zapLogger)

	var eg = &errgroup.Group{}
	eg, ctx = errgroup.WithContext(ctx)

	eg.Go(func() error {
		fn := func(p *plugin.Plugin) error {
			return Main(ctx, p)
		}
		plugin.Main(fn)
		return nil
	})
	eg.Go(func() error {
		return Child(ctx)
	})
	if err := eg.Wait(); err != nil {
		zapLogger.Fatal("eg.Wait", zap.Error(err))
	}

	sigc := make(chan os.Signal, 1)
	signal.Notify(sigc, syscall.SIGINT, syscall.SIGTERM)
	select {
	case sig := <-sigc:
		switch sig {
		case syscall.SIGINT, syscall.SIGTERM:
			zapLogger.Debug("main", zap.String("interrupted %s signal", sig.String()))
			cancel() // avoid goroutine leak
			return
		}
	}
}

func Main(ctx context.Context, p *plugin.Plugin) error {
	debug := os.Getenv("NVIM_GO_DEBUG") != ""
	pprof := os.Getenv("NVIM_GO_PPROF") != ""

	log := logger.FromContext(ctx).Named("main")

	buildctxt := buildctx.NewContext()
	c := command.Register(ctx, p, buildctxt)
	autocmd.Register(ctx, p, buildctxt, c)

	if debug {
		// starts the gops agent
		if err := agent.Listen(&agent.Options{NoShutdownCleanup: true}); err != nil {
			return err
		}

		if pprof {
			const addr = "localhost:14715" // (n: 14)vim-(g: 7)(o: 15)
			log.Debug("start the pprof debugging", zap.String("listen at", addr))

			// enable the report of goroutine blocking events
			runtime.SetBlockProfileRate(1)
			go logpkg.Println(http.ListenAndServe(addr, nil))
		}
	}

	return nil
}

func Child(ctx context.Context) error {
	log := logger.FromContext(ctx).Named("child")

	s, err := server.NewServer(ctx)
	if err != nil {
		return errors.Wrap(err, "failed to create NewServer")
	}
	defer s.Close()

	bufs, err := s.Buffers()
	if err != nil {
		return errors.Wrap(err, "failed to get buffers")
	}
	// Get the names using a single atomic call to Nvim.
	names := make([]string, len(bufs))
	b := s.NewBatch()
	for i, buf := range bufs {
		b.BufferName(buf, &names[i])
	}

	if err := b.Execute(); err != nil {
		return errors.Wrap(err, "failed to execute batch")
	}

	for _, name := range names {
		log.Info("", zap.String("name", name))
	}

	return nil
}
<commit_msg>cmd/nvim-go: fix Serve and Close server logic<commit_after>// Copyright 2016 The nvim-go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// nvim-go is a msgpack remote plugin for Neovim
package main

import (
	"context"
	logpkg "log"
	"net/http"
	_ "net/http/pprof"
	"os"
	"os/signal"
	"runtime"
	"syscall"

	"github.com/google/gops/agent"
	"github.com/neovim/go-client/nvim/plugin"
	"github.com/pkg/errors"
	"github.com/zchee/nvim-go/src/autocmd"
	"github.com/zchee/nvim-go/src/buildctx"
	"github.com/zchee/nvim-go/src/command"
	"github.com/zchee/nvim-go/src/logger"
	"github.com/zchee/nvim-go/src/server"
	"go.uber.org/zap"
	"golang.org/x/sync/errgroup"
)

func main() {
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	zapLogger, undo := logger.NewRedirectZapLogger()
	defer undo()
	ctx = logger.NewContext(ctx, zapLogger)

	var eg = &errgroup.Group{}
	eg, ctx = errgroup.WithContext(ctx)
	eg.Go(func() error {
		fn := func(p *plugin.Plugin) error {
			return Main(ctx, p)
		}
		plugin.Main(fn)
		return nil
	})
	eg.Go(func() error {
		return Child(ctx)
	})
	if err := eg.Wait(); err != nil {
		zapLogger.Fatal("eg.Wait", zap.Error(err))
	}

	sigc := make(chan os.Signal, 1)
	signal.Notify(sigc, syscall.SIGINT, syscall.SIGTERM)
	select {
	case sig := <-sigc:
		switch sig {
		case syscall.SIGINT, syscall.SIGTERM:
			zapLogger.Debug("main", zap.String("interrupted %s signal", sig.String()))
			cancel() // avoid goroutine leak
			return
		}
	}
}

func Main(ctx context.Context, p *plugin.Plugin) error {
	debug := os.Getenv("NVIM_GO_DEBUG") != ""
	pprof := os.Getenv("NVIM_GO_PPROF") != ""

	log := logger.FromContext(ctx).Named("main")

	buildctxt := buildctx.NewContext()
	c := command.Register(ctx, p, buildctxt)
	autocmd.Register(ctx, p, buildctxt, c)

	if debug {
		// starts the gops agent
		if err := agent.Listen(&agent.Options{NoShutdownCleanup: true}); err != nil {
			return err
		}

		if pprof {
			const addr = "localhost:14715" // (n: 14)vim-(g: 7)(o: 15)
			log.Debug("start the pprof debugging", zap.String("listen at", addr))

			// enable the report of goroutine blocking events
			runtime.SetBlockProfileRate(1)
			go logpkg.Println(http.ListenAndServe(addr, nil))
		}
	}

	return nil
}

func Child(ctx context.Context) error {
	log := logger.FromContext(ctx).Named("child")

	s, err := server.NewServer(ctx)
	if err != nil {
		return errors.Wrap(err, "failed to create NewServer")
	}
	go s.Serve()
	defer func() {
		if err := s.Close(); err != nil {
			log.Fatal("Close", zap.Error(err))
		}
	}()

	bufs, err := s.Buffers()
	if err != nil {
		return errors.Wrap(err, "failed to get buffers")
	}
	// Get the names using a single atomic call to Nvim.
	names := make([]string, len(bufs))
	b := s.NewBatch()
	for i, buf := range bufs {
		b.BufferName(buf, &names[i])
	}

	if err := b.Execute(); err != nil {
		return errors.Wrap(err, "failed to execute batch")
	}

	for _, name := range names {
		log.Info("", zap.String("name", name))
	}

	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"io"

	"github.com/DeedleFake/wdte/scanner"
)

type Grammar struct {
	NTerms map[string]NTerm
}

func LoadGrammar(r io.Reader) (Grammar, error) {
	panic("Not implemented.")
}

type Term struct {
	scanner.Token
}

type NTerm struct {
	Name   string
	First  map[string]struct{}
	Follow map[string]struct{}
}

type Epsilon struct{}
<commit_msg>cmd/pgen: Still working on basic implementation.<commit_after>package main

import (
	"bufio"
	"io"
	"strings"

	"github.com/DeedleFake/wdte/scanner"
)

type Grammar struct {
	NTerms map[string]NTerm
}

func LoadGrammar(r io.Reader) (g Grammar, err error) {
	g = Grammar{
		NTerms: make(map[string]NTerm),
	}

	var cur NTerm
	s := bufio.NewScanner(r)
	for s.Scan() {
		line := s.Text()

		parts := strings.SplitN(line, "->", 2)
		if len(parts) < 2 {
			parts = strings.SplitN(line, "|", 2)
			parts[0] = cur.Name
		}
		name := strings.TrimSpace(parts[0])
		if nt, ok := g.NTerms[name]; ok {
			cur = nt
		}
		cur.Name = name
	}
	if s.Err() != nil {
		return g, s.Err()
	}

	panic("Not implemented.")
}

type Term scanner.TokenType

type NTerm struct {
	Name   string
	First  map[scanner.TokenType]struct{}
	Follow map[scanner.TokenType]struct{}
}

type Epsilon struct{}
<|endoftext|>


<commit_before>// Copyright 2015 CoreOS, Inc.
//
// 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 main

import (
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"net/url"
	"os"
	"path/filepath"
	"strings"

	"github.com/coreos/mantle/auth"
	"github.com/coreos/mantle/cli"
	"google.golang.org/cloud"
	"google.golang.org/cloud/storage"
)

var (
	cmdIndex = &cli.Command{

		Name:        "upload",
		Summary:     "Upload os image",
		Usage:       "-bucket gs://bucket/prefix/ -image filepath",
		Description: "Upload os image to Google Storage bucket",
		Flags:       *flag.NewFlagSet("upload", flag.ExitOnError),
		Run:         runUpload,
	}
	bucket    string
	image     string
	imageName string
	projectID string
)

func init() {
	cmdIndex.Flags.StringVar(&bucket, "bucket", "gs://coreos-plume", "gs://bucket/prefix/")
	cmdIndex.Flags.StringVar(&projectID, "projectID", "coreos-gce-testing", "found in developers console")
	cmdIndex.Flags.StringVar(&imageName, "name", "", "filename for uploaded image, defaults to COREOS_VERSION")
	cmdIndex.Flags.StringVar(&image, "image",
		"/mnt/host/source/src/build/images/amd64-usr/latest/coreos_production_gce.tar.gz",
		"path_to_coreos_image (build with: ./image_to_vm.sh --format=gce ...)")
	cli.Register(cmdIndex)
}

func runUpload(args []string) int {
	if len(args) != 0 {
		fmt.Fprintf(os.Stderr, "Unrecognized args in plume upload cmd: %v\n", args)
		return 2
	}

	// if an image name is unspecified try to use version.txt
	if imageName == "" {
		imageName = getImageVersion(image)
		if imageName == "" {
			fmt.Fprintf(os.Stderr, "Unable to get version from image directory, provide a -name flag or include version.txt in the image directory\n")
			return 1
		}
	}
	imageName += ".tar.gz"

	gsURL, err := url.Parse(bucket)
	if err != nil {
		fmt.Fprintf(os.Stderr, "%v\n", err)
		return 1
	}
	if gsURL.Scheme != "gs" {
		fmt.Fprintf(os.Stderr, "URL missing gs:// scheme prefix: %v\n", bucket)
		return 1
	}
	if gsURL.Host == "" {
		fmt.Fprintf(os.Stderr, "URL missing bucket name %v\n", bucket)
		return 1
	}
	bucket = gsURL.Host
	imageName += gsURL.Path

	client, err := auth.GoogleClient(false)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Authentication failed: %v\n", err)
		return 1
	}

	fmt.Printf("Writing %v to %v\n", imageName, bucket)

	if err := writeFile(client, imageName); err != nil {
		fmt.Fprintf(os.Stderr, "Uploading image failed: %v\n", err)
		return 1
	}

	fmt.Printf("Update successful!\n")
	return 0
}

// Attempt to get version.txt from image build directory. Return "" if
// unable to retrieve version.txt from directory.
func getImageVersion(imagePath string) string {
	imageDir := filepath.Dir(imagePath)
	b, err := ioutil.ReadFile(filepath.Join(imageDir, "version.txt"))
	if err != nil {
		return ""
	}

	lines := strings.Split(string(b), "\n")
	var version string
	for _, str := range lines {
		if strings.Contains(str, "COREOS_VERSION=") {
			version = strings.TrimPrefix(str, "COREOS_VERSION=")
			break
		}
	}
	return version
}

func writeFile(client *http.Client, filename string) error {
	ctx := cloud.NewContext(projectID, client)
	wc := storage.NewWriter(ctx, bucket, filename)
	wc.ContentType = "application/x-gzip"
	wc.ACL = []storage.ACLRule{{storage.AllAuthenticatedUsers, storage.RoleReader}}

	imageFile, err := os.Open(image)
	if err != nil {
		return err
	}
	_, err = io.Copy(wc, imageFile)
	if err != nil {
		return err
	}
	if err := wc.Close(); err != nil {
		return err
	}

	return nil
}
<commit_msg>cmd/plume: fix prefix handling in upload subcommand<commit_after>// Copyright 2015 CoreOS, Inc.
//
// 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 main

import (
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"net/url"
	"os"
	"path/filepath"
	"strings"

	"github.com/coreos/mantle/auth"
	"github.com/coreos/mantle/cli"
	"google.golang.org/cloud"
	"google.golang.org/cloud/storage"
)

var (
	cmdIndex = &cli.Command{

		Name:        "upload",
		Summary:     "Upload os image",
		Usage:       "-bucket gs://bucket/prefix/ -image filepath",
		Description: "Upload os image to Google Storage bucket",
		Flags:       *flag.NewFlagSet("upload", flag.ExitOnError),
		Run:         runUpload,
	}
	bucket    string
	image     string
	imageName string
	projectID string
)

func init() {
	cmdIndex.Flags.StringVar(&bucket, "bucket", "gs://coreos-plume", "gs://bucket/prefix/")
	cmdIndex.Flags.StringVar(&projectID, "projectID", "coreos-gce-testing", "found in developers console")
	cmdIndex.Flags.StringVar(&imageName, "name", "", "filename for uploaded image, defaults to COREOS_VERSION")
	cmdIndex.Flags.StringVar(&image, "image",
		"/mnt/host/source/src/build/images/amd64-usr/latest/coreos_production_gce.tar.gz",
		"path_to_coreos_image (build with: ./image_to_vm.sh --format=gce ...)")
	cli.Register(cmdIndex)
}

func runUpload(args []string) int {
	if len(args) != 0 {
		fmt.Fprintf(os.Stderr, "Unrecognized args in plume upload cmd: %v\n", args)
		return 2
	}

	// if an image name is unspecified try to use version.txt
	if imageName == "" {
		imageName = getImageVersion(image)
		if imageName == "" {
			fmt.Fprintf(os.Stderr, "Unable to get version from image directory, provide a -name flag or include version.txt in the image directory\n")
			return 1
		}
	}
	imageName += ".tar.gz"

	gsURL, err := url.Parse(bucket)
	if err != nil {
		fmt.Fprintf(os.Stderr, "%v\n", err)
		return 1
	}
	if gsURL.Scheme != "gs" {
		fmt.Fprintf(os.Stderr, "URL missing gs:// scheme prefix: %v\n", bucket)
		return 1
	}
	if gsURL.Host == "" {
		fmt.Fprintf(os.Stderr, "URL missing bucket name %v\n", bucket)
		return 1
	}
	bucket = gsURL.Host
	imageName = strings.TrimPrefix(gsURL.Path+"/"+imageName, "/")

	client, err := auth.GoogleClient(false)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Authentication failed: %v\n", err)
		return 1
	}

	fmt.Printf("Writing %v to %v\n", imageName, bucket)

	if err := writeFile(client, imageName); err != nil {
		fmt.Fprintf(os.Stderr, "Uploading image failed: %v\n", err)
		return 1
	}

	fmt.Printf("Update successful!\n")
	return 0
}

// Attempt to get version.txt from image build directory. Return "" if
// unable to retrieve version.txt from directory.
func getImageVersion(imagePath string) string {
	imageDir := filepath.Dir(imagePath)
	b, err := ioutil.ReadFile(filepath.Join(imageDir, "version.txt"))
	if err != nil {
		return ""
	}

	lines := strings.Split(string(b), "\n")
	var version string
	for _, str := range lines {
		if strings.Contains(str, "COREOS_VERSION=") {
			version = strings.TrimPrefix(str, "COREOS_VERSION=")
			break
		}
	}
	return version
}

func writeFile(client *http.Client, filename string) error {
	ctx := cloud.NewContext(projectID, client)
	wc := storage.NewWriter(ctx, bucket, filename)
	wc.ContentType = "application/x-gzip"
	wc.ACL = []storage.ACLRule{{storage.AllAuthenticatedUsers, storage.RoleReader}}

	imageFile, err := os.Open(image)
	if err != nil {
		return err
	}
	_, err = io.Copy(wc, imageFile)
	if err != nil {
		return err
	}
	if err := wc.Close(); err != nil {
		return err
	}

	return nil
}
<|endoftext|>