Example #1
0
func (v *typeView) insertMethod(m *[]*types.Func, elems *[]*typeView, before bool, i int, success, fail func()) {
	if !before {
		i++
	}
	sig := &types.Signature{Recv: types.NewVar(0, v.currentPkg, "", *v.typ)}
	*m = append((*m)[:i], append([]*types.Func{types.NewFunc(0, v.currentPkg, "", sig)}, (*m)[i:]...)...)
	v.refresh()
	t := (*elems)[i]
	t.edit(func() {
		if *t.typ == nil || len(t.name.Text()) == 0 {
			*m = append((*m)[:i], (*m)[i+1:]...)
			v.refresh()
			if fail != nil {
				fail()
			} else {
				if !before {
					i--
				}
				SetKeyFocus((*elems)[i])
			}
		} else {
			if success != nil {
				success()
			} else {
				SetKeyFocus(t)
			}
		}
	})
}
Example #2
0
func (r *reader) obj(x ast.Expr) types.Object {
	switch x := x.(type) {
	case *ast.Ident:
		if obj := r.scope.LookupParent(x.Name); obj != nil {
			if v, ok := obj.(*types.Var); ok && v.Pkg == nil { // ignore local vars
				return nil
			}
			return obj
		}
		return unknownObject{pkg: r.pkg, name: x.Name}
	case *ast.SelectorExpr:
		// TODO: Type.Method and pkg.Type.Method
		n1 := name(x.X)
		n2 := x.Sel.Name
		switch obj := r.scope.LookupParent(n1).(type) {
		case *types.PkgName:
			if obj := obj.Pkg.Scope().Lookup(n2); obj != nil {
				return obj
			}
			return unknownObject{pkg: obj.Pkg, name: n2}
		case *types.Var:
			t := obj.Type
			fm, _, addr := types.LookupFieldOrMethod(t, r.pkg, n2)
			switch fm := fm.(type) {
			case *types.Func:
				sig := fm.Type.(*types.Signature)
				return types.NewFunc(0, r.pkg, n2, types.NewSignature(nil, newVar("", t), sig.Params, sig.Results, sig.IsVariadic))
			case *types.Var:
				return field{fm, t, addr}
			}
			return unknownObject{pkg: obj.Pkg, recv: t, name: n2}
		}
	}
	panic("unreachable")
}
Example #3
0
func (r *reader) value(b *block, x, y ast.Expr, set bool, s ast.Stmt) {
	if x2, ok := x.(*ast.UnaryExpr); ok {
		x = x2.X
	}
	var obj types.Object
	if _, ok := x.(*ast.StarExpr); !ok { // StarExpr indicates an assignment (*x = y), for which obj must be nil
		obj = r.obj(x)
		if u, ok := obj.(unknownObject); ok {
			if _, ok := s.(*ast.DeclStmt); ok {
				obj = types.NewConst(0, u.pkg, u.name, nil, nil)
			} else {
				var t types.Type
				if set {
					t = r.scope.Lookup(name(y)).(*types.Var).Type
				}
				addr := false
				if s, ok := s.(*ast.AssignStmt); ok {
					if s.Tok == token.DEFINE {
						_, addr = s.Rhs[0].(*ast.UnaryExpr)
					}
				}
				if u.recv != nil {
					obj = field{types.NewVar(0, u.pkg, u.name, t), u.recv, addr} // it may be a method val, but a non-addressable field is close enough
				} else {
					if addr {
						obj = types.NewVar(0, u.pkg, u.name, t)
					} else {
						obj = types.NewFunc(0, u.pkg, u.name, types.NewSignature(nil, newVar("", u.recv), nil, nil, false))
					}
				}
			}
			//unknown(obj) == true
		}
	}
	n := newValueNode(obj, r.pkg, set)
	b.addNode(n)
	switch x := x.(type) {
	case *ast.SelectorExpr:
		r.in(x.X, n.x)
	case *ast.StarExpr:
		r.in(x.X, n.x)
	}
	if set {
		r.in(y, n.y)
	} else {
		r.out(y, n.y)
	}
	r.seq(n, s)
}
Example #4
0
func (b *browser) KeyPress(event KeyEvent) {
	if b.options.canFuncAsVal && event.Shift != b.funcAsVal {
		b.funcAsVal = event.Shift
		b.refresh()
	}
	switch event.Key {
	case KeyUp:
		if b.newObj == nil {
			b.i--
			if b.i < 0 {
				b.i += len(b.objs)
			}
			b.refresh()
		}
	case KeyDown:
		if b.newObj == nil {
			b.i++
			if b.i >= len(b.objs) {
				b.i -= len(b.objs)
			}
			b.refresh()
		}
	case KeyLeft:
		if len(b.path) > 0 && b.newObj == nil {
			parent := b.path[0]
			b.path = b.path[1:]

			i := len(b.pathTexts) - 1
			b.pathTexts[i].Close()
			b.pathTexts = b.pathTexts[:i]

			b.clearText()
			b.makeCurrent(parent)
		}
	case KeyEnter:
		obj := b.currentObj()
		if obj == nil {
			return
		}
		if pkg, ok := obj.(*pkgObject); ok && event.Shift && b.options.mutable && b.newObj == nil {
			Show(b.pkgName)
			b.pkgName.Accept = func(name string) {
				if pkg.pkgName != name {
					pkg.pkgName = name
					savePackageName(pkg.importPath, name)
				}
				b.refresh()
				SetKeyFocus(b)
			}
			b.pkgName.Reject = func() {
				b.refresh()
				SetKeyFocus(b)
			}
			SetKeyFocus(b.pkgName)
			return
		}
		if event.Command && b.options.mutable && b.newObj == nil {
			b.newObj = obj
			b.oldName = obj.GetName()
			if p, ok := obj.(*pkgObject); ok {
				delete(pkgs, p.importPath)
				delete(pkgObjects, p.importPath)
			} else {
				if objs := obj.GetPkg().Scope().Objects; objs[obj.GetName()] == obj {
					delete(objs, obj.GetName())
				} else {
					t, _ := indirect(obj.(*types.Func).Type.(*types.Signature).Recv.Type)
					n := t.(*types.Named)
					for i, f2 := range n.Methods {
						if f2 == obj {
							n.Methods = append(n.Methods[:i], n.Methods[i+1:]...)
							break
						}
					}
				}
			}
			b.text.SetText(obj.GetName())
			return
		}

		if obj := b.newObj; obj != nil {
			if !b.unique(obj.GetName()) {
				return
			}
			b.newObj = nil
			if p, ok := obj.(*pkgObject); ok {
				oldImportPath := p.importPath
				if len(b.path) > 0 {
					parent := b.path[0].(*pkgObject)
					p.srcDir = parent.srcDir
					p.importPath = path.Join(parent.importPath, p.name)
				} else {
					dirs := build.Default.SrcDirs()
					p.srcDir = dirs[len(dirs)-1]
					p.importPath = p.name
				}
				pkgObjects[p.importPath] = p
				if b.oldName != "" {
					b.oldName = ""
					if err := refactor.MovePackage(oldImportPath, p.importPath); err != nil {
						fmt.Printf("error moving package %s: %s\n", oldImportPath, err)
					}
					b.clearText()
					return
				}
				p.pkgName = p.name
				path := p.fullPath()
				if err := os.Mkdir(path, 0777); err != nil {
					fmt.Printf("error creating %s: %s\n", path, err)
					b.clearText()
					return
				}
			} else {
				if isMethod(obj) {
					recv := b.path[0].(*types.TypeName).Type.(*types.Named)
					recv.Methods = append(recv.Methods, obj.(*types.Func))
				} else {
					pkgs[b.path[0].(*pkgObject).importPath].Scope().Insert(obj)
				}
				if b.oldName != "" {
					newName := obj.GetName()
					setObjectName(obj, b.oldName)
					oldPaths := []string{fluxPath(obj)}
					if t, ok := obj.(*types.TypeName); ok {
						for _, m := range t.Type.(*types.Named).Methods {
							oldPaths = append(oldPaths, fluxPath(m))
						}
					}
					recv := ""
					if isMethod(obj) {
						t, _ := indirect(obj.(*types.Func).Type.(*types.Signature).Recv.Type)
						recv = t.(*types.Named).Obj.Name
					}
					if err := refactor.Rename(obj.GetPkg().Path, recv, b.oldName, newName); err != nil {
						fmt.Printf("error renaming %v to %v: %v\n", b.oldName, newName, err)
						b.oldName = ""
						b.clearText()
						return
					}
					setObjectName(obj, newName)
					newPaths := []string{fluxPath(obj)}
					if t, ok := obj.(*types.TypeName); ok {
						for _, m := range t.Type.(*types.Named).Methods {
							newPaths = append(newPaths, fluxPath(m))
						}
					}
					for i := range oldPaths {
						if err := os.Rename(oldPaths[i], newPaths[i]); err != nil {
							fmt.Println("error renaming files: ", err)
						}
					}
					b.oldName = ""
					b.clearText()
					return
				}
			}
			b.makeCurrent(obj)
		}

		_, isPkg := obj.(*pkgObject)
		_, isType := obj.(*types.TypeName)
		if !(isPkg || isType && !b.options.acceptTypes) {
			b.finished = true
			b.accepted(obj)
			return
		}
		fallthrough
	case KeyRight:
		if b.newObj == nil {
			switch obj := b.currentObj().(type) {
			case *pkgObject, *types.TypeName:
				if t, ok := obj.(*types.TypeName); ok {
					if _, ok = t.Type.(*types.Basic); ok || t.Type == nil || !b.options.enterTypes {
						break
					}
				}
				b.path = append(objects{obj}, b.path...)

				sep := "."
				if _, ok := obj.(*pkgObject); ok {
					sep = "/"
				}
				t := NewText(obj.GetName() + sep)
				t.SetTextColor(color(obj, true, b.funcAsVal))
				t.SetBackgroundColor(Color{0, 0, 0, .7})
				b.Add(t)
				x := 0.0
				if t, ok := b.lastPathText(); ok {
					x = Pos(t).X + Width(t)
				}
				t.Move(Pt(x, 0))
				b.pathTexts = append(b.pathTexts, t)

				b.clearText()
			}
		}
	case KeyEscape:
		if b.newObj != nil {
			if b.oldName != "" {
				setObjectName(b.newObj, b.oldName)
				b.oldName = ""
				if isMethod(b.newObj) {
					recv := b.path[0].(*types.TypeName).Type.(*types.Named)
					recv.Methods = append(recv.Methods, b.newObj.(*types.Func))
				} else {
					pkgs[b.path[0].(*pkgObject).importPath].Scope().Insert(b.newObj)
				}
			} else if b.i < len(b.objs)-1 {
				b.i++
			}
			b.newObj = nil
			b.clearText()
		} else {
			b.cancel()
		}
	case KeyBackspace:
		if !event.Command && len(b.text.Text()) > 0 {
			b.text.KeyPress(event)
			break
		}
		fallthrough
	case KeyDelete:
		if event.Command && b.options.mutable && b.newObj == nil {
			b.clearText()
			if deleteObj(b.currentObj()) {
				if b.i > 0 {
					b.i--
				}
				b.clearText()
			}
		}
	default:
		if event.Command && (event.Key == KeyN || event.Key == KeyW || event.Key == KeyQ) {
			b.ViewBase.KeyPress(event)
			return
		}

		if !b.options.mutable {
			b.text.KeyPress(event)
			return
		}

		makeInPkg := false
		var pkg *types.Package
		var recv *types.TypeName
		if len(b.path) > 0 {
			switch obj := b.path[0].(type) {
			case *pkgObject:
				makeInPkg = true
				pkg = pkgs[obj.importPath]
			case *types.TypeName:
				recv = obj
				pkg = obj.Pkg
			}
		}
		makePkgInRoot := len(b.path) == 0 && event.Text == "1"
		makeMethod := recv != nil && event.Text == "3"
		if b.newObj == nil && event.Command && (makePkgInRoot || makeInPkg || makeMethod) {
			switch event.Text {
			case "1":
				b.newObj = &pkgObject{}
			case "2":
				t := types.NewTypeName(0, pkg, "", nil)
				t.Type = &types.Named{Obj: t}
				b.newObj = t
			case "3":
				sig := &types.Signature{}
				if recv != nil {
					sig.Recv = newVar("", &types.Pointer{Elem: recv.Type})
				}
				b.newObj = types.NewFunc(0, pkg, "", sig)
			case "4":
				b.newObj = types.NewVar(0, pkg, "", nil)
			case "5":
				b.newObj = types.NewConst(0, pkg, "", nil, nil)
			default:
				b.text.KeyPress(event)
				return
			}
			b.clearText()
		} else {
			b.text.KeyPress(event)
		}
	}
}
Example #5
0
func (b browser) filteredObjs() (objs objects) {
	add := func(obj types.Object) {
		if invisible(obj, b.currentPkg) {
			return
		}
		if _, ok := obj.(*pkgObject); ok || b.options.objFilter == nil || b.options.objFilter(obj) {
			objs = append(objs, obj)
		}
	}

	addSubPkgs := func(importPath string) {
		seen := map[string]bool{}
		for _, srcDir := range build.Default.SrcDirs() {
			files, err := ioutil.ReadDir(filepath.Join(srcDir, importPath))
			if err != nil {
				continue
			}
			for _, f := range files {
				name := filepath.Base(f.Name())
				if !f.IsDir() || !unicode.IsLetter([]rune(name)[0]) || name == "testdata" || seen[name] {
					continue
				}
				if _, ok := b.newObj.(*pkgObject); ok && name == b.oldName {
					// when editing a package path, it will be added in filteredObjs as newObj, so don't add it here
					continue
				}
				seen[name] = true

				importPath := path.Join(importPath, name)
				pkgObj, ok := pkgObjects[importPath]
				if !ok {
					if pkg, err := build.Import(importPath, "", build.AllowBinary); err == nil {
						name = pkg.Name
					}
					pkgObj = &pkgObject{nil, path.Base(importPath), srcDir, importPath, name}
					pkgObjects[importPath] = pkgObj
				}
				add(pkgObj)
			}
		}
	}

	if b.typ != nil {
		mset := types.NewMethodSet(b.typ)
		for i := 0; i < mset.Len(); i++ {
			m := mset.At(i)
			// m.Type() has the correct receiver for inherited methods (m.Obj does not)
			add(types.NewFunc(0, m.Obj.GetPkg(), m.Obj.GetName(), m.Type().(*types.Signature)))
		}
		fset := types.NewFieldSet(b.typ)
		for i := 0; i < fset.Len(); i++ {
			f := fset.At(i)
			add(field{f.Obj.(*types.Var), f.Recv, f.Indirect})
		}
	} else if len(b.path) > 0 {
		switch obj := b.path[0].(type) {
		case *pkgObject:
			if pkg, err := getPackage(obj.importPath); err == nil {
				for _, obj := range pkg.Scope().Objects {
					add(obj)
				}
			} else {
				if _, ok := err.(*build.NoGoError); !ok {
					fmt.Println(err)
				}
				pkgs[obj.importPath] = types.NewPackage(obj.importPath, obj.pkgName, types.NewScope(types.Universe))
			}
			addSubPkgs(obj.importPath)
		case *types.TypeName:
			for _, m := range intuitiveMethodSet(obj.Type) {
				if types.IsIdentical(m.Obj.(*types.Func).Type.(*types.Signature).Recv.Type, m.Recv) {
					// preserve Object identity for non-inherited methods so that fluxObjs works
					add(m.Obj)
				} else {
					// m.Type() has the correct receiver for inherited methods (m.Obj does not)
					add(types.NewFunc(0, m.Obj.GetPkg(), m.Obj.GetName(), m.Type().(*types.Signature)))
				}
			}
		}
	} else {
		for _, name := range []string{"break", "call", "continue", "convert", "defer", "func", "go", "if", "loop", "return", "select", "typeAssert"} {
			add(special{newVar(name, nil)})
		}
		for _, name := range []string{"=", "*"} {
			add(newVar(name, nil))
		}
		pkgs := b.imports
		if b.currentPkg != nil {
			pkgs = append(pkgs, b.currentPkg)
		}
		for _, p := range pkgs {
			for _, obj := range p.Scope().Objects {
				add(obj)
			}
		}
		for _, obj := range types.Universe.Objects {
			switch obj.GetName() {
			case "nil", "print", "println":
				continue
			}
			add(obj)
		}
		for _, op := range []string{"!", "&&", "||", "+", "-", "*", "/", "%", "&", "|", "^", "&^", "<<", ">>", "==", "!=", "<", "<=", ">", ">=", "[]", "[:]", "<-"} {
			add(types.NewFunc(0, nil, op, nil))
		}
		for _, t := range []*types.TypeName{protoPointer, protoArray, protoSlice, protoMap, protoChan, protoFunc, protoInterface, protoStruct} {
			add(t)
		}
		addSubPkgs("")
	}

	sort.Sort(objs)
	return
}
Example #6
0
func (r *reader) typ(x ast.Expr) types.Type {
	switch x := x.(type) {
	case *ast.Ident:
		if t, ok := r.scope.LookupParent(x.Name).(*types.TypeName); ok {
			return t.Type
		}
		return types.NewNamed(types.NewTypeName(0, r.pkg, x.Name, nil), types.Typ[types.Invalid], nil) //unknown(t.Obj) == true
	case *ast.SelectorExpr:
		pkg := r.scope.LookupParent(name(x.X)).(*types.PkgName).Pkg
		if t, ok := pkg.Scope().Lookup(x.Sel.Name).(*types.TypeName); ok {
			return t.Type
		}
		return types.NewNamed(types.NewTypeName(0, r.pkg, x.Sel.Name, nil), types.Typ[types.Invalid], nil) //unknown(t.Obj) == true
	case *ast.StarExpr:
		return types.NewPointer(r.typ(x.X))
	case *ast.ArrayType:
		elem := r.typ(x.Elt)
		if x.Len != nil {
			// TODO: x.Len
			return types.NewArray(elem, 0)
		}
		return types.NewSlice(elem)
	case *ast.Ellipsis:
		return types.NewSlice(r.typ(x.Elt))
	case *ast.MapType:
		return types.NewMap(r.typ(x.Key), r.typ(x.Value))
	case *ast.ChanType:
		dir := types.SendRecv
		if x.Dir&ast.SEND == 0 {
			dir = types.RecvOnly
		}
		if x.Dir&ast.RECV == 0 {
			dir = types.SendOnly
		}
		return types.NewChan(dir, r.typ(x.Value))
	case *ast.FuncType:
		var params, results []*types.Var
		for _, f := range x.Params.List {
			t := r.typ(f.Type)
			if f.Names == nil {
				params = append(params, types.NewParam(0, r.pkg, "", t))
			}
			for _, n := range f.Names {
				params = append(params, types.NewParam(0, r.pkg, n.Name, t))
			}
		}
		variadic := false
		if x.Results != nil {
			for _, f := range x.Results.List {
				t := r.typ(f.Type)
				if f.Names == nil {
					results = append(results, types.NewParam(0, r.pkg, "", t))
				}
				for _, n := range f.Names {
					results = append(results, types.NewParam(0, r.pkg, n.Name, t))
				}
				_, variadic = f.Type.(*ast.Ellipsis)
			}
		}
		return types.NewSignature(nil, nil, params, results, variadic)
	case *ast.StructType:
		var fields []*types.Var
		if x.Fields != nil {
			for _, f := range x.Fields.List {
				t := r.typ(f.Type)
				if f.Names == nil {
					fields = append(fields, types.NewField(0, r.pkg, "", t, true))
				}
				for _, n := range f.Names {
					fields = append(fields, types.NewField(0, r.pkg, n.Name, t, false))
				}
			}
		}
		return types.NewStruct(fields, nil)
	case *ast.InterfaceType:
		var methods []*types.Func
		var embeddeds []*types.Named
		if x.Methods != nil {
			for _, f := range x.Methods.List {
				switch t := r.typ(f.Type).(type) {
				case *types.Signature:
					methods = append(methods, types.NewFunc(0, r.pkg, f.Names[0].Name, t))
				case *types.Named:
					embeddeds = append(embeddeds, t)
				}
			}
		}
		return types.NewInterface(methods, embeddeds)
	}
	panic("unreachable")
}
Example #7
0
func (r *reader) call(b *block, x *ast.CallExpr, godefer string, s ast.Stmt) node {
	obj := r.obj(x.Fun)
	if u, ok := obj.(unknownObject); ok {
		sig := &types.Signature{}
		if u.recv != nil {
			sig.Recv = newVar("", u.recv)
		}
		for _, arg := range x.Args {
			sig.Params = append(sig.Params, newVar("", r.scope.Lookup(name(arg)).(*types.Var).Type))
		}
		if s, ok := s.(*ast.AssignStmt); ok {
			for _ = range s.Lhs {
				sig.Results = append(sig.Results, newVar("", nil))
			}
		}
		obj = types.NewFunc(0, u.pkg, u.name, sig) //unknown(obj) == true
	}
	n := newCallNode(obj, r.pkg, godefer)
	b.addNode(n)
	args := x.Args
	switch {
	case isMethod(obj):
		recv := x.Fun.(*ast.SelectorExpr).X
		args = append([]ast.Expr{recv}, args...)
	case obj == nil: // func value call
		args = append([]ast.Expr{x.Fun}, args...)
	}
	if n, ok := n.(interface {
		setType(types.Type)
	}); ok {
		n.setType(r.typ(args[0]))
		args = args[1:]
	}
	for i, arg := range args {
		if i >= len(ins(n)) {
			var newInput func(*types.Var) *port
			var v *types.Var
			switch n := n.(type) {
			case *callNode:
				newInput = n.newInput
				_, v = n.variadic()
			case *appendNode:
				newInput = n.newInput
				v = ins(n)[0].obj
			}
			if v != nil {
				if x.Ellipsis == 0 {
					v = newVar(v.Name, v.Type.(*types.Slice).Elem)
				}
				in := newInput(v)
				if x.Ellipsis != 0 {
					in.valView.setEllipsis()
				}
			} else {
				in := newInput(newVar("", r.scope.Lookup(name(arg)).(*types.Var).Type))
				in.bad = true
			}
		}
		r.in(arg, ins(n)[i])
	}
	r.seq(n, s)
	return n
}
Example #8
0
func (r *reader) block(b *block, s []ast.Stmt) {
	for _, s := range s {
		switch s := s.(type) {
		case *ast.AssignStmt:
			if s.Tok == token.DEFINE {
				switch x := s.Rhs[0].(type) {
				case *ast.BinaryExpr:
					n := newOperatorNode(types.NewFunc(0, nil, x.Op.String(), nil))
					b.addNode(n)
					r.in(x.X, n.ins[0])
					r.in(x.Y, n.ins[1])
					r.out(s.Lhs[0], n.outs[0])
				case *ast.CallExpr:
					if p, ok := x.Fun.(*ast.ParenExpr); ok { // writer puts conversions in parens for easy recognition
						n := newConvertNode(r.pkg)
						b.addNode(n)
						n.setType(r.typ(p.X))
						r.in(x.Args[0], n.ins[0])
						r.out(s.Lhs[0], n.outs[0])
					} else {
						n := r.call(b, x, "", s)
						for i, res := range s.Lhs {
							if i >= len(outs(n)) {
								out := n.(*callNode).newOutput(nil)
								out.bad = true
							}
							r.out(res, outs(n)[i])
						}
					}
				case *ast.CompositeLit:
					r.compositeLit(b, x, false, s)
				case *ast.FuncLit:
					n := newFuncNode(nil, b.childArranged)
					b.addNode(n)
					n.output.setType(r.typ(x.Type))
					r.out(s.Lhs[0], n.output)
					r.fun(n, x.Type, x.Body)
				case *ast.Ident, *ast.SelectorExpr, *ast.StarExpr:
					r.value(b, x, s.Lhs[0], false, s)
				case *ast.IndexExpr:
					r.index(b, x, s.Lhs[0], false, s)
				case *ast.SliceExpr:
					n := newSliceNode()
					b.addNode(n)
					r.in(x.X, n.x)
					r.in(x.Low, n.low)
					if x.High == nil {
						n.removePortBase(n.high)
						n.high = nil
					} else {
						r.in(x.High, n.high)
					}
					if x.Max != nil {
						n.max = n.newInput(newVar("max", types.Typ[types.Int]))
						r.in(x.Max, n.max)
					}
					r.out(s.Lhs[0], n.y)
				case *ast.TypeAssertExpr:
					n := newTypeAssertNode(r.pkg)
					b.addNode(n)
					n.setType(r.typ(x.Type))
					r.in(x.X, n.ins[0])
					r.out(s.Lhs[0], n.outs[0])
					r.out(s.Lhs[1], n.outs[1])
				case *ast.UnaryExpr:
					switch x.Op {
					case token.AND:
						switch y := x.X.(type) {
						case *ast.CompositeLit:
							r.compositeLit(b, y, true, s)
						case *ast.IndexExpr:
							r.index(b, y, s.Lhs[0], false, s)
						default:
							r.value(b, x, s.Lhs[0], false, s)
						}
					case token.NOT:
						n := newOperatorNode(types.NewFunc(0, nil, x.Op.String(), nil))
						b.addNode(n)
						r.in(x.X, n.ins[0])
						r.out(s.Lhs[0], n.outs[0])
					case token.ARROW:
						n := r.sendrecv(b, x.X, nil, s)
						r.out(s.Lhs[0], n.elem)
						r.out(s.Lhs[1], n.ok)
					}
				}
			} else {
				lh := s.Lhs[0]
				rh := s.Rhs[0]
				if x, ok := lh.(*ast.IndexExpr); ok {
					r.index(b, x, rh, true, s)
				} else if id, ok := lh.(*ast.Ident); !ok || r.conns[id.Name] == nil {
					r.value(b, lh, rh, true, s)
				} else {
					c := newConnection()
					lh := name(lh)
					rh := name(rh)
					c.setSrc(r.ports[rh])
					if cmt, ok := r.cmap[s]; ok {
						c.src.conntxt.SetText(cmt[0].List[0].Text[2:])
						c.toggleHidden()
					}
					if p, ok := r.ports[lh]; ok {
						c.feedback = true
						c.setDst(p)
					} else {
						r.conns[lh] = append(r.conns[lh], c)
					}
				}
			}
		case *ast.BranchStmt:
			n := newBranchNode(s.Tok.String())
			b.addNode(n)
			r.seq(n, s)
		case *ast.DeclStmt:
			decl := s.Decl.(*ast.GenDecl)
			v := decl.Specs[0].(*ast.ValueSpec)
			switch decl.Tok {
			case token.VAR:
				name := v.Names[0].Name
				if v.Type != nil {
					r.scope.Insert(newVar(name, r.typ(v.Type))) // local var has nil Pkg
					r.conns[name] = []*connection{}
				} else {
					r.out(v.Names[0], b.node.(*loopNode).inputsNode.outs[1])
				}
			case token.CONST:
				switch x := v.Values[0].(type) {
				case *ast.BasicLit:
					n := newBasicLiteralNode(x.Kind)
					b.addNode(n)
					switch x.Kind {
					case token.INT, token.FLOAT:
						n.text.SetText(x.Value)
					case token.IMAG:
						// TODO
					case token.STRING, token.CHAR:
						text, _ := strconv.Unquote(x.Value)
						n.text.SetText(text)
					}
					r.out(v.Names[0], n.outs[0])
					r.seq(n, s)
				case *ast.Ident, *ast.SelectorExpr:
					r.value(b, x, v.Names[0], false, s)
				}
			}
		case *ast.DeferStmt:
			r.call(b, s.Call, "defer ", s)
		case *ast.ExprStmt:
			switch x := s.X.(type) {
			case *ast.CallExpr:
				r.call(b, x, "", s)
			case *ast.UnaryExpr:
				r.sendrecv(b, x.X, nil, s)
			}
		case *ast.ForStmt:
			n := newLoopNode(b.childArranged)
			b.addNode(n)
			if s.Cond != nil {
				r.in(s.Cond.(*ast.BinaryExpr).Y, n.input)
			}
			if s.Init != nil {
				r.out(s.Init.(*ast.AssignStmt).Lhs[0], n.inputsNode.outs[0])
			}
			r.block(n.loopblk, s.Body.List)
			r.seq(n, s)
		case *ast.GoStmt:
			r.call(b, s.Call, "go ", s)
		case *ast.IfStmt:
			n := newIfNode(b.childArranged)
			b.addNode(n)
			for s := ast.Stmt(s); s != nil; {
				b, cond := n.newBlock()
				switch s2 := s.(type) {
				case *ast.IfStmt:
					r.in(s2.Cond, cond)
					r.block(b, s2.Body.List)
					s = s2.Else
				case *ast.BlockStmt:
					r.block(b, s2.List)
					s = nil
				}
			}
			r.seq(n, s)
		case *ast.RangeStmt:
			n := newLoopNode(b.childArranged)
			b.addNode(n)
			r.in(s.X, n.input)
			r.out(s.Key, n.inputsNode.outs[0])
			if s.Value != nil {
				r.out(s.Value, n.inputsNode.outs[1])
			}
			r.block(n.loopblk, s.Body.List)
			r.seq(n, s)
		case *ast.ReturnStmt:
			n := newBranchNode("return")
			b.addNode(n)
			r.seq(n, s)
		case *ast.SelectStmt:
			n := newSelectNode(b.childArranged)
			b.addNode(n)
			for _, s := range s.Body.List {
				s := s.(*ast.CommClause)
				c := n.newCase()
				switch s := s.Comm.(type) {
				case *ast.AssignStmt:
					c.send = false
					r.in(s.Rhs[0].(*ast.UnaryExpr).X, c.ch)
					r.out(s.Lhs[0], c.elemOk.outs[0])
					r.out(s.Lhs[1], c.elemOk.outs[1])
				case *ast.ExprStmt:
					c.send = false
					r.in(s.X.(*ast.UnaryExpr).X, c.ch)
				case *ast.SendStmt:
					r.in(s.Chan, c.ch)
					r.in(s.Value, c.elem)
				case nil:
					c.setDefault()
				}
				r.block(c.blk, s.Body)
			}
			r.seq(n, s)
		case *ast.SendStmt:
			r.sendrecv(b, s.Chan, s.Value, s)
		}
	}
}