github.com/goplus/gop@v1.2.6/x/watcher/changes.go (about) 1 /* 2 * Copyright (c) 2023 The GoPlus Authors (goplus.org). All rights reserved. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package watcher 18 19 import ( 20 "io/fs" 21 "log" 22 "path" 23 "path/filepath" 24 "strings" 25 "sync" 26 27 "github.com/goplus/mod/gopmod" 28 ) 29 30 // ----------------------------------------------------------------------------------------- 31 32 type module struct { 33 exts []string 34 } 35 36 func (p *module) ignore(fname string) bool { 37 ext := path.Ext(fname) 38 for _, v := range p.exts { 39 if ext == v { 40 return false 41 } 42 } 43 return true 44 } 45 46 // ----------------------------------------------------------------------------------------- 47 48 type none struct{} 49 50 type Changes struct { 51 changed map[string]none 52 mods map[string]*module 53 mutex sync.Mutex 54 cond sync.Cond 55 56 root string 57 } 58 59 func NewChanges(root string) *Changes { 60 changed := make(map[string]none) 61 mods := make(map[string]*module) 62 root, _ = filepath.Abs(root) 63 c := &Changes{changed: changed, mods: mods, root: root + "/"} 64 c.cond.L = &c.mutex 65 return c 66 } 67 68 func (p *Changes) doLookupMod(name string) *module { 69 mod, ok := p.mods[name] 70 if !ok { 71 mod = new(module) 72 mod.exts = make([]string, 0, 8) 73 m, e := gopmod.Load(p.root + name) 74 if e == nil { 75 m.ImportClasses(func(c *gopmod.Project) { 76 mod.exts = append(mod.exts, c.Ext) 77 for _, w := range c.Works { 78 if w.Ext != c.Ext { 79 mod.exts = append(mod.exts, w.Ext) 80 } 81 } 82 }) 83 } 84 mod.exts = append(mod.exts, ".gop", ".go", ".gox", ".gmx") 85 p.mods[name] = mod 86 if debugMod { 87 log.Println("Mod:", name, "Exts:", mod.exts) 88 } 89 } 90 return mod 91 } 92 93 func (p *Changes) lookupMod(name string) *module { 94 name = strings.TrimSuffix(name, "/") 95 p.mutex.Lock() 96 mod := p.doLookupMod(name) 97 p.mutex.Unlock() 98 return mod 99 } 100 101 func (p *Changes) deleteMod(dir string) { 102 p.mutex.Lock() 103 delete(p.mods, dir) 104 p.mutex.Unlock() 105 } 106 107 func (p *Changes) Fetch(fullPath bool) (dir string) { 108 p.mutex.Lock() 109 for len(p.changed) == 0 { 110 p.cond.Wait() 111 } 112 for dir = range p.changed { 113 delete(p.changed, dir) 114 break 115 } 116 p.mutex.Unlock() 117 if fullPath { 118 dir = p.root + dir 119 } 120 return 121 } 122 123 func (p *Changes) Ignore(name string, isDir bool) bool { 124 dir, fname := path.Split(name) 125 if strings.HasPrefix(fname, "_") { 126 return true 127 } 128 return !isDir && (isHiddenTemp(fname) || isAutogen(fname) || p.lookupMod(dir).ignore(fname)) 129 } 130 131 func (p *Changes) FileChanged(name string) { 132 dir := path.Dir(name) 133 p.mutex.Lock() 134 n := len(p.changed) 135 p.changed[dir] = none{} 136 p.mutex.Unlock() 137 if n == 0 { 138 p.cond.Broadcast() 139 } 140 } 141 142 func (p *Changes) EntryDeleted(name string, isDir bool) { 143 if isDir { 144 p.deleteMod(name) 145 } else { 146 p.FileChanged(name) 147 } 148 } 149 150 func (p *Changes) DirAdded(name string) { 151 dir := p.root + name 152 filepath.WalkDir(dir, func(entry string, d fs.DirEntry, err error) error { 153 if err != nil || d.IsDir() { 154 return err 155 } 156 entry, _ = filepath.Rel(dir, entry) 157 entry = filepath.ToSlash(entry) 158 if !p.Ignore(entry, false) { 159 p.FileChanged(entry) 160 } 161 return nil 162 }) 163 } 164 165 // pattern: gop_autogen*.go 166 func isAutogen(fname string) bool { 167 return strings.HasPrefix(fname, "gop_autogen") && strings.HasSuffix(fname, ".go") 168 } 169 170 // pattern: .* or *~ 171 func isHiddenTemp(fname string) bool { 172 return strings.HasPrefix(fname, ".") || strings.HasSuffix(fname, "~") 173 } 174 175 // -----------------------------------------------------------------------------------------