#include #include #include #include #include #include #include "buffer.h" #include "manager.h" #include "throw.h" using namespace std; Buffer::Buffer(Manager *manager) :manager(manager){} void Buffer::receive(const Command &cmd){ //TODO: optimise the renewLayout's in this function if(cmd[0]=="open_file"){ const string &fname=cmd[1]; filename=fname; ifstream file(fname); if(!file){ manager->receive({"error","Cannot open file '"+fname+"'"}); return; } tb.read(file); } else if(cmd[0]=="insert_char"){ char c=cmd[1][0]; if(c=='\n'){ assert(false); } else { tb.insert(cursor.line,cursor.x,cmd[1][0]); cursor.x++; } renewLayout(screen[0].line,screen[0].part,lastWidth,lastHeight); } else if(cmd[0]=="insert_newline"){ tb.insert(cursor.line,cursor.x,'\n'); cursor.x=0; cursor.line++; renewLayout(screen[0].line,screen[0].part,lastWidth,lastHeight); } else if(cmd[0]=="delete_backward"){ if(cursor.x==0&&cursor.line==0){ bel(); } else { if(cursor.x>0){ cursor.x--; tb.erase(cursor.line,cursor.x); } else { cursor.line--; cursor.x=tb.lineLen(cursor.line); tb.erase(cursor.line,cursor.x); } renewLayout(screen[0].line,screen[0].part,lastWidth,lastHeight); } } else if(cmd[0]=="delete_forward"){ if(cursor.line==tb.numLines()-1&&cursor.x==tb.lineLen(cursor.line)){ bel(); } else { tb.erase(cursor.line,cursor.x); renewLayout(screen[0].line,screen[0].part,lastWidth,lastHeight); } } else if(cmd[0]=="move_forward"){ if(cursor.line==tb.numLines()-1&&cursor.x==tb.lineLen(cursor.line)){ bel(); } else { if(cursor.x==tb.lineLen(cursor.line)){ cursor.x=0; cursor.line++; } else { cursor.x++; } renewLayout(screen[0].line,screen[0].part,lastWidth,lastHeight); } } else if(cmd[0]=="move_backward"){ if(cursor.line==0&&cursor.x==0){ bel(); } else { if(cursor.x==0){ cursor.line--; cursor.x=tb.lineLen(cursor.line); } else { cursor.x--; } renewLayout(screen[0].line,screen[0].part,lastWidth,lastHeight); } } else { THROW("Unknown command"); } //cerr<<"New cursor is (line="<=32&&c<127)return {c}; return {'\\','x', "0123456789abcdef"[(unsigned char)c/16], "0123456789abcdef"[(unsigned char)c%16]}; } vector Buffer::layoutLine(const string &line,i64 linenum,i64 width){ assert(width>=4); if(line.size()==0){ return {{linenum,0,0,{}}}; } vector layout; vector cur; i64 x=0; i64 linepart=0,fromx=0; for(i64 i=0;i<(i64)line.size();i++){ char c=line[i]; string repr; bool special=false; if(c=='\t'){ repr=string(4-cur.size()%4,' '); } else { repr=showChar(c); special=repr.size()>1; } assert(repr.size()>0); if(x+(i64)repr.size()>=width){ layout.push_back({linenum,linepart,fromx,move(cur)}); cur.clear(); linepart++; fromx=i; x=0; } Cell cell={'\0',special,i}; for(char rc : repr){ cell.c=rc; cur.push_back(cell); x++; } } if(cur.size()>0)layout.push_back({linenum,linepart,fromx,move(cur)}); return layout; } //Takes width without gutter void Buffer::performInitialLayout(i64 width,i64 height){ assert(width>0&&height>0); const i64 targetCursorScreenY=height/2; screen.clear(); if(tb.numLines()==0){ screen.push_back({0,0,0,{}}); return; } assert(cursor.line>=0&&cursor.x>=0&& cursor.line=0&&cursorScreenY layout=layoutLine(tb[topLine],topLine,width); screen.insert(screen.begin(),layout.begin(),layout.end()); cursorScreenY+=layout.size(); topLine--; } //Remove any lines from the top until the cursor is exactly halfway the screen. //Store the removed lines in `spliced`. vector spliced; if(cursorScreenY>targetCursorScreenY){ for(i64 i=0;i layout=layoutLine(tb[bottomLine],bottomLine,width); screen.insert(screen.end(),layout.begin(),layout.end()); // cerr<<"Added bottomLine="<height){ //Oops, we overran a bit. Prune the end so that we fill the screen exactly. screen.resize(height); } else if((i64)screen.size()=0&&(i64)screen.size() layout=layoutLine(tb[topLine],topLine,width); screen.insert(screen.begin(),layout.begin(),layout.end()); topLine--; } if((i64)screen.size()>height){ //Oops, we overran a bit. Prune the end so that we fill the screen exactly. screen.resize(height); } } } } //Takes width without gutter void Buffer::renewLayout(i64 topLine,i64 topPart,i64 width,i64 height){ assert(width>0&&height>0); assert(topLine>=0&&topPart>=0); screen.clear(); if(tb.numLines()==0){ screen.push_back({0,0,0,{}}); return; } screen=layoutLine(tb.getLine(topLine),topLine,width); if(topPart>0)screen.erase(screen.begin(),screen.begin()+topPart); i64 bottomLine=topLine+1; while(bottomLine layout=layoutLine(tb[bottomLine],bottomLine,width); screen.insert(screen.end(),layout.begin(),layout.end()); bottomLine++; } if((i64)screen.size()>height){ screen.resize(height); } } i64 numberWidth(i64 number){ if(number<0)return 1+numberWidth(-number); if(number==0)return 1; return (i64)log10(number)+1; } void Buffer::show(i64 atx,i64 aty,i64 width,i64 height){ static const Style gutterStyle={3,9,false,false}; static const Style textStyle={9,9,false,false}; static const Style specialStyle={5,9,false,false}; i64 gutterWidth=numberWidth(tb.numLines())+2; i64 newWidth=width-gutterWidth; if(newWidth!=lastWidth||height!=lastHeight){ if(lastWidth==-1)performInitialLayout(newWidth,height); else renewLayout(screen[0].line,screen[0].part,newWidth,height); lastWidth=newWidth; lastHeight=height; } setstyle(&textStyle); fillrect(atx,aty,width,height,' '); pushcursor(); i64 y; i64 curScreenX=-1,curScreenY=-1; // cerr<<"Buffer::show: screen.size()="<cursor.line)){ if(screenX==atx+width)curScreenX=screenX-1; else curScreenX=screenX; curScreenY=aty+y; } } setstyle(&textStyle); if(y