func sublime_RunCommand(tu *py.Tuple) (py.Object, error) { var ( arg1 string arg2 backend.Args ) if v, err := tu.GetItem(0); err != nil { return nil, err } else { if v3, err2 := fromPython(v); err2 != nil { return nil, err2 } else { if v2, ok := v3.(string); !ok { return nil, fmt.Errorf("Expected type string for backend.Editor.RunCommand() arg1, not %s", v.Type()) } else { arg1 = v2 } } } arg2 = make(backend.Args) if v, err := tu.GetItem(1); err == nil { if v3, err2 := fromPython(v); err2 != nil { return nil, err2 } else { if v2, ok := v3.(backend.Args); !ok { return nil, fmt.Errorf("Expected type backend.Args for backend.Editor.RunCommand() arg2, not %s", v.Type()) } else { arg2 = v2 } } } backend.GetEditor().RunCommand(arg1, arg2) return toPython(nil) }
func (o *View) Py_run_command(tu *py.Tuple) (py.Object, error) { var ( arg1 string arg2 backend.Args ) v, err := tu.GetItem(0) if err != nil { return nil, err } if v2, ok := v.(*py.Unicode); !ok { return nil, fmt.Errorf("Expected type *py.Unicode for backend.View.RunCommand() arg1, not %s", v.Type()) } else { arg1 = v2.String() } arg2 = make(backend.Args) if v, err := tu.GetItem(1); err == nil { v2, ok := v.(*py.Dict) if !ok { return nil, fmt.Errorf("Expected type *py.Dict for backend.View.RunCommand() arg2, not %s", v.Type()) } if v, err := fromPython(v2); err != nil { return nil, err } else { arg2 = v.(backend.Args) } } backend.GetEditor().CommandHandler().RunTextCommand(o.data, arg1, arg2) return toPython(nil) }
// key HandleFunc for the http /key endpoint. This only happens if the client // doesn't support websockets. func (t *tbfe) key(w http.ResponseWriter, req *http.Request) { log.Debug("key: %s", req) kc := req.FormValue("keyCode") var kp keys.KeyPress v, _ := strconv.ParseInt(kc, 10, 32) if req.FormValue("altKey") == "true" { kp.Alt = true } if req.FormValue("ctrlKey") == "true" { kp.Ctrl = true } if req.FormValue("metaKey") == "true" { kp.Super = true } if req.FormValue("shiftKey") == "true" { kp.Shift = true } if !kp.Shift { v = int64(unicode.ToLower(rune(v))) } kp.Key = keys.Key(v) kp.Text = string(v) backend.GetEditor().HandleInput(kp) }
func sublime_OkCancelDialog(tu *py.Tuple) (py.Object, error) { var ( arg1 string arg2 string ) if v, err := tu.GetItem(0); err != nil { return nil, err } else { if v3, err2 := fromPython(v); err2 != nil { return nil, err2 } else { if v2, ok := v3.(string); !ok { return nil, fmt.Errorf("Expected type string for backend.DummyFrontend.OkCancelDialog() arg1, not %s", v.Type()) } else { arg1 = v2 } } } if v, err := tu.GetItem(1); err != nil { return nil, err } else { if v3, err2 := fromPython(v); err2 != nil { return nil, err2 } else { if v2, ok := v3.(string); !ok { return nil, fmt.Errorf("Expected type string for backend.DummyFrontend.OkCancelDialog() arg2, not %s", v.Type()) } else { arg2 = v2 } } } backend.GetEditor().Frontend().OkCancelDialog(arg1, arg2) return toPython(nil) }
func register(cmds []cmd) { e := backend.GetEditor() for i := range cmds { if err := e.CommandHandler().Register(cmds[i].name, cmds[i].cmd); err != nil { log4go.Error("Failed to register command %s: %s", cmds[i].name, 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 (t *tbfe) setupEditor() *backend.Editor { ed := backend.GetEditor() ed.SetFrontend(t) ed.LogInput(false) ed.LogCommands(false) 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 sublime_PackagesPath() (py.Object, error) { ret0 := backend.GetEditor().PackagesPath() var err error var pyret0 py.Object pyret0, err = toPython(ret0) if err != nil { return nil, err } return pyret0, err }
func sublime_NewWindow() (py.Object, error) { ret0 := backend.GetEditor().NewWindow() var err error var pyret0 py.Object pyret0, err = toPython(ret0) if err != nil { return nil, err } return pyret0, err }
func (p *plugin) loadKeyBindings() { ed := backend.GetEditor() tmp := ed.KeyBindings().Parent() ed.KeyBindings().SetParent(p) p.KeyBindings().Parent().KeyBindings().SetParent(tmp) pt := path.Join(p.Name(), "Default.sublime-keymap") p.load(packages.NewPacket(pt, p.KeyBindings().Parent().KeyBindings())) pt = path.Join(p.Name(), "Default ("+ed.Plat()+").sublime-keymap") p.load(packages.NewPacket(pt, p.KeyBindings())) }
func (p *plugin) loadSettings() { ed := backend.GetEditor() tmp := ed.Settings().Parent() ed.Settings().SetParent(p) p.Settings().Parent().Settings().Parent().Settings().SetParent(tmp) pt := path.Join(p.Name(), "Preferences.sublime-settings") p.load(packages.NewPacket(pt, p.Settings().Parent().Settings().Parent().Settings())) pt = path.Join(p.Name(), "Preferences ("+ed.Plat()+").sublime-settings") p.load(packages.NewPacket(pt, p.Settings().Parent().Settings())) pt = path.Join(backend.LIME_USER_PACKAGES_PATH, "Preferences.sublime-settings") p.load(packages.NewPacket(pt, p.Settings())) }
func (o *View) Py_visible_region() (py.Object, error) { ret0 := backend.GetEditor().Frontend().VisibleRegion(o.data) var err error var pyret0 py.Object pyret0, err = _regionClass.Alloc(1) if err != nil { return nil, err } else if v2, ok := pyret0.(*Region); !ok { return nil, fmt.Errorf("Unable to convert return value to the right type?!: %s", pyret0.Type()) } else { v2.data = ret0 } if err != nil { return nil, err } return pyret0, err }
func sublime_SetClipboard(tu *py.Tuple) (py.Object, error) { var ( arg1 string ) if v, err := tu.GetItem(0); err != nil { return nil, err } else { if v3, err2 := fromPython(v); err2 != nil { return nil, err2 } else { if v2, ok := v3.(string); !ok { return nil, fmt.Errorf("Expected type string for backend.Editor.SetClipboard() arg1, not %s", v.Type()) } else { arg1 = v2 } } } backend.GetEditor().SetClipboard(arg1) return toPython(nil) }
func TestUpdateVisibleRegion(t *testing.T) { var ( fe tbfe e = backend.GetEditor() w = e.NewWindow() v = w.NewFile() ) fe.layout = make(map[*backend.View]layout) fe.layout[v] = layout{0, 0, 100, 100 - *consoleHeight - 1, Region{}, 0} fe.setupCallbacks(v) edit := v.BeginEdit() v.Insert(edit, 0, "foo") v.EndEdit(edit) if end := fe.layout[v].visible.End(); end != 3 { t.Fatalf("Expected 3, got %d", end) } }
func sublime_LogInput(tu *py.Tuple) (py.Object, error) { var ( arg1 bool ) if v, err := tu.GetItem(0); err != nil { return nil, err } else { if v3, err2 := fromPython(v); err2 != nil { return nil, err2 } else { if v2, ok := v3.(bool); !ok { return nil, fmt.Errorf("Expected type bool for backend.Editor.LogInput() arg1, not %s", v.Type()) } else { arg1 = v2 } } } backend.GetEditor().LogInput(arg1) return toPython(nil) }
func (view *View) Py_show(tu *py.Tuple, kw *py.Dict) (py.Object, error) { var ( arg1 text.Region ) v, err := tu.GetItem(0) if err != nil { return nil, err } if v2, ok := v.(*Region); !ok { if v2, ok := v.(*py.Long); !ok { return nil, fmt.Errorf("Expected type *Region or *Int for primitives.Buffer.Substr() arg1, not %s", v.Type()) } else { arg1.A = int(v2.Int64()) arg1.B = arg1.A + 1 } } else { arg1 = v2.data } backend.GetEditor().Frontend().Show(view.data, arg1) return toPython(nil) }
func sublime_Unregister(tu *py.Tuple) (py.Object, error) { var ( arg1 string ) if v, err := tu.GetItem(0); err != nil { return nil, err } else { if v3, err2 := fromPython(v); err2 != nil { return nil, err2 } else { if v2, ok := v3.(string); !ok { return nil, fmt.Errorf("Expected type string for backend.commandHandler.Unregister() arg1, not %s", v.Type()) } else { arg1 = v2 } } } if err := backend.GetEditor().CommandHandler().Unregister(arg1); err != nil { return nil, err } else { return toPython(nil) } }
func (t *qmlfrontend) HandleInput(keycode int, modifiers int) bool { log4go.Debug("qmlfrontend.HandleInput: key=%x, modifiers=%x", keycode, modifiers) shift := false alt := false ctrl := false super := false if key, ok := lut[keycode]; ok { ed := backend.GetEditor() if (modifiers & shift_mod) != 0 { shift = true } if (modifiers & alt_mod) != 0 { alt = true } if (modifiers & ctrl_mod) != 0 { if runtime.GOOS == "darwin" { super = true } else { ctrl = true } } if (modifiers & meta_mod) != 0 { if runtime.GOOS == "darwin" { ctrl = true } else { super = true } } ed.HandleInput(backend.KeyPress{Key: key, Shift: shift, Alt: alt, Ctrl: ctrl, Super: super}) return true } return false }
func (t *qmlfrontend) RunCommandWithArgs(command string, args backend.Args) { ed := backend.GetEditor() go ed.RunCommand(command, args) }
func (t *tbfe) loop() { backend.OnNew.Add(func(v *backend.View) { v.Settings().AddOnChange("lime.frontend.html.render", func(name string) { t.dirty = true }) }) backend.OnModified.Add(func(v *backend.View) { t.dirty = true }) backend.OnSelectionModified.Add(func(v *backend.View) { t.dirty = true }) ed := backend.GetEditor() ed.SetFrontend(t) ed.LogInput(false) ed.LogCommands(false) c := ed.Console() if sc, err := textmate.LoadTheme("../../3rdparty/bundles/TextMate-Themes/GlitterBomb.tmTheme"); err != nil { log4go.Error(err) } else { scheme = sc } defer func() { fmt.Println(util.Prof) }() w := ed.NewWindow() v := w.OpenFile("main.go", 0) v.Settings().Set("trace", true) v.Settings().Set("syntax", "../../3rdparty/bundles/go.tmbundle/Syntaxes/Go.tmLanguage") c.Buffer().AddCallback(t.scroll) sel := v.Sel() sel.Clear() // end := v.Buffer().Size() - 2 sel.Add(Region{0, 0}) // sel.Add(Region{end - 22, end - 22}) // sel.Add(Region{end - 16, end - 20}) // sel.Add(Region{end - 13, end - 10}) { w, h := 800, 600 t.lock.Lock() t.layout[v] = layout{0, 0, w, h - console_height - 1, Region{}, 0} t.layout[c] = layout{0, h - console_height + 1, w, console_height - 5, Region{}, 0} t.lock.Unlock() t.Show(v, Region{1, 1}) } t.Show(v, Region{100, 100}) t.Show(v, Region{1, 1}) go func() { ed.Init() sublime.Init() }() log4go.Debug("serving") http.HandleFunc("/key", t.key) http.HandleFunc("/", t.ServeHTTP) http.HandleFunc("/view", t.view) if err := http.ListenAndServe("localhost:8080", nil); err != nil { log4go.Error("Error serving: %s", err) } log4go.Debug("Done") }
func (t *tbfe) scroll(b Buffer, pos, delta int) { t.Show(backend.GetEditor().Console(), Region{b.Size(), b.Size()}) }
func TestSublime(t *testing.T) { ed := backend.GetEditor() ed.Console().Buffer().AddCallback(func(b text.Buffer, pos, delta int) { t.Logf("%s", b.Substr(text.Region{pos, pos + delta})) }) w := ed.NewWindow() Init() l := py.NewLock() py.AddToPath("testdata") py.AddToPath("testdata/plugins") if m, err := py.Import("sublime_plugin"); err != nil { t.Fatal(err) } else { scanpath("testdata/", m) } subl, err := py.Import("sublime") if err != nil { t.Fatal(err) } if w, err := _windowClass.Alloc(1); err != nil { t.Fatal(err) } else { (w.(*Window)).data = &backend.Window{} subl.AddObject("test_window", w) } if dir, err := os.Open("testdata"); err != nil { t.Error(err) } else if files, err := dir.Readdirnames(0); err != nil { t.Error(err) } else { for _, fn := range files { if filepath.Ext(fn) == ".py" { log4go.Debug("Running %s", fn) if _, err := py.Import(fn[:len(fn)-3]); err != nil { log4go.Error(err) t.Error(err) } else { log4go.Debug("Ran %s", fn) } } } } var f func(indent string, v py.Object, buf *bytes.Buffer) f = func(indent string, v py.Object, buf *bytes.Buffer) { b := v.Base() if dir, err := b.Dir(); err != nil { t.Error(err) } else { if l, ok := dir.(*py.List); ok { sl := l.Slice() if indent == "" { for _, v2 := range sl { if item, err := b.GetAttr(v2); err != nil { t.Error(err) } else { ty := item.Type() line := fmt.Sprintf("%s%s\n", indent, v2) buf.WriteString(line) if ty == py.TypeType { f(indent+"\t", item, buf) } item.Decref() } } } else { for _, v2 := range sl { buf.WriteString(fmt.Sprintf("%s%s\n", indent, v2)) } } } else { ty := dir.Type() t.Error("Unexpected type:", ty) } dir.Decref() } } buf := bytes.NewBuffer(nil) f("", subl, buf) l.Unlock() const expfile = "testdata/api.txt" if d, err := ioutil.ReadFile(expfile); err != nil { if err := ioutil.WriteFile(expfile, buf.Bytes(), 0644); err != nil { t.Error(err) } } else if diff := util.Diff(string(d), buf.String()); diff != "" { t.Error(diff) } ed.LogCommands(true) tests := []string{ "state", "registers", "settings", "constants", "registers", "cmd_data", "marks", } for _, test := range tests { ed.CommandHandler().RunWindowCommand(w, "vintage_ex_run_data_file_based_tests", backend.Args{"suite_name": test}) } for _, w := range ed.Windows() { for _, v := range w.Views() { if strings.HasSuffix(v.Buffer().FileName(), "sample.txt") { continue } if strings.Index(v.Buffer().Substr(text.Region{0, v.Buffer().Size()}), "FAILED") != -1 { t.Error(v.Buffer()) } } } var v *backend.View for _, v2 := range w.Views() { if v == nil || v2.Buffer().Size() > v.Buffer().Size() { v = v2 } } }
func (t *qmlfrontend) scroll(b Buffer) { t.Show(backend.GetEditor().Console(), Region{b.Size(), b.Size()}) }
func (t *qmlfrontend) loop() (err error) { backend.OnNew.Add(t.onNew) backend.OnClose.Add(t.onClose) backend.OnLoad.Add(t.onLoad) ed := backend.GetEditor() ed.Init() go sublime.Init() ed.SetFrontend(t) ed.LogInput(false) ed.LogCommands(false) c := ed.Console() t.Console = &frontendView{bv: c} c.Buffer().AddObserver(t.Console) c.Buffer().AddObserver(t) var ( engine *qml.Engine component qml.Object // WaitGroup keeping track of open windows wg sync.WaitGroup ) // create and setup a new engine, destroying // the old one if one exists. // // This is needed to re-load qml files to get // the new file contents from disc as otherwise // the old file would still be what is referenced. newEngine := func() (err error) { if engine != nil { log.Debug("calling destroy") // TODO(.): calling this appears to make the editor *very* crash-prone, just let it leak for now // engine.Destroy() engine = nil } log.Debug("calling newEngine") engine = qml.NewEngine() log.Debug("setvar frontend") engine.Context().SetVar("frontend", t) log.Debug("setvar editor") engine.Context().SetVar("editor", backend.GetEditor()) log.Debug("loadfile") component, err = engine.LoadFile(qmlMainFile) if err != nil { return err } limeViewComponent, err = engine.LoadFile(qmlViewFile) return } if err := newEngine(); err != nil { log.Error(err) } backend.OnNewWindow.Add(func(w *backend.Window) { fw := &frontendWindow{bw: w} t.windows[w] = fw if component != nil { fw.launch(&wg, component) } }) // TODO: should be done backend side if sc, err := textmate.LoadTheme("../../packages/themes/TextMate-Themes/Monokai.tmTheme"); err != nil { log.Error(err) } else { scheme = sc } defer func() { fmt.Println(util.Prof) }() w := ed.NewWindow() v := w.OpenFile("main.go", 0) // TODO: should be done backend side v.Settings().Set("syntax", "../../packages/go.tmbundle/Syntaxes/Go.tmLanguage") v = w.OpenFile("../../backend/editor.go", 0) // TODO: should be done backend side v.Settings().Set("syntax", "../../packages/go.tmbundle/Syntaxes/Go.tmLanguage") watch, err := fsnotify.NewWatcher() if err != nil { log.Errorf("Unable to create file watcher: %s", err) return } defer watch.Close() watch.Watch(".") defer watch.RemoveWatch(".") reloadRequested := false go func() { for { select { case ev := <-watch.Event: if ev != nil && strings.HasSuffix(ev.Name, ".qml") && ev.IsModify() && !ev.IsAttrib() { reloadRequested = true // Close all open windows to de-reference all // qml objects for _, v := range t.windows { if v.window != nil { v.window.Hide() v.window.Destroy() v.window = nil } } } } } }() for { // Reset reload status reloadRequested = false log.Debug("Waiting for all windows to close") // wg would be the WaitGroup all windows belong to, so first we wait for // all windows to close. wg.Wait() log.Debug("All windows closed. reloadRequest: %v", reloadRequested) // then we check if there's a reload request in the pipe if !reloadRequested || len(t.windows) == 0 { // This would be a genuine exit; all windows closed by the user break } // *We* closed all windows because we want to reload freshly changed qml // files. for { log.Debug("Calling newEngine") if err := newEngine(); err != nil { // Reset reload status reloadRequested = false log.Error(err) for !reloadRequested { // This loop allows us to re-try reloading // if there was an error in the file this time, // we just loop around again when we receive the next // reload request (ie on the next save of the file). time.Sleep(time.Second) } continue } log.Debug("break") break } log.Debug("re-launching all windows") // Succeeded loading the file, re-launch all windows for _, v := range t.windows { v.launch(&wg, component) } } return }
func main() { cleanup() var sublime_methods = "" sn := func(t reflect.Type, m reflect.Method) string { sn := "sublime_" + m.Name sublime_methods += fmt.Sprintf("{Name: \"%s\", Func: %s},\n", pyname(m.Name)[1:], sn) return sn } data := [][]string{ {"../backend/sublime/region_generated.go", generateWrapper(reflect.TypeOf(text.Region{}), true, regexp.MustCompile("Cut").MatchString)}, {"../backend/sublime/regionset_generated.go", generateWrapper(reflect.TypeOf(&text.RegionSet{}), false, regexp.MustCompile("Less|Swap|Adjust|Has|Cut").MatchString)}, {"../backend/sublime/edit_generated.go", generateWrapper(reflect.TypeOf(&backend.Edit{}), false, regexp.MustCompile("Apply|Undo").MatchString)}, {"../backend/sublime/view_generated.go", generateWrapper(reflect.TypeOf(&backend.View{}), false, regexp.MustCompile("Buffer|Syntax|CommandHistory|Show|AddRegions|UndoStack|Transform").MatchString)}, {"../backend/sublime/window_generated.go", generateWrapper(reflect.TypeOf(&backend.Window{}), false, regexp.MustCompile("OpenFile|SetActiveView").MatchString)}, {"../backend/sublime/settings_generated.go", generateWrapper(reflect.TypeOf(&text.Settings{}), false, regexp.MustCompile("Parent|Set|Get|UnmarshalJSON|MarshalJSON").MatchString)}, {"../backend/sublime/view_buffer_generated.go", generatemethodsEx( reflect.TypeOf(backend.GetEditor().Console().Buffer()), regexp.MustCompile("Erase|Insert|Substr|SetFile|AddCallback|Data|Runes|Settings|Index|Close|Unlock|Lock").MatchString, "o.data.Buffer().", func(t reflect.Type, m reflect.Method) string { mn := "" switch m.Name { case "Line", "LineR", "FullLine", "FullLineR", "WordR", "Word": mn = strings.ToLower(m.Name) case "Id": mn = "Py_buffer_id" default: mn = "Py" + pyname(m.Name) } return "(o *View) " + mn })}, {"../backend/sublime/commands_generated.go", generatemethodsEx(reflect.TypeOf(backend.GetEditor().CommandHandler()), regexp.MustCompile("RunWindowCommand|RunTextCommand|RunApplicationCommand").MatchString, "backend.GetEditor().CommandHandler().", sn), }, {"../backend/sublime/frontend_generated.go", generatemethodsEx(reflect.TypeOf(backend.GetEditor().Frontend()), regexp.MustCompile("Show|VisibleRegion").MatchString, "backend.GetEditor().Frontend().", sn), }, {"../backend/sublime/sublime_api_generated.go", generatemethodsEx(reflect.TypeOf(backend.GetEditor()), regexp.MustCompile("Info|HandleInput|CommandHandler|Windows|Frontend|Console|SetActiveWindow|Init|Watch|Watcher").MatchString, "backend.GetEditor().", sn), }, } data[len(data)-1][1] += fmt.Sprintf(`var sublime_methods = []py.Method{ %s }`, sublime_methods) for _, gen := range data { if gen[0] == "" { continue } wr := `// This file was generated as part of a build step and shouldn't be manually modified package sublime import ( "fmt" "lime/3rdparty/libs/gopy/lib" "lime/backend" "github.com/quarnster/util/text" ) var ( _ = backend.View{} _ = text.Region{} _ = fmt.Errorf ) ` + gen[1] if err := ioutil.WriteFile(gen[0], []byte(wr), 0644); err != nil { panic(err) } else { c := exec.Command("go", "fmt", gen[0]) if o, err := c.CombinedOutput(); err != nil { panic(fmt.Errorf("%s, %s", o, err)) } else { fmt.Printf("%s", string(o)) } } } }
func (t *qmlfrontend) loop() (err error) { backend.OnNew.Add(func(v *backend.View) { fv := &frontendView{bv: v} v.Buffer().AddCallback(fv.bufferChanged) v.Settings().AddOnChange("blah", func(name string) { if name == "lime.syntax.updated" { // force redraw, as the syntax regions might have changed... for i := range fv.FormattedLine { fv.formatLine(i) } } }) fv.Title.Text = v.Buffer().FileName() if len(fv.Title.Text) == 0 { fv.Title.Text = "untitled" } w2 := t.windows[v.Window()] w2.views = append(w2.views, fv) w2.Len = len(w2.views) t.qmlChanged(w2, &w2.Len) }) backend.OnClose.Add(func(v *backend.View) { w2 := t.windows[v.Window()] for i := range w2.views { if w2.views[i].bv == v { copy(w2.views[i:], w2.views[i+1:]) w2.views = w2.views[:len(w2.views)-1] w2.Len = len(w2.views) t.qmlChanged(w2, &w2.Len) return } } log4go.Error("Couldn't find closed view...") }) backend.OnLoad.Add(func(v *backend.View) { w2 := t.windows[v.Window()] i := 0 for i, _ = range w2.views { if w2.views[i].bv == v { break } } v2 := w2.views[i] v2.Title.Text = v.Buffer().FileName() t.qmlChanged(v2, &v2.Title) }) ed := backend.GetEditor() ed.SetFrontend(t) ed.LogInput(false) ed.LogCommands(false) c := ed.Console() t.Console = &frontendView{bv: c} c.Buffer().AddCallback(t.Console.bufferChanged) c.Buffer().AddCallback(t.scroll) const qmlMainFile = "main.qml" var ( engine *qml.Engine component qml.Object // WaitGroup keeping track of open windows wg sync.WaitGroup ) // create and setup a new engine, destroying // the old one if one exists. // // This is needed to re-load qml files to get // the new file contents from disc as otherwise // the old file would still be what is referenced. newEngine := func() (err error) { if engine != nil { log4go.Debug("calling destroy") // TODO(.): calling this appears to make the editor *very* crash-prone, just let it leak for now // engine.Destroy() engine = nil } log4go.Debug("calling newEngine") engine = qml.NewEngine() log4go.Debug("setvar frontend") engine.Context().SetVar("frontend", t) log4go.Debug("setvar editor") engine.Context().SetVar("editor", backend.GetEditor()) log4go.Debug("loadfile") component, err = engine.LoadFile(qmlMainFile) return } if err := newEngine(); err != nil { log4go.Error(err) } backend.OnNewWindow.Add(func(w *backend.Window) { fw := &frontendWindow{bw: w} t.windows[w] = fw if component != nil { fw.launch(&wg, component) } }) // TODO: should be done backend side if sc, err := textmate.LoadTheme("../../3rdparty/bundles/TextMate-Themes/Monokai.tmTheme"); err != nil { log4go.Error(err) } else { scheme = sc } defer func() { fmt.Println(util.Prof) }() w := ed.NewWindow() v := w.OpenFile("main.go", 0) // TODO: should be done backend side v.Settings().Set("syntax", "../../3rdparty/bundles/go.tmbundle/Syntaxes/Go.tmLanguage") v.Sel().Clear() v.Sel().Add(Region{0, 0}) v = w.OpenFile("../../backend/editor.go", 0) // TODO: should be done backend side v.Settings().Set("syntax", "../../3rdparty/bundles/go.tmbundle/Syntaxes/Go.tmLanguage") v.Sel().Clear() v.Sel().Add(Region{0, 0}) ed.Init() sublime.Init() watch, err := fsnotify.NewWatcher() if err != nil { log4go.Error("Unable to create file watcher: %s", err) return } defer watch.Close() watch.Watch(".") defer watch.RemoveWatch(".") reloadRequested := false go func() { for { select { case ev := <-watch.Event: if ev != nil && strings.HasSuffix(ev.Name, ".qml") && ev.IsModify() && !ev.IsAttrib() { reloadRequested = true // Close all open windows to de-reference all // qml objects for _, v := range t.windows { if v.window != nil { v.window.Hide() v.window.Destroy() v.window = nil } } } } } }() for { // Reset reload status reloadRequested = false log4go.Debug("Waiting for all windows to close") // wg would be the WaitGroup all windows belong to, so first we wait for // all windows to close. wg.Wait() log4go.Debug("All windows closed. reloadRequest: %v", reloadRequested) // then we check if there's a reload request in the pipe if !reloadRequested || len(t.windows) == 0 { // This would be a genuine exit; all windows closed by the user break } // *We* closed all windows because we want to reload freshly changed qml // files. for { log4go.Debug("Calling newEngine") if err := newEngine(); err != nil { // Reset reload status reloadRequested = false log4go.Error(err) for !reloadRequested { // This loop allows us to re-try reloading // if there was an error in the file this time, // we just loop around again when we receive the next // reload request (ie on the next save of the file). time.Sleep(time.Second) } continue } log4go.Debug("break") break } log4go.Debug("re-launching all windows") // Succeeded loading the file, re-launch all windows for _, v := range t.windows { v.launch(&wg, component) } } return }
func main() { cleanup() var sublime_methods = "" sn := func(t reflect.Type, m reflect.Method) string { sn := "sublime_" + m.Name sublime_methods += fmt.Sprintf("{Name: \"%s\", Func: %s},\n", pyname(m.Name)[1:], sn) return sn } data := [][]string{ {"./backend/sublime/region_generated.go", generateWrapper(reflect.TypeOf(text.Region{}), true, regexp.MustCompile("Cut").MatchString)}, {"./backend/sublime/regionset_generated.go", generateWrapper(reflect.TypeOf(&text.RegionSet{}), false, regexp.MustCompile("Less|Swap|Adjust|Has|Cut").MatchString)}, {"./backend/sublime/edit_generated.go", generateWrapper(reflect.TypeOf(&backend.Edit{}), false, regexp.MustCompile("Apply|Undo").MatchString)}, {"./backend/sublime/view_generated.go", generateWrapper(reflect.TypeOf(&backend.View{}), false, regexp.MustCompile("Buffer|Syntax|CommandHistory|Show|AddRegions|UndoStack|Transform|Reload|Save|Close|ExpandByClass|Erased|FileChanged|Inserted").MatchString)}, {"./backend/sublime/window_generated.go", generateWrapper(reflect.TypeOf(&backend.Window{}), false, regexp.MustCompile("OpenFile|SetActiveView|Close").MatchString)}, {"./backend/sublime/settings_generated.go", generateWrapper(reflect.TypeOf(&text.Settings{}), false, regexp.MustCompile("Parent|Set|Get|UnmarshalJSON|MarshalJSON").MatchString)}, {"./backend/sublime/view_buffer_generated.go", generatemethodsEx( reflect.TypeOf(backend.GetEditor().Console().Buffer()), regexp.MustCompile("Erase|Insert|Substr|SetFile|AddCallback|AddObserver|RemoveObserver|Data|Runes|Settings|Index|Close|Unlock|Lock").MatchString, "o.data.Buffer().", func(t reflect.Type, m reflect.Method) string { mn := "" switch m.Name { case "Line", "LineR", "FullLine", "FullLineR", "WordR", "Word": mn = strings.ToLower(m.Name) case "Id": mn = "Py_buffer_id" default: mn = "Py" + pyname(m.Name) } return "(o *View) " + mn })}, {"./backend/sublime/commands_generated.go", generatemethodsEx(reflect.TypeOf(backend.GetEditor().CommandHandler()), regexp.MustCompile("RunWindowCommand|RunTextCommand|RunApplicationCommand|RegisterWithDefault").MatchString, "backend.GetEditor().CommandHandler().", sn), }, {"./backend/sublime/frontend_generated.go", generatemethodsEx(reflect.TypeOf(backend.GetEditor().Frontend()), regexp.MustCompile("Show|VisibleRegion|DefaultAction").MatchString, "backend.GetEditor().Frontend().", sn), }, {"./backend/sublime/sublime_api_generated.go", generatemethodsEx(reflect.TypeOf(backend.GetEditor()), regexp.MustCompile("Info|HandleInput|CommandHandler|Windows|Frontend|Console|SetActiveWindow|Init|Watch|Observe|SetClipboardFuncs|KeyBindings").MatchString, "backend.GetEditor().", sn), }, } data[len(data)-1][1] += fmt.Sprintf(`var sublime_methods = []py.Method{ %s }`, sublime_methods) var year = strconv.FormatInt(int64(time.Now().Year()), 10) for _, gen := range data { if gen[0] == "" { continue } wr := `// Copyright ` + year + ` The lime Authors. // Use of this source code is governed by a 2-clause // BSD-style license that can be found in the LICENSE file. // This file was generated by tasks/build/gen_python_api.go and shouldn't be manually modified package sublime import ( "fmt" "github.com/limetext/gopy/lib" "github.com/limetext/lime/backend" "github.com/limetext/text" ) var ( _ = backend.View{} _ = text.Region{} _ = fmt.Errorf ) ` + gen[1] if err := ioutil.WriteFile(gen[0], []byte(wr), 0644); err != nil { panic(err) } else { c := exec.Command("go", "fmt", gen[0]) if o, err := c.CombinedOutput(); err != nil { panic(fmt.Errorf("%s, %s", o, err)) } else { fmt.Printf("%s", string(o)) } } } }
func TestSublime(t *testing.T) { ed := backend.GetEditor() ed.Console().Buffer().AddCallback(func(b text.Buffer, pos, delta int) { t.Logf("%s", b.Substr(text.Region{pos, pos + delta})) }) w := ed.NewWindow() Init() l := py.NewLock() py.AddToPath("testdata") py.AddToPath("testdata/plugins") if m, err := py.Import("sublime_plugin"); err != nil { t.Fatal(err) } else { plugins := backend.ScanPlugins("testdata/", ".py") for _, p := range plugins { loadPlugin(p, m) } } subl, err := py.Import("sublime") if err != nil { t.Fatal(err) } if w, err := _windowClass.Alloc(1); err != nil { t.Fatal(err) } else { (w.(*Window)).data = &backend.Window{} subl.AddObject("test_window", w) } if dir, err := os.Open("testdata"); err != nil { t.Error(err) } else if files, err := dir.Readdirnames(0); err != nil { t.Error(err) } else { for _, fn := range files { if filepath.Ext(fn) == ".py" { log4go.Debug("Running %s", fn) if _, err := py.Import(fn[:len(fn)-3]); err != nil { log4go.Error(err) t.Error(err) } else { log4go.Debug("Ran %s", fn) } } } } // Testing plugin reload data := []byte(`import sublime, sublime_plugin class TestToxt(sublime_plugin.TextCommand): def run(self, edit): print("my view's id is: %d" % self.view.id()) self.view.insert(edit, 0, "Tada") `) if err := ioutil.WriteFile("testdata/plugins/reload.py", data, 0644); err != nil { t.Fatalf("Couldn't write file: %s", err) } data = []byte(`try: import traceback import sublime print("new file") v = sublime.test_window.new_file() print("running command") v.run_command("test_toxt") print("command ran") assert v.substr(sublime.Region(0, v.size())) == "Tada" except: traceback.print_exc() raise `) time.Sleep(time.Millisecond * 10) if err := ioutil.WriteFile("testdata/reload_test.py", data, 0644); err != nil { t.Fatalf("Couldn't write file: %s", err) } log4go.Debug("Running %s", "reload_test.py") if _, err := py.Import("reload_test"); err != nil { log4go.Error(err) t.Error(err) } else { log4go.Debug("Ran %s", "reload_test.py") } os.Remove("testdata/plugins/reload.py") os.Remove("testdata/reload_test.py") var f func(indent string, v py.Object, buf *bytes.Buffer) f = func(indent string, v py.Object, buf *bytes.Buffer) { b := v.Base() if dir, err := b.Dir(); err != nil { t.Error(err) } else { if l, ok := dir.(*py.List); ok { sl := l.Slice() if indent == "" { for _, v2 := range sl { if item, err := b.GetAttr(v2); err != nil { t.Error(err) } else { ty := item.Type() line := fmt.Sprintf("%s%s\n", indent, v2) buf.WriteString(line) if ty == py.TypeType { f(indent+"\t", item, buf) } item.Decref() } } } else { for _, v2 := range sl { buf.WriteString(fmt.Sprintf("%s%s\n", indent, v2)) } } } else { ty := dir.Type() t.Error("Unexpected type:", ty) } dir.Decref() } } buf := bytes.NewBuffer(nil) f("", subl, buf) l.Unlock() const expfile = "testdata/api.txt" if d, err := ioutil.ReadFile(expfile); err != nil { if err := ioutil.WriteFile(expfile, buf.Bytes(), 0644); err != nil { t.Error(err) } } else if diff := util.Diff(string(d), buf.String()); diff != "" { t.Error(diff) } ed.LogCommands(true) tests := []string{ "state", "registers", "settings", "constants", "registers", "cmd_data", "marks", } for _, test := range tests { ed.CommandHandler().RunWindowCommand(w, "vintage_ex_run_data_file_based_tests", backend.Args{"suite_name": test}) } for _, w := range ed.Windows() { for _, v := range w.Views() { if strings.HasSuffix(v.Buffer().FileName(), "sample.txt") { continue } if strings.Index(v.Buffer().Substr(text.Region{0, v.Buffer().Size()}), "FAILED") != -1 { t.Error(v.Buffer()) } } } var v *backend.View for _, v2 := range w.Views() { if v == nil || v2.Buffer().Size() > v.Buffer().Size() { v = v2 } } }
// Launches the provided command in a new goroutine // (to avoid locking up the GUI) func (t *qmlfrontend) RunCommand(command string) { ed := backend.GetEditor() go ed.RunCommand(command, make(backend.Args)) }