// Called when a new view is opened func (t *qmlfrontend) onNew(v *backend.View) { fv := &frontendView{bv: v} v.Buffer().AddObserver(fv) v.Settings().AddOnChange("blah", fv.onChange) 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) tabs := w2.window.ObjectByName("tabs") tab := tabs.Call("addTab", "", limeViewComponent).(qml.Object) try_now := func() { item := tab.Property("item").(qml.Object) if item.Addr() == 0 { // Happens as the item isn't actually loaded until we switch to the tab. // Hence connecting to the loaded signal return } item.Set("myView", fv) item.Set("fontSize", v.Settings().Get("font_size", 12).(float64)) item.Set("fontFace", v.Settings().Get("font_face", "Helvetica").(string)) } tab.On("loaded", try_now) try_now() tabs.Set("currentIndex", tabs.Property("count").(int)-1) }
// Called when a new view is opened func (t *qmlfrontend) onNew(v *backend.View) { fv := &frontendView{bv: v} v.Buffer().AddCallback(fv.bufferChanged) v.Settings().AddOnChange("blah", fv.onChange) 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) tabs := w2.window.ObjectByName("tabs") tab := tabs.Call("addTab", "", limeViewComponent).(qml.Object) try_now := func() { item := tab.Property("item").(qml.Object) if item.Addr() == 0 { // Happens as the item isn't actually loaded until we switch to the tab. // Hence connecting to the loaded signal return } item.Set("myView", fv) } tab.On("loaded", try_now) try_now() }
// called when a view has loaded func (t *qmlfrontend) onLoad(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) }
func (t *tbfe) VisibleRegion(v *backend.View) Region { t.lock.Lock() r, ok := t.layout[v] t.lock.Unlock() if !ok || r.lastUpdate != v.Buffer().ChangeCount() { t.Show(v, r.visible) t.lock.Lock() r = t.layout[v] t.lock.Unlock() } return r.visible }
// called when a view has loaded func (t *qmlfrontend) onLoad(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() tabs := w2.window.ObjectByName("tabs") tab := tabs.Call("getTab", i).(qml.Object) tab.Set("title", v2.Title.Text) }
func (t *tbfe) FormatLine(v *backend.View, line int) string { buf := bytes.NewBuffer(nil) vr := v.Buffer().Line(v.Buffer().TextPoint(line, 0)) log4go.Debug("FormatLine: %d, %s", line, vr) if vr.Size() == 0 { return "" } recipie := v.Transform(scheme, vr).Transcribe() highlight_line := false if b, ok := v.Settings().Get("highlight_line", highlight_line).(bool); ok { highlight_line = b } lastEnd := vr.Begin() for _, reg := range recipie { if lastEnd != reg.Region.Begin() { fmt.Fprintf(buf, "<span>%s</span>", v.Buffer().Substr(Region{lastEnd, reg.Region.Begin()})) } fmt.Fprintf(buf, "<span style=\"white-space:pre; color:#%s; background:#%s\">%s</span>", htmlcol(reg.Flavour.Foreground), htmlcol(reg.Flavour.Background), v.Buffer().Substr(reg.Region)) lastEnd = reg.Region.End() } if lastEnd != vr.End() { io.WriteString(buf, v.Buffer().Substr(Region{lastEnd, vr.End()})) } return buf.String() }
func (t *tbfe) setupCallbacks(view *backend.View) { // Ensure that the visible region currently presented is // inclusive of the insert/erase delta. view.Buffer().AddObserver(&tbfeBufferDeltaObserver{t: t, view: view}) backend.OnNew.Add(func(v *backend.View) { v.Settings().AddOnChange("lime.frontend.termbox.render", func(name string) { t.render() }) }) backend.OnModified.Add(func(v *backend.View) { t.render() }) backend.OnSelectionModified.Add(func(v *backend.View) { t.render() }) }
func (t *tbfe) clip(v *backend.View, s, e int) Region { p := util.Prof.Enter("clip") defer p.Exit() h := t.layout[v].height if e-s > h { e = s + h } else if e-s < h { s = e - h } if e2, _ := v.Buffer().RowCol(v.Buffer().TextPoint(e, 0)); e2 < e { e = e2 } if s < 0 { s = 0 } e = s + h r := Region{v.Buffer().TextPoint(s, 0), v.Buffer().TextPoint(e, 0)} return v.Buffer().LineR(r) }
func (t tbfe) setupCallbacks(view *backend.View) { // Ensure that the visible region currently presented is // inclusive of the insert/erase delta. view.Buffer().AddCallback(func(b Buffer, pos, delta int) { visible := t.layout[view].visible t.Show(view, Region{visible.Begin(), visible.End() + delta}) }) backend.OnNew.Add(func(v *backend.View) { v.Settings().AddOnChange("lime.frontend.termbox.render", func(name string) { t.render() }) }) backend.OnModified.Add(func(v *backend.View) { t.render() }) backend.OnSelectionModified.Add(func(v *backend.View) { t.render() }) }
func (t *tbfe) Show(v *backend.View, r Region) { t.lock.Lock() l := t.layout[v] t.lock.Unlock() p := util.Prof.Enter("show") defer p.Exit() lv := l.visible s1, _ := v.Buffer().RowCol(lv.Begin()) e1, _ := v.Buffer().RowCol(lv.End()) s2, _ := v.Buffer().RowCol(r.Begin()) e2, _ := v.Buffer().RowCol(r.End()) r1 := Region{s1, e1} r2 := Region{s2, e2} r3 := r1.Cover(r2) diff := 0 if d1, d2 := Abs(r1.Begin()-r3.Begin()), Abs(r1.End()-r3.End()); d1 > d2 { diff = r3.Begin() - r1.Begin() } else { diff = r3.End() - r1.End() } r3.A = r1.Begin() + diff r3.B = r1.End() + diff r3 = t.clip(v, r3.A, r3.B) l.visible = r3 t.lock.Lock() t.layout[v] = l t.lock.Unlock() t.render() }
func (t *tbfe) renderView(wr io.Writer, v *backend.View, lay layout) { p := util.Prof.Enter("render") defer p.Exit() vr := lay.visible runes := v.Buffer().Substr(vr) recipie := v.Transform(scheme, vr).Transcribe() highlight_line := false if b, ok := v.Settings().Get("highlight_line", highlight_line).(bool); ok { highlight_line = b } lastEnd := 0 for _, reg := range recipie { if lastEnd != reg.Region.Begin() { io.WriteString(wr, runes[lastEnd:reg.Region.Begin()]) } fmt.Fprintf(wr, "<span style=\"color:#%s; background-color:#%s\">%s</span>", htmlcol(reg.Flavour.Foreground), htmlcol(reg.Flavour.Background), runes[reg.Region.Begin():reg.Region.End()]) lastEnd = reg.Region.End() } if lastEnd != vr.End() { io.WriteString(wr, v.Buffer().Substr(Region{lastEnd, vr.End()})) } }
func (t *tbfe) renderLStatus(v *backend.View, y int, fg, bg termbox.Attribute) { st := v.Status() sel := v.Sel() j := 0 for k, v := range st { s := fmt.Sprintf("%s: %s, ", k, v) addString(j, y, s, fg, bg) } if sel.Len() == 0 { return } else if l := sel.Len(); l > 1 { s := fmt.Sprintf("%d selection regions", l) j = addString(j, y, s, fg, bg) } else if r := sel.Get(0); r.Size() == 0 { row, col := v.Buffer().RowCol(r.A) s := fmt.Sprintf("Line %d, Column %d", row, col) j = addString(j, y, s, fg, bg) } else { ls := v.Buffer().Lines(r) s := v.Buffer().Substr(r) if len(ls) < 2 { s := fmt.Sprintf("%d characters selected", len(s)) j = addString(j, y, s, fg, bg) } else { s := fmt.Sprintf("%d lines %d characters selected", len(ls), len(s)) j = addString(j, y, s, fg, bg) } } if t.status_message != "" { s := fmt.Sprintf("; %s", t.status_message) addString(j, y, s, fg, bg) } }
func (t *tbfe) renderView(v *backend.View, lay layout) { p := util.Prof.Enter("render") defer p.Exit() sx, sy, w, h := lay.x, lay.y, lay.width, lay.height vr := lay.visible runes := v.Buffer().Substr(vr) x, y := sx, sy ex, ey := sx+w, sy+h caretStyle := t.settings.caretStyle if t.settings.caretBlink && blink { t.settings.caretStyle = 0 } recipie := v.Transform(scheme, vr).Transcribe() fg, bg := defaultFg, defaultBg sel := v.Sel() line, _ := v.Buffer().RowCol(vr.Begin()) eofline, _ := v.Buffer().RowCol(v.Buffer().Size()) lineNumberRenderSize := len(intToRunes(eofline)) for i, r := range runes { o := vr.Begin() + i curr := 0 fg, bg = defaultFg, defaultBg if t.settings.lineNumbers { renderLineNumber(&line, &x, y, lineNumberRenderSize, fg, bg) } for curr < len(recipie) && (o >= recipie[curr].Region.Begin()) { if o < recipie[curr].Region.End() { fg = palLut(textmate.Color(recipie[curr].Flavour.Foreground)) bg = palLut(textmate.Color(recipie[curr].Flavour.Background)) } curr++ } if sel.Contains(Region{o, o}) { fg = fg | t.settings.caretStyle } if r == '\t' { add := (x + 1 + (t.settings.tabSize - 1)) &^ (t.settings.tabSize - 1) for x < add { if x < ex { termbox.SetCell(x, y, ' ', fg, bg) } fg = fg &^ termbox.AttrUnderline // Just looks weird with a long underline x++ } continue } else if r == '\n' { x = sx y++ if y > ey { break } continue } if x < ex { termbox.SetCell(x, y, r, fg, bg) } x++ } // restore original caretStyle before blink modification t.settings.caretStyle = caretStyle }
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) VisibleRegion(v *backend.View) Region { // TODO return Region{0, v.Buffer().Size()} }
func (t *tbfe) renderView(v *backend.View, lay layout) { p := util.Prof.Enter("render") defer p.Exit() sx, sy, w, h := lay.x, lay.y, lay.width, lay.height vr := lay.visible runes := v.Buffer().Substr(vr) x, y := sx, sy ex, ey := sx+w, sy+h style, _ := v.Settings().Get("caret_style", "underline").(string) inverse, _ := v.Settings().Get("inverse_caret_state", false).(bool) caretStyle := getCaretStyle(style, inverse) oldCaretStyle := caretStyle caretBlink, _ := v.Settings().Get("caret_blink", true).(bool) if caretBlink && blink { caretStyle = 0 } tabSize := 4 if i, ok := v.Settings().Get("tab_size", tabSize).(int); ok { tabSize = i } lineNumbers, _ := v.Settings().Get("line_numbers", true).(bool) recipie := v.Transform(scheme, vr).Transcribe() fg, bg := defaultFg, defaultBg sel := v.Sel() line, _ := v.Buffer().RowCol(vr.Begin()) eofline, _ := v.Buffer().RowCol(v.Buffer().Size()) lineNumberRenderSize := len(intToRunes(eofline)) for i, r := range runes { o := vr.Begin() + i curr := 0 fg, bg = defaultFg, defaultBg if lineNumbers { renderLineNumber(&line, &x, y, lineNumberRenderSize, fg, bg) } // TODO: doc for curr < len(recipie) && (o >= recipie[curr].Region.Begin()) { if o < recipie[curr].Region.End() { fg = palLut(textmate.Color(recipie[curr].Flavour.Foreground)) bg = palLut(textmate.Color(recipie[curr].Flavour.Background)) } curr++ } iscursor := sel.Contains(Region{o, o}) if iscursor { fg = fg | caretStyle termbox.SetCell(x, y, ' ', fg, bg) } if r == '\t' { add := (x + 1 + (tabSize - 1)) &^ (tabSize - 1) for ; x < add; x++ { if x < ex { termbox.SetCell(x, y, ' ', fg, bg) } // A long cursor looks weird fg = fg & ^(termbox.AttrUnderline | termbox.AttrReverse) } continue } else if r == '\n' { x = sx if y++; y > ey { break } else if lineNumbers { // This results in additional calls to renderLineNumber. // Maybe just accumulate positions needing line numbers, rendering them // after the loop? renderLineNumber(&line, &x, y, lineNumberRenderSize, defaultFg, defaultBg) } continue } if x < ex { termbox.SetCell(x, y, r, fg, bg) } x++ } fg, bg = defaultFg, defaultBg // Need this if the cursor is at the end of the buffer o := vr.Begin() + len(runes) iscursor := sel.Contains(Region{o, o}) if iscursor { fg = fg | caretStyle termbox.SetCell(x, y, ' ', fg, bg) } // restore original caretStyle before blink modification caretStyle = oldCaretStyle if rs := sel.Regions(); len(rs) > 0 { if r := rs[len(rs)-1]; !vr.Covers(r) { t.Show(v, r) } } fg, bg = defaultFg, palLut(textmate.Color{28, 29, 26, 1}) y = t.window_layout.height - statusbarHeight // Draw status bar bottom of window for i := 0; i < t.window_layout.width; i++ { termbox.SetCell(i, y, ' ', fg, bg) } t.renderLStatus(v, y, fg, bg) // The right status rns := []rune(fmt.Sprintf("Tab Size:%d %s", tabSize, "Go")) x = t.window_layout.width - 1 - len(rns) addRunes(x, y, rns, fg, bg) }
func (t *tbfe) VisibleRegion(v *backend.View) Region { // TODO return Region{0, v.Buffer().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 { 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 } } }
func (t *tbfe) renderView(v *backend.View, lay layout) { p := util.Prof.Enter("render") defer p.Exit() sx, sy, w, h := lay.x, lay.y, lay.width, lay.height vr := lay.visible runes := v.Buffer().Substr(vr) x, y := sx, sy ex, ey := sx+w, sy+h style, _ := v.Settings().Get("caret_style", "underline").(string) inverse, _ := v.Settings().Get("inverse_caret_state", false).(bool) caretStyle := getCaretStyle(style, inverse) caretBlink, _ := v.Settings().Get("caret_blink", true).(bool) if caretBlink && blink { caretStyle = 0 } tabSize := 4 ts := v.Settings().Get("tab_size", tabSize) // TODO(.): crikey... if i, ok := ts.(int); ok { tabSize = i } else if f, ok := ts.(float64); ok { tabSize = int(f) } lineNumbers, _ := v.Settings().Get("line_numbers", true).(bool) recipie := v.Transform(scheme, vr).Transcribe() fg, bg := defaultFg, defaultBg sel := v.Sel() line, _ := v.Buffer().RowCol(vr.Begin()) eofline, _ := v.Buffer().RowCol(v.Buffer().Size()) lineNumberRenderSize := len(intToRunes(eofline)) for i, r := range runes { o := vr.Begin() + i curr := 0 fg, bg = defaultFg, defaultBg if lineNumbers { renderLineNumber(&line, &x, y, lineNumberRenderSize, fg, bg) } for curr < len(recipie) && (o >= recipie[curr].Region.Begin()) { if o < recipie[curr].Region.End() { fg = palLut(textmate.Color(recipie[curr].Flavour.Foreground)) bg = palLut(textmate.Color(recipie[curr].Flavour.Background)) } curr++ } iscursor := sel.Contains(Region{o, o}) if iscursor { fg = fg | caretStyle termbox.SetCell(x, y, ' ', fg, bg) } if r == '\t' { add := (x + 1 + (tabSize - 1)) &^ (tabSize - 1) for x < add { if x < ex { termbox.SetCell(x, y, ' ', fg, bg) } // A long cursor looks weird fg = fg & ^(termbox.AttrUnderline | termbox.AttrReverse) x++ } continue } else if r == '\n' { x = sx if y++; y > ey { break } else if lineNumbers { renderLineNumber(&line, &x, y, lineNumberRenderSize, fg, bg) } continue } if x < ex { termbox.SetCell(x, y, r, fg, bg) } x++ } fg, bg = defaultFg, defaultBg // Need this if the cursor is at the end of the buffer o := vr.Begin() + len(runes) iscursor := sel.Contains(Region{o, o}) if iscursor { fg = fg | caretStyle termbox.SetCell(x, y, ' ', fg, bg) } // restore original caretStyle before blink modification caretStyle = caretStyle }