➕ Update vendor
This commit is contained in:
		
							parent
							
								
									0a51a03f35
								
							
						
					
					
						commit
						0b8da63175
					
				| 
						 | 
					@ -0,0 +1,23 @@
 | 
				
			||||||
 | 
					Copyright (c) 2011 Keith Rarick
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Permission is hereby granted, free of charge, to any person
 | 
				
			||||||
 | 
					obtaining a copy of this software and associated
 | 
				
			||||||
 | 
					documentation files (the "Software"), to deal in the
 | 
				
			||||||
 | 
					Software without restriction, including without limitation
 | 
				
			||||||
 | 
					the rights to use, copy, modify, merge, publish, distribute,
 | 
				
			||||||
 | 
					sublicense, and/or sell copies of the Software, and to
 | 
				
			||||||
 | 
					permit persons to whom the Software is furnished to do so,
 | 
				
			||||||
 | 
					subject to the following conditions:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The above copyright notice and this permission notice shall
 | 
				
			||||||
 | 
					be included in all copies or substantial portions of the
 | 
				
			||||||
 | 
					Software.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
 | 
				
			||||||
 | 
					KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
 | 
				
			||||||
 | 
					WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
 | 
				
			||||||
 | 
					PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
 | 
				
			||||||
 | 
					OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
 | 
				
			||||||
 | 
					OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
 | 
				
			||||||
 | 
					OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
 | 
				
			||||||
 | 
					SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,36 @@
 | 
				
			||||||
 | 
					# pty
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Pty is a Go package for using unix pseudo-terminals.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Install
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    go get github.com/kr/pty
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Example
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```go
 | 
				
			||||||
 | 
					package main
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"github.com/kr/pty"
 | 
				
			||||||
 | 
						"io"
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
 | 
						"os/exec"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func main() {
 | 
				
			||||||
 | 
						c := exec.Command("grep", "--color=auto", "bar")
 | 
				
			||||||
 | 
						f, err := pty.Start(c)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							panic(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						go func() {
 | 
				
			||||||
 | 
							f.Write([]byte("foo\n"))
 | 
				
			||||||
 | 
							f.Write([]byte("bar\n"))
 | 
				
			||||||
 | 
							f.Write([]byte("baz\n"))
 | 
				
			||||||
 | 
							f.Write([]byte{4}) // EOT
 | 
				
			||||||
 | 
						}()
 | 
				
			||||||
 | 
						io.Copy(os.Stdout, f)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,16 @@
 | 
				
			||||||
 | 
					// Package pty provides functions for working with Unix terminals.
 | 
				
			||||||
 | 
					package pty
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"errors"
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ErrUnsupported is returned if a function is not
 | 
				
			||||||
 | 
					// available on the current platform.
 | 
				
			||||||
 | 
					var ErrUnsupported = errors.New("unsupported")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Opens a pty and its corresponding tty.
 | 
				
			||||||
 | 
					func Open() (pty, tty *os.File, err error) {
 | 
				
			||||||
 | 
						return open()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,13 @@
 | 
				
			||||||
 | 
					// +build !windows
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package pty
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import "syscall"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func ioctl(fd, cmd, ptr uintptr) error {
 | 
				
			||||||
 | 
						_, _, e := syscall.Syscall(syscall.SYS_IOCTL, fd, cmd, ptr)
 | 
				
			||||||
 | 
						if e != 0 {
 | 
				
			||||||
 | 
							return e
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,39 @@
 | 
				
			||||||
 | 
					// +build darwin dragonfly freebsd netbsd openbsd
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package pty
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// from <sys/ioccom.h>
 | 
				
			||||||
 | 
					const (
 | 
				
			||||||
 | 
						_IOC_VOID    uintptr = 0x20000000
 | 
				
			||||||
 | 
						_IOC_OUT     uintptr = 0x40000000
 | 
				
			||||||
 | 
						_IOC_IN      uintptr = 0x80000000
 | 
				
			||||||
 | 
						_IOC_IN_OUT  uintptr = _IOC_OUT | _IOC_IN
 | 
				
			||||||
 | 
						_IOC_DIRMASK         = _IOC_VOID | _IOC_OUT | _IOC_IN
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						_IOC_PARAM_SHIFT = 13
 | 
				
			||||||
 | 
						_IOC_PARAM_MASK  = (1 << _IOC_PARAM_SHIFT) - 1
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func _IOC_PARM_LEN(ioctl uintptr) uintptr {
 | 
				
			||||||
 | 
						return (ioctl >> 16) & _IOC_PARAM_MASK
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func _IOC(inout uintptr, group byte, ioctl_num uintptr, param_len uintptr) uintptr {
 | 
				
			||||||
 | 
						return inout | (param_len&_IOC_PARAM_MASK)<<16 | uintptr(group)<<8 | ioctl_num
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func _IO(group byte, ioctl_num uintptr) uintptr {
 | 
				
			||||||
 | 
						return _IOC(_IOC_VOID, group, ioctl_num, 0)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func _IOR(group byte, ioctl_num uintptr, param_len uintptr) uintptr {
 | 
				
			||||||
 | 
						return _IOC(_IOC_OUT, group, ioctl_num, param_len)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func _IOW(group byte, ioctl_num uintptr, param_len uintptr) uintptr {
 | 
				
			||||||
 | 
						return _IOC(_IOC_IN, group, ioctl_num, param_len)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func _IOWR(group byte, ioctl_num uintptr, param_len uintptr) uintptr {
 | 
				
			||||||
 | 
						return _IOC(_IOC_IN_OUT, group, ioctl_num, param_len)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,19 @@
 | 
				
			||||||
 | 
					#!/usr/bin/env bash
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					GOOSARCH="${GOOS}_${GOARCH}"
 | 
				
			||||||
 | 
					case "$GOOSARCH" in
 | 
				
			||||||
 | 
					_* | *_ | _)
 | 
				
			||||||
 | 
						echo 'undefined $GOOS_$GOARCH:' "$GOOSARCH" 1>&2
 | 
				
			||||||
 | 
						exit 1
 | 
				
			||||||
 | 
						;;
 | 
				
			||||||
 | 
					esac
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					GODEFS="go tool cgo -godefs"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					$GODEFS types.go |gofmt > ztypes_$GOARCH.go
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					case $GOOS in
 | 
				
			||||||
 | 
					freebsd|dragonfly)
 | 
				
			||||||
 | 
						$GODEFS types_$GOOS.go |gofmt > ztypes_$GOOSARCH.go
 | 
				
			||||||
 | 
						;;
 | 
				
			||||||
 | 
					esac
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,60 @@
 | 
				
			||||||
 | 
					package pty
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"errors"
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
 | 
						"syscall"
 | 
				
			||||||
 | 
						"unsafe"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func open() (pty, tty *os.File, err error) {
 | 
				
			||||||
 | 
						p, err := os.OpenFile("/dev/ptmx", os.O_RDWR, 0)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						sname, err := ptsname(p)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = grantpt(p)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = unlockpt(p)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						t, err := os.OpenFile(sname, os.O_RDWR, 0)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return p, t, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func ptsname(f *os.File) (string, error) {
 | 
				
			||||||
 | 
						n := make([]byte, _IOC_PARM_LEN(syscall.TIOCPTYGNAME))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err := ioctl(f.Fd(), syscall.TIOCPTYGNAME, uintptr(unsafe.Pointer(&n[0])))
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return "", err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for i, c := range n {
 | 
				
			||||||
 | 
							if c == 0 {
 | 
				
			||||||
 | 
								return string(n[:i]), nil
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return "", errors.New("TIOCPTYGNAME string not NUL-terminated")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func grantpt(f *os.File) error {
 | 
				
			||||||
 | 
						return ioctl(f.Fd(), syscall.TIOCPTYGRANT, 0)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func unlockpt(f *os.File) error {
 | 
				
			||||||
 | 
						return ioctl(f.Fd(), syscall.TIOCPTYUNLK, 0)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,76 @@
 | 
				
			||||||
 | 
					package pty
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"errors"
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
						"syscall"
 | 
				
			||||||
 | 
						"unsafe"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// same code as pty_darwin.go
 | 
				
			||||||
 | 
					func open() (pty, tty *os.File, err error) {
 | 
				
			||||||
 | 
						p, err := os.OpenFile("/dev/ptmx", os.O_RDWR, 0)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						sname, err := ptsname(p)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = grantpt(p)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = unlockpt(p)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						t, err := os.OpenFile(sname, os.O_RDWR, 0)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return p, t, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func grantpt(f *os.File) error {
 | 
				
			||||||
 | 
						_, err := isptmaster(f.Fd())
 | 
				
			||||||
 | 
						return err
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func unlockpt(f *os.File) error {
 | 
				
			||||||
 | 
						_, err := isptmaster(f.Fd())
 | 
				
			||||||
 | 
						return err
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func isptmaster(fd uintptr) (bool, error) {
 | 
				
			||||||
 | 
						err := ioctl(fd, syscall.TIOCISPTMASTER, 0)
 | 
				
			||||||
 | 
						return err == nil, err
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var (
 | 
				
			||||||
 | 
						emptyFiodgnameArg fiodgnameArg
 | 
				
			||||||
 | 
						ioctl_FIODNAME    = _IOW('f', 120, unsafe.Sizeof(emptyFiodgnameArg))
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func ptsname(f *os.File) (string, error) {
 | 
				
			||||||
 | 
						name := make([]byte, _C_SPECNAMELEN)
 | 
				
			||||||
 | 
						fa := fiodgnameArg{Name: (*byte)(unsafe.Pointer(&name[0])), Len: _C_SPECNAMELEN, Pad_cgo_0: [4]byte{0, 0, 0, 0}}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err := ioctl(f.Fd(), ioctl_FIODNAME, uintptr(unsafe.Pointer(&fa)))
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return "", err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for i, c := range name {
 | 
				
			||||||
 | 
							if c == 0 {
 | 
				
			||||||
 | 
								s := "/dev/" + string(name[:i])
 | 
				
			||||||
 | 
								return strings.Replace(s, "ptm", "pts", -1), nil
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return "", errors.New("TIOCPTYGNAME string not NUL-terminated")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,73 @@
 | 
				
			||||||
 | 
					package pty
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"errors"
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
 | 
						"syscall"
 | 
				
			||||||
 | 
						"unsafe"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func posix_openpt(oflag int) (fd int, err error) {
 | 
				
			||||||
 | 
						r0, _, e1 := syscall.Syscall(syscall.SYS_POSIX_OPENPT, uintptr(oflag), 0, 0)
 | 
				
			||||||
 | 
						fd = int(r0)
 | 
				
			||||||
 | 
						if e1 != 0 {
 | 
				
			||||||
 | 
							err = e1
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func open() (pty, tty *os.File, err error) {
 | 
				
			||||||
 | 
						fd, err := posix_openpt(syscall.O_RDWR | syscall.O_CLOEXEC)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						p := os.NewFile(uintptr(fd), "/dev/pts")
 | 
				
			||||||
 | 
						sname, err := ptsname(p)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						t, err := os.OpenFile("/dev/"+sname, os.O_RDWR, 0)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return p, t, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func isptmaster(fd uintptr) (bool, error) {
 | 
				
			||||||
 | 
						err := ioctl(fd, syscall.TIOCPTMASTER, 0)
 | 
				
			||||||
 | 
						return err == nil, err
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var (
 | 
				
			||||||
 | 
						emptyFiodgnameArg fiodgnameArg
 | 
				
			||||||
 | 
						ioctl_FIODGNAME   = _IOW('f', 120, unsafe.Sizeof(emptyFiodgnameArg))
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func ptsname(f *os.File) (string, error) {
 | 
				
			||||||
 | 
						master, err := isptmaster(f.Fd())
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return "", err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if !master {
 | 
				
			||||||
 | 
							return "", syscall.EINVAL
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const n = _C_SPECNAMELEN + 1
 | 
				
			||||||
 | 
						var (
 | 
				
			||||||
 | 
							buf = make([]byte, n)
 | 
				
			||||||
 | 
							arg = fiodgnameArg{Len: n, Buf: (*byte)(unsafe.Pointer(&buf[0]))}
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
 | 
						err = ioctl(f.Fd(), ioctl_FIODGNAME, uintptr(unsafe.Pointer(&arg)))
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return "", err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for i, c := range buf {
 | 
				
			||||||
 | 
							if c == 0 {
 | 
				
			||||||
 | 
								return string(buf[:i]), nil
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return "", errors.New("FIODGNAME string not NUL-terminated")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,46 @@
 | 
				
			||||||
 | 
					package pty
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
 | 
						"strconv"
 | 
				
			||||||
 | 
						"syscall"
 | 
				
			||||||
 | 
						"unsafe"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func open() (pty, tty *os.File, err error) {
 | 
				
			||||||
 | 
						p, err := os.OpenFile("/dev/ptmx", os.O_RDWR, 0)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						sname, err := ptsname(p)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = unlockpt(p)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						t, err := os.OpenFile(sname, os.O_RDWR|syscall.O_NOCTTY, 0)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return p, t, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func ptsname(f *os.File) (string, error) {
 | 
				
			||||||
 | 
						var n _C_uint
 | 
				
			||||||
 | 
						err := ioctl(f.Fd(), syscall.TIOCGPTN, uintptr(unsafe.Pointer(&n)))
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return "", err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return "/dev/pts/" + strconv.Itoa(int(n)), nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func unlockpt(f *os.File) error {
 | 
				
			||||||
 | 
						var u _C_int
 | 
				
			||||||
 | 
						// use TIOCSPTLCK with a zero valued arg to clear the slave pty lock
 | 
				
			||||||
 | 
						return ioctl(f.Fd(), syscall.TIOCSPTLCK, uintptr(unsafe.Pointer(&u)))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,11 @@
 | 
				
			||||||
 | 
					// +build !linux,!darwin,!freebsd,!dragonfly
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package pty
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func open() (pty, tty *os.File, err error) {
 | 
				
			||||||
 | 
						return nil, nil, ErrUnsupported
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,34 @@
 | 
				
			||||||
 | 
					// +build !windows
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package pty
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
 | 
						"os/exec"
 | 
				
			||||||
 | 
						"syscall"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Start assigns a pseudo-terminal tty os.File to c.Stdin, c.Stdout,
 | 
				
			||||||
 | 
					// and c.Stderr, calls c.Start, and returns the File of the tty's
 | 
				
			||||||
 | 
					// corresponding pty.
 | 
				
			||||||
 | 
					func Start(c *exec.Cmd) (pty *os.File, err error) {
 | 
				
			||||||
 | 
						pty, tty, err := Open()
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						defer tty.Close()
 | 
				
			||||||
 | 
						c.Stdout = tty
 | 
				
			||||||
 | 
						c.Stdin = tty
 | 
				
			||||||
 | 
						c.Stderr = tty
 | 
				
			||||||
 | 
						if c.SysProcAttr == nil {
 | 
				
			||||||
 | 
							c.SysProcAttr = &syscall.SysProcAttr{}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						c.SysProcAttr.Setctty = true
 | 
				
			||||||
 | 
						c.SysProcAttr.Setsid = true
 | 
				
			||||||
 | 
						err = c.Start()
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							pty.Close()
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return pty, err
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,37 @@
 | 
				
			||||||
 | 
					// +build !windows
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package pty
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
 | 
						"syscall"
 | 
				
			||||||
 | 
						"unsafe"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Getsize returns the number of rows (lines) and cols (positions
 | 
				
			||||||
 | 
					// in each line) in terminal t.
 | 
				
			||||||
 | 
					func Getsize(t *os.File) (rows, cols int, err error) {
 | 
				
			||||||
 | 
						var ws winsize
 | 
				
			||||||
 | 
						err = windowrect(&ws, t.Fd())
 | 
				
			||||||
 | 
						return int(ws.ws_row), int(ws.ws_col), err
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type winsize struct {
 | 
				
			||||||
 | 
						ws_row    uint16
 | 
				
			||||||
 | 
						ws_col    uint16
 | 
				
			||||||
 | 
						ws_xpixel uint16
 | 
				
			||||||
 | 
						ws_ypixel uint16
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func windowrect(ws *winsize, fd uintptr) error {
 | 
				
			||||||
 | 
						_, _, errno := syscall.Syscall(
 | 
				
			||||||
 | 
							syscall.SYS_IOCTL,
 | 
				
			||||||
 | 
							fd,
 | 
				
			||||||
 | 
							syscall.TIOCGWINSZ,
 | 
				
			||||||
 | 
							uintptr(unsafe.Pointer(ws)),
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
 | 
						if errno != 0 {
 | 
				
			||||||
 | 
							return syscall.Errno(errno)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,9 @@
 | 
				
			||||||
 | 
					// Created by cgo -godefs - DO NOT EDIT
 | 
				
			||||||
 | 
					// cgo -godefs types.go
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package pty
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type (
 | 
				
			||||||
 | 
						_C_int  int32
 | 
				
			||||||
 | 
						_C_uint uint32
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,9 @@
 | 
				
			||||||
 | 
					// Created by cgo -godefs - DO NOT EDIT
 | 
				
			||||||
 | 
					// cgo -godefs types.go
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package pty
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type (
 | 
				
			||||||
 | 
						_C_int  int32
 | 
				
			||||||
 | 
						_C_uint uint32
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,9 @@
 | 
				
			||||||
 | 
					// Created by cgo -godefs - DO NOT EDIT
 | 
				
			||||||
 | 
					// cgo -godefs types.go
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package pty
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type (
 | 
				
			||||||
 | 
						_C_int  int32
 | 
				
			||||||
 | 
						_C_uint uint32
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,11 @@
 | 
				
			||||||
 | 
					// Created by cgo -godefs - DO NOT EDIT
 | 
				
			||||||
 | 
					// cgo -godefs types.go
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// +build arm64
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package pty
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type (
 | 
				
			||||||
 | 
						_C_int  int32
 | 
				
			||||||
 | 
						_C_uint uint32
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,14 @@
 | 
				
			||||||
 | 
					// Created by cgo -godefs - DO NOT EDIT
 | 
				
			||||||
 | 
					// cgo -godefs types_dragonfly.go
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package pty
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const (
 | 
				
			||||||
 | 
						_C_SPECNAMELEN = 0x3f
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type fiodgnameArg struct {
 | 
				
			||||||
 | 
						Name      *byte
 | 
				
			||||||
 | 
						Len       uint32
 | 
				
			||||||
 | 
						Pad_cgo_0 [4]byte
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,13 @@
 | 
				
			||||||
 | 
					// Created by cgo -godefs - DO NOT EDIT
 | 
				
			||||||
 | 
					// cgo -godefs types_freebsd.go
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package pty
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const (
 | 
				
			||||||
 | 
						_C_SPECNAMELEN = 0x3f
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type fiodgnameArg struct {
 | 
				
			||||||
 | 
						Len int32
 | 
				
			||||||
 | 
						Buf *byte
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,14 @@
 | 
				
			||||||
 | 
					// Created by cgo -godefs - DO NOT EDIT
 | 
				
			||||||
 | 
					// cgo -godefs types_freebsd.go
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package pty
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const (
 | 
				
			||||||
 | 
						_C_SPECNAMELEN = 0x3f
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type fiodgnameArg struct {
 | 
				
			||||||
 | 
						Len       int32
 | 
				
			||||||
 | 
						Pad_cgo_0 [4]byte
 | 
				
			||||||
 | 
						Buf       *byte
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,13 @@
 | 
				
			||||||
 | 
					// Created by cgo -godefs - DO NOT EDIT
 | 
				
			||||||
 | 
					// cgo -godefs types_freebsd.go
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package pty
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const (
 | 
				
			||||||
 | 
						_C_SPECNAMELEN = 0x3f
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type fiodgnameArg struct {
 | 
				
			||||||
 | 
						Len int32
 | 
				
			||||||
 | 
						Buf *byte
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,12 @@
 | 
				
			||||||
 | 
					// Created by cgo -godefs - DO NOT EDIT
 | 
				
			||||||
 | 
					// cgo -godefs types.go
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// +build linux
 | 
				
			||||||
 | 
					// +build mips mipsle mips64 mips64le
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package pty
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type (
 | 
				
			||||||
 | 
						_C_int  int32
 | 
				
			||||||
 | 
						_C_uint uint32
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,11 @@
 | 
				
			||||||
 | 
					// +build ppc64
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Created by cgo -godefs - DO NOT EDIT
 | 
				
			||||||
 | 
					// cgo -godefs types.go
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package pty
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type (
 | 
				
			||||||
 | 
						_C_int  int32
 | 
				
			||||||
 | 
						_C_uint uint32
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,11 @@
 | 
				
			||||||
 | 
					// +build ppc64le
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Created by cgo -godefs - DO NOT EDIT
 | 
				
			||||||
 | 
					// cgo -godefs types.go
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package pty
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type (
 | 
				
			||||||
 | 
						_C_int  int32
 | 
				
			||||||
 | 
						_C_uint uint32
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,11 @@
 | 
				
			||||||
 | 
					// +build s390x
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Created by cgo -godefs - DO NOT EDIT
 | 
				
			||||||
 | 
					// cgo -godefs types.go
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package pty
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type (
 | 
				
			||||||
 | 
						_C_int  int32
 | 
				
			||||||
 | 
						_C_uint uint32
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,27 @@
 | 
				
			||||||
 | 
					Copyright (c) 2013, Patrick Mezard
 | 
				
			||||||
 | 
					All rights reserved.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Redistribution and use in source and binary forms, with or without
 | 
				
			||||||
 | 
					modification, are permitted provided that the following conditions are
 | 
				
			||||||
 | 
					met:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Redistributions of source code must retain the above copyright
 | 
				
			||||||
 | 
					notice, this list of conditions and the following disclaimer.
 | 
				
			||||||
 | 
					    Redistributions in binary form must reproduce the above copyright
 | 
				
			||||||
 | 
					notice, this list of conditions and the following disclaimer in the
 | 
				
			||||||
 | 
					documentation and/or other materials provided with the distribution.
 | 
				
			||||||
 | 
					    The names of its contributors may not be used to endorse or promote
 | 
				
			||||||
 | 
					products derived from this software without specific prior written
 | 
				
			||||||
 | 
					permission.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
 | 
				
			||||||
 | 
					IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 | 
				
			||||||
 | 
					TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 | 
				
			||||||
 | 
					PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 | 
				
			||||||
 | 
					HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 | 
				
			||||||
 | 
					SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
 | 
				
			||||||
 | 
					TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 | 
				
			||||||
 | 
					PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 | 
				
			||||||
 | 
					LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 | 
				
			||||||
 | 
					NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 | 
				
			||||||
 | 
					SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,772 @@
 | 
				
			||||||
 | 
					// Package difflib is a partial port of Python difflib module.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// It provides tools to compare sequences of strings and generate textual diffs.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// The following class and functions have been ported:
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// - SequenceMatcher
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// - unified_diff
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// - context_diff
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Getting unified diffs was the main goal of the port. Keep in mind this code
 | 
				
			||||||
 | 
					// is mostly suitable to output text differences in a human friendly way, there
 | 
				
			||||||
 | 
					// are no guarantees generated diffs are consumable by patch(1).
 | 
				
			||||||
 | 
					package difflib
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"bufio"
 | 
				
			||||||
 | 
						"bytes"
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"io"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func min(a, b int) int {
 | 
				
			||||||
 | 
						if a < b {
 | 
				
			||||||
 | 
							return a
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return b
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func max(a, b int) int {
 | 
				
			||||||
 | 
						if a > b {
 | 
				
			||||||
 | 
							return a
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return b
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func calculateRatio(matches, length int) float64 {
 | 
				
			||||||
 | 
						if length > 0 {
 | 
				
			||||||
 | 
							return 2.0 * float64(matches) / float64(length)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return 1.0
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Match struct {
 | 
				
			||||||
 | 
						A    int
 | 
				
			||||||
 | 
						B    int
 | 
				
			||||||
 | 
						Size int
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type OpCode struct {
 | 
				
			||||||
 | 
						Tag byte
 | 
				
			||||||
 | 
						I1  int
 | 
				
			||||||
 | 
						I2  int
 | 
				
			||||||
 | 
						J1  int
 | 
				
			||||||
 | 
						J2  int
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// SequenceMatcher compares sequence of strings. The basic
 | 
				
			||||||
 | 
					// algorithm predates, and is a little fancier than, an algorithm
 | 
				
			||||||
 | 
					// published in the late 1980's by Ratcliff and Obershelp under the
 | 
				
			||||||
 | 
					// hyperbolic name "gestalt pattern matching".  The basic idea is to find
 | 
				
			||||||
 | 
					// the longest contiguous matching subsequence that contains no "junk"
 | 
				
			||||||
 | 
					// elements (R-O doesn't address junk).  The same idea is then applied
 | 
				
			||||||
 | 
					// recursively to the pieces of the sequences to the left and to the right
 | 
				
			||||||
 | 
					// of the matching subsequence.  This does not yield minimal edit
 | 
				
			||||||
 | 
					// sequences, but does tend to yield matches that "look right" to people.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// SequenceMatcher tries to compute a "human-friendly diff" between two
 | 
				
			||||||
 | 
					// sequences.  Unlike e.g. UNIX(tm) diff, the fundamental notion is the
 | 
				
			||||||
 | 
					// longest *contiguous* & junk-free matching subsequence.  That's what
 | 
				
			||||||
 | 
					// catches peoples' eyes.  The Windows(tm) windiff has another interesting
 | 
				
			||||||
 | 
					// notion, pairing up elements that appear uniquely in each sequence.
 | 
				
			||||||
 | 
					// That, and the method here, appear to yield more intuitive difference
 | 
				
			||||||
 | 
					// reports than does diff.  This method appears to be the least vulnerable
 | 
				
			||||||
 | 
					// to synching up on blocks of "junk lines", though (like blank lines in
 | 
				
			||||||
 | 
					// ordinary text files, or maybe "<P>" lines in HTML files).  That may be
 | 
				
			||||||
 | 
					// because this is the only method of the 3 that has a *concept* of
 | 
				
			||||||
 | 
					// "junk" <wink>.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Timing:  Basic R-O is cubic time worst case and quadratic time expected
 | 
				
			||||||
 | 
					// case.  SequenceMatcher is quadratic time for the worst case and has
 | 
				
			||||||
 | 
					// expected-case behavior dependent in a complicated way on how many
 | 
				
			||||||
 | 
					// elements the sequences have in common; best case time is linear.
 | 
				
			||||||
 | 
					type SequenceMatcher struct {
 | 
				
			||||||
 | 
						a              []string
 | 
				
			||||||
 | 
						b              []string
 | 
				
			||||||
 | 
						b2j            map[string][]int
 | 
				
			||||||
 | 
						IsJunk         func(string) bool
 | 
				
			||||||
 | 
						autoJunk       bool
 | 
				
			||||||
 | 
						bJunk          map[string]struct{}
 | 
				
			||||||
 | 
						matchingBlocks []Match
 | 
				
			||||||
 | 
						fullBCount     map[string]int
 | 
				
			||||||
 | 
						bPopular       map[string]struct{}
 | 
				
			||||||
 | 
						opCodes        []OpCode
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func NewMatcher(a, b []string) *SequenceMatcher {
 | 
				
			||||||
 | 
						m := SequenceMatcher{autoJunk: true}
 | 
				
			||||||
 | 
						m.SetSeqs(a, b)
 | 
				
			||||||
 | 
						return &m
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func NewMatcherWithJunk(a, b []string, autoJunk bool,
 | 
				
			||||||
 | 
						isJunk func(string) bool) *SequenceMatcher {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						m := SequenceMatcher{IsJunk: isJunk, autoJunk: autoJunk}
 | 
				
			||||||
 | 
						m.SetSeqs(a, b)
 | 
				
			||||||
 | 
						return &m
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Set two sequences to be compared.
 | 
				
			||||||
 | 
					func (m *SequenceMatcher) SetSeqs(a, b []string) {
 | 
				
			||||||
 | 
						m.SetSeq1(a)
 | 
				
			||||||
 | 
						m.SetSeq2(b)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Set the first sequence to be compared. The second sequence to be compared is
 | 
				
			||||||
 | 
					// not changed.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// SequenceMatcher computes and caches detailed information about the second
 | 
				
			||||||
 | 
					// sequence, so if you want to compare one sequence S against many sequences,
 | 
				
			||||||
 | 
					// use .SetSeq2(s) once and call .SetSeq1(x) repeatedly for each of the other
 | 
				
			||||||
 | 
					// sequences.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// See also SetSeqs() and SetSeq2().
 | 
				
			||||||
 | 
					func (m *SequenceMatcher) SetSeq1(a []string) {
 | 
				
			||||||
 | 
						if &a == &m.a {
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						m.a = a
 | 
				
			||||||
 | 
						m.matchingBlocks = nil
 | 
				
			||||||
 | 
						m.opCodes = nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Set the second sequence to be compared. The first sequence to be compared is
 | 
				
			||||||
 | 
					// not changed.
 | 
				
			||||||
 | 
					func (m *SequenceMatcher) SetSeq2(b []string) {
 | 
				
			||||||
 | 
						if &b == &m.b {
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						m.b = b
 | 
				
			||||||
 | 
						m.matchingBlocks = nil
 | 
				
			||||||
 | 
						m.opCodes = nil
 | 
				
			||||||
 | 
						m.fullBCount = nil
 | 
				
			||||||
 | 
						m.chainB()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (m *SequenceMatcher) chainB() {
 | 
				
			||||||
 | 
						// Populate line -> index mapping
 | 
				
			||||||
 | 
						b2j := map[string][]int{}
 | 
				
			||||||
 | 
						for i, s := range m.b {
 | 
				
			||||||
 | 
							indices := b2j[s]
 | 
				
			||||||
 | 
							indices = append(indices, i)
 | 
				
			||||||
 | 
							b2j[s] = indices
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Purge junk elements
 | 
				
			||||||
 | 
						m.bJunk = map[string]struct{}{}
 | 
				
			||||||
 | 
						if m.IsJunk != nil {
 | 
				
			||||||
 | 
							junk := m.bJunk
 | 
				
			||||||
 | 
							for s, _ := range b2j {
 | 
				
			||||||
 | 
								if m.IsJunk(s) {
 | 
				
			||||||
 | 
									junk[s] = struct{}{}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							for s, _ := range junk {
 | 
				
			||||||
 | 
								delete(b2j, s)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Purge remaining popular elements
 | 
				
			||||||
 | 
						popular := map[string]struct{}{}
 | 
				
			||||||
 | 
						n := len(m.b)
 | 
				
			||||||
 | 
						if m.autoJunk && n >= 200 {
 | 
				
			||||||
 | 
							ntest := n/100 + 1
 | 
				
			||||||
 | 
							for s, indices := range b2j {
 | 
				
			||||||
 | 
								if len(indices) > ntest {
 | 
				
			||||||
 | 
									popular[s] = struct{}{}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							for s, _ := range popular {
 | 
				
			||||||
 | 
								delete(b2j, s)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						m.bPopular = popular
 | 
				
			||||||
 | 
						m.b2j = b2j
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (m *SequenceMatcher) isBJunk(s string) bool {
 | 
				
			||||||
 | 
						_, ok := m.bJunk[s]
 | 
				
			||||||
 | 
						return ok
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Find longest matching block in a[alo:ahi] and b[blo:bhi].
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// If IsJunk is not defined:
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Return (i,j,k) such that a[i:i+k] is equal to b[j:j+k], where
 | 
				
			||||||
 | 
					//     alo <= i <= i+k <= ahi
 | 
				
			||||||
 | 
					//     blo <= j <= j+k <= bhi
 | 
				
			||||||
 | 
					// and for all (i',j',k') meeting those conditions,
 | 
				
			||||||
 | 
					//     k >= k'
 | 
				
			||||||
 | 
					//     i <= i'
 | 
				
			||||||
 | 
					//     and if i == i', j <= j'
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// In other words, of all maximal matching blocks, return one that
 | 
				
			||||||
 | 
					// starts earliest in a, and of all those maximal matching blocks that
 | 
				
			||||||
 | 
					// start earliest in a, return the one that starts earliest in b.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// If IsJunk is defined, first the longest matching block is
 | 
				
			||||||
 | 
					// determined as above, but with the additional restriction that no
 | 
				
			||||||
 | 
					// junk element appears in the block.  Then that block is extended as
 | 
				
			||||||
 | 
					// far as possible by matching (only) junk elements on both sides.  So
 | 
				
			||||||
 | 
					// the resulting block never matches on junk except as identical junk
 | 
				
			||||||
 | 
					// happens to be adjacent to an "interesting" match.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// If no blocks match, return (alo, blo, 0).
 | 
				
			||||||
 | 
					func (m *SequenceMatcher) findLongestMatch(alo, ahi, blo, bhi int) Match {
 | 
				
			||||||
 | 
						// CAUTION:  stripping common prefix or suffix would be incorrect.
 | 
				
			||||||
 | 
						// E.g.,
 | 
				
			||||||
 | 
						//    ab
 | 
				
			||||||
 | 
						//    acab
 | 
				
			||||||
 | 
						// Longest matching block is "ab", but if common prefix is
 | 
				
			||||||
 | 
						// stripped, it's "a" (tied with "b").  UNIX(tm) diff does so
 | 
				
			||||||
 | 
						// strip, so ends up claiming that ab is changed to acab by
 | 
				
			||||||
 | 
						// inserting "ca" in the middle.  That's minimal but unintuitive:
 | 
				
			||||||
 | 
						// "it's obvious" that someone inserted "ac" at the front.
 | 
				
			||||||
 | 
						// Windiff ends up at the same place as diff, but by pairing up
 | 
				
			||||||
 | 
						// the unique 'b's and then matching the first two 'a's.
 | 
				
			||||||
 | 
						besti, bestj, bestsize := alo, blo, 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// find longest junk-free match
 | 
				
			||||||
 | 
						// during an iteration of the loop, j2len[j] = length of longest
 | 
				
			||||||
 | 
						// junk-free match ending with a[i-1] and b[j]
 | 
				
			||||||
 | 
						j2len := map[int]int{}
 | 
				
			||||||
 | 
						for i := alo; i != ahi; i++ {
 | 
				
			||||||
 | 
							// look at all instances of a[i] in b; note that because
 | 
				
			||||||
 | 
							// b2j has no junk keys, the loop is skipped if a[i] is junk
 | 
				
			||||||
 | 
							newj2len := map[int]int{}
 | 
				
			||||||
 | 
							for _, j := range m.b2j[m.a[i]] {
 | 
				
			||||||
 | 
								// a[i] matches b[j]
 | 
				
			||||||
 | 
								if j < blo {
 | 
				
			||||||
 | 
									continue
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								if j >= bhi {
 | 
				
			||||||
 | 
									break
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								k := j2len[j-1] + 1
 | 
				
			||||||
 | 
								newj2len[j] = k
 | 
				
			||||||
 | 
								if k > bestsize {
 | 
				
			||||||
 | 
									besti, bestj, bestsize = i-k+1, j-k+1, k
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							j2len = newj2len
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Extend the best by non-junk elements on each end.  In particular,
 | 
				
			||||||
 | 
						// "popular" non-junk elements aren't in b2j, which greatly speeds
 | 
				
			||||||
 | 
						// the inner loop above, but also means "the best" match so far
 | 
				
			||||||
 | 
						// doesn't contain any junk *or* popular non-junk elements.
 | 
				
			||||||
 | 
						for besti > alo && bestj > blo && !m.isBJunk(m.b[bestj-1]) &&
 | 
				
			||||||
 | 
							m.a[besti-1] == m.b[bestj-1] {
 | 
				
			||||||
 | 
							besti, bestj, bestsize = besti-1, bestj-1, bestsize+1
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						for besti+bestsize < ahi && bestj+bestsize < bhi &&
 | 
				
			||||||
 | 
							!m.isBJunk(m.b[bestj+bestsize]) &&
 | 
				
			||||||
 | 
							m.a[besti+bestsize] == m.b[bestj+bestsize] {
 | 
				
			||||||
 | 
							bestsize += 1
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Now that we have a wholly interesting match (albeit possibly
 | 
				
			||||||
 | 
						// empty!), we may as well suck up the matching junk on each
 | 
				
			||||||
 | 
						// side of it too.  Can't think of a good reason not to, and it
 | 
				
			||||||
 | 
						// saves post-processing the (possibly considerable) expense of
 | 
				
			||||||
 | 
						// figuring out what to do with it.  In the case of an empty
 | 
				
			||||||
 | 
						// interesting match, this is clearly the right thing to do,
 | 
				
			||||||
 | 
						// because no other kind of match is possible in the regions.
 | 
				
			||||||
 | 
						for besti > alo && bestj > blo && m.isBJunk(m.b[bestj-1]) &&
 | 
				
			||||||
 | 
							m.a[besti-1] == m.b[bestj-1] {
 | 
				
			||||||
 | 
							besti, bestj, bestsize = besti-1, bestj-1, bestsize+1
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						for besti+bestsize < ahi && bestj+bestsize < bhi &&
 | 
				
			||||||
 | 
							m.isBJunk(m.b[bestj+bestsize]) &&
 | 
				
			||||||
 | 
							m.a[besti+bestsize] == m.b[bestj+bestsize] {
 | 
				
			||||||
 | 
							bestsize += 1
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return Match{A: besti, B: bestj, Size: bestsize}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Return list of triples describing matching subsequences.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Each triple is of the form (i, j, n), and means that
 | 
				
			||||||
 | 
					// a[i:i+n] == b[j:j+n].  The triples are monotonically increasing in
 | 
				
			||||||
 | 
					// i and in j. It's also guaranteed that if (i, j, n) and (i', j', n') are
 | 
				
			||||||
 | 
					// adjacent triples in the list, and the second is not the last triple in the
 | 
				
			||||||
 | 
					// list, then i+n != i' or j+n != j'. IOW, adjacent triples never describe
 | 
				
			||||||
 | 
					// adjacent equal blocks.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// The last triple is a dummy, (len(a), len(b), 0), and is the only
 | 
				
			||||||
 | 
					// triple with n==0.
 | 
				
			||||||
 | 
					func (m *SequenceMatcher) GetMatchingBlocks() []Match {
 | 
				
			||||||
 | 
						if m.matchingBlocks != nil {
 | 
				
			||||||
 | 
							return m.matchingBlocks
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var matchBlocks func(alo, ahi, blo, bhi int, matched []Match) []Match
 | 
				
			||||||
 | 
						matchBlocks = func(alo, ahi, blo, bhi int, matched []Match) []Match {
 | 
				
			||||||
 | 
							match := m.findLongestMatch(alo, ahi, blo, bhi)
 | 
				
			||||||
 | 
							i, j, k := match.A, match.B, match.Size
 | 
				
			||||||
 | 
							if match.Size > 0 {
 | 
				
			||||||
 | 
								if alo < i && blo < j {
 | 
				
			||||||
 | 
									matched = matchBlocks(alo, i, blo, j, matched)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								matched = append(matched, match)
 | 
				
			||||||
 | 
								if i+k < ahi && j+k < bhi {
 | 
				
			||||||
 | 
									matched = matchBlocks(i+k, ahi, j+k, bhi, matched)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return matched
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						matched := matchBlocks(0, len(m.a), 0, len(m.b), nil)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// It's possible that we have adjacent equal blocks in the
 | 
				
			||||||
 | 
						// matching_blocks list now.
 | 
				
			||||||
 | 
						nonAdjacent := []Match{}
 | 
				
			||||||
 | 
						i1, j1, k1 := 0, 0, 0
 | 
				
			||||||
 | 
						for _, b := range matched {
 | 
				
			||||||
 | 
							// Is this block adjacent to i1, j1, k1?
 | 
				
			||||||
 | 
							i2, j2, k2 := b.A, b.B, b.Size
 | 
				
			||||||
 | 
							if i1+k1 == i2 && j1+k1 == j2 {
 | 
				
			||||||
 | 
								// Yes, so collapse them -- this just increases the length of
 | 
				
			||||||
 | 
								// the first block by the length of the second, and the first
 | 
				
			||||||
 | 
								// block so lengthened remains the block to compare against.
 | 
				
			||||||
 | 
								k1 += k2
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								// Not adjacent.  Remember the first block (k1==0 means it's
 | 
				
			||||||
 | 
								// the dummy we started with), and make the second block the
 | 
				
			||||||
 | 
								// new block to compare against.
 | 
				
			||||||
 | 
								if k1 > 0 {
 | 
				
			||||||
 | 
									nonAdjacent = append(nonAdjacent, Match{i1, j1, k1})
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								i1, j1, k1 = i2, j2, k2
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if k1 > 0 {
 | 
				
			||||||
 | 
							nonAdjacent = append(nonAdjacent, Match{i1, j1, k1})
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						nonAdjacent = append(nonAdjacent, Match{len(m.a), len(m.b), 0})
 | 
				
			||||||
 | 
						m.matchingBlocks = nonAdjacent
 | 
				
			||||||
 | 
						return m.matchingBlocks
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Return list of 5-tuples describing how to turn a into b.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Each tuple is of the form (tag, i1, i2, j1, j2).  The first tuple
 | 
				
			||||||
 | 
					// has i1 == j1 == 0, and remaining tuples have i1 == the i2 from the
 | 
				
			||||||
 | 
					// tuple preceding it, and likewise for j1 == the previous j2.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// The tags are characters, with these meanings:
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// 'r' (replace):  a[i1:i2] should be replaced by b[j1:j2]
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// 'd' (delete):   a[i1:i2] should be deleted, j1==j2 in this case.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// 'i' (insert):   b[j1:j2] should be inserted at a[i1:i1], i1==i2 in this case.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// 'e' (equal):    a[i1:i2] == b[j1:j2]
 | 
				
			||||||
 | 
					func (m *SequenceMatcher) GetOpCodes() []OpCode {
 | 
				
			||||||
 | 
						if m.opCodes != nil {
 | 
				
			||||||
 | 
							return m.opCodes
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						i, j := 0, 0
 | 
				
			||||||
 | 
						matching := m.GetMatchingBlocks()
 | 
				
			||||||
 | 
						opCodes := make([]OpCode, 0, len(matching))
 | 
				
			||||||
 | 
						for _, m := range matching {
 | 
				
			||||||
 | 
							//  invariant:  we've pumped out correct diffs to change
 | 
				
			||||||
 | 
							//  a[:i] into b[:j], and the next matching block is
 | 
				
			||||||
 | 
							//  a[ai:ai+size] == b[bj:bj+size]. So we need to pump
 | 
				
			||||||
 | 
							//  out a diff to change a[i:ai] into b[j:bj], pump out
 | 
				
			||||||
 | 
							//  the matching block, and move (i,j) beyond the match
 | 
				
			||||||
 | 
							ai, bj, size := m.A, m.B, m.Size
 | 
				
			||||||
 | 
							tag := byte(0)
 | 
				
			||||||
 | 
							if i < ai && j < bj {
 | 
				
			||||||
 | 
								tag = 'r'
 | 
				
			||||||
 | 
							} else if i < ai {
 | 
				
			||||||
 | 
								tag = 'd'
 | 
				
			||||||
 | 
							} else if j < bj {
 | 
				
			||||||
 | 
								tag = 'i'
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if tag > 0 {
 | 
				
			||||||
 | 
								opCodes = append(opCodes, OpCode{tag, i, ai, j, bj})
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							i, j = ai+size, bj+size
 | 
				
			||||||
 | 
							// the list of matching blocks is terminated by a
 | 
				
			||||||
 | 
							// sentinel with size 0
 | 
				
			||||||
 | 
							if size > 0 {
 | 
				
			||||||
 | 
								opCodes = append(opCodes, OpCode{'e', ai, i, bj, j})
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						m.opCodes = opCodes
 | 
				
			||||||
 | 
						return m.opCodes
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Isolate change clusters by eliminating ranges with no changes.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Return a generator of groups with up to n lines of context.
 | 
				
			||||||
 | 
					// Each group is in the same format as returned by GetOpCodes().
 | 
				
			||||||
 | 
					func (m *SequenceMatcher) GetGroupedOpCodes(n int) [][]OpCode {
 | 
				
			||||||
 | 
						if n < 0 {
 | 
				
			||||||
 | 
							n = 3
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						codes := m.GetOpCodes()
 | 
				
			||||||
 | 
						if len(codes) == 0 {
 | 
				
			||||||
 | 
							codes = []OpCode{OpCode{'e', 0, 1, 0, 1}}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						// Fixup leading and trailing groups if they show no changes.
 | 
				
			||||||
 | 
						if codes[0].Tag == 'e' {
 | 
				
			||||||
 | 
							c := codes[0]
 | 
				
			||||||
 | 
							i1, i2, j1, j2 := c.I1, c.I2, c.J1, c.J2
 | 
				
			||||||
 | 
							codes[0] = OpCode{c.Tag, max(i1, i2-n), i2, max(j1, j2-n), j2}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if codes[len(codes)-1].Tag == 'e' {
 | 
				
			||||||
 | 
							c := codes[len(codes)-1]
 | 
				
			||||||
 | 
							i1, i2, j1, j2 := c.I1, c.I2, c.J1, c.J2
 | 
				
			||||||
 | 
							codes[len(codes)-1] = OpCode{c.Tag, i1, min(i2, i1+n), j1, min(j2, j1+n)}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						nn := n + n
 | 
				
			||||||
 | 
						groups := [][]OpCode{}
 | 
				
			||||||
 | 
						group := []OpCode{}
 | 
				
			||||||
 | 
						for _, c := range codes {
 | 
				
			||||||
 | 
							i1, i2, j1, j2 := c.I1, c.I2, c.J1, c.J2
 | 
				
			||||||
 | 
							// End the current group and start a new one whenever
 | 
				
			||||||
 | 
							// there is a large range with no changes.
 | 
				
			||||||
 | 
							if c.Tag == 'e' && i2-i1 > nn {
 | 
				
			||||||
 | 
								group = append(group, OpCode{c.Tag, i1, min(i2, i1+n),
 | 
				
			||||||
 | 
									j1, min(j2, j1+n)})
 | 
				
			||||||
 | 
								groups = append(groups, group)
 | 
				
			||||||
 | 
								group = []OpCode{}
 | 
				
			||||||
 | 
								i1, j1 = max(i1, i2-n), max(j1, j2-n)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							group = append(group, OpCode{c.Tag, i1, i2, j1, j2})
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if len(group) > 0 && !(len(group) == 1 && group[0].Tag == 'e') {
 | 
				
			||||||
 | 
							groups = append(groups, group)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return groups
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Return a measure of the sequences' similarity (float in [0,1]).
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Where T is the total number of elements in both sequences, and
 | 
				
			||||||
 | 
					// M is the number of matches, this is 2.0*M / T.
 | 
				
			||||||
 | 
					// Note that this is 1 if the sequences are identical, and 0 if
 | 
				
			||||||
 | 
					// they have nothing in common.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// .Ratio() is expensive to compute if you haven't already computed
 | 
				
			||||||
 | 
					// .GetMatchingBlocks() or .GetOpCodes(), in which case you may
 | 
				
			||||||
 | 
					// want to try .QuickRatio() or .RealQuickRation() first to get an
 | 
				
			||||||
 | 
					// upper bound.
 | 
				
			||||||
 | 
					func (m *SequenceMatcher) Ratio() float64 {
 | 
				
			||||||
 | 
						matches := 0
 | 
				
			||||||
 | 
						for _, m := range m.GetMatchingBlocks() {
 | 
				
			||||||
 | 
							matches += m.Size
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return calculateRatio(matches, len(m.a)+len(m.b))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Return an upper bound on ratio() relatively quickly.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// This isn't defined beyond that it is an upper bound on .Ratio(), and
 | 
				
			||||||
 | 
					// is faster to compute.
 | 
				
			||||||
 | 
					func (m *SequenceMatcher) QuickRatio() float64 {
 | 
				
			||||||
 | 
						// viewing a and b as multisets, set matches to the cardinality
 | 
				
			||||||
 | 
						// of their intersection; this counts the number of matches
 | 
				
			||||||
 | 
						// without regard to order, so is clearly an upper bound
 | 
				
			||||||
 | 
						if m.fullBCount == nil {
 | 
				
			||||||
 | 
							m.fullBCount = map[string]int{}
 | 
				
			||||||
 | 
							for _, s := range m.b {
 | 
				
			||||||
 | 
								m.fullBCount[s] = m.fullBCount[s] + 1
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// avail[x] is the number of times x appears in 'b' less the
 | 
				
			||||||
 | 
						// number of times we've seen it in 'a' so far ... kinda
 | 
				
			||||||
 | 
						avail := map[string]int{}
 | 
				
			||||||
 | 
						matches := 0
 | 
				
			||||||
 | 
						for _, s := range m.a {
 | 
				
			||||||
 | 
							n, ok := avail[s]
 | 
				
			||||||
 | 
							if !ok {
 | 
				
			||||||
 | 
								n = m.fullBCount[s]
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							avail[s] = n - 1
 | 
				
			||||||
 | 
							if n > 0 {
 | 
				
			||||||
 | 
								matches += 1
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return calculateRatio(matches, len(m.a)+len(m.b))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Return an upper bound on ratio() very quickly.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// This isn't defined beyond that it is an upper bound on .Ratio(), and
 | 
				
			||||||
 | 
					// is faster to compute than either .Ratio() or .QuickRatio().
 | 
				
			||||||
 | 
					func (m *SequenceMatcher) RealQuickRatio() float64 {
 | 
				
			||||||
 | 
						la, lb := len(m.a), len(m.b)
 | 
				
			||||||
 | 
						return calculateRatio(min(la, lb), la+lb)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Convert range to the "ed" format
 | 
				
			||||||
 | 
					func formatRangeUnified(start, stop int) string {
 | 
				
			||||||
 | 
						// Per the diff spec at http://www.unix.org/single_unix_specification/
 | 
				
			||||||
 | 
						beginning := start + 1 // lines start numbering with one
 | 
				
			||||||
 | 
						length := stop - start
 | 
				
			||||||
 | 
						if length == 1 {
 | 
				
			||||||
 | 
							return fmt.Sprintf("%d", beginning)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if length == 0 {
 | 
				
			||||||
 | 
							beginning -= 1 // empty ranges begin at line just before the range
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return fmt.Sprintf("%d,%d", beginning, length)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Unified diff parameters
 | 
				
			||||||
 | 
					type UnifiedDiff struct {
 | 
				
			||||||
 | 
						A        []string // First sequence lines
 | 
				
			||||||
 | 
						FromFile string   // First file name
 | 
				
			||||||
 | 
						FromDate string   // First file time
 | 
				
			||||||
 | 
						B        []string // Second sequence lines
 | 
				
			||||||
 | 
						ToFile   string   // Second file name
 | 
				
			||||||
 | 
						ToDate   string   // Second file time
 | 
				
			||||||
 | 
						Eol      string   // Headers end of line, defaults to LF
 | 
				
			||||||
 | 
						Context  int      // Number of context lines
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Compare two sequences of lines; generate the delta as a unified diff.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Unified diffs are a compact way of showing line changes and a few
 | 
				
			||||||
 | 
					// lines of context.  The number of context lines is set by 'n' which
 | 
				
			||||||
 | 
					// defaults to three.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// By default, the diff control lines (those with ---, +++, or @@) are
 | 
				
			||||||
 | 
					// created with a trailing newline.  This is helpful so that inputs
 | 
				
			||||||
 | 
					// created from file.readlines() result in diffs that are suitable for
 | 
				
			||||||
 | 
					// file.writelines() since both the inputs and outputs have trailing
 | 
				
			||||||
 | 
					// newlines.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// For inputs that do not have trailing newlines, set the lineterm
 | 
				
			||||||
 | 
					// argument to "" so that the output will be uniformly newline free.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// The unidiff format normally has a header for filenames and modification
 | 
				
			||||||
 | 
					// times.  Any or all of these may be specified using strings for
 | 
				
			||||||
 | 
					// 'fromfile', 'tofile', 'fromfiledate', and 'tofiledate'.
 | 
				
			||||||
 | 
					// The modification times are normally expressed in the ISO 8601 format.
 | 
				
			||||||
 | 
					func WriteUnifiedDiff(writer io.Writer, diff UnifiedDiff) error {
 | 
				
			||||||
 | 
						buf := bufio.NewWriter(writer)
 | 
				
			||||||
 | 
						defer buf.Flush()
 | 
				
			||||||
 | 
						wf := func(format string, args ...interface{}) error {
 | 
				
			||||||
 | 
							_, err := buf.WriteString(fmt.Sprintf(format, args...))
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						ws := func(s string) error {
 | 
				
			||||||
 | 
							_, err := buf.WriteString(s)
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if len(diff.Eol) == 0 {
 | 
				
			||||||
 | 
							diff.Eol = "\n"
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						started := false
 | 
				
			||||||
 | 
						m := NewMatcher(diff.A, diff.B)
 | 
				
			||||||
 | 
						for _, g := range m.GetGroupedOpCodes(diff.Context) {
 | 
				
			||||||
 | 
							if !started {
 | 
				
			||||||
 | 
								started = true
 | 
				
			||||||
 | 
								fromDate := ""
 | 
				
			||||||
 | 
								if len(diff.FromDate) > 0 {
 | 
				
			||||||
 | 
									fromDate = "\t" + diff.FromDate
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								toDate := ""
 | 
				
			||||||
 | 
								if len(diff.ToDate) > 0 {
 | 
				
			||||||
 | 
									toDate = "\t" + diff.ToDate
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								if diff.FromFile != "" || diff.ToFile != "" {
 | 
				
			||||||
 | 
									err := wf("--- %s%s%s", diff.FromFile, fromDate, diff.Eol)
 | 
				
			||||||
 | 
									if err != nil {
 | 
				
			||||||
 | 
										return err
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									err = wf("+++ %s%s%s", diff.ToFile, toDate, diff.Eol)
 | 
				
			||||||
 | 
									if err != nil {
 | 
				
			||||||
 | 
										return err
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							first, last := g[0], g[len(g)-1]
 | 
				
			||||||
 | 
							range1 := formatRangeUnified(first.I1, last.I2)
 | 
				
			||||||
 | 
							range2 := formatRangeUnified(first.J1, last.J2)
 | 
				
			||||||
 | 
							if err := wf("@@ -%s +%s @@%s", range1, range2, diff.Eol); err != nil {
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							for _, c := range g {
 | 
				
			||||||
 | 
								i1, i2, j1, j2 := c.I1, c.I2, c.J1, c.J2
 | 
				
			||||||
 | 
								if c.Tag == 'e' {
 | 
				
			||||||
 | 
									for _, line := range diff.A[i1:i2] {
 | 
				
			||||||
 | 
										if err := ws(" " + line); err != nil {
 | 
				
			||||||
 | 
											return err
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									continue
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								if c.Tag == 'r' || c.Tag == 'd' {
 | 
				
			||||||
 | 
									for _, line := range diff.A[i1:i2] {
 | 
				
			||||||
 | 
										if err := ws("-" + line); err != nil {
 | 
				
			||||||
 | 
											return err
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								if c.Tag == 'r' || c.Tag == 'i' {
 | 
				
			||||||
 | 
									for _, line := range diff.B[j1:j2] {
 | 
				
			||||||
 | 
										if err := ws("+" + line); err != nil {
 | 
				
			||||||
 | 
											return err
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Like WriteUnifiedDiff but returns the diff a string.
 | 
				
			||||||
 | 
					func GetUnifiedDiffString(diff UnifiedDiff) (string, error) {
 | 
				
			||||||
 | 
						w := &bytes.Buffer{}
 | 
				
			||||||
 | 
						err := WriteUnifiedDiff(w, diff)
 | 
				
			||||||
 | 
						return string(w.Bytes()), err
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Convert range to the "ed" format.
 | 
				
			||||||
 | 
					func formatRangeContext(start, stop int) string {
 | 
				
			||||||
 | 
						// Per the diff spec at http://www.unix.org/single_unix_specification/
 | 
				
			||||||
 | 
						beginning := start + 1 // lines start numbering with one
 | 
				
			||||||
 | 
						length := stop - start
 | 
				
			||||||
 | 
						if length == 0 {
 | 
				
			||||||
 | 
							beginning -= 1 // empty ranges begin at line just before the range
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if length <= 1 {
 | 
				
			||||||
 | 
							return fmt.Sprintf("%d", beginning)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return fmt.Sprintf("%d,%d", beginning, beginning+length-1)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type ContextDiff UnifiedDiff
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Compare two sequences of lines; generate the delta as a context diff.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Context diffs are a compact way of showing line changes and a few
 | 
				
			||||||
 | 
					// lines of context. The number of context lines is set by diff.Context
 | 
				
			||||||
 | 
					// which defaults to three.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// By default, the diff control lines (those with *** or ---) are
 | 
				
			||||||
 | 
					// created with a trailing newline.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// For inputs that do not have trailing newlines, set the diff.Eol
 | 
				
			||||||
 | 
					// argument to "" so that the output will be uniformly newline free.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// The context diff format normally has a header for filenames and
 | 
				
			||||||
 | 
					// modification times.  Any or all of these may be specified using
 | 
				
			||||||
 | 
					// strings for diff.FromFile, diff.ToFile, diff.FromDate, diff.ToDate.
 | 
				
			||||||
 | 
					// The modification times are normally expressed in the ISO 8601 format.
 | 
				
			||||||
 | 
					// If not specified, the strings default to blanks.
 | 
				
			||||||
 | 
					func WriteContextDiff(writer io.Writer, diff ContextDiff) error {
 | 
				
			||||||
 | 
						buf := bufio.NewWriter(writer)
 | 
				
			||||||
 | 
						defer buf.Flush()
 | 
				
			||||||
 | 
						var diffErr error
 | 
				
			||||||
 | 
						wf := func(format string, args ...interface{}) {
 | 
				
			||||||
 | 
							_, err := buf.WriteString(fmt.Sprintf(format, args...))
 | 
				
			||||||
 | 
							if diffErr == nil && err != nil {
 | 
				
			||||||
 | 
								diffErr = err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						ws := func(s string) {
 | 
				
			||||||
 | 
							_, err := buf.WriteString(s)
 | 
				
			||||||
 | 
							if diffErr == nil && err != nil {
 | 
				
			||||||
 | 
								diffErr = err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if len(diff.Eol) == 0 {
 | 
				
			||||||
 | 
							diff.Eol = "\n"
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						prefix := map[byte]string{
 | 
				
			||||||
 | 
							'i': "+ ",
 | 
				
			||||||
 | 
							'd': "- ",
 | 
				
			||||||
 | 
							'r': "! ",
 | 
				
			||||||
 | 
							'e': "  ",
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						started := false
 | 
				
			||||||
 | 
						m := NewMatcher(diff.A, diff.B)
 | 
				
			||||||
 | 
						for _, g := range m.GetGroupedOpCodes(diff.Context) {
 | 
				
			||||||
 | 
							if !started {
 | 
				
			||||||
 | 
								started = true
 | 
				
			||||||
 | 
								fromDate := ""
 | 
				
			||||||
 | 
								if len(diff.FromDate) > 0 {
 | 
				
			||||||
 | 
									fromDate = "\t" + diff.FromDate
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								toDate := ""
 | 
				
			||||||
 | 
								if len(diff.ToDate) > 0 {
 | 
				
			||||||
 | 
									toDate = "\t" + diff.ToDate
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								if diff.FromFile != "" || diff.ToFile != "" {
 | 
				
			||||||
 | 
									wf("*** %s%s%s", diff.FromFile, fromDate, diff.Eol)
 | 
				
			||||||
 | 
									wf("--- %s%s%s", diff.ToFile, toDate, diff.Eol)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							first, last := g[0], g[len(g)-1]
 | 
				
			||||||
 | 
							ws("***************" + diff.Eol)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							range1 := formatRangeContext(first.I1, last.I2)
 | 
				
			||||||
 | 
							wf("*** %s ****%s", range1, diff.Eol)
 | 
				
			||||||
 | 
							for _, c := range g {
 | 
				
			||||||
 | 
								if c.Tag == 'r' || c.Tag == 'd' {
 | 
				
			||||||
 | 
									for _, cc := range g {
 | 
				
			||||||
 | 
										if cc.Tag == 'i' {
 | 
				
			||||||
 | 
											continue
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
										for _, line := range diff.A[cc.I1:cc.I2] {
 | 
				
			||||||
 | 
											ws(prefix[cc.Tag] + line)
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									break
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							range2 := formatRangeContext(first.J1, last.J2)
 | 
				
			||||||
 | 
							wf("--- %s ----%s", range2, diff.Eol)
 | 
				
			||||||
 | 
							for _, c := range g {
 | 
				
			||||||
 | 
								if c.Tag == 'r' || c.Tag == 'i' {
 | 
				
			||||||
 | 
									for _, cc := range g {
 | 
				
			||||||
 | 
										if cc.Tag == 'd' {
 | 
				
			||||||
 | 
											continue
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
										for _, line := range diff.B[cc.J1:cc.J2] {
 | 
				
			||||||
 | 
											ws(prefix[cc.Tag] + line)
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									break
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return diffErr
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Like WriteContextDiff but returns the diff a string.
 | 
				
			||||||
 | 
					func GetContextDiffString(diff ContextDiff) (string, error) {
 | 
				
			||||||
 | 
						w := &bytes.Buffer{}
 | 
				
			||||||
 | 
						err := WriteContextDiff(w, diff)
 | 
				
			||||||
 | 
						return string(w.Bytes()), err
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Split a string on "\n" while preserving them. The output can be used
 | 
				
			||||||
 | 
					// as input for UnifiedDiff and ContextDiff structures.
 | 
				
			||||||
 | 
					func SplitLines(s string) []string {
 | 
				
			||||||
 | 
						lines := strings.SplitAfter(s, "\n")
 | 
				
			||||||
 | 
						lines[len(lines)-1] += "\n"
 | 
				
			||||||
 | 
						return lines
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -1,4 +1,4 @@
 | 
				
			||||||
Copyright (c) 2015, visualfc <visualfc@gmail.com>
 | 
					Copyright (c) 2011-2017, visualfc <visualfc@gmail.com>
 | 
				
			||||||
All rights reserved.
 | 
					All rights reserved.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Redistribution and use in source and binary forms, with or without
 | 
					Redistribution and use in source and binary forms, with or without
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,2 +1,28 @@
 | 
				
			||||||
# gotools
 | 
					LiteIDE Golang Tools
 | 
				
			||||||
liteide golang tools
 | 
					=========
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### LiteIDE
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					_LiteIDE is a simple, open source, cross-platform Go IDE._
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### GoTools
 | 
				
			||||||
 | 
					_GoTools is a golang tools support for LiteIDE._
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Website
 | 
				
			||||||
 | 
					* LiteIDE Source code
 | 
				
			||||||
 | 
					<https://github.com/visualfc/liteide>
 | 
				
			||||||
 | 
					* Gotools Source code
 | 
				
			||||||
 | 
					<https://github.com/visualfc/gotools>
 | 
				
			||||||
 | 
					* Binary downloads 
 | 
				
			||||||
 | 
					<http://sourceforge.net/projects/liteide/files>
 | 
				
			||||||
 | 
					* Google group
 | 
				
			||||||
 | 
					<https://groups.google.com/group/liteide-dev>
 | 
				
			||||||
 | 
					* How to Install
 | 
				
			||||||
 | 
					<https://github.com/visualfc/liteide/blob/master/liteidex/deploy/welcome/en/install.md>
 | 
				
			||||||
 | 
					* FAQ
 | 
				
			||||||
 | 
					<https://github.com/visualfc/liteide/blob/master/liteidex/deploy/welcome/en/guide.md>
 | 
				
			||||||
 | 
					* Changes
 | 
				
			||||||
 | 
					<https://github.com/visualfc/liteide/blob/master/liteidex/deploy/welcome/en/changes.md>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Donate
 | 
				
			||||||
 | 
					* <http://visualfc.github.com/support>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -191,7 +191,10 @@ func Main() {
 | 
				
			||||||
			cmd.Stdin = Stdin
 | 
								cmd.Stdin = Stdin
 | 
				
			||||||
			cmd.Stdout = Stdout
 | 
								cmd.Stdout = Stdout
 | 
				
			||||||
			cmd.Stderr = Stderr
 | 
								cmd.Stderr = Stderr
 | 
				
			||||||
			cmd.Run(cmd, args)
 | 
								err := cmd.Run(cmd, args)
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									SetExitStatus(2)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
			Exit()
 | 
								Exit()
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,4 +1,4 @@
 | 
				
			||||||
// Copyright 2011-2015 visualfc <visualfc@gmail.com>. All rights reserved.
 | 
					// Copyright 2011-2017 visualfc <visualfc@gmail.com>. All rights reserved.
 | 
				
			||||||
// Use of this source code is governed by a BSD-style
 | 
					// Use of this source code is governed by a BSD-style
 | 
				
			||||||
// license that can be found in the LICENSE file.
 | 
					// license that can be found in the LICENSE file.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,135 @@
 | 
				
			||||||
 | 
					// Copyright 2011-2015 visualfc <visualfc@gmail.com>. All rights reserved.
 | 
				
			||||||
 | 
					// Use of this source code is governed by a BSD-style
 | 
				
			||||||
 | 
					// license that can be found in the LICENSE file.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package finddecl
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"errors"
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"go/ast"
 | 
				
			||||||
 | 
						"go/parser"
 | 
				
			||||||
 | 
						"go/token"
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
 | 
						"path/filepath"
 | 
				
			||||||
 | 
						"strconv"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/visualfc/gotools/command"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var Command = &command.Command{
 | 
				
			||||||
 | 
						Run:       runFindDecl,
 | 
				
			||||||
 | 
						UsageLine: "finddecl",
 | 
				
			||||||
 | 
						Short:     "golang finddecl util",
 | 
				
			||||||
 | 
						Long:      `golang finddecl util.`,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					var (
 | 
				
			||||||
 | 
						filePath string
 | 
				
			||||||
 | 
						fileLine int
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func init() {
 | 
				
			||||||
 | 
						Command.Flag.StringVar(&filePath, "file", "", "file path")
 | 
				
			||||||
 | 
						Command.Flag.IntVar(&fileLine, "line", -1, "file line")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func runFindDecl(cmd *command.Command, args []string) error {
 | 
				
			||||||
 | 
						if len(filePath) == 0 || fileLine == -1 {
 | 
				
			||||||
 | 
							cmd.Usage()
 | 
				
			||||||
 | 
							return os.ErrInvalid
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if !filepath.IsAbs(filePath) {
 | 
				
			||||||
 | 
							dir, err := os.Getwd()
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							filePath = filepath.Join(dir, filePath)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						fset := token.NewFileSet()
 | 
				
			||||||
 | 
						f, err := parser.ParseFile(fset, filePath, nil, 0)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						decl := findDecl(fset, f, fileLine)
 | 
				
			||||||
 | 
						if decl == nil {
 | 
				
			||||||
 | 
							fmt.Println("-")
 | 
				
			||||||
 | 
							return errors.New("error find decl")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						printDecl(fset, decl, fileLine)
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Info struct {
 | 
				
			||||||
 | 
						Type      string
 | 
				
			||||||
 | 
						Name      string
 | 
				
			||||||
 | 
						BeginLine int
 | 
				
			||||||
 | 
						EndLine   int
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func printDecl(fset *token.FileSet, decl ast.Decl, line int) {
 | 
				
			||||||
 | 
						var tag string
 | 
				
			||||||
 | 
						var name string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						tag = "-"
 | 
				
			||||||
 | 
						name = "-"
 | 
				
			||||||
 | 
						switch d := decl.(type) {
 | 
				
			||||||
 | 
						case *ast.GenDecl:
 | 
				
			||||||
 | 
							switch d.Tok {
 | 
				
			||||||
 | 
							case token.IMPORT:
 | 
				
			||||||
 | 
								tag = "import"
 | 
				
			||||||
 | 
								if len(d.Specs) > 0 {
 | 
				
			||||||
 | 
									if ts := d.Specs[0].(*ast.ImportSpec); ts != nil {
 | 
				
			||||||
 | 
										name, _ = strconv.Unquote(ts.Path.Value)
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							case token.TYPE:
 | 
				
			||||||
 | 
								tag = "type"
 | 
				
			||||||
 | 
								if len(d.Specs) > 0 {
 | 
				
			||||||
 | 
									if ts := d.Specs[0].(*ast.TypeSpec); ts != nil {
 | 
				
			||||||
 | 
										name = ts.Name.Name
 | 
				
			||||||
 | 
										switch ts.Type.(type) {
 | 
				
			||||||
 | 
										case *ast.StructType:
 | 
				
			||||||
 | 
											tag = "struct"
 | 
				
			||||||
 | 
										case *ast.InterfaceType:
 | 
				
			||||||
 | 
											tag = "interface"
 | 
				
			||||||
 | 
										default:
 | 
				
			||||||
 | 
											tag = "type"
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							case token.VAR, token.CONST:
 | 
				
			||||||
 | 
								tag = d.Tok.String()
 | 
				
			||||||
 | 
								var testName string
 | 
				
			||||||
 | 
								for _, ds := range d.Specs {
 | 
				
			||||||
 | 
									if ts := ds.(*ast.ValueSpec); ts != nil {
 | 
				
			||||||
 | 
										name = ts.Names[0].Name
 | 
				
			||||||
 | 
										for _, n := range ts.Names {
 | 
				
			||||||
 | 
											if line >= fset.Position(n.Pos()).Line && line <= fset.Position(n.End()).Line {
 | 
				
			||||||
 | 
												testName = n.Name
 | 
				
			||||||
 | 
												break
 | 
				
			||||||
 | 
											}
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								if testName != "" {
 | 
				
			||||||
 | 
									name = testName
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							default:
 | 
				
			||||||
 | 
								tag = d.Tok.String()
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						case *ast.FuncDecl:
 | 
				
			||||||
 | 
							tag = "func"
 | 
				
			||||||
 | 
							name = d.Name.Name
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						fmt.Println(tag, name, fset.Position(decl.Pos()).Line, fset.Position(decl.End()).Line)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func findDecl(fset *token.FileSet, file *ast.File, line int) ast.Decl {
 | 
				
			||||||
 | 
						for _, decl := range file.Decls {
 | 
				
			||||||
 | 
							if line >= fset.Position(decl.Pos()).Line && line <= fset.Position(decl.End()).Line {
 | 
				
			||||||
 | 
								return decl
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,64 @@
 | 
				
			||||||
 | 
					// Copyright 2011-2015 visualfc <visualfc@gmail.com>. All rights reserved.
 | 
				
			||||||
 | 
					// Use of this source code is governed by a BSD-style
 | 
				
			||||||
 | 
					// license that can be found in the LICENSE file.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package godiff
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"io/ioutil"
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
 | 
						"os/exec"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/pmezard/go-difflib/difflib"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func UnifiedDiffLines(a []string, b []string) (string, error) {
 | 
				
			||||||
 | 
						diff := difflib.UnifiedDiff{
 | 
				
			||||||
 | 
							A:        a,
 | 
				
			||||||
 | 
							B:        b,
 | 
				
			||||||
 | 
							FromFile: "Original",
 | 
				
			||||||
 | 
							ToFile:   "Current",
 | 
				
			||||||
 | 
							Context:  3,
 | 
				
			||||||
 | 
							Eol:      "\n",
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return difflib.GetUnifiedDiffString(diff)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func UnifiedDiffString(a string, b string) (string, error) {
 | 
				
			||||||
 | 
						diff := difflib.UnifiedDiff{
 | 
				
			||||||
 | 
							A:        difflib.SplitLines(a),
 | 
				
			||||||
 | 
							B:        difflib.SplitLines(b),
 | 
				
			||||||
 | 
							FromFile: "Original",
 | 
				
			||||||
 | 
							ToFile:   "Current",
 | 
				
			||||||
 | 
							Context:  3,
 | 
				
			||||||
 | 
							Eol:      "\n",
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return difflib.GetUnifiedDiffString(diff)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func UnifiedDiffBytesByCmd(b1, b2 []byte) (data []byte, err error) {
 | 
				
			||||||
 | 
						f1, err := ioutil.TempFile("", "godiff")
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						defer os.Remove(f1.Name())
 | 
				
			||||||
 | 
						defer f1.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						f2, err := ioutil.TempFile("", "godiff")
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						defer os.Remove(f2.Name())
 | 
				
			||||||
 | 
						defer f2.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						f1.Write(b1)
 | 
				
			||||||
 | 
						f2.Write(b2)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						data, err = exec.Command("diff", "-u", f1.Name(), f2.Name()).CombinedOutput()
 | 
				
			||||||
 | 
						if len(data) > 0 {
 | 
				
			||||||
 | 
							// diff exits with a non-zero status when the files don't match.
 | 
				
			||||||
 | 
							// Ignore that failure as long as we get output.
 | 
				
			||||||
 | 
							err = nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,198 @@
 | 
				
			||||||
 | 
					// Copyright 2013 The 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.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package gofmt
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"bytes"
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"go/scanner"
 | 
				
			||||||
 | 
						"go/token"
 | 
				
			||||||
 | 
						"io"
 | 
				
			||||||
 | 
						"io/ioutil"
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
 | 
						"path/filepath"
 | 
				
			||||||
 | 
						"runtime"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
						"sync"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/visualfc/gotools/command"
 | 
				
			||||||
 | 
						"github.com/visualfc/gotools/godiff"
 | 
				
			||||||
 | 
						"golang.org/x/tools/imports"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var Command = &command.Command{
 | 
				
			||||||
 | 
						Run:       runGofmt,
 | 
				
			||||||
 | 
						UsageLine: "gofmt [flags] [path ...]",
 | 
				
			||||||
 | 
						Short:     "gofmt formats Go source.",
 | 
				
			||||||
 | 
						Long:      `gofmt formats Go source`,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var (
 | 
				
			||||||
 | 
						gofmtList         bool
 | 
				
			||||||
 | 
						gofmtWrite        bool
 | 
				
			||||||
 | 
						gofmtDiff         bool
 | 
				
			||||||
 | 
						gofmtAllErrors    bool
 | 
				
			||||||
 | 
						gofmtFixImports   bool
 | 
				
			||||||
 | 
						gofmtSortImports  bool
 | 
				
			||||||
 | 
						gofmtUseGodiffLib bool
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// layout control
 | 
				
			||||||
 | 
						gofmtComments  bool
 | 
				
			||||||
 | 
						gofmtTabWidth  int
 | 
				
			||||||
 | 
						gofmtTabIndent bool
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//func init
 | 
				
			||||||
 | 
					func init() {
 | 
				
			||||||
 | 
						Command.Flag.BoolVar(&gofmtList, "l", false, "list files whose formatting differs from goimport's")
 | 
				
			||||||
 | 
						Command.Flag.BoolVar(&gofmtWrite, "w", false, "write result to (source) file instead of stdout")
 | 
				
			||||||
 | 
						Command.Flag.BoolVar(&gofmtDiff, "d", false, "display diffs instead of rewriting files")
 | 
				
			||||||
 | 
						Command.Flag.BoolVar(&gofmtAllErrors, "e", false, "report all errors (not just the first 10 on different lines)")
 | 
				
			||||||
 | 
						Command.Flag.BoolVar(&gofmtFixImports, "fiximports", false, "updates Go import lines, adding missing ones and removing unreferenced ones")
 | 
				
			||||||
 | 
						Command.Flag.BoolVar(&gofmtSortImports, "sortimports", false, "sort Go import lines use goimports style")
 | 
				
			||||||
 | 
						Command.Flag.BoolVar(&gofmtUseGodiffLib, "godiff", true, "diff use godiff library")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// layout control
 | 
				
			||||||
 | 
						Command.Flag.BoolVar(&gofmtComments, "comments", true, "print comments")
 | 
				
			||||||
 | 
						Command.Flag.IntVar(&gofmtTabWidth, "tabwidth", 8, "tab width")
 | 
				
			||||||
 | 
						Command.Flag.BoolVar(&gofmtTabIndent, "tabs", true, "indent with tabs")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var (
 | 
				
			||||||
 | 
						fileSet  = token.NewFileSet() // per process FileSet
 | 
				
			||||||
 | 
						exitCode = 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						initModesOnce sync.Once // guards calling initModes
 | 
				
			||||||
 | 
						//parserMode    parser.Mode
 | 
				
			||||||
 | 
						//printerMode   printer.Mode
 | 
				
			||||||
 | 
						options *imports.Options
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func report(err error) {
 | 
				
			||||||
 | 
						scanner.PrintError(os.Stderr, err)
 | 
				
			||||||
 | 
						exitCode = 2
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func runGofmt(cmd *command.Command, args []string) error {
 | 
				
			||||||
 | 
						runtime.GOMAXPROCS(runtime.NumCPU())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if gofmtTabWidth < 0 {
 | 
				
			||||||
 | 
							fmt.Fprintf(os.Stderr, "negative tabwidth %d\n", gofmtTabWidth)
 | 
				
			||||||
 | 
							exitCode = 2
 | 
				
			||||||
 | 
							os.Exit(exitCode)
 | 
				
			||||||
 | 
							return os.ErrInvalid
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if gofmtFixImports {
 | 
				
			||||||
 | 
							gofmtSortImports = true
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						options = &imports.Options{
 | 
				
			||||||
 | 
							FormatOnly: !gofmtFixImports,
 | 
				
			||||||
 | 
							TabWidth:   gofmtTabWidth,
 | 
				
			||||||
 | 
							TabIndent:  gofmtTabIndent,
 | 
				
			||||||
 | 
							Comments:   gofmtComments,
 | 
				
			||||||
 | 
							AllErrors:  gofmtAllErrors,
 | 
				
			||||||
 | 
							Fragment:   true,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if len(args) == 0 {
 | 
				
			||||||
 | 
							if err := processFile("<standard input>", os.Stdin, os.Stdout, true); err != nil {
 | 
				
			||||||
 | 
								report(err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							for _, path := range args {
 | 
				
			||||||
 | 
								switch dir, err := os.Stat(path); {
 | 
				
			||||||
 | 
								case err != nil:
 | 
				
			||||||
 | 
									report(err)
 | 
				
			||||||
 | 
								case dir.IsDir():
 | 
				
			||||||
 | 
									walkDir(path)
 | 
				
			||||||
 | 
								default:
 | 
				
			||||||
 | 
									if err := processFile(path, nil, os.Stdout, false); err != nil {
 | 
				
			||||||
 | 
										report(err)
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						os.Exit(exitCode)
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func isGoFile(f os.FileInfo) bool {
 | 
				
			||||||
 | 
						// ignore non-Go files
 | 
				
			||||||
 | 
						name := f.Name()
 | 
				
			||||||
 | 
						return !f.IsDir() && !strings.HasPrefix(name, ".") && strings.HasSuffix(name, ".go")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func processFile(filename string, in io.Reader, out io.Writer, stdin bool) error {
 | 
				
			||||||
 | 
						if in == nil {
 | 
				
			||||||
 | 
							f, err := os.Open(filename)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							defer f.Close()
 | 
				
			||||||
 | 
							in = f
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						src, err := ioutil.ReadAll(in)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						res, err := imports.Process(filename, src, options)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if !bytes.Equal(src, res) {
 | 
				
			||||||
 | 
							// formatting has changed
 | 
				
			||||||
 | 
							if gofmtList {
 | 
				
			||||||
 | 
								fmt.Fprintln(out, filename)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if gofmtWrite {
 | 
				
			||||||
 | 
								err = ioutil.WriteFile(filename, res, 0)
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									return err
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if gofmtDiff {
 | 
				
			||||||
 | 
								if gofmtUseGodiffLib {
 | 
				
			||||||
 | 
									data, err := godiff.UnifiedDiffString(string(src), string(res))
 | 
				
			||||||
 | 
									if err != nil {
 | 
				
			||||||
 | 
										return fmt.Errorf("computing diff: %s", err)
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									fmt.Printf("diff %s gofmt/%s\n", filename, filename)
 | 
				
			||||||
 | 
									out.Write([]byte(data))
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									data, err := godiff.UnifiedDiffBytesByCmd(src, res)
 | 
				
			||||||
 | 
									if err != nil {
 | 
				
			||||||
 | 
										return fmt.Errorf("computing diff: %s", err)
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									fmt.Printf("diff %s gofmt/%s\n", filename, filename)
 | 
				
			||||||
 | 
									out.Write(data)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if !gofmtList && !gofmtWrite && !gofmtDiff {
 | 
				
			||||||
 | 
							_, err = out.Write(res)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return err
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func visitFile(path string, f os.FileInfo, err error) error {
 | 
				
			||||||
 | 
						if err == nil && isGoFile(f) {
 | 
				
			||||||
 | 
							err = processFile(path, nil, os.Stdout, false)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							report(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func walkDir(path string) {
 | 
				
			||||||
 | 
						filepath.Walk(path, visitFile)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,99 @@
 | 
				
			||||||
 | 
					// Copyright 2011-2015 visualfc <visualfc@gmail.com>. All rights reserved.
 | 
				
			||||||
 | 
					// Use of this source code is governed by a BSD-style
 | 
				
			||||||
 | 
					// license that can be found in the LICENSE file.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package gotest
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"go/build"
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
 | 
						"os/exec"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/visualfc/gotools/command"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var Command = &command.Command{
 | 
				
			||||||
 | 
						Run:         runGotest,
 | 
				
			||||||
 | 
						UsageLine:   "gotest -f filename [build/test flags]",
 | 
				
			||||||
 | 
						Short:       "go test go filename",
 | 
				
			||||||
 | 
						Long:        `go test go filename`,
 | 
				
			||||||
 | 
						CustomFlags: true,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var testFileName string
 | 
				
			||||||
 | 
					var testFileArgs string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//func init() {
 | 
				
			||||||
 | 
					//	Command.Flag.StringVar(&testFileName, "f", "", "test go filename")
 | 
				
			||||||
 | 
					//}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func runGotest(cmd *command.Command, args []string) error {
 | 
				
			||||||
 | 
						index := -1
 | 
				
			||||||
 | 
						for n, arg := range args {
 | 
				
			||||||
 | 
							if arg == "-f" {
 | 
				
			||||||
 | 
								index = n
 | 
				
			||||||
 | 
								break
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if index >= 0 && index < len(args) {
 | 
				
			||||||
 | 
							testFileName = args[index+1]
 | 
				
			||||||
 | 
							var r []string
 | 
				
			||||||
 | 
							r = append(r, args[0:index]...)
 | 
				
			||||||
 | 
							r = append(r, args[index+2:]...)
 | 
				
			||||||
 | 
							args = r
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if testFileName == "" {
 | 
				
			||||||
 | 
							cmd.Usage()
 | 
				
			||||||
 | 
							return os.ErrInvalid
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if !strings.HasSuffix(testFileName, "_test.go") {
 | 
				
			||||||
 | 
							fmt.Println("The test filename must xxx_test.go")
 | 
				
			||||||
 | 
							return os.ErrInvalid
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						pkg, err := build.ImportDir(".", 0)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							fmt.Println("import dir error", err)
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var testFiles []string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for _, file := range pkg.XTestGoFiles {
 | 
				
			||||||
 | 
							if file == testFileName {
 | 
				
			||||||
 | 
								testFiles = append(testFiles, file)
 | 
				
			||||||
 | 
								break
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						for _, file := range pkg.TestGoFiles {
 | 
				
			||||||
 | 
							if file == testFileName {
 | 
				
			||||||
 | 
								testFiles = append(testFiles, pkg.GoFiles...)
 | 
				
			||||||
 | 
								testFiles = append(testFiles, file)
 | 
				
			||||||
 | 
								break
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						gobin, err := exec.LookPath("go")
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							fmt.Println("error look go", err)
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var testArgs []string
 | 
				
			||||||
 | 
						testArgs = append(testArgs, "test")
 | 
				
			||||||
 | 
						if len(args) > 0 {
 | 
				
			||||||
 | 
							testArgs = append(testArgs, args...)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						testArgs = append(testArgs, testFiles...)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						command := exec.Command(gobin, testArgs...)
 | 
				
			||||||
 | 
						command.Dir = pkg.Dir
 | 
				
			||||||
 | 
						command.Stdin = os.Stdin
 | 
				
			||||||
 | 
						command.Stdout = os.Stdout
 | 
				
			||||||
 | 
						command.Stderr = os.Stderr
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return command.Run()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -1,4 +1,4 @@
 | 
				
			||||||
// Copyright 2011-2015 visualfc <visualfc@gmail.com>. All rights reserved.
 | 
					// Copyright 2011-2017 visualfc <visualfc@gmail.com>. All rights reserved.
 | 
				
			||||||
// Use of this source code is governed by a BSD-style
 | 
					// Use of this source code is governed by a BSD-style
 | 
				
			||||||
// license that can be found in the LICENSE file.
 | 
					// license that can be found in the LICENSE file.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -8,14 +8,17 @@ import (
 | 
				
			||||||
	"github.com/visualfc/gotools/astview"
 | 
						"github.com/visualfc/gotools/astview"
 | 
				
			||||||
	"github.com/visualfc/gotools/command"
 | 
						"github.com/visualfc/gotools/command"
 | 
				
			||||||
	"github.com/visualfc/gotools/docview"
 | 
						"github.com/visualfc/gotools/docview"
 | 
				
			||||||
 | 
						"github.com/visualfc/gotools/finddecl"
 | 
				
			||||||
	"github.com/visualfc/gotools/finddoc"
 | 
						"github.com/visualfc/gotools/finddoc"
 | 
				
			||||||
	"github.com/visualfc/gotools/goapi"
 | 
						"github.com/visualfc/gotools/goapi"
 | 
				
			||||||
	"github.com/visualfc/gotools/goimports"
 | 
						"github.com/visualfc/gotools/gofmt"
 | 
				
			||||||
	"github.com/visualfc/gotools/gopresent"
 | 
						"github.com/visualfc/gotools/gopresent"
 | 
				
			||||||
 | 
						"github.com/visualfc/gotools/gotest"
 | 
				
			||||||
	"github.com/visualfc/gotools/jsonfmt"
 | 
						"github.com/visualfc/gotools/jsonfmt"
 | 
				
			||||||
	"github.com/visualfc/gotools/oracle"
 | 
						"github.com/visualfc/gotools/oracle"
 | 
				
			||||||
	"github.com/visualfc/gotools/pkgs"
 | 
						"github.com/visualfc/gotools/pkgs"
 | 
				
			||||||
	"github.com/visualfc/gotools/runcmd"
 | 
						"github.com/visualfc/gotools/runcmd"
 | 
				
			||||||
 | 
						"github.com/visualfc/gotools/terminal"
 | 
				
			||||||
	"github.com/visualfc/gotools/types"
 | 
						"github.com/visualfc/gotools/types"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -26,16 +29,19 @@ func init() {
 | 
				
			||||||
	command.Register(runcmd.Command)
 | 
						command.Register(runcmd.Command)
 | 
				
			||||||
	command.Register(docview.Command)
 | 
						command.Register(docview.Command)
 | 
				
			||||||
	command.Register(astview.Command)
 | 
						command.Register(astview.Command)
 | 
				
			||||||
	command.Register(goimports.Command)
 | 
					 | 
				
			||||||
	command.Register(gopresent.Command)
 | 
						command.Register(gopresent.Command)
 | 
				
			||||||
	command.Register(goapi.Command)
 | 
						command.Register(goapi.Command)
 | 
				
			||||||
	command.Register(pkgs.Command)
 | 
						command.Register(pkgs.Command)
 | 
				
			||||||
	command.Register(oracle.Command)
 | 
						command.Register(oracle.Command)
 | 
				
			||||||
 | 
						command.Register(gofmt.Command)
 | 
				
			||||||
 | 
						command.Register(gotest.Command)
 | 
				
			||||||
 | 
						command.Register(finddecl.Command)
 | 
				
			||||||
 | 
						command.Register(terminal.Command)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func main() {
 | 
					func main() {
 | 
				
			||||||
	command.AppName = "gotools"
 | 
						command.AppName = "gotools"
 | 
				
			||||||
	command.AppVersion = "1.0"
 | 
						command.AppVersion = "1.9"
 | 
				
			||||||
	command.AppInfo = "Go tools for liteide."
 | 
						command.AppInfo = "Go tools for liteide."
 | 
				
			||||||
	command.Main()
 | 
						command.Main()
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,33 @@
 | 
				
			||||||
 | 
					// +build !windows
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package terminal
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"io"
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
 | 
						"os/exec"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/kr/pty"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func GetShell() (cmd string, args []string) {
 | 
				
			||||||
 | 
						return "/bin/sh", []string{"-l", "-i"}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func Execute(c *exec.Cmd) error {
 | 
				
			||||||
 | 
						f, err := pty.Start(c)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						go func() {
 | 
				
			||||||
 | 
							for {
 | 
				
			||||||
 | 
								io.Copy(f, os.Stdin)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}()
 | 
				
			||||||
 | 
						go func() {
 | 
				
			||||||
 | 
							for {
 | 
				
			||||||
 | 
								io.Copy(os.Stdout, f)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}()
 | 
				
			||||||
 | 
						return c.Wait()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,34 @@
 | 
				
			||||||
 | 
					// +build windows
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package terminal
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
 | 
						"os/exec"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func checkFiles(names ...string) string {
 | 
				
			||||||
 | 
						for _, name := range names {
 | 
				
			||||||
 | 
							_, err := os.Stat(name)
 | 
				
			||||||
 | 
							if err == nil {
 | 
				
			||||||
 | 
								return name
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return ""
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func GetShell() (cmd string, args []string) {
 | 
				
			||||||
 | 
						windir := os.Getenv("windir")
 | 
				
			||||||
 | 
						if windir == "" {
 | 
				
			||||||
 | 
							windir = "c:\\windows"
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						cmd = checkFiles(windir+"\\Sysnative\\cmd.exe", windir+"\\System32\\cmd.exe")
 | 
				
			||||||
 | 
						return
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func Execute(c *exec.Cmd) error {
 | 
				
			||||||
 | 
						c.Stdin = os.Stdin
 | 
				
			||||||
 | 
						c.Stdout = os.Stdout
 | 
				
			||||||
 | 
						c.Stderr = os.Stderr
 | 
				
			||||||
 | 
						return c.Run()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,39 @@
 | 
				
			||||||
 | 
					// Copyright 2011-2017 visualfc <visualfc@gmail.com>. All rights reserved.
 | 
				
			||||||
 | 
					// Use of this source code is governed by a BSD-style
 | 
				
			||||||
 | 
					// license that can be found in the LICENSE file.
 | 
				
			||||||
 | 
					package terminal
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
 | 
						"os/exec"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/visualfc/gotools/command"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var Command = &command.Command{
 | 
				
			||||||
 | 
						Run:         runTerminal,
 | 
				
			||||||
 | 
						UsageLine:   "terminal [program_name arguments...]",
 | 
				
			||||||
 | 
						Short:       "terminal [program]",
 | 
				
			||||||
 | 
						Long:        `terminal program and arguments`,
 | 
				
			||||||
 | 
						CustomFlags: true,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func runTerminal(cmd *command.Command, args []string) (err error) {
 | 
				
			||||||
 | 
						var c *exec.Cmd
 | 
				
			||||||
 | 
						if len(args) >= 1 {
 | 
				
			||||||
 | 
							var carg []string
 | 
				
			||||||
 | 
							if len(args) >= 2 {
 | 
				
			||||||
 | 
								carg = append(carg, args[1:]...)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							c = exec.Command(args[0], carg...)
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							shellCmd, shellArgs := GetShell()
 | 
				
			||||||
 | 
							c = exec.Command(shellCmd, shellArgs...)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if c == nil {
 | 
				
			||||||
 | 
							return os.ErrInvalid
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						err = Execute(c)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -1,173 +0,0 @@
 | 
				
			||||||
// +build ignore
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Copyright 2013 The 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.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Command mkindex creates the file "pkgindex.go" containing an index of the Go
 | 
					 | 
				
			||||||
// standard library. The file is intended to be built as part of the imports
 | 
					 | 
				
			||||||
// package, so that the package may be used in environments where a GOROOT is
 | 
					 | 
				
			||||||
// not available (such as App Engine).
 | 
					 | 
				
			||||||
package main
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import (
 | 
					 | 
				
			||||||
	"bytes"
 | 
					 | 
				
			||||||
	"fmt"
 | 
					 | 
				
			||||||
	"go/ast"
 | 
					 | 
				
			||||||
	"go/build"
 | 
					 | 
				
			||||||
	"go/format"
 | 
					 | 
				
			||||||
	"go/parser"
 | 
					 | 
				
			||||||
	"go/token"
 | 
					 | 
				
			||||||
	"io/ioutil"
 | 
					 | 
				
			||||||
	"log"
 | 
					 | 
				
			||||||
	"os"
 | 
					 | 
				
			||||||
	"path"
 | 
					 | 
				
			||||||
	"path/filepath"
 | 
					 | 
				
			||||||
	"strings"
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
var (
 | 
					 | 
				
			||||||
	pkgIndex = make(map[string][]pkg)
 | 
					 | 
				
			||||||
	exports  = make(map[string]map[string]bool)
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func main() {
 | 
					 | 
				
			||||||
	// Don't use GOPATH.
 | 
					 | 
				
			||||||
	ctx := build.Default
 | 
					 | 
				
			||||||
	ctx.GOPATH = ""
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Populate pkgIndex global from GOROOT.
 | 
					 | 
				
			||||||
	for _, path := range ctx.SrcDirs() {
 | 
					 | 
				
			||||||
		f, err := os.Open(path)
 | 
					 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			log.Print(err)
 | 
					 | 
				
			||||||
			continue
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		children, err := f.Readdir(-1)
 | 
					 | 
				
			||||||
		f.Close()
 | 
					 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			log.Print(err)
 | 
					 | 
				
			||||||
			continue
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		for _, child := range children {
 | 
					 | 
				
			||||||
			if child.IsDir() {
 | 
					 | 
				
			||||||
				loadPkg(path, child.Name())
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	// Populate exports global.
 | 
					 | 
				
			||||||
	for _, ps := range pkgIndex {
 | 
					 | 
				
			||||||
		for _, p := range ps {
 | 
					 | 
				
			||||||
			e := loadExports(p.dir)
 | 
					 | 
				
			||||||
			if e != nil {
 | 
					 | 
				
			||||||
				exports[p.dir] = e
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Construct source file.
 | 
					 | 
				
			||||||
	var buf bytes.Buffer
 | 
					 | 
				
			||||||
	fmt.Fprint(&buf, pkgIndexHead)
 | 
					 | 
				
			||||||
	fmt.Fprintf(&buf, "var pkgIndexMaster = %#v\n", pkgIndex)
 | 
					 | 
				
			||||||
	fmt.Fprintf(&buf, "var exportsMaster = %#v\n", exports)
 | 
					 | 
				
			||||||
	src := buf.Bytes()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Replace main.pkg type name with pkg.
 | 
					 | 
				
			||||||
	src = bytes.Replace(src, []byte("main.pkg"), []byte("pkg"), -1)
 | 
					 | 
				
			||||||
	// Replace actual GOROOT with "/go".
 | 
					 | 
				
			||||||
	src = bytes.Replace(src, []byte(ctx.GOROOT), []byte("/go"), -1)
 | 
					 | 
				
			||||||
	// Add some line wrapping.
 | 
					 | 
				
			||||||
	src = bytes.Replace(src, []byte("}, "), []byte("},\n"), -1)
 | 
					 | 
				
			||||||
	src = bytes.Replace(src, []byte("true, "), []byte("true,\n"), -1)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	var err error
 | 
					 | 
				
			||||||
	src, err = format.Source(src)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		log.Fatal(err)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Write out source file.
 | 
					 | 
				
			||||||
	err = ioutil.WriteFile("pkgindex.go", src, 0644)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		log.Fatal(err)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const pkgIndexHead = `package imports
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func init() {
 | 
					 | 
				
			||||||
	pkgIndexOnce.Do(func() {
 | 
					 | 
				
			||||||
		pkgIndex.m = pkgIndexMaster
 | 
					 | 
				
			||||||
	})
 | 
					 | 
				
			||||||
	loadExports = func(dir string) map[string]bool {
 | 
					 | 
				
			||||||
		return exportsMaster[dir]
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
`
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
type pkg struct {
 | 
					 | 
				
			||||||
	importpath string // full pkg import path, e.g. "net/http"
 | 
					 | 
				
			||||||
	dir        string // absolute file path to pkg directory e.g. "/usr/lib/go/src/fmt"
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
var fset = token.NewFileSet()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func loadPkg(root, importpath string) {
 | 
					 | 
				
			||||||
	shortName := path.Base(importpath)
 | 
					 | 
				
			||||||
	if shortName == "testdata" {
 | 
					 | 
				
			||||||
		return
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	dir := filepath.Join(root, importpath)
 | 
					 | 
				
			||||||
	pkgIndex[shortName] = append(pkgIndex[shortName], pkg{
 | 
					 | 
				
			||||||
		importpath: importpath,
 | 
					 | 
				
			||||||
		dir:        dir,
 | 
					 | 
				
			||||||
	})
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	pkgDir, err := os.Open(dir)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	children, err := pkgDir.Readdir(-1)
 | 
					 | 
				
			||||||
	pkgDir.Close()
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	for _, child := range children {
 | 
					 | 
				
			||||||
		name := child.Name()
 | 
					 | 
				
			||||||
		if name == "" {
 | 
					 | 
				
			||||||
			continue
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if c := name[0]; c == '.' || ('0' <= c && c <= '9') {
 | 
					 | 
				
			||||||
			continue
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if child.IsDir() {
 | 
					 | 
				
			||||||
			loadPkg(root, filepath.Join(importpath, name))
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func loadExports(dir string) map[string]bool {
 | 
					 | 
				
			||||||
	exports := make(map[string]bool)
 | 
					 | 
				
			||||||
	buildPkg, err := build.ImportDir(dir, 0)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		if strings.Contains(err.Error(), "no buildable Go source files in") {
 | 
					 | 
				
			||||||
			return nil
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		log.Printf("could not import %q: %v", dir, err)
 | 
					 | 
				
			||||||
		return nil
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	for _, file := range buildPkg.GoFiles {
 | 
					 | 
				
			||||||
		f, err := parser.ParseFile(fset, filepath.Join(dir, file), nil, 0)
 | 
					 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			log.Printf("could not parse %q: %v", file, err)
 | 
					 | 
				
			||||||
			continue
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		for name := range f.Scope.Objects {
 | 
					 | 
				
			||||||
			if ast.IsExported(name) {
 | 
					 | 
				
			||||||
				exports[name] = true
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return exports
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,90 +0,0 @@
 | 
				
			||||||
// +build ignore
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// mkstdlib generates the zstdlib.go file, containing the Go standard
 | 
					 | 
				
			||||||
// library API symbols. It's baked into the binary to avoid scanning
 | 
					 | 
				
			||||||
// GOPATH in the common case.
 | 
					 | 
				
			||||||
package main
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import (
 | 
					 | 
				
			||||||
	"bufio"
 | 
					 | 
				
			||||||
	"bytes"
 | 
					 | 
				
			||||||
	"fmt"
 | 
					 | 
				
			||||||
	"go/format"
 | 
					 | 
				
			||||||
	"io"
 | 
					 | 
				
			||||||
	"log"
 | 
					 | 
				
			||||||
	"os"
 | 
					 | 
				
			||||||
	"path"
 | 
					 | 
				
			||||||
	"path/filepath"
 | 
					 | 
				
			||||||
	"regexp"
 | 
					 | 
				
			||||||
	"sort"
 | 
					 | 
				
			||||||
	"strings"
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func mustOpen(name string) io.Reader {
 | 
					 | 
				
			||||||
	f, err := os.Open(name)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		log.Fatal(err)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return f
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func api(base string) string {
 | 
					 | 
				
			||||||
	return filepath.Join(os.Getenv("GOROOT"), "api", base)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
var sym = regexp.MustCompile(`^pkg (\S+).*?, (?:var|func|type|const) ([A-Z]\w*)`)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func main() {
 | 
					 | 
				
			||||||
	var buf bytes.Buffer
 | 
					 | 
				
			||||||
	outf := func(format string, args ...interface{}) {
 | 
					 | 
				
			||||||
		fmt.Fprintf(&buf, format, args...)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	outf("// AUTO-GENERATED BY mkstdlib.go\n\n")
 | 
					 | 
				
			||||||
	outf("package imports\n")
 | 
					 | 
				
			||||||
	outf("var stdlib = map[string]string{\n")
 | 
					 | 
				
			||||||
	f := io.MultiReader(
 | 
					 | 
				
			||||||
		mustOpen(api("go1.txt")),
 | 
					 | 
				
			||||||
		mustOpen(api("go1.1.txt")),
 | 
					 | 
				
			||||||
		mustOpen(api("go1.2.txt")),
 | 
					 | 
				
			||||||
	)
 | 
					 | 
				
			||||||
	sc := bufio.NewScanner(f)
 | 
					 | 
				
			||||||
	fullImport := map[string]string{} // "zip.NewReader" => "archive/zip"
 | 
					 | 
				
			||||||
	ambiguous := map[string]bool{}
 | 
					 | 
				
			||||||
	var keys []string
 | 
					 | 
				
			||||||
	for sc.Scan() {
 | 
					 | 
				
			||||||
		l := sc.Text()
 | 
					 | 
				
			||||||
		has := func(v string) bool { return strings.Contains(l, v) }
 | 
					 | 
				
			||||||
		if has("struct, ") || has("interface, ") || has(", method (") {
 | 
					 | 
				
			||||||
			continue
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if m := sym.FindStringSubmatch(l); m != nil {
 | 
					 | 
				
			||||||
			full := m[1]
 | 
					 | 
				
			||||||
			key := path.Base(full) + "." + m[2]
 | 
					 | 
				
			||||||
			if exist, ok := fullImport[key]; ok {
 | 
					 | 
				
			||||||
				if exist != full {
 | 
					 | 
				
			||||||
					ambiguous[key] = true
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
			} else {
 | 
					 | 
				
			||||||
				fullImport[key] = full
 | 
					 | 
				
			||||||
				keys = append(keys, key)
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if err := sc.Err(); err != nil {
 | 
					 | 
				
			||||||
		log.Fatal(err)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	sort.Strings(keys)
 | 
					 | 
				
			||||||
	for _, key := range keys {
 | 
					 | 
				
			||||||
		if ambiguous[key] {
 | 
					 | 
				
			||||||
			outf("\t// %q is ambiguous\n", key)
 | 
					 | 
				
			||||||
		} else {
 | 
					 | 
				
			||||||
			outf("\t%q: %q,\n", key, fullImport[key])
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	outf("}\n")
 | 
					 | 
				
			||||||
	fmtbuf, err := format.Source(buf.Bytes())
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		log.Fatal(err)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	os.Stdout.Write(fmtbuf)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
| 
						 | 
					@ -5,8 +5,8 @@
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			"checksumSHA1": "ZesPb1w0u5/uv/fdVc+G6wOee30=",
 | 
								"checksumSHA1": "ZesPb1w0u5/uv/fdVc+G6wOee30=",
 | 
				
			||||||
			"path": "github.com/b3log/wide",
 | 
								"path": "github.com/b3log/wide",
 | 
				
			||||||
			"revision": "bfd836c6fc8fbc007ce437db1406455493cd532f",
 | 
								"revision": "0a51a03f35a3fa9edbfa790698aa615f4dd49783",
 | 
				
			||||||
			"revisionTime": "2018-03-13T04:57:02Z"
 | 
								"revisionTime": "2018-03-13T05:05:45Z"
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			"checksumSHA1": "83iEp3SqOoIkZUYyR7BOVP4vaGE=",
 | 
								"checksumSHA1": "83iEp3SqOoIkZUYyR7BOVP4vaGE=",
 | 
				
			||||||
| 
						 | 
					@ -44,6 +44,13 @@
 | 
				
			||||||
			"revision": "a3ec486e6a7a41858210b0fc5d7b5df593b3c4a3",
 | 
								"revision": "a3ec486e6a7a41858210b0fc5d7b5df593b3c4a3",
 | 
				
			||||||
			"revisionTime": "2015-05-30T03:03:52Z"
 | 
								"revisionTime": "2015-05-30T03:03:52Z"
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								"checksumSHA1": "8jdAN2n9kJasxFmikoyDZAbv8ig=",
 | 
				
			||||||
 | 
								"origin": "github.com/visualfc/gotools/vendor/github.com/kr/pty",
 | 
				
			||||||
 | 
								"path": "github.com/kr/pty",
 | 
				
			||||||
 | 
								"revision": "a74583d2ad8773983f273657291df981033308a4",
 | 
				
			||||||
 | 
								"revisionTime": "2018-02-17T01:27:43Z"
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			"checksumSHA1": "gKm38IQHqLfYHwS7B6JDTJzXYYI=",
 | 
								"checksumSHA1": "gKm38IQHqLfYHwS7B6JDTJzXYYI=",
 | 
				
			||||||
			"path": "github.com/nsf/gocode",
 | 
								"path": "github.com/nsf/gocode",
 | 
				
			||||||
| 
						 | 
					@ -51,10 +58,17 @@
 | 
				
			||||||
			"revisionTime": "2018-01-07T08:36:41Z"
 | 
								"revisionTime": "2018-01-07T08:36:41Z"
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			"checksumSHA1": "4fp/TH5nX7seO5p4qJfIj/BokbI=",
 | 
								"checksumSHA1": "R3yk3OJzuq4MVfVGX5AhkkYv1ko=",
 | 
				
			||||||
 | 
								"origin": "github.com/visualfc/gotools/vendor/github.com/pmezard/go-difflib/difflib",
 | 
				
			||||||
 | 
								"path": "github.com/pmezard/go-difflib/difflib",
 | 
				
			||||||
 | 
								"revision": "a74583d2ad8773983f273657291df981033308a4",
 | 
				
			||||||
 | 
								"revisionTime": "2018-02-17T01:27:43Z"
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								"checksumSHA1": "+1Mo4q3vpSB8ibclS1t/AWeDQ50=",
 | 
				
			||||||
			"path": "github.com/visualfc/gotools",
 | 
								"path": "github.com/visualfc/gotools",
 | 
				
			||||||
			"revision": "b8348693492ca3791bccfa028f3c19634c11c5b5",
 | 
								"revision": "a74583d2ad8773983f273657291df981033308a4",
 | 
				
			||||||
			"revisionTime": "2015-04-09T14:25:36Z"
 | 
								"revisionTime": "2018-02-17T01:27:43Z"
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			"checksumSHA1": "QBwzyczN2ftqO2Vxag5TS79q32E=",
 | 
								"checksumSHA1": "QBwzyczN2ftqO2Vxag5TS79q32E=",
 | 
				
			||||||
| 
						 | 
					@ -63,10 +77,10 @@
 | 
				
			||||||
			"revisionTime": "2015-04-09T14:25:36Z"
 | 
								"revisionTime": "2015-04-09T14:25:36Z"
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			"checksumSHA1": "wqPqP7RGlukrSMzGKVk1tvyWh1o=",
 | 
								"checksumSHA1": "02f7N5GTTddWpIDhsRthRKZs4nI=",
 | 
				
			||||||
			"path": "github.com/visualfc/gotools/command",
 | 
								"path": "github.com/visualfc/gotools/command",
 | 
				
			||||||
			"revision": "b8348693492ca3791bccfa028f3c19634c11c5b5",
 | 
								"revision": "a74583d2ad8773983f273657291df981033308a4",
 | 
				
			||||||
			"revisionTime": "2015-04-09T14:25:36Z"
 | 
								"revisionTime": "2018-02-17T01:27:43Z"
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			"checksumSHA1": "sA2FN414rFhNEgac/a0IyUR8dMA=",
 | 
								"checksumSHA1": "sA2FN414rFhNEgac/a0IyUR8dMA=",
 | 
				
			||||||
| 
						 | 
					@ -74,6 +88,12 @@
 | 
				
			||||||
			"revision": "b8348693492ca3791bccfa028f3c19634c11c5b5",
 | 
								"revision": "b8348693492ca3791bccfa028f3c19634c11c5b5",
 | 
				
			||||||
			"revisionTime": "2015-04-09T14:25:36Z"
 | 
								"revisionTime": "2015-04-09T14:25:36Z"
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								"checksumSHA1": "5sCU5oJIkeXHHhzNyzusSH+de/8=",
 | 
				
			||||||
 | 
								"path": "github.com/visualfc/gotools/finddecl",
 | 
				
			||||||
 | 
								"revision": "a74583d2ad8773983f273657291df981033308a4",
 | 
				
			||||||
 | 
								"revisionTime": "2018-02-17T01:27:43Z"
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			"checksumSHA1": "LvxdwB9kJ77cFS4qiRAh9ts2nz4=",
 | 
								"checksumSHA1": "LvxdwB9kJ77cFS4qiRAh9ts2nz4=",
 | 
				
			||||||
			"path": "github.com/visualfc/gotools/finddoc",
 | 
								"path": "github.com/visualfc/gotools/finddoc",
 | 
				
			||||||
| 
						 | 
					@ -86,6 +106,18 @@
 | 
				
			||||||
			"revision": "b8348693492ca3791bccfa028f3c19634c11c5b5",
 | 
								"revision": "b8348693492ca3791bccfa028f3c19634c11c5b5",
 | 
				
			||||||
			"revisionTime": "2015-04-09T14:25:36Z"
 | 
								"revisionTime": "2015-04-09T14:25:36Z"
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								"checksumSHA1": "o0usAkdrTaqufvGTZu++Cc/DVMM=",
 | 
				
			||||||
 | 
								"path": "github.com/visualfc/gotools/godiff",
 | 
				
			||||||
 | 
								"revision": "a74583d2ad8773983f273657291df981033308a4",
 | 
				
			||||||
 | 
								"revisionTime": "2018-02-17T01:27:43Z"
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								"checksumSHA1": "Wnc0wf1qEhXNdqLPMlIA3vCPpPY=",
 | 
				
			||||||
 | 
								"path": "github.com/visualfc/gotools/gofmt",
 | 
				
			||||||
 | 
								"revision": "a74583d2ad8773983f273657291df981033308a4",
 | 
				
			||||||
 | 
								"revisionTime": "2018-02-17T01:27:43Z"
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			"checksumSHA1": "RLl3M7ywfbgNYR2HfBywCruQz9c=",
 | 
								"checksumSHA1": "RLl3M7ywfbgNYR2HfBywCruQz9c=",
 | 
				
			||||||
			"path": "github.com/visualfc/gotools/goimports",
 | 
								"path": "github.com/visualfc/gotools/goimports",
 | 
				
			||||||
| 
						 | 
					@ -98,6 +130,12 @@
 | 
				
			||||||
			"revision": "b8348693492ca3791bccfa028f3c19634c11c5b5",
 | 
								"revision": "b8348693492ca3791bccfa028f3c19634c11c5b5",
 | 
				
			||||||
			"revisionTime": "2015-04-09T14:25:36Z"
 | 
								"revisionTime": "2015-04-09T14:25:36Z"
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								"checksumSHA1": "f8zd5auG22z/GqXqW9giRDYEw6A=",
 | 
				
			||||||
 | 
								"path": "github.com/visualfc/gotools/gotest",
 | 
				
			||||||
 | 
								"revision": "a74583d2ad8773983f273657291df981033308a4",
 | 
				
			||||||
 | 
								"revisionTime": "2018-02-17T01:27:43Z"
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			"checksumSHA1": "kyKBo2ux+HflucDj/qzoko5YoMA=",
 | 
								"checksumSHA1": "kyKBo2ux+HflucDj/qzoko5YoMA=",
 | 
				
			||||||
			"path": "github.com/visualfc/gotools/jsonfmt",
 | 
								"path": "github.com/visualfc/gotools/jsonfmt",
 | 
				
			||||||
| 
						 | 
					@ -128,6 +166,12 @@
 | 
				
			||||||
			"revision": "b8348693492ca3791bccfa028f3c19634c11c5b5",
 | 
								"revision": "b8348693492ca3791bccfa028f3c19634c11c5b5",
 | 
				
			||||||
			"revisionTime": "2015-04-09T14:25:36Z"
 | 
								"revisionTime": "2015-04-09T14:25:36Z"
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								"checksumSHA1": "6CSw3Gtu+hJ6QTfhJ+985cualxs=",
 | 
				
			||||||
 | 
								"path": "github.com/visualfc/gotools/terminal",
 | 
				
			||||||
 | 
								"revision": "a74583d2ad8773983f273657291df981033308a4",
 | 
				
			||||||
 | 
								"revisionTime": "2018-02-17T01:27:43Z"
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			"checksumSHA1": "qlJsyinWZ07PaOyTYyxXOmhDN/I=",
 | 
								"checksumSHA1": "qlJsyinWZ07PaOyTYyxXOmhDN/I=",
 | 
				
			||||||
			"path": "github.com/visualfc/gotools/types",
 | 
								"path": "github.com/visualfc/gotools/types",
 | 
				
			||||||
| 
						 | 
					@ -220,7 +264,7 @@
 | 
				
			||||||
			"revision": ""
 | 
								"revision": ""
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			"checksumSHA1": "p3oUXqh8ecOL76mLRB5h/Qlz9lE=",
 | 
								"checksumSHA1": "TzQP7S5QaE3ipab7SNo/PcFb2bI=",
 | 
				
			||||||
			"path": "golang.org/x/tools/imports",
 | 
								"path": "golang.org/x/tools/imports",
 | 
				
			||||||
			"revision": ""
 | 
								"revision": ""
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue