diff -urN v0/argmnt.py v1/argmnt.py --- v0/argmnt.py 1970-01-01 09:00:00.000000000 +0900 +++ v1/argmnt.py 2018-10-12 22:26:51.000000000 +0900 @@ -0,0 +1,430 @@ +#!/usr/bin/env python + +import sys +import six +import nkf +import yaml +import opt +import term +import key +import rect +import txbuf +import txview +#import dbg + +frm_info = { + '': ' ', + 'a': '+-|', + 'b': ' -|', + 'c': '===', +} + +class Empty: + pass + +def get_tx_rect(o): + return (o.x, o.y, o.w, o.h) + +def get_frm_rect(o): + r = get_tx_rect(o) + + (bx, by) = o.frm.bdr + + ha = o.frm.ha + (x, y, w, h) = r + if ha == 'l': + r = (x, y, w+bx, h) + elif ha == 'r': + r = (x-bx, y, w+bx, h) + else: # c + r = (x-int(bx/2), y, w+bx, h) + + va = o.frm.va + (x, y, w, h) = r + if va == 'u': + r = (x, y, w, h+by) + elif va == 'd': + r = (x, y-by, w, h+by) + else: # c + r = (x, y-int(by/2), w, h+by) + + (x, y, w, h) = r + r = (x-1, y-1, w+2, h+2) + return r + +def p_on_frm(p, o): + rects = rect.frame_rects( get_frm_rect(o) ) + for r in rects: + if rect.in_rect(p, r): + return True + return False + +def align_str(s, w, a): + m = w - nkf.str_width(s) + if a == 'l': + s += ' ' * m + elif a == 'r': + s = ' ' * m + s + else: # c + m2 = int(m/2) + s = ' ' * m2 + s + ' ' * (m-m2) + return s + +def draw_frm_buf_atts(o, buf, atts): + cs = frm_info.get( o.frm.kind ) + (x, y, w, h) = get_frm_rect(o) + + s = cs[0] + cs[1]*(w-2) + cs[0] + buf.set(x, y, s, atts) + buf.set(x, y+h-1, s, atts) + for i in range(1,h-1): + buf.set(x, y+i, cs[2], atts) + buf.set(x+w-1, y+i, cs[2], atts) + +def draw_o_buf_atts(o, buf, atts, do_frm, frm_atts): + for i in range(o.h): + s = align_str( o.s[i], o.w, o.a ) + buf.set( o.x, o.y+i, s, atts ) + if do_frm: + draw_frm_buf_atts(o, buf, frm_atts) + +def new(lst, nkf_opt): + e = Empty() + + rects = list( map( get_frm_rect, lst ) ) + r = rect.rects_contain_rect(rects) + view = txview.new(r, nkf_opt) + + (x, y, w, h) = view.get_rect() + e.cur = (x, y) + + def frm_visible(o): + if o.frm.kind != '': + return True + if o != e.sel: + return False + return e.mode in ('sel_2', 'sel_frm') + + def get_o_rect(o): + return get_frm_rect(o) if frm_visible(o) else get_tx_rect(o) + + def get_atts(o, buf): + if o == e.sel: + if e.mode in ('sel', 'move', 'frm_align'): + return [ buf.rev ] + elif e.mode in ('sel_align'): + return [ buf.uline ] + return [] + + def get_frm_atts(o, buf): + if o == e.sel: + if e.mode in ('sel', 'move', 'sel_3', 'resize'): + return [ buf.rev ] + if e.mode in ('sel_2', 'sel_frm'): + return [ buf.uline ] + return [] + + def draw_o_buf(o, buf): + atts = get_atts(o, buf) + do_frm = frm_visible(o) + frm_atts = get_frm_atts(o, buf) + draw_o_buf_atts(o, buf, atts, do_frm, frm_atts) + + def draw_r(r): + buf = txbuf.new(r) + + for o in lst: + ro = get_o_rect(o) + if rect.is_area( rect.op_and(ro, r) ): + draw_o_buf(o, buf) + + # cursor + if e.mode == 'normal': + if rect.in_rect(e.cur, r): + (x, y) = e.cur + buf.set(x, y, '+') + buf.adjust() + + (rx, ry, rw, rh) = r + for i in range(rh): + y = ry + i + (x, s) = buf.get_line(y) + view.out(x, y, s) + + def draw_r2(bak, r): + sc = view.scroll(r) + + vr = view.get_rect() + rects = [ vr ] + + if not sc: + bak = rect.op_and(bak, vr) + is_bak = rect.is_area(bak) + + r = rect.op_and(r, vr) + is_r = rect.is_area(r) + + if is_bak and is_r: + rects = rect.gather(bak, r) + elif is_bak: + rects = [ bak ] + elif is_r: + rects = [ r ] + else: + return + for r in rects: + draw_r(r) + + def draw_o(o): + draw_r( get_o_rect(o) ) + + def move_cur(dx, dy): + bak = rect.p_wh_to( e.cur, (1, 1) ) + e.cur = rect.p_add( e.cur, (dx, dy) ) + r = rect.p_wh_to( e.cur, (1, 1) ) + draw_r2(bak, r) + + def get_o_under_cursor(): + for o in lst: + if rect.in_rect( e.cur, get_o_rect(o) ): + return o + return None + + def set_mode(mode): + if mode == 'sel': + e.sel = get_o_under_cursor() + o = e.sel + bak = get_o_rect(o) if o else None + + e.mode = mode + if mode == 'normal': + e.sel = None + + if o and bak: + draw_r2( bak, get_o_rect(o) ) + + def normal(k): + d = key.allow(k) + (dx, dy) = key.dxy( k, [0, 0] ) + + (x, y, w, h) = view.get_rect() + (cx, cy) = e.cur + if d == 'pu': + dy = y - cy + elif d == 'pd': + dy = y+h-1 - cy + elif d == 'pl': + dx = x - cx + elif d == 'pr': + dx = x+w-1 - cx + + if dx != 0 or dy != 0: + move_cur(dx, dy) + + def resize(k): + o = e.sel + bak = get_o_rect(o) + (bx, by) = o.frm.bdr + d = key.allow(k) + if d == 'u' and by > 0: + by -= 1 + elif d == 'd': + by += 1 + elif d == 'l' and bx > 0: + bx -= 1 + elif d == 'r': + bx += 1 + if (bx, by) != o.frm.bdr: + o.frm.bdr = [bx, by] # list + draw_r2( bak, get_o_rect(o) ) + + def sel_align(k): + o = e.sel + lst = ['l', 'c', 'r'] + o.a = lst[ ( lst.index(o.a) + 1 ) % len(lst) ] + draw_o(o) + + def move(k): + o = e.sel + (dx, dy) = key.dxy( k, [0, 0] ) + bak = get_o_rect(o) + (o.x, o.y) = (o.x + dx, o.y + dy) + draw_r2( bak, get_o_rect(o) ) + move_cur(dx, dy) # hidden + + def sel_frm(k): + o = e.sel + lst = sorted( frm_info.keys() ) + o.frm.kind = lst[ ( lst.index(o.frm.kind) + 1 ) % len(lst) ] + draw_o(o) + + def frm_align(k): + o = e.sel + bak = get_o_rect(o) + d = key.allow(k) + if d in 'ud': + lst = ['u', 'c', 'd'] + o.frm.va = lst[ ( lst.index(o.frm.va) + 1 ) % len(lst) ] + draw_r2( bak, get_o_rect(o) ) + elif d in 'lr': + lst = ['l', 'c', 'r'] + o.frm.ha = lst[ ( lst.index(o.frm.ha) + 1 ) % len(lst) ] + draw_r2( bak, get_o_rect(o) ) + + def has_bdr(k): + (bx, by) = e.sel.frm.bdr + return bx > 0 or by > 0 + + hdl = { + 'normal': { + 'ok': { + 'f': ( lambda k: get_o_under_cursor() != None ), + True: 'sel' }, + 'allow': normal }, + 'sel': { 'ok': 'sel_2', 'allow': [ 'move', move ] }, + 'move': { 'ok': 'normal', 'allow': move }, + 'sel_2': { + 'ok': { + 'f': ( lambda k: p_on_frm(e.cur, e.sel) ), + True: 'sel_3', + False: { + 'f': ( lambda k: len(e.sel.s) > 1 ), + True: 'sel_align', + False: 'normal' } + }, + 'allow': [ 'sel_frm', sel_frm ] }, + 'sel_frm': { 'ok': 'normal', 'allow': sel_frm }, + 'sel_align': { 'ok': 'normal', 'allow': sel_align }, + 'sel_3': { + 'ok': { 'f': has_bdr, True: 'frm_align', False: 'normal' }, + 'allow': [ 'resize', resize ] }, + 'resize': { 'ok': 'normal', 'allow': resize }, + 'frm_align': { 'ok': 'normal', 'allow': frm_align }, + } + + def call(o, k): + if callable(o): + return o(k) + if type(o) == str: + return set_mode(o) + if type(o) == list: + for e in o: + ret = call(e, k) + return ret + if type(o) == dict: + if 'f' in o: + ret = call( o.get('f'), k ) + return call( o.get(ret), k ) + if key.is_ok(k): + return call( o.get('ok'), k ) + if key.allow(k) in 'udlr': + return call( o.get('allow'), k ) + return None + + def get_key(klst): + key.init() + while True: + k = key.get() + if k in klst: + break + o = hdl.get(e.mode) + call(o, k) + key.fini() + e.get_key = get_key + + e.fini = lambda : view.fini() + + e.mode = 'normal' + e.sel = None + + draw_r(r) + + return e + +def to_empty(d): + e = Empty() + for (k, v) in d.items(): + setattr(e, k, v) + return e + +def cvt_data_load(o, y): + if isinstance(o, six.string_types): + o = o.strip().split('\n') + if type(o) == list: + o = { 's': o } + # dict + if 'xy' not in o: + o['xy'] = [ 1, y+1 ] + y += len( o.get('s') ) + 1 + if 'a' not in o: + o['a'] = 'l' + if 'frm' not in o: + o['frm'] = { 'kind': '', 'va': 'c', 'ha': 'c', 'bdr': [ 0, 0 ] } + + e = to_empty(o) + if type(e.s) != list: + e.s = [ e.s ] + (e.x, e.y) = e.xy + e.w = max( map( nkf.str_width, e.s ) ) + e.h = len(e.s) + e.frm = to_empty(e.frm) + + return (e, y) + +def cvt_data_save(e): + if len(e.s) == 1: + e.s = e.s[0] + d = {} + d['s'] = e.s + d['xy'] = [ e.x, e.y ] + d['a'] = e.a + + frm = e.frm + f = {} + f['kind'] = frm.kind + f['va'] = frm.va + f['ha'] = frm.ha + f['bdr'] = frm.bdr + + d['frm'] = f + return d + +def txt_out(lst, nkf_opt): + rects = list( map( get_frm_rect, lst ) ) + r = rect.rects_contain_rect(rects) + buf = txbuf.new(r) + for o in lst: + draw_o_buf_atts(o, buf, [], o.frm.kind, []) + + s = nkf.enc( buf.txt_out() ) + if nkf_opt != '-u': + s = nkf.cvt(s, nkf_opt) + nkf.put_stdout(s + '\n') + +if __name__ == "__main__": + (s, nkf_opt) = nkf.to_str( nkf.get_stdin() ) + lst = yaml.load(s) + + y = 0 + (bak, lst) = (lst, []) + for o in bak: + (o, y) = cvt_data_load(o, y) + lst.append(o) + + o = opt.get('-o', '') + if not o: + o = nkf_opt + + if opt.index('-txt') >= 0: + txt_out(lst, o) + sys.exit(0) + + # edit + a = new(lst, o) + a.get_key( ['q'] ) + a.fini() + + lst = list( map( cvt_data_save, lst ) ) + u8 = yaml.dump( lst, default_flow_style=False, allow_unicode=True, encoding='utf-8' ) + nkf.put_stdout( nkf.cvt(u8, nkf_opt) if nkf_opt != '-u' else u8 ) +# EOF diff -urN v0/key.py v1/key.py --- v0/key.py 2018-10-04 22:37:00.000000000 +0900 +++ v1/key.py 2018-10-11 23:32:50.000000000 +0900 @@ -9,8 +9,10 @@ def init(): term.stty('echo', False) term.stty('icanon', False) + term.cursor_show(False) def fini(): + term.cursor_show(True) term.stty('icanon', True) term.stty('echo', True) @@ -27,14 +29,23 @@ [ ctl('p'), ctl('n'), ctl('b'), ctl('f') ], # emacs [ 'k', 'j', 'h', 'l' ], # vi ],[ - [ 'pu', 'pd' ], - [ esc('5~'), esc('6~') ], # allow - [ chr(0xf6), ctl('v') ], # emacs - [ 'b', ' ' ], # less + [ 'pu', 'pd', 'pl', 'pr' ], + [ esc('5~'), esc('6~'), esc('8~'), esc('7~') ], # allow + [ chr(0xf6), ctl('v'), ctl('a'), ctl('e') ], # emacs + [ 'b', ' ', '^', '$' ], # less, vi ]] for tbl in tbls: for lst in tbl[1:]: if k in lst: return tbl[0][ lst.index(k) ] return '' + +def dxy(k, def_val=None): + d = allow(k) + dic = { 'l': (-1,0), 'r': (1,0), 'u': (0,-1), 'd': (0,1) } + return dic.get(d, def_val) + +is_ok = lambda k: k in ('\n', ' ') +is_cancel = lambda k: k in ( chr(0x1b) ) + # EOF diff -urN v0/rect.py v1/rect.py --- v0/rect.py 1970-01-01 09:00:00.000000000 +0900 +++ v1/rect.py 2018-10-12 22:19:16.000000000 +0900 @@ -0,0 +1,116 @@ +#!/usr/bin/env python + +def p_add(pa, pb): + (xa, ya) = pa + (xb, yb) = pb + return (xa + xb, ya + yb) + +def p_sub(pa, pb): + (xa, ya) = pa + (xb, yb) = pb + return (xa - xb, ya - yb) + +def to_p_wh(r): + (x, y, w, h) = r + return ( (x, y), (w, h) ) + +def p_wh_to(p, wh): + ( (x, y), (w, h) ) = (p, wh) + return (x, y, w, h) + +def to_p2(r): + (x, y, w, h) = r + p = (x, y) + wh = (w, h) + return ( p, p_add(p, wh) ) + +def p2_to(p, p2): + (x, y) = p + (w, h) = p_sub(p2, p) + return (x, y, w, h) + +def op_and(ra, rb): + ( (ax, ay), (ax2, ay2) ) = to_p2(ra) + ( (bx, by), (bx2, by2) ) = to_p2(rb) + p = ( max(ax, bx), max(ay, by) ) + p2 = ( min(ax2, bx2), min(ay2, by2) ) + return p2_to(p, p2) + +def is_area(r): + (x, y, w, h) = r + return w > 0 and h > 0 + +def in_rect(p, r): + (px, py) = p + ( (x, y), (x2, y2) ) = to_p2(r) + return x <= px and px < x2 and y <= py and py < y2 + +def r_in_rect(ri, ro): + (p, p2) = to_p2(ri) + p2 = p_sub( p2, (1, 1) ) + return in_rect(p, ro) and in_rect(p2, ro) + +def frame_rects(r): + (x, y, w, h) = r + return [ + (x, y, w, 1), + (x, y+h-1, w, 1), + (x, y+1, 1, h-2), + (x+w-1, y+1, 1, h-2), + ] + +def rects_contain_rect(rects): + rx = None + for r in rects: + ( (x, y), (x2, y2) ) = to_p2(r) + if rx == None: + (rx, ry, rx2, ry2) = (x, y, x2, y2) + else: + (rx, ry, rx2, ry2) = ( min(rx, x), min(ry, y), max(rx2, x2), max(ry2, y2) ) + return p2_to( (rx, ry), (rx2, ry2) ) + +def cvt_fxy(fxy, r): + (p, p2) = to_p2(r) + return p2_to( fxy(*p), fxy(*p2) ) + +def gather(ra, rb): # return rects + (x, y, w, h) = r = op_and(ra, rb) + if w < 0 or h < h: # ! don't use is_area() + return [ ra, rb ] + if r == ra: + return [ rb ] + if r == rb: + return [ ra ] + + ( (ax, ay), (ax2, ay2) ) = to_p2(ra) + ( (bx, by), (bx2, by2) ) = to_p2(rb) + if ax == bx and ax2 == bx2: + y = min(ay, by) + y2 = max(ay2, by2) + return [ p2_to( (ax, y), (ax2, y2) ) ] + if ay == by and ay2 == by2: + x = min(ax, bx) + x2 = max(ax2, bx2) + return [ p2_to( (x, ay), (x2, ay2) ) ] + + rects = [] + y = min(ay, by) + h = max(ay2, by2) - y + for dy in range(h): + y_ = y + dy + in_a = ( ay <= y_ and y_ < ay2 ) + in_b = ( by <= y_ and y_ < by2 ) + if in_a and in_b: + (x, x2) = ( min(ax, bx), max(ax2, bx2) ) + elif in_a: + (x, x2) = (ax, ax2) + else: + (x, x2) = (bx, bx2) + + if not rects or ( rects[-1][0] != x or rects[-1][0] + rects[-1][2] != x2 ): + rects += [ (x, y_, x2-x, 1) ] + else: + (x, y, w, h) = rects[-1] + rects[-1] = (x, y, w, h+1) + return rects +# EOF diff -urN v0/term.py v1/term.py --- v0/term.py 2018-10-04 22:37:00.000000000 +0900 +++ v1/term.py 2018-10-10 22:08:59.000000000 +0900 @@ -39,12 +39,14 @@ tty_w.flush() esc = lambda s: chr(0x1b) + '[' + s +esc_ex = lambda d, v: esc( '?{}{}'.format(d, 'h' if v else 'l' ) ) loc = lambda x, y: out( esc( '{};{}H'.format(y+1, x+1) ) ) rev = lambda : out( esc('7m') ) uline = lambda : out( esc('4m') ) reset = lambda : out( esc('0m') ) cls = lambda : out( esc('2J') ) +cursor_show = lambda v: out( esc_ex(25, v) ) def scroll(m): am = abs(m) diff -urN v0/txbuf.py v1/txbuf.py --- v0/txbuf.py 1970-01-01 09:00:00.000000000 +0900 +++ v1/txbuf.py 2018-10-12 22:19:16.000000000 +0900 @@ -0,0 +1,110 @@ +import term +import rect + +class Empty: + pass + +is_k1 = lambda c: ord(c) >= 0x80 +k2 = chr(0) + +def adj_line(s): + for i in range( len(s)-1 ): + (c0, c1) = s[i:i+2] + if is_k1(c0) and c1 != k2: + s = s[:i] + ' ' + s[i+1:] + if not is_k1(c0) and c1 == k2: + s = s[:i+1] + ' ' + s[i+2:] + if s[0] == k2: + s = ' ' + s[1:] + if is_k1( s[-1] ): + s = s[:-1] + ' ' + return s + +def new(r): + e = Empty() + (rx, ry, rw, rh) = r + arr_r = (rx-1, ry, rw+2, rh) + + e.rev = term.esc('7m') + e.uline = term.esc('4m') + e.reset = term.esc('0m') + + e.arr = list( map( lambda i: ' ' * (rw+2), range(rh) ) ) + e.att = list( map( lambda i: [[]] * (rw+2), range(rh) ) ) + + to_idx = lambda x, y: ( x - rx + 1, y - ry ) + idx_to = lambda x, y: ( x - 1 + rx, y + ry ) + + def set(x, y, s, atts=[]): + s = ''.join( map( lambda c: c+k2 if is_k1(c) else c, s ) ) + + (bx, by) = x, y + r = (x, y, len(s), 1) + + (x, y, w, h) = r = rect.op_and(r, arr_r) + if not rect.is_area(r): + return + + if x > bx: + s = s[ x-bx : ] + if w < len(s): + s = s[ : w-len(s) ] + + (ix, iy) = to_idx(x, y) + l = e.arr[iy] + e.arr[iy] = l[:ix] + s + l[ix+w:] + + e.att[iy][ix:ix+w] = [atts] * w + e.set = set + + def adjust(): + e.arr = list( map( adj_line, e.arr ) ) + e.adjust = adjust + + def get_line(y): # return (x, s) + (ix, iy) = to_idx(rx, y) + s = e.arr[iy] + + x = rx + (c0, c1) = s[:2] + if is_k1(c0) and c1 == k2: + x -= 1 + else: + s = s[1:] + + (c0, c1) = s[-2:] + if is_k1(c0) and c1 == k2: + pass + else: + s = s[:-1] + + ix -= rx - x + bak = [] + mix = '' + for i in range( len(s) ): + a = e.att[iy][ix+i] + if a != bak: + if bak or a == []: + mix += e.reset + mix += ''.join(a) + bak = a + mix += s[i] + if bak: + mix += e.reset + + mix = ''.join( map( lambda c: '' if c == k2 else c, mix ) ) + return (x, mix) + e.get_line = get_line + + def txt_out(): # for all area + ss = [] + for i in range(rh): + (_, s) = get_line(ry+i) + while s[-1:] in (' ', '\t'): + s = s[:-1] + ss += [s] + return '\n'.join(ss) + e.txt_out = txt_out + + return e +# EOF diff -urN v0/txview.py v1/txview.py --- v0/txview.py 1970-01-01 09:00:00.000000000 +0900 +++ v1/txview.py 2018-10-12 22:19:16.000000000 +0900 @@ -0,0 +1,83 @@ +import nkf +import term +import rect + +def sc_sub(vp, vp2, t_l, p, l, sc): + if vp < 0: + n = -vp + if p > 0: + m = min(p, n) + p -= m + l += m + sc -= m + n -= m + if n > 0 and p + l < t_l: + m = min( t_l - (p + l), n ) + l += m + sc -= m + n -= m + sc -= n + if l < vp2: + n = vp2 - l + if p + l < t_l: + m = min( t_l - (p + l), n ) + l += m + n -= m + if n > 0 and p > 0: + m = min(p, n) + p -= m + l += m + n -= m + sc += n + return (p, l, sc) + +class Empty: + pass + +def new(r, nkf_opt): + e = Empty() + (t_w, t_h) = term.get_sz() + (e.x, e.y) = term.get_pos() + + (rx, ry, rw, rh) = r + + (e.sx, e.sy) = (rx, ry) + (e.w, e.h) = ( min(rw, t_w), min(rh, t_h) ) + + sc = max( e.h - (t_h - e.y), 0 ) + if sc: + term.scroll(sc) + e.y -= sc + + to_view = lambda x, y: (x - e.sx, y - e.sy) + view_to = lambda x, y: (x + e.sx, y + e.sy) + + def out(x, y, s): + s = nkf.enc(s) + if nkf_opt != '-u': + s = nkf.cvt(s, nkf_opt) + (x, y) = to_view(x, y) + term.loc( x + e.x, y + e.y ) + term.out(s) + e.out = out + + def scroll(r): + vr = rect.cvt_fxy( to_view, r ) + if rect.r_in_rect( vr, (0, 0, e.w, e.h) ): + return False + + (x, y, w, h) = r + ( (vx, vy), (vx2, vy2) ) = rect.to_p2(vr) + + (e.x, e.w, e.sx) = sc_sub(vx, vx2, t_w, e.x, e.w, e.sx) + (e.y, e.h, e.sy) = sc_sub(vy, vy2, t_h, e.y, e.h, e.sy) + + return True + e.scroll = scroll + + e.get_rect = lambda : rect.cvt_fxy( view_to, (0, 0, e.w, e.h) ) + + e.fini = lambda : term.loc( 0, e.y + e.h ) + + return e +# EOF