func onInit() { l := py.NewLock() defer l.Unlock() m, err := py.Import("sublime_plugin") if err != nil { panic(err) } sys, err := py.Import("sys") if err != nil { log.Debug(err) } else { defer sys.Decref() } if watcher, err = watch.NewWatcher(); err != nil { log.Error("Couldn't create watcher: %s", err) } // TODO: add all plugins after supporting all commands // plugins := packages.ScanPlugins(backend.LIME_PACKAGES_PATH, ".py") // for _, p := range plugins { // newPlugin(p, m) // } newPlugin(packages.NewPlugin(path.Join(backend.LIME_PACKAGES_PATH, "Vintageous"), ".py"), m) go watcher.Observe() }
func (p *plugin) loadPlugin() { fi := p.Get().([]os.FileInfo) for _, f := range fi { fn := f.Name() s, err := py.NewUnicode(path.Base(p.Name()) + "." + fn[:len(fn)-3]) if err != nil { log.Error(err) return } if r, err := p.m.Base().CallMethodObjArgs("reload_plugin", s); err != nil { log.Error(err) } else if r != nil { r.Decref() } } }
func main() { flag.Parse() log.AddFilter("file", log.FINEST, log.NewFileLogWriter("debug.log", *rotateLog)) defer func() { py.NewLock() py.Finalize() }() if err := termbox.Init(); err != nil { log.Error(err) log.Close() return } defer func() { termbox.Close() log.Debug(util.Prof) if err := recover(); err != nil { log.Critical(err) panic(err) } }() t := createFrontend() go t.renderthread() go t.editor.Init() t.loop() }
func (t *tbfe) renderthread() { pc := 0 dorender := func() { defer func() { if r := recover(); r != nil { log.Error("Panic in renderthread: %v\n%s", r, string(debug.Stack())) if pc > 1 { panic(r) } pc++ } }() termbox.Clear(defaultFg, defaultBg) t.lock.Lock() vs := make([]*backend.View, 0, len(t.layout)) ls := make([]layout, 0, len(t.layout)) for v, l := range t.layout { vs = append(vs, v) ls = append(ls, l) } t.lock.Unlock() for i, v := range vs { t.renderView(v, ls[i]) } termbox.Flush() } for range t.dorender { dorender() } }
func (w *Window) OpenFile(filename string, flags int) *View { v := w.NewFile() v.SetScratch(true) e := v.BeginEdit() if fn, err := filepath.Abs(filename); err != nil { v.Buffer().SetFileName(filename) } else { v.Buffer().SetFileName(fn) } if d, err := ioutil.ReadFile(filename); err != nil { log.Error("Couldn't load file %s: %s", filename, err) } else { v.Insert(e, 0, string(d)) } v.EndEdit(e) v.selection.Clear() v.selection.Add(text.Region{A: 0, B: 0}) v.Settings().Set("lime.last_save_change_count", v.buffer.ChangeCount()) v.SetScratch(false) OnLoad.Call(v) w.SetActiveView(v) return v }
// Will load view settings respect to current syntax // e.g if current syntax is Python settings order will be: // Packages/Python/Python.sublime-settings // Packages/User/Python.sublime-settings // <Buffer Specific Settings> func (v *View) loadSettings() { syntax := v.Settings().Get("syntax", "").(string) if syntax == "" { v.Settings().SetParent(v.window) return } defSettings, usrSettings := &HasSettings{}, &HasSettings{} defSettings.Settings().SetParent(v.window) usrSettings.Settings().SetParent(defSettings) v.Settings().SetParent(usrSettings) ed := GetEditor() if r, err := rubex.Compile(`([A-Za-z]+?)\.(?:[^.]+)$`); err != nil { log.Error(err) return } else if s := r.FindStringSubmatch(syntax); s != nil { p := path.Join(LIME_PACKAGES_PATH, s[1], s[1]+".sublime-settings") ed.load(packages.NewPacket(p, defSettings.Settings())) p = path.Join(LIME_USER_PACKAGES_PATH, s[1]+".sublime-settings") ed.load(packages.NewPacket(p, usrSettings.Settings())) } }
func (fv *frontendView) Line(index int) *lineStruct { if index < 0 || index >= len(fv.FormattedLine) { log.Error("Error? Line index out of bounds: %v %v\n", index, len(fv.FormattedLine)) return nil } return fv.FormattedLine[index] }
func GetEditor() *Editor { edl.Lock() defer edl.Unlock() if ed == nil { ed = &Editor{ cmdHandler: commandHandler{ ApplicationCommands: make(appcmd), TextCommands: make(textcmd), WindowCommands: make(wndcmd), verbose: true, }, frontend: &DummyFrontend{}, console: &View{ buffer: NewBuffer(), scratch: true, }, keyInput: make(chan keys.KeyPress, 32), } var err error if ed.Watcher, err = watch.NewWatcher(); err != nil { log.Error("Couldn't create watcher: %s", err) } ed.console.Settings().Set("is_widget", true) ed.defaultSettings = new(HasSettings) ed.platformSettings = new(HasSettings) ed.Settings() // Just to initialize it ed.defaultBindings = new(keys.HasKeyBindings) ed.platformBindings = new(keys.HasKeyBindings) ed.userBindings = new(keys.HasKeyBindings) log.AddFilter("console", log.DEBUG, log.NewLogWriter(ed.handleLog)) go ed.inputthread() go ed.Observe() } return ed }
func register(cmds []backend.Command) { ch := backend.GetEditor().CommandHandler() for _, cmd := range cmds { if err := ch.RegisterWithDefault(cmd); err != nil { log.Error("Failed to register command: %s", err) } } }
func registerByName(cmds []namedCmd) { ch := backend.GetEditor().CommandHandler() for _, cmd := range cmds { if err := ch.Register(cmd.name, cmd.cmd); err != nil { log.Error("Failed to register command %s: %s", cmd.name, err) } } }
func (e *Editor) SetClipboard(n string) { if err := e.clipboardSetter(n); err != nil { log.Error("Could not set clipboard: %v", err) } // Keep a local copy in case the system clipboard isn't working e.clipboard = n }
func (w *Window) runCommand(c WindowCommand, name string) error { defer func() { if r := recover(); r != nil { log.Error("Paniced while running window command %s %v: %v\n%s", name, c, r, string(debug.Stack())) } }() return c.Run(w) }
func setColorMode() { var ( mode256 bool pal = make([]termbox.RGB, 0, 256) ) if err := termbox.SetColorMode(termbox.ColorMode256); err != nil { log.Error("Unable to use 256 color mode: %s", err) } else { log.Debug("Using 256 color mode") mode256 = true } if !mode256 { pal = pal[:10] // Not correct, but whatever pal[termbox.ColorBlack] = termbox.RGB{R: 0, G: 0, B: 0} pal[termbox.ColorWhite] = termbox.RGB{R: 255, G: 255, B: 255} pal[termbox.ColorRed] = termbox.RGB{R: 255, G: 0, B: 0} pal[termbox.ColorGreen] = termbox.RGB{R: 0, G: 255, B: 0} pal[termbox.ColorBlue] = termbox.RGB{R: 0, G: 0, B: 255} pal[termbox.ColorMagenta] = termbox.RGB{R: 255, G: 0, B: 255} pal[termbox.ColorYellow] = termbox.RGB{R: 255, G: 255, B: 0} pal[termbox.ColorCyan] = termbox.RGB{R: 0, G: 255, B: 255} diff := func(i, j byte) int { v := int(i) - int(j) if v < 0 { return -v } return v } palLut = func(col textmate.Color) termbox.Attribute { mindist := 10000000 mini := 0 for i, c := range pal { if dist := diff(c.R, col.R) + diff(c.G, col.G) + diff(c.B, col.B); dist < mindist { mindist = dist mini = i } } return termbox.Attribute(mini) } } else { palLut = func(col textmate.Color) termbox.Attribute { tc := termbox.RGB{R: col.R, G: col.G, B: col.B} for i, c := range pal { if c == tc { return termbox.Attribute(i) } } l := len(pal) log.Debug("Adding colour: %d %+v %+v", l, col, tc) pal = append(pal, tc) termbox.SetColorPalette(pal) return termbox.Attribute(l) } } }
func (e *Editor) GetClipboard() string { if n, err := e.clipboardGetter(); err == nil { return n } else { log.Error("Could not get clipboard: %v", err) } return e.clipboard }
func (e *Editor) load(pkg *packages.Packet) { if err := pkg.Load(); err != nil { log.Error("Failed to load packet %s: %s", pkg.Name(), err) } else { log.Info("Loaded %s", pkg.Name()) if err := e.Watch(pkg.Name(), pkg); err != nil { log.Warn("Couldn't watch %s: %s", pkg.Name(), err) } } }
func newPlugin(pl *packages.Plugin, m *py.Module) (p *plugin) { p = &plugin{pl, m} p.FileChanged(p.Name()) if err := watcher.Watch(p.Name(), p); err != nil { log.Error("Couldn't watch %s: %s", p.Name(), err) } p.loadKeyBindings() p.loadSettings() return }
// Put back watchers on watching files under the directory func (w *Watcher) removeDir(name string) { for p, _ := range w.watched { if filepath.Dir(p) == name { if err := w.watch(p); err != nil { log.Error("Could not watch: %s", err) continue } } } w.dirs = remove(w.dirs, name) }
// On plugin reload we will scan for plugin files // and packets in plugin path func (p *Plugin) Reload() { var files []os.FileInfo log.Info("Reloading plugin %s", p.Name()) f, err := os.Open(p.path) if err != nil { log.Error("Couldn't open dir: %s", err) return } defer f.Close() fi, err := f.Readdir(-1) if err != nil { log.Error("Couldn't read dir: %s", err) return } for _, f := range fi { if p.suffix != "" && strings.HasSuffix(f.Name(), p.suffix) { files = append(files, f) } } p.files = files }
// called when a view is closed func (t *qmlfrontend) onClose(v *backend.View) { w2 := t.windows[v.Window()] for i := range w2.views { if w2.views[i].bv == v { w2.window.ObjectByName("tabs").Call("removeTab", i) copy(w2.views[i:], w2.views[i+1:]) w2.views = w2.views[:len(w2.views)-1] return } } log.Error("Couldn't find closed view...") }
// Remove watchers created on files under this directory because // one watcher on the parent directory is enough for all of them func (w *Watcher) flushDir(name string) { if exist(w.dirs, name) { return } w.dirs = append(w.dirs, name) for _, p := range w.watchers { if filepath.Dir(p) == name && !exist(w.dirs, p) { if err := w.removeWatch(p); err != nil { log.Error("Couldn't unwatch file %s: %s", p, err) } } } }
// Observe dispatches notifications received by the watcher. This function will // return when the watcher is closed. func (w *Watcher) Observe() { for { select { case ev, ok := <-w.wchr.Events: if !ok { break } func() { w.lock.Lock() defer w.lock.Unlock() w.apply(ev) name := ev.Name // If the name refers to a directory run all watched // callbacks for wathed files under the directory if exist(w.dirs, name) { for p, _ := range w.watched { if filepath.Dir(p) == name { ev.Name = p w.apply(ev) } } } dir := filepath.Dir(name) // The watcher will be removed if the file is deleted // so we need to watch the parent directory for when the // file is created again if ev.Op&fsnotify.Remove != 0 { w.watchers = remove(w.watchers, name) w.lock.Unlock() w.Watch(dir, nil) w.lock.Lock() } // We will apply parent directory FileChanged callbacks to, // if one of the files inside the directory has changed if cbs, exist := w.watched[dir]; ev.Op&fsnotify.Write != 0 && exist { for _, cb := range cbs { if c, ok := cb.(FileChangedCallback); ok { c.FileChanged(dir) } } } }() case err, ok := <-w.wchr.Errors: if !ok { break } log.Error("Watcher error: %s", err) } } }
func (w *Window) remove(v *View) { w.lock.Lock() defer w.lock.Unlock() for i, vv := range w.views { if v == vv { end := len(w.views) - 1 if i != end { copy(w.views[i:], w.views[i+1:]) } w.views = w.views[:end] return } } log.Error("Wanted to remove view %+v, but it doesn't appear to be a child of this window", v) }
func (c *CommandGlue) callBool(name string, args backend.Args) bool { gs := py.GilState_Ensure() defer gs.Release() var ( pyargs, r py.Object err error ) if pyargs, err = c.CreatePyArgs(args); err != nil { log.Error(err) return false } defer pyargs.Decref() if r, err = c.CallMethodObjArgs(name, pyargs); err != nil { log.Error(err) return true } defer r.Decref() if r, ok := r.(*py.Bool); ok { return r.Bool() } return true }
func (c *CommandGlue) Description() string { gs := py.GilState_Ensure() defer gs.Release() var ( pyargs, r py.Object err error ) if pyargs, err = c.CreatePyArgs(c.args); err != nil { log.Error(err) return "" } defer pyargs.Decref() if r, err = c.CallMethodObjArgs("description", pyargs); err != nil { log.Error(err) return "" } defer r.Decref() if r, ok := r.(*py.Unicode); ok { return r.String() } return "" }
func (c *ViewEventGlue) onEvent(v *backend.View) { l := py.NewLock() defer l.Unlock() pv, err := toPython(v) if err != nil { log.Error(err) } defer pv.Decref() log.Fine("onEvent: %v, %v, %v", c, c.inner, pv) // interrupt := true // defer func() { interrupt = false }() // go func() { // <-time.After(time.Second * 5) // if interrupt { // py.SetInterrupt() // } // }() if ret, err := c.inner.Base().CallFunctionObjArgs(pv); err != nil { log.Error(err) } else if ret != nil { ret.Decref() } }
func (e *Editor) remove(w *Window) { edl.Lock() defer edl.Unlock() for i, ww := range e.windows { if w == ww { end := len(e.windows) - 1 if i != end { copy(e.windows[i:], e.windows[i+1:]) } e.windows = e.windows[:end] return } } log.Error("Wanted to remove window %+v, but it doesn't appear to be a child of this editor", w) }
func (v *View) runCommand(cmd TextCommand, name string) error { e := v.BeginEdit() e.command = name // e.args = args e.bypassUndo = cmd.BypassUndo() defer func() { v.EndEdit(e) if r := recover(); r != nil { log.Error("Paniced while running text command %s %v: %v\n%s", name, cmd, r, string(debug.Stack())) } }() p := Prof.Enter("view.cmd." + name) defer p.Exit() return cmd.Run(v, e) }
// Returns packet file data if any error occurred // on reading file we will return nil func (p *Packet) Get() interface{} { e := []byte(`{}`) if p.group() == "keymap" { e = []byte(`[]`) } if _, err := os.Stat(p.path); os.IsNotExist(err) { log.Finest("%s doesn't exist yet", p.path) return e } d, err := ioutil.ReadFile(p.path) if err != nil { log.Error("Couldn't read file: %s", err) return e } return d }
func (v *View) FileChanged(filename string) { log.Finest("Reloading %s", filename) if saving, ok := v.Settings().Get("lime.saving", false).(bool); ok && saving { // This reload was triggered by ourselves saving to this file, so don't reload it return } if !GetEditor().Frontend().OkCancelDialog("File was changed by another program, reload?", "reload") { return } if d, err := ioutil.ReadFile(filename); err != nil { log.Error("Could not read file: %s\n. Error was: %v", filename, err) } else { edit := v.BeginEdit() end := v.buffer.Size() v.Replace(edit, Region{0, end}, string(d)) v.EndEdit(edit) } }
func (v *View) Find(pat string, pos int, flags int) Region { r := Region{pos, v.buffer.Size()} s := v.buffer.Substr(r) if flags&LITERAL != 0 { pat = "\\Q" + pat } if flags&IGNORECASE != 0 { pat = "(?im)" + pat } else { pat = "(?m)" + pat } // Using regexp instead of rubex because rubex doesn't // support flag for treating pattern as a literal text if re, err := regexp.Compile(pat); err != nil { log.Error(err) } else if loc := re.FindStringIndex(s); loc != nil { return Region{pos + loc[0], pos + loc[1]} } return Region{-1, -1} }