fork of https://github.com/sourcegraph/zoekt
0

Configure Feed

Select the types of activity you want to include in your feed.

1// Copyright 2017 Google Inc. All rights reserved. 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15package ctags 16 17import ( 18 "fmt" 19 "log" 20 "os" 21 "strings" 22 "sync" 23 "time" 24 25 goctags "github.com/sourcegraph/go-ctags" 26) 27 28const debug = false 29 30type Parser = goctags.Parser 31type Entry = goctags.Entry 32 33type parseReq struct { 34 Name string 35 Content []byte 36} 37 38type parseResp struct { 39 Entries []*Entry 40 Err error 41} 42 43type lockedParser struct { 44 mu sync.Mutex 45 opts goctags.Options 46 p Parser 47 send chan<- parseReq 48 recv <-chan parseResp 49} 50 51// parseTimeout is how long we wait for a response for parsing a single file 52// in ctags. 1 minute is a very conservative timeout which we should only hit 53// if ctags hangs. 54const parseTimeout = time.Minute 55 56// Parse wraps go-ctags Parse. It lazily starts the process and adds a timeout 57// around parse requests. Additionally it serializes access to the parsing 58// process. The timeout is important since we occasionally come across 59// documents which hang universal-ctags. 60func (lp *lockedParser) Parse(name string, content []byte) ([]*Entry, error) { 61 lp.mu.Lock() 62 defer lp.mu.Unlock() 63 64 if lp.p == nil { 65 p, err := goctags.New(lp.opts) 66 if err != nil { 67 return nil, err 68 } 69 send := make(chan parseReq) 70 // buf of 1 so we avoid blocking sends in the parser if we exit early. 71 recv := make(chan parseResp, 1) 72 73 go func() { 74 defer close(recv) 75 for req := range send { 76 entries, err := p.Parse(req.Name, req.Content) 77 recv <- parseResp{Entries: entries, Err: err} 78 } 79 }() 80 81 lp.p = p 82 lp.send = send 83 lp.recv = recv 84 } 85 86 lp.send <- parseReq{Name: name, Content: content} 87 88 deadline := time.NewTimer(parseTimeout) 89 defer deadline.Stop() 90 91 select { 92 case resp := <-lp.recv: 93 return resp.Entries, resp.Err 94 case <-deadline.C: 95 // Error out since ctags hanging is a sign something bad is happening. 96 lp.close() 97 return nil, fmt.Errorf("ctags timedout after %s parsing %s", parseTimeout, name) 98 } 99} 100 101func (lp *lockedParser) Close() { 102 lp.mu.Lock() 103 defer lp.mu.Unlock() 104 lp.close() 105} 106 107// close assumes lp.mu is held. 108func (lp *lockedParser) close() { 109 if lp.p == nil { 110 return 111 } 112 113 lp.p.Close() 114 lp.p = nil 115 close(lp.send) 116 lp.send = nil 117 lp.recv = nil 118} 119 120// NewParser creates a parser that is implemented by the given 121// universal-ctags binary. The parser is safe for concurrent use. 122func NewParser(bin string) (Parser, error) { 123 if strings.Contains(bin, "universal-ctags") { 124 opts := goctags.Options{ 125 Bin: bin, 126 } 127 if debug { 128 opts.Info = log.New(os.Stderr, "CTAGS INF: ", log.LstdFlags) 129 opts.Debug = log.New(os.Stderr, "CTAGS DBG: ", log.LstdFlags) 130 } 131 return &lockedParser{ 132 opts: opts, 133 }, nil 134 } 135 136 log.Fatal("not implemented") 137 return nil, nil 138}