Line editor (more text here)
C (gcc), 1288 1212 1188 1171 1146 bytes

#define P printf(
#define A case
#define B break
#define L(i,e,t,f,s)for(o=i;o e;){strcpy(l[o t],l[o f]);c[o t]=c[s o];}
#define R(m,o){return b&lt;1|b&gt;m?m o:b;}
#define N(f)sscanf(f,"%d,%d",&amp;i,&amp;j)||sscanf(f,",%d",&amp;j)
r,i,j,o,z,c[999],*C,x=1,y=1;char a[999],l[999][999],(*L)[999];D(){L(r,&gt;y,,-1,--)r++?strcpy(l[o],l[o-1]+--x),c[o-1]=x,l[o-1][x]=0:0;c[y++]=strlen(l[o]);x=1;}U(){strcat(*L,l[y]);*C=strlen(*L);L(y+1,&lt;r,-1,,++)--r;*l[r]=c[r]=0;}X(b)R(c[y-1],+1)Y(b)R(r,)main(){for(;;){z=i=y;L=l+--z;C=c+z;j=x;!r||y/r&amp;&amp;x&gt;*C?P"end&gt; "):P"%d,%d&gt; ",y,x);scanf("%[^\n]%*c",a)?a[2]*=!!a[1],N(a)?y=Y(i+(i&lt;0|*a=='+')*y),x=X(j+(j&lt;0||strchr(a+1,'+'))*x):0:(*a=D(),scanf("%*c"));switch(*a){A'e':y=r;x=c[r-1]+1;B;A'b':y=1;x=1;B;A'L':for(o=y-4;++o&lt;y+2;)o&lt;0^o&lt;r&amp;&amp;P"%c%s\n",o^z?' ':'&gt;',l[o]);for(o=x+1;--o;)P" ");P"^\n");B;A'l':puts(*L);B;A'p':i=1;j=0;N(a+2);for(o=Y(i)-1;o&lt;Y(j);++o)puts(l[o]);B;A'A':y=r++;strcpy(l[y],a+2);x=c[y]=strlen(a+2);++x;++y;B;A'i':D();--y;x=X(0);A'r':strcpy(*L+x-1,a+2);*C=strlen(*L);x=1;++y&gt;r&amp;&amp;++r;B;A'I':o=strlen(a+2);memmove(*L+x+o-1,*L+x-1,*C-x+1);*C+=o;memcpy(*L+x-1,a+2,o);x+=o;B;A'd':**L?**L=*C=0,x=1:U();y=y&gt;r?r:y;B;A'j':y&lt;r&amp;&amp;U();}}}


Demo

First time trying to do something like this, so there are probably some missed opportunities for making it smaller. (Thanks Jerry Jeremiah for pointing out some)

Here's a (slightly) more readable version:

// often used functions/keywords:
#define P printf(
#define A case
#define B break

// loops for copying rows upwards/downwards are similar -&gt; macro
#define L(i,e,t,f,s)for(o=i;o e;){strcpy(l[o t],l[o f]);c[o t]=c[s o];}

// range check for rows/columns is similar -&gt; macro
#define R(m,o){return b&lt;1|b&gt;m?m o:b;}

// checking for numerical input is needed twice (move and print command):
#define N(f)sscanf(f,"%d,%d",&amp;i,&amp;j)||sscanf(f,",%d",&amp;j)

// room for 999 rows with each 999 cols (not specified, should be enough)
// also declare "current line pointers" (*L for data, *C for line length),
// an input buffer (a) and scratch variables
r,i,j,o,z,c[999],*C,x=1,y=1;char a[999],l[999][999],(*L)[999];

// move rows down from current cursor position
D()
{
    L(r,&gt;y,,-1,--)
    r++?strcpy(l[o],l[o-1]+--x),c[o-1]=x,l[o-1][x]=0:0;
    c[y++]=strlen(l[o]);
    x=1;
}

// move rows up, appending uppermost to current line
U()
{
    strcat(*L,l[y]);
    *C=strlen(*L);
    L(y+1,&lt;r,-1,,++)
    --r;
    *l[r]=c[r]=0;
}

// normalize positions, treat 0 as max
X(b)R(c[y-1],+1)
Y(b)R(r,)

main()
{
    for(;;)
    {
        // initialize z as current line index, the current line pointers,
        // i and j for default values of positioning
        z=i=y;L=l+--z;C=c+z;j=x;

        // prompt:
        !r||y/r&amp;&amp;x&gt;*C?P"end&gt; "):P"%d,%d&gt; ",y,x);

        // read a line of input (using scanf so we don't need an include)
        scanf("%[^\n]%*c",a)?

        // no command arguments -&gt; make check easier:
        a[2]*=!!a[1],

        // numerical input -&gt; have move command:
        // calculate new coordinates, checking for "relative"
        N(a)?y=Y(i+(i&lt;0|*a=='+')*y),x=X(j+(j&lt;0||strchr(a+1,'+'))*x):0:

        // check for empty input, read single newline and perform &lt;return&gt; command:
        (*a=D(),scanf("%*c"));

        switch(*a)
        {
            A'e':y=r;x=c[r-1]+1;B;
            A'b':y=1;x=1;B;
            A'L':for(o=y-4;++o&lt;y+2;)o&lt;0^o&lt;r&amp;&amp;P"%c%s\n",o^z?' ':'&gt;',l[o]);for(o=x+1;--o;)P" ");P"^\n");B;
            A'l':puts(*L);B;
            A'p':i=1;j=0;N(a+2);for(o=Y(i)-1;o&lt;Y(j);++o)puts(l[o]);B;
            A'A':y=r++;strcpy(l[y],a+2);x=c[y]=strlen(a+2);++x;++y;B;
            A'i':D();--y;x=X(0);
            // Commands i and r are very similar -&gt; fall through from i to r
            // after moving rows down and setting position at end of line:
            A'r':strcpy(*L+x-1,a+2);*C=strlen(*L);x=1;++y&gt;r&amp;&amp;++r;B;
            A'I':o=strlen(a+2);memmove(*L+x+o-1,*L+x-1,*C-x+1);*C+=o;memcpy(*L+x-1,a+2,o);x+=o;B;
            A'd':**L?**L=*C=0,x=1:U();y=y&gt;r?r:y;B;
            A'j':y&lt;r&amp;&amp;U();
        }
    }
}




This challenge leaves quite a lot of corner cases to explore -- I guess I fixed all bugs by now, but if somebody finds one more, please comment. Regarding the revisions, I removed byte counts for earlier versions that contained subtle bugs.