github.com/motemen/ghq@v1.0.3/cmd_list.go (about)

     1  package main
     2  
     3  import (
     4  	"fmt"
     5  	"path/filepath"
     6  	"strings"
     7  	"sync"
     8  
     9  	"github.com/urfave/cli/v2"
    10  )
    11  
    12  func doList(c *cli.Context) error {
    13  	var (
    14  		w                = c.App.Writer
    15  		query            = c.Args().First()
    16  		exact            = c.Bool("exact")
    17  		vcsBackend       = c.String("vcs")
    18  		printFullPaths   = c.Bool("full-path")
    19  		printUniquePaths = c.Bool("unique")
    20  	)
    21  
    22  	filterByQuery := func(_ *LocalRepository) bool {
    23  		return true
    24  	}
    25  	if query != "" {
    26  		if hasSchemePattern.MatchString(query) || scpLikeURLPattern.MatchString(query) {
    27  			if url, err := newURL(query, false, false); err == nil {
    28  				if repo, err := LocalRepositoryFromURL(url); err == nil {
    29  					query = filepath.ToSlash(repo.RelPath)
    30  				}
    31  			}
    32  		}
    33  
    34  		if exact {
    35  			filterByQuery = func(repo *LocalRepository) bool {
    36  				return repo.Matches(query)
    37  			}
    38  		} else {
    39  			var host string
    40  			paths := strings.Split(query, "/")
    41  			if len(paths) > 1 && looksLikeAuthorityPattern.MatchString(paths[0]) {
    42  				query = strings.Join(paths[1:], "/")
    43  				host = paths[0]
    44  			}
    45  			filterByQuery = func(repo *LocalRepository) bool {
    46  				return strings.Contains(repo.NonHostPath(), query) &&
    47  					(host == "" || repo.PathParts[0] == host)
    48  			}
    49  		}
    50  	}
    51  
    52  	var (
    53  		repos []*LocalRepository
    54  		mu    sync.Mutex
    55  	)
    56  	if err := walkLocalRepositories(vcsBackend, func(repo *LocalRepository) {
    57  		if !filterByQuery(repo) {
    58  			return
    59  		}
    60  		mu.Lock()
    61  		defer mu.Unlock()
    62  		repos = append(repos, repo)
    63  	}); err != nil {
    64  		return fmt.Errorf("failed to filter repos while walkLocalRepositories(repo): %w", err)
    65  	}
    66  
    67  	if printUniquePaths {
    68  		subpathCount := map[string]int{} // Count duplicated subpaths (ex. foo/dotfiles and bar/dotfiles)
    69  		reposCount := map[string]int{}   // Check duplicated repositories among roots
    70  
    71  		// Primary first
    72  		for _, repo := range repos {
    73  			if reposCount[repo.RelPath] == 0 {
    74  				for _, p := range repo.Subpaths() {
    75  					subpathCount[p] = subpathCount[p] + 1
    76  				}
    77  			}
    78  
    79  			reposCount[repo.RelPath] = reposCount[repo.RelPath] + 1
    80  		}
    81  
    82  		for _, repo := range repos {
    83  			if reposCount[repo.RelPath] > 1 && !repo.IsUnderPrimaryRoot() {
    84  				continue
    85  			}
    86  
    87  			for _, p := range repo.Subpaths() {
    88  				if subpathCount[p] == 1 {
    89  					fmt.Fprintln(w, p)
    90  					break
    91  				}
    92  			}
    93  		}
    94  	} else {
    95  		for _, repo := range repos {
    96  			if printFullPaths {
    97  				fmt.Fprintln(w, repo.FullPath)
    98  			} else {
    99  				fmt.Fprintln(w, repo.RelPath)
   100  			}
   101  		}
   102  	}
   103  	return nil
   104  }