1
0
forked from x/icebergs

opt iterm

This commit is contained in:
IT 老营长 @云轩领航-创始人 2023-05-13 20:45:20 +08:00
parent bd15da5f40
commit 2f031d6a2b
9 changed files with 98 additions and 51 deletions

View File

@ -177,8 +177,22 @@ func CopyFile(m *ice.Message, to, from string, cb func([]byte, int) []byte) {
}) })
}) })
} }
func Pipe(m *ice.Message, cb func(string)) io.WriteCloser { func Pipe(m *ice.Message, cb ice.Any) io.WriteCloser {
r, w := io.Pipe() r, w := io.Pipe()
switch cb := cb.(type) {
case func(string):
m.Go(func() { kit.For(r, cb) }) m.Go(func() { kit.For(r, cb) })
case func([]byte):
m.Go(func() {
buf := make([]byte, ice.MOD_BUFS)
for {
n, e := r.Read(buf)
if cb(buf[:n]); e != nil {
break
}
}
})
default:
}
return w return w
} }

View File

@ -22,20 +22,20 @@ fieldset.macos.desktop>div.output>div.desktop>div.item img { width:80px; border-
fieldset.macos.desktop>div.output>div.desktop>div.item>div.name { font-size:12px; width:80px; overflow:hidden; } fieldset.macos.desktop>div.output>div.desktop>div.item>div.name { font-size:12px; width:80px; overflow:hidden; }
fieldset.macos.desktop>div.output>div.desktop>fieldset { border-radius:10px; position:absolute; } fieldset.macos.desktop>div.output>div.desktop>fieldset { border-radius:10px; position:absolute; }
fieldset.macos.desktop>div.output>div.desktop>fieldset>div.output>table.content { width:100%; } fieldset.macos.desktop>div.output>div.desktop>fieldset>div.output>table.content { width:100%; }
fieldset.macos.desktop>div.output>div.desktop>fieldset>div.item.button { border-radius:20px; height:20px; width:20px; scale:0.7; position:absolute; top:17px; right:10px; } fieldset.macos.desktop>div.output>div.desktop>fieldset>div.item.button { border-radius:20px; height:20px; width:20px; scale:0.7; position:absolute; top:16px; right:10px; }
fieldset.macos.desktop>div.output>div.desktop>fieldset>legend { background-color:unset; padding-right:10px; margin:10px 0; } fieldset.macos.desktop>div.output>div.desktop>fieldset>legend { background-color:unset; padding-right:10px; margin:10px 0; }
fieldset.macos.desktop>div.output>div.desktop>fieldset>form.option>*:not(.textarea) { margin:10px 0px 10px 10px; } fieldset.macos.desktop>div.output>div.desktop>fieldset>form.option>*:not(.textarea) { margin:10px 0px 10px 10px; }
fieldset.macos.desktop>div.output>div.desktop>fieldset>form.option>div.icon { margin-left:0; margin-top:12px; margin-bottom:8px; } fieldset.macos.desktop>div.output>div.desktop>fieldset>form.option>div.icon { margin-left:0; margin-top:10px; margin-bottom:8px; }
fieldset.macos.desktop>div.output>div.desktop>fieldset>form.option>div.item.icons { margin-left:0; } fieldset.macos.desktop>div.output>div.desktop>fieldset>form.option>div.item.icons { margin-left:0; }
fieldset.macos.desktop>div.output>div.desktop>fieldset>form.option>div.item:last-child { margin-right:20px; } fieldset.macos.desktop>div.output>div.desktop>fieldset>form.option>div.item:last-child { margin-right:20px; }
fieldset.macos.desktop>div.output>div.desktop>fieldset>form.option>div.item:last-child { margin-right:80px; } fieldset.macos.desktop>div.output>div.desktop>fieldset>form.option>div.item:last-child { margin-right:80px; }
fieldset.macos.desktop>div.output>div.desktop>fieldset>div.action>* { margin:10px 0px 10px 10px; } fieldset.macos.desktop>div.output>div.desktop>fieldset>div.action>* { margin:10px 0px 10px 10px; }
fieldset.macos.desktop>div.output>div.desktop>fieldset>div.action>div.item.icons>span.icon { margin-top:12px; } fieldset.macos.desktop>div.output>div.desktop>fieldset>div.action>div.item.icons>span.icon { margin-top:10px; }
fieldset.macos.desktop>div.output>div.desktop>fieldset>div.action>div.item:last-child { margin-right:80px; } fieldset.macos.desktop>div.output>div.desktop>fieldset>div.action>div.item:last-child { margin-right:80px; }
fieldset.macos.desktop>div.output>div.desktop>fieldset.web.wiki.feel>div.status { display:none; } fieldset.macos.desktop>div.output>div.desktop>fieldset.web.wiki.feel>div.status { display:none; }
fieldset.macos.desktop>div.output>div.desktop>fieldset.web.code.vimer>div.status { display:none; } fieldset.macos.desktop>div.output>div.desktop>fieldset.web.code.vimer>div.status { display:none; }
fieldset.macos.desktop>div.output>div.desktop>fieldset.web.code.xterm>div.status { display:none; } fieldset.macos.desktop>div.output>div.desktop>fieldset.web.code.xterm>div.status { display:none; }
fieldset.macos.desktop>div.output>div.desktop>fieldset.web.code.xterm>div.action>div.tabs:only-child { display:none; } fieldset.web.code.xterm>div.action>div.tabs:only-child { display:none; }
fieldset.macos.desktop>div.output>div.desktop>fieldset.web.code.compile>form.option>div.item:last-child { margin-right:80px; } fieldset.macos.desktop>div.output>div.desktop>fieldset.web.code.compile>form.option>div.item:last-child { margin-right:80px; }
fieldset.macos.desktop>div.output>div.desktop>fieldset.web.chat.iframe>div.status { display:none; } fieldset.macos.desktop>div.output>div.desktop>fieldset.web.chat.iframe>div.status { display:none; }
fieldset.macos.desktop>div.output>div.desktop>fieldset.web.chat.iframe>form.option>div.item.hash>input { width:360px; } fieldset.macos.desktop>div.output>div.desktop>fieldset.web.chat.iframe>form.option>div.item.hash>input { width:360px; }
@ -87,11 +87,13 @@ body.dark fieldset.macos.desktop>div.output>div.desktop>fieldset>div.status { bo
body.dark div.carte.macos.float { background-color:#29323beb; } body.dark div.carte.macos.float { background-color:#29323beb; }
body.black fieldset.macos.desktop>div.output>fieldset.macos.searchs a { color:cyan; } body.black fieldset.macos.desktop>div.output>fieldset.macos.searchs a { color:cyan; }
body.black fieldset.macos.desktop>div.output>div.desktop>fieldset { background-color:#3433337a; } body.black fieldset.macos.desktop>div.output>div.desktop>fieldset { background-color:#3433337a; }
body.black fieldset.macos.desktop>div.output>div.desktop fieldset input { background-color:#243783bd; box-shadow:unset; }
body.black fieldset.macos.desktop>div.output>div.desktop fieldset table.content tbody tr:nth-child(odd) { background-color:#1a1d1ee0; } body.black fieldset.macos.desktop>div.output>div.desktop fieldset table.content tbody tr:nth-child(odd) { background-color:#1a1d1ee0; }
body.black fieldset.macos.desktop>div.output>div.desktop fieldset table.content tbody tr:nth-child(even) { background-color:#282b2fc7; } body.black fieldset.macos.desktop>div.output>div.desktop fieldset table.content tbody tr:nth-child(even) { background-color:#282b2fc7; }
body.black fieldset.macos.desktop>div.output>div.desktop>fieldset>div.output { background-color:#1a1d1d9c; } body.black fieldset.macos.desktop>div.output>div.desktop>fieldset>div.output { background-color:#1a1d1d9c; }
body.white fieldset.macos.desktop>div.output>fieldset.macos.searchs a { color:black; } body.white fieldset.macos.desktop>div.output>fieldset.macos.searchs a { color:black; }
body.white fieldset.macos.desktop>div.output>div.desktop>fieldset { background-color:#f5f5f594; } body.white fieldset.macos.desktop>div.output>div.desktop>fieldset { background-color:#f5f5f594; }
body.white fieldset.macos.desktop>div.output>div.desktop fieldset input { background-color:#ffffff7d; box-shadow:unset; }
body.white fieldset.macos.desktop>div.output>div.desktop fieldset table.content tbody tr:nth-child(odd) { background-color:#a5afbdb5; } body.white fieldset.macos.desktop>div.output>div.desktop fieldset table.content tbody tr:nth-child(odd) { background-color:#a5afbdb5; }
body.white fieldset.macos.desktop>div.output>div.desktop fieldset table.content tbody tr:nth-child(even) { background-color:#d4dddfd4; } body.white fieldset.macos.desktop>div.output>div.desktop fieldset table.content tbody tr:nth-child(even) { background-color:#d4dddfd4; }
body.white fieldset.macos.desktop>div.output>div.desktop>fieldset>div.output { background-color:#ffffff94; } body.white fieldset.macos.desktop>div.output>div.desktop>fieldset>div.output { background-color:#ffffff94; }

View File

@ -53,24 +53,23 @@ Volcanos(chat.ONIMPORT, {
if (can.ConfHeight() < 800) { item.top = 25, item.height = can.ConfHeight()-145, item.width = can.ConfWidth()-110 } if (can.ConfHeight() < 800) { item.top = 25, item.height = can.ConfHeight()-145, item.width = can.ConfWidth()-110 }
if (can.user.isMobile) { item.left = 0, item.top = 25, item.height = can.ConfHeight()-145, item.width = can.ConfWidth() } if (can.user.isMobile) { item.left = 0, item.top = 25, item.height = can.ConfHeight()-145, item.width = can.ConfWidth() }
can.onappend.plugin(can, item, function(sub) { can.ondetail.select(can, sub._target) can.onappend.plugin(can, item, function(sub) { can.ondetail.select(can, sub._target)
can.page.style(can, sub._target, html.MIN_WIDTH, 480)
var index = 0; can.core.Item({ var index = 0; can.core.Item({
"#f95f57": function(event) { sub.onaction.close(event, sub) }, "#f95f57": function(event) { sub.onaction.close(event, sub) },
"#fcbc2f": function(event) { "#fcbc2f": function(event) { var dock = can.page.Append(can, can.ui.dock._output, [{view: html.ITEM, list: [{view: html.ICON, list: [{img: can.misc.PathJoin(item.icon)}]}], onclick: function() {
var dock = can.page.Append(can, can.ui.dock._output, [{view: html.ITEM, list: [{view: html.ICON, list: [{img: can.misc.PathJoin(item.icon)}]}], onclick: function() {
can.onmotion.toggle(can, sub._target, true), can.page.Remove(can, dock) can.onmotion.toggle(can, sub._target, true), can.page.Remove(can, dock)
}}])._target; sub.onmotion.hidden(sub, sub._target) }}])._target; sub.onmotion.hidden(sub, sub._target) },
},
"#32c840": function(event) { sub.onaction.full(event, sub) }, "#32c840": function(event) { sub.onaction.full(event, sub) },
}, function(color, cb) { can.page.insertBefore(can, [{view: [[html.ITEM, html.BUTTON]], style: {"background-color": color, right: 10+20*index++}, onclick: cb}], sub._output) }) }, function(color, cb) { can.page.insertBefore(can, [{view: [[html.ITEM, html.BUTTON]], style: {"background-color": color, right: 10+20*index++}, onclick: cb}], sub._output) })
sub.onimport.size(sub, item.height, item.width, true), can.onmotion.move(can, sub._target, {"z-index": 10, top: item.top, left: item.left})
sub.onimport._open = function(sub, msg, arg) { can.onimport._window(can, {index: "web.chat.iframe", args: [arg]}) }
sub.onmotion.resize(can, sub._target, function(height, width) { sub.onimport.size(sub, height, width) }, 25)
sub.onexport.record = function(sub, value, key, item) { can.onimport._window(can, item) }
sub.onexport.actionHeight = function(sub) { return can.page.ClassList.has(can, sub._target, html.OUTPUT)? 0: html.ACTION_HEIGHT+20 },
sub.onexport.marginTop = function() { return 25 }
sub.onappend.desktop = function(item) { can.onimport._item(can, item) } sub.onappend.desktop = function(item) { can.onimport._item(can, item) }
sub.onappend.dock = function(item) { can.ui.dock.runAction(can.request(event, item), mdb.CREATE, [], function() { can.ui.dock.Update() }) } sub.onappend.dock = function(item) { can.ui.dock.runAction(can.request(event, item), mdb.CREATE, [], function() { can.ui.dock.Update() }) }
sub.onimport._open = function(sub, msg, arg) { can.onimport._window(can, {index: web.CHAT_IFRAME, args: [arg]}) }
sub.onexport.output = function() { if (item.index == "web.chat.macos.opens") { can.page.Remove(can, sub._target) } } sub.onexport.output = function() { if (item.index == "web.chat.macos.opens") { can.page.Remove(can, sub._target) } }
sub.onexport.record = function(sub, value, key, item) { can.onimport._window(can, item) }
sub.onexport.marginTop = function() { return 25 }
sub.onexport.actionHeight = function(sub) { return can.page.ClassList.has(can, sub._target, html.OUTPUT)? 0: html.ACTION_HEIGHT+20 },
sub.onmotion.resize(can, sub._target, function(height, width) { sub.onimport.size(sub, height, width) }, 25)
sub.onimport.size(sub, item.height, item.width, true), can.onmotion.move(can, sub._target, {"z-index": 10, top: item.top, left: item.left})
sub._target.onclick = function(event) { can.page.Select(can, sub._target.parentNode, html.FIELDSET, function(target) { can.page.style(can, target, "z-index", target == sub._target? "10": "9") }) } sub._target.onclick = function(event) { can.page.Select(can, sub._target.parentNode, html.FIELDSET, function(target) { can.page.style(can, target, "z-index", target == sub._target? "10": "9") }) }
}, can.ui.desktop) }, can.ui.desktop)
}, },

View File

@ -101,18 +101,18 @@ func init() {
Index.MergeCommands(ice.Commands{ Index.MergeCommands(ice.Commands{
GO: {Name: "go path auto", Help: "后端编程", Actions: ice.MergeActions(ice.Actions{ GO: {Name: "go path auto", Help: "后端编程", Actions: ice.MergeActions(ice.Actions{
mdb.RENDER: {Hand: func(m *ice.Message, arg ...string) { mdb.RENDER: {Hand: func(m *ice.Message, arg ...string) {
if arg[1] == "misc/xterm/iterm.go" { if arg[1] == "main.go" {
ProcessXterm(m, "ish", "", arg[1]) ProcessXterm(m, "ish", "", arg[1])
} else if arg[1] == "main.go" { } else if arg[1] == "misc/xterm/iterm.go" {
ProcessXterm(m, "ish", "", arg[1]) ProcessXterm(m, "ish", "", arg[1])
} else { } else {
ctx.ProcessCommand(m, yac.STACK, kit.Simple(path.Join(arg[2], arg[1]))) ctx.ProcessCommand(m, yac.STACK, kit.Simple(path.Join(arg[2], arg[1])))
} }
}}, }},
mdb.ENGINE: {Hand: func(m *ice.Message, arg ...string) { mdb.ENGINE: {Hand: func(m *ice.Message, arg ...string) {
if arg[1] == "misc/xterm/iterm.go" { if arg[1] == "main.go" {
ProcessXterm(m, "ish", "", arg[1]) ProcessXterm(m, "ish", "", arg[1])
} else if arg[1] == "main.go" { } else if arg[1] == "misc/xterm/iterm.go" {
ProcessXterm(m, "ish", "", arg[1]) ProcessXterm(m, "ish", "", arg[1])
} else if cmd := ctx.GetFileCmd(path.Join(arg[2], arg[1])); cmd != "" { } else if cmd := ctx.GetFileCmd(path.Join(arg[2], arg[1])); cmd != "" {
ctx.ProcessCommand(m, cmd, kit.Simple()) ctx.ProcessCommand(m, cmd, kit.Simple())

View File

@ -8,6 +8,7 @@ import (
"shylinux.com/x/icebergs/base/ctx" "shylinux.com/x/icebergs/base/ctx"
"shylinux.com/x/icebergs/base/mdb" "shylinux.com/x/icebergs/base/mdb"
"shylinux.com/x/icebergs/base/nfs" "shylinux.com/x/icebergs/base/nfs"
"shylinux.com/x/icebergs/base/web"
kit "shylinux.com/x/toolkits" kit "shylinux.com/x/toolkits"
) )
@ -29,12 +30,12 @@ func init() {
Index.MergeCommands(ice.Commands{ Index.MergeCommands(ice.Commands{
JS: {Name: "js path auto", Help: "前端", Actions: ice.MergeActions(ice.Actions{ JS: {Name: "js path auto", Help: "前端", Actions: ice.MergeActions(ice.Actions{
mdb.RENDER: {Hand: func(m *ice.Message, arg ...string) { mdb.RENDER: {Hand: func(m *ice.Message, arg ...string) {
_js_show(m, arg...)
return
if arg[1] == "main.js" { if arg[1] == "main.js" {
m.EchoIFrame(nfs.PS) ctx.ProcessCommand(m, "web.chat.iframe", kit.Simple(web.UserHost(m)))
return } else {
_js_show(m, arg...)
} }
return
ProcessXterm(m, "node", kit.Format(`require("./usr/volcanos/proto.js"), require("./usr/volcanos/publish/client/nodejs/proto.js"), Volcanos.meta._main("%s")`, path.Join(nfs.PS, arg[2], arg[1]))) ProcessXterm(m, "node", kit.Format(`require("./usr/volcanos/proto.js"), require("./usr/volcanos/publish/client/nodejs/proto.js"), Volcanos.meta._main("%s")`, path.Join(nfs.PS, arg[2], arg[1])))
}}, }},
mdb.ENGINE: {Hand: func(m *ice.Message, arg ...string) { mdb.ENGINE: {Hand: func(m *ice.Message, arg ...string) {

View File

@ -26,6 +26,7 @@ const (
BASH = "bash" BASH = "bash"
CONF = "conf" CONF = "conf"
VIM = "vim" VIM = "vim"
ISH = "ish"
) )
const SH = nfs.SH const SH = nfs.SH

View File

@ -32,7 +32,7 @@ func _xterm_get(m *ice.Message, h string) xterm.XTerm {
m.Go(func() { m.Go(func() {
defer term.Close() defer term.Close()
defer mdb.HashRemove(m, mdb.HASH, h) defer mdb.HashRemove(m, mdb.HASH, h)
// m.Log(cli.START, strings.Join(term.Args, lex.SP)) m.Log(cli.START, strings.Join(ls, lex.SP))
buf := make([]byte, ice.MOD_BUFS) buf := make([]byte, ice.MOD_BUFS)
for { for {
if n, e := term.Read(buf); !m.Warn(e) && e == nil { if n, e := term.Read(buf); !m.Warn(e) && e == nil {
@ -78,12 +78,12 @@ func init() {
return []string{ssh.SHELL, SH, "/bin/sh"} return []string{ssh.SHELL, SH, "/bin/sh"}
} }
}) })
mdb.IsSearchForEach(m, arg, func() []string { return []string{ssh.SHELL, "ice", "ish"} }) mdb.IsSearchForEach(m, arg, func() []string { return []string{ssh.SHELL, "ice", "/bin/ish"} })
}}, }},
mdb.INPUTS: {Hand: func(m *ice.Message, arg ...string) { mdb.INPUTS: {Hand: func(m *ice.Message, arg ...string) {
switch mdb.HashInputs(m, arg); arg[0] { switch mdb.HashInputs(m, arg); arg[0] {
case mdb.TYPE: case mdb.TYPE:
m.Push(arg[0], "ish", BASH, SH) m.Push(arg[0], ISH, SH)
m.Cmd(mdb.SEARCH, mdb.FOREACH, ssh.SHELL, ice.OptionFields("type,name,text"), func(value ice.Maps) { m.Cmd(mdb.SEARCH, mdb.FOREACH, ssh.SHELL, ice.OptionFields("type,name,text"), func(value ice.Maps) {
kit.If(value[mdb.TYPE] == ssh.SHELL, func() { m.Push(arg[0], value[mdb.TEXT]) }) kit.If(value[mdb.TYPE] == ssh.SHELL, func() { m.Push(arg[0], value[mdb.TEXT]) })
}) })
@ -126,7 +126,7 @@ func init() {
ctx.PROCESS: {Hand: func(m *ice.Message, arg ...string) { ctx.PROCESS: {Hand: func(m *ice.Message, arg ...string) {
ctx.ProcessField(m, m.PrefixKey(), func() string { return m.Cmdx("", mdb.CREATE, arg) }, arg...) ctx.ProcessField(m, m.PrefixKey(), func() string { return m.Cmdx("", mdb.CREATE, arg) }, arg...)
}}, }},
}, ctx.CmdAction(), ctx.ProcessAction(), web.DreamAction(), mdb.HashAction(mdb.FIELD, "time,hash,type,name,text,path,theme,daemon")), Hand: func(m *ice.Message, arg ...string) { }, ctx.CmdAction(), ctx.ProcessAction(), web.DreamAction(), mdb.ImportantHashAction(), mdb.HashAction(mdb.FIELD, "time,hash,type,name,text,path,theme,daemon")), Hand: func(m *ice.Message, arg ...string) {
if mdb.HashSelect(m, arg...); len(arg) == 0 { if mdb.HashSelect(m, arg...); len(arg) == 0 {
if m.Length() == 0 { if m.Length() == 0 {
m.Action(mdb.CREATE) m.Action(mdb.CREATE)

View File

@ -1,6 +1,7 @@
package xterm package xterm
import ( import (
"bytes"
"os" "os"
"strings" "strings"
"time" "time"
@ -25,6 +26,7 @@ type idata struct {
cmds []string cmds []string
list []string list []string
pos int pos int
pipe *os.File
} }
type iterm struct { type iterm struct {
m *ice.Message m *ice.Message
@ -35,7 +37,11 @@ type iterm struct {
func newiterm(m *ice.Message) (XTerm, error) { func newiterm(m *ice.Message) (XTerm, error) {
r, w, e := os.Pipe() r, w, e := os.Pipe()
return &iterm{m: m, r: r, w: w, idata: &idata{cmds: append(append(kit.SortedKey(ice.Info.Index), m.Cmd(ctx.COMMAND).Appendv(ctx.INDEX)...), m.Cmd(nfs.DIR, "/bin", mdb.NAME).Appendv(mdb.NAME)...)}}, e return &iterm{m: m, r: r, w: w, idata: &idata{cmds: kit.Simple(
kit.SortedKey(ice.Info.Index),
m.Cmd(ctx.COMMAND).Appendv(ctx.INDEX),
m.Cmd(nfs.DIR, "/bin", mdb.NAME).Appendv(mdb.NAME),
)}}, e
} }
func (s iterm) Setsize(rows, cols string) error { func (s iterm) Setsize(rows, cols string) error {
s.w.Write([]byte(s.prompt())) s.w.Write([]byte(s.prompt()))
@ -43,6 +49,9 @@ func (s iterm) Setsize(rows, cols string) error {
} }
func (s iterm) Writeln(data string, arg ...ice.Any) { s.Write(kit.Format(data, arg...) + lex.NL) } func (s iterm) Writeln(data string, arg ...ice.Any) { s.Write(kit.Format(data, arg...) + lex.NL) }
func (s iterm) Write(data string) (int, error) { func (s iterm) Write(data string) (int, error) {
if s.pipe != nil {
return s.pipe.Write([]byte(data))
}
res, ctrl := "", "" res, ctrl := "", ""
for _, c := range data { for _, c := range data {
switch c := string(c); c { switch c := string(c); c {
@ -57,6 +66,8 @@ func (s iterm) Write(data string) (int, error) {
if len(s.app) > 0 { if len(s.app) > 0 {
s.app = s.app[1:] s.app = s.app[1:]
res = s.rest(res) res = s.rest(res)
} else {
s.w.Close()
} }
case ENQ: // Ctrl+E case ENQ: // Ctrl+E
res += s.repeat(s.app, ESC_C) res += s.repeat(s.app, ESC_C)
@ -68,7 +79,7 @@ func (s iterm) Write(data string) (int, error) {
case BS: // Ctrl+H case BS: // Ctrl+H
res = s.dels(res) res = s.dels(res)
case NL: // Ctrl+J case NL: // Ctrl+J
res = s.exec(res) res = s.exec(s.m, res)
case VT: // Ctrl+K case VT: // Ctrl+K
if len(s.app) > 0 { if len(s.app) > 0 {
s.cut, s.app = s.app, "" s.cut, s.app = s.app, ""
@ -77,16 +88,15 @@ func (s iterm) Write(data string) (int, error) {
case NP: // Ctrl+L case NP: // Ctrl+L
res = s.rest(res + ESC_H + ESC_2J + s.prompt() + s.arg) res = s.rest(res + ESC_H + ESC_2J + s.prompt() + s.arg)
case CR: // Ctrl+M case CR: // Ctrl+M
res = s.exec(res) res = s.exec(s.m, res)
case SO: // Ctrl+N case SO: // Ctrl+N
res = s.hist(res, 1) res = s.hist(res, 1)
case SI: // Ctrl+O case SI: // Ctrl+O
if s.arg == "" { if s.arg == "" {
s.arg = kit.Select("", s.list, -1) s.arg = kit.Select("", s.list, -1)
res += s.arg res = s.exec(s.m, res+s.arg)
res = s.exec(res)
} else { } else {
res = s.exec(res) res = s.exec(s.m, res)
} }
case DLE: // Ctrl+P case DLE: // Ctrl+P
res = s.hist(res, -1) res = s.hist(res, -1)
@ -212,14 +222,25 @@ func (s iterm) dels(res string) string {
return res return res
} }
func (s iterm) tips(arg string) (tip string) { func (s iterm) tips(arg string) (tip string) {
if kit.HasSuffix(s.arg+s.app, lex.SP, nfs.PS) { if kit.HasSuffix(arg, lex.SP, nfs.PS) {
s.args = s.m.CmdList(kit.Split(s.arg + s.app)...) s.args = s.m.CmdList(kit.Split(arg)...)
kit.For(s.args, func(i int, v string) { s.args[i] = strings.TrimPrefix(v, kit.Select("", kit.Split(arg), -1)) })
s.due = CRNL + ESC_K + strings.Join(s.args, lex.SP) s.due = CRNL + ESC_K + strings.Join(s.args, lex.SP)
} else if len(s.args) > 0 { } else if len(s.args) > 0 {
args, key := []string{}, kit.Select("", kit.Split(s.arg+s.app), -1) args, key := []string{}, kit.Select("", kit.Split(arg, "\t ./"), -1)
kit.If(kit.HasSuffix(arg, ".", "/"), func() { key = "" })
kit.For(s.args, func(arg string) { kit.If(strings.HasPrefix(arg, key), func() { args = append(args, arg) }) }) kit.For(s.args, func(arg string) { kit.If(strings.HasPrefix(arg, key), func() { args = append(args, arg) }) })
s.due = CRNL + ESC_K + strings.Join(args, lex.SP) s.due = CRNL + ESC_K + strings.Join(args, lex.SP)
return strings.TrimPrefix(kit.Select("", args, 0), key) return strings.TrimPrefix(kit.Select("", args, 0), key)
} else {
s.due = ""
}
if strings.HasSuffix(arg, nfs.PT) {
s.args = nil
kit.For(s.cmds, func(cmd string) {
kit.If(strings.HasPrefix(cmd, arg), func() { s.args = append(s.args, strings.TrimPrefix(cmd, arg)) })
})
s.due = CRNL + ESC_K + strings.Join(s.args, lex.SP)
} }
for i := len(s.list) - 1; i >= 0; i-- { for i := len(s.list) - 1; i >= 0; i-- {
if v := s.list[i]; strings.HasPrefix(v, arg) { if v := s.list[i]; strings.HasPrefix(v, arg) {
@ -237,21 +258,29 @@ func (s iterm) rest(res string) string {
s.tip = s.tips(s.arg + s.app) s.tip = s.tips(s.arg + s.app)
return res + ESC_K + ESC_s + s.app + s.style("2", s.tip+s.due) + ESC_u return res + ESC_K + ESC_s + s.app + s.style("2", s.tip+s.due) + ESC_u
} }
func (s iterm) exec(res string) string { func (s iterm) exec(m *ice.Message, res string) string {
res += CRNL defer func() { s.arg, s.app, s.args, s.pipe = "", "", nil, nil }()
arg := kit.Split(s.arg + s.app) arg := kit.Split(s.arg + s.app)
if len(arg) == 0 { if len(arg) == 0 {
return res + s.prompt() return CRNL + s.prompt()
} else if len(s.list) == 0 || s.arg+s.app != s.list[len(s.list)-1] { } else if len(s.list) == 0 || s.arg+s.app != s.list[len(s.list)-1] {
s.list = append(s.list, s.arg+s.app) s.list, s.pos = append(s.list, s.arg+s.app), len(s.list)+1
s.pos = len(s.list)
} }
defer func() { s.arg, s.app, s.due, s.args = "", "", "", []string{} }() msg, end := m.Cmd(arg, kit.Dict(ice.MSG_USERUA, "ish")), false
msg := s.m.Cmd(arg, kit.Dict(ice.MSG_USERUA, "ish")) if res += CRNL + ESC_K; msg.IsErrNotFound() {
kit.If(msg.IsErrNotFound(), func() { msg = s.m.Cmd(cli.SYSTEM, arg) }) s.w.Write([]byte(res))
r, w, _ := os.Pipe()
res, s.pipe = "", w
m.Cmd(cli.SYSTEM, arg, kit.Dict(cli.CMD_INPUT, r, cli.CMD_OUTPUT, nfs.Pipe(m, func(buf []byte) {
s.w.Write(bytes.ReplaceAll(buf, []byte(lex.NL), []byte(CRNL)))
end = bytes.HasSuffix(buf, []byte(lex.NL))
}), cli.CMD_ENV, kit.EnvList("TERM", "xterm", "LINES", m.Option("rows"), "COLUMNS", m.Option("cols"), "SHELL", "/bin/ish", "USER", m.Option(ice.MSG_USERNAME))))
} else {
kit.If(msg.Result() == "", func() { msg.TableEcho() }) kit.If(msg.Result() == "", func() { msg.TableEcho() })
res += ESC_K + strings.ReplaceAll(msg.Result(), lex.NL, CRNL) res += strings.ReplaceAll(msg.Result(), lex.NL, CRNL)
kit.If(!strings.HasSuffix(res, CRNL), func() { res += CRNL }) end = strings.HasSuffix(res, lex.NL)
}
kit.If(!end, func() { res += CRNL })
return res + s.prompt() return res + s.prompt()
} }
func (s iterm) hist(res string, skip int) string { func (s iterm) hist(res string, skip int) string {
@ -301,8 +330,8 @@ const (
SUB = "\032" // Ctrl+Z SUB = "\032" // Ctrl+Z
ESC = "\033" // Ctrl+[ ESC = "\033" // Ctrl+[
ESC_C = "\033[C" ESC_C = "\033[C"
ESC_K = "\033[K"
ESC_H = "\033[H" ESC_H = "\033[H"
ESC_K = "\033[K"
ESC_2J = "\033[2J" ESC_2J = "\033[2J"
ESC_s = "\033[s" ESC_s = "\033[s"
ESC_u = "\033[u" ESC_u = "\033[u"

View File

@ -3,6 +3,7 @@ package xterm
import ( import (
"os" "os"
"os/exec" "os/exec"
"path"
ice "shylinux.com/x/icebergs" ice "shylinux.com/x/icebergs"
"shylinux.com/x/icebergs/base/lex" "shylinux.com/x/icebergs/base/lex"
@ -38,7 +39,7 @@ func (s xterm) Read(buf []byte) (int, error) { return s.File.Read(buf) }
func (s xterm) Close() error { return s.Cmd.Process.Kill() } func (s xterm) Close() error { return s.Cmd.Process.Kill() }
func Command(m *ice.Message, dir string, cli string, arg ...string) (XTerm, error) { func Command(m *ice.Message, dir string, cli string, arg ...string) (XTerm, error) {
if cli == "ish" { if path.Base(cli) == "ish" {
return newiterm(m) return newiterm(m)
} }
cmd := exec.Command(cli, arg...) cmd := exec.Command(cli, arg...)