// TODO func Init() { 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) } plugins := packages.ScanPlugins(backend.LIME_USER_PACKAGES_PATH, ".py") for _, p := range plugins { // TODO: add all plugins after supporting all commands if p.Name() == path.Join("..", "..", "3rdparty", "bundles", "Vintageous") { pl := newPlugin(p, m) pl.reload() if err := watcher.Watch(pl.Name(), pl); err != nil { log.Error("Couldn't watch %s: %s", pl.Name(), err) } } } 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() } } }
// 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/Python/Python (Windows).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, platSettings := &HasSettings{}, &HasSettings{}, &HasSettings{} defSettings.Settings().SetParent(v.window) platSettings.Settings().SetParent(defSettings) usrSettings.Settings().SetParent(platSettings) 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_PACKAGES_PATH, s[1], s[1]+" ("+ed.Plat()+").sublime-settings") ed.load(packages.NewPacket(p, platSettings.Settings())) p = path.Join(LIME_USER_PACKAGES_PATH, s[1]+".sublime-settings") ed.load(packages.NewPacket(p, usrSettings.Settings())) } }
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.Settings() // Just to initialize it log.AddFilter("console", log.DEBUG, log.NewLogWriter(ed.handleLog)) go ed.inputthread() go ed.Observe() } return ed }
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{0, 0}) v.buffer.Settings().Set("lime.last_save_change_count", v.buffer.ChangeCount()) v.SetScratch(false) OnLoad.Call(v) w.SetActiveView(v) return v }
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 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 (e *Editor) loadSetting(pkg *packages.Packet) { if err := pkg.Load(); err != nil { log.Error(err) } else { log.Info("Loaded %s", pkg.Name()) e.Watch(pkg.Name(), pkg) } }
// Returns packet file data if any error occurred // on reading file we will return nil func (p *Packet) Get() interface{} { d, err := ioutil.ReadFile(p.path) if err != nil { log.Error("Couldn't read file: %s", err) return []byte{} } return d }
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{0, 0, 0} pal[termbox.ColorWhite] = termbox.RGB{255, 255, 255} pal[termbox.ColorRed] = termbox.RGB{255, 0, 0} pal[termbox.ColorGreen] = termbox.RGB{0, 255, 0} pal[termbox.ColorBlue] = termbox.RGB{0, 0, 255} pal[termbox.ColorMagenta] = termbox.RGB{255, 0, 255} pal[termbox.ColorYellow] = termbox.RGB{255, 255, 0} pal[termbox.ColorCyan] = termbox.RGB{0, 255, 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{col.R, col.G, 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) loadKeyBinding(pkg *packages.Packet) { if err := pkg.Load(); err != nil { log.Error(err) } else { log.Info("Loaded %s", pkg.Name()) e.Watch(pkg.Name(), pkg) } e.keyBindings.Merge(pkg.MarshalTo().(*keys.KeyBindings)) }
// 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) }
// Initialize scan for loading user and limetext defaults // except settings because for settings we have a hierarchy // i.e commands, snippets etc func ScanPackets(path string) Packets { var packets Packets walkFn := func(path string, info os.FileInfo, err error) error { if err != nil { log.Error("Error on walking: %s", err) return err } s := filepath.Ext(info.Name()) for _, t := range types { if t != "settings" && strings.Contains(s, t) { packets = append(packets, NewPacket(path, new(keys.KeyBindings))) } } return nil } if err := filepath.Walk(path, walkFn); err != nil { log.Error("Can't walk: %s", err) } return packets }
// 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...") }
// On plugin reload we will scan for plugin files // and packets in plugin path func (p *Plugin) Reload() { var ( files []os.FileInfo pckts Packets ) 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) continue } s := filepath.Ext(f.Name()) for _, t := range types { if !strings.Contains(s, t) { continue } var pckt *Packet if t == "keymap" { pckt = NewPacket(pt.Join(p.path, f.Name()), new(keys.KeyBindings)) } else { // We don't have any settings hierarchy for plugins at this moment pckt = NewPacket(pt.Join(p.path, f.Name()), p.Settings()) } pckts = append(pckts, pckt) } } p.files = files p.packets = pckts }
// 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 { if err := w.removeWatch(p); err != nil { log.Error("Couldn't unwatch file %s: %s", p, err) } } } }
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 *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 (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) 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 (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) }
func (lp *LanguageParser) Parse() (*parser.Node, error) { sdata := string(lp.data) rn := parser.Node{P: lp, Name: lp.l.ScopeName} defer func() { if r := recover(); r != nil { log.Error("Panic during parse: %v\n", r) log.Debug("%v", rn) } }() iter := maxiter for i := 0; i < len(sdata) && iter > 0; iter-- { pat, ret := lp.l.RootPattern.Cache(sdata, i) nl := strings.IndexAny(sdata[i:], "\n\r") if nl != -1 { nl += i } if ret == nil { break } else if nl > 0 && nl <= ret[0] { i = nl for i < len(sdata) && (sdata[i] == '\n' || sdata[i] == '\r') { i++ } } else { n := pat.CreateNode(sdata, i, lp, ret) rn.Append(n) i = n.Range.B } } rn.UpdateRange() if len(sdata) != 0 { lut := make([]int, len(sdata)+1) j := 0 for i := range sdata { lut[i] = j j++ } lut[len(sdata)] = len(lp.data) lp.patch(lut, &rn) } if iter == 0 { panic("reached maximum number of iterations") } return &rn, nil }
func (w *Watcher) Observe() { for { select { case ev := <-w.wchr.Events: 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 := <-w.wchr.Errors: log.Error("Watcher error: %s", err) } } }
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) } }
// Creates and initializes the frontend. func createFrontend() *tbfe { var t tbfe t.dorender = make(chan bool, render_chan_len) t.shutdown = make(chan bool, 2) t.layout = make(map[*backend.View]layout) t.editor = t.setupEditor() t.console = t.editor.Console() t.currentWindow = t.editor.NewWindow() // Assuming that all extra arguments are files if files := flag.Args(); len(files) > 0 { for _, file := range files { t.currentView = createNewView(file, t.currentWindow) } } else { t.currentView = t.currentWindow.NewFile() } t.console.Buffer().AddCallback(t.scroll) t.setupCallbacks(t.currentView) path := path.Join("..", "..", "3rdparty", "bundles", "TextMate-Themes", "Monokai.tmTheme") if sc, err := textmate.LoadTheme(path); err != nil { log.Error(err) } else { scheme = sc } setColorMode() setSchemeSettings() w, h := termbox.Size() t.handleResize(h, w, true) // These might take a while t.editor.Init() go sublime.Init() return &t }
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)) l := make([]layout, 0, len(t.layout)) for k, v := range t.layout { vs = append(vs, k) l = append(l, v) } runes := []rune(t.status_message) t.lock.Unlock() w, h := termbox.Size() for i := 0; i < w && i < len(runes); i++ { termbox.SetCell(i, h-2, runes[i], defaultFg, defaultBg) } for i, v := range vs { t.renderView(v, l[i]) } termbox.Flush() } for _ = range t.dorender { log.Finest("Rendering") dorender() } }