9fans.net/go@v0.0.7/cmd/acme/internal/complete/complete.go (about)

     1  // libcomplete/complete.c
     2  
     3  package complete
     4  
     5  import (
     6  	"fmt"
     7  	"io/ioutil"
     8  	"strings"
     9  	"unicode/utf8"
    10  )
    11  
    12  type Completion struct {
    13  	Progress bool     /* whether forward progress has been made */
    14  	Done     bool     /* whether the completion now represents a file or directory */
    15  	Text     string   /* the string to advance, suffixed " " or "/" for file or directory */
    16  	NumMatch int      /* number of files that matched */
    17  	Files    []string /* files returned */
    18  }
    19  
    20  func Complete(dir, s string) (*Completion, error) {
    21  	if strings.Contains(s, "/") {
    22  		return nil, fmt.Errorf("slash character in name argument to complete")
    23  	}
    24  
    25  	// Note: ioutil.ReadDir sorts, so no sort below.
    26  	dirs, err := ioutil.ReadDir(dir)
    27  	if err != nil {
    28  		return nil, err
    29  	}
    30  
    31  	// find the matches
    32  	c := new(Completion)
    33  	for _, info := range dirs {
    34  		if name := info.Name(); strings.HasPrefix(name, s) {
    35  			suffix := ""
    36  			if info.IsDir() {
    37  				suffix = "/"
    38  			}
    39  			c.Files = append(c.Files, name+suffix)
    40  		}
    41  	}
    42  
    43  	if len(c.Files) > 0 {
    44  		/* report interesting results */
    45  		/* trim length back to longest common initial string */
    46  		minlen := len(c.Files[0])
    47  		for i := 1; i < len(c.Files); i++ {
    48  			minlen = longestprefixlength(c.Files[0], c.Files[i], minlen)
    49  		}
    50  
    51  		/* build the answer */
    52  		c.Done = len(c.Files) == 1
    53  		c.Progress = c.Done || minlen > len(s)
    54  		c.Text = c.Files[0][len(s):minlen]
    55  		if c.Done && !strings.HasSuffix(c.Text, "/") {
    56  			c.Text += " "
    57  		}
    58  		c.NumMatch = len(c.Files)
    59  	} else {
    60  		/* no match, so return all possible strings */
    61  		for _, info := range dirs {
    62  			suffix := ""
    63  			if info.IsDir() {
    64  				suffix = "/"
    65  			}
    66  			c.Files = append(c.Files, info.Name()+suffix)
    67  		}
    68  	}
    69  	return c, nil
    70  }
    71  
    72  func longestprefixlength(a, b string, n int) int {
    73  	var i int
    74  	for i = 0; i < n && i < len(a) && i < len(b); {
    75  		ra, wa := utf8.DecodeRuneInString(a)
    76  		rb, wb := utf8.DecodeRuneInString(b)
    77  		if ra != rb || wa != wb {
    78  			break
    79  		}
    80  		i += wa
    81  	}
    82  	return i
    83  }