This diff adds transpose-words to mg. It behaves slightly differently from emacs when it comes to muliple iterations. Take this text for example:
abc def ghi jkl mno pqr If the cursor is on the 'd', then you do C-u 3 M-t, in emacs the result will be: abc ghi jkl mno def pqr The attached diff gives this result in mg: abc ghi jkl mno def pqr Which personally I think is better. Also in emacs if you have this situation: 123 456 789 abc def ghi jkl mno pqr And the cursor is on 'a', then you do C-u 3 M-t, the result will be: 123 456 abc def ghi 789 jkl mno pqr Which I find quite zany when compared to the first example. The attached diff for mg does this: 123 456 abc def ghi 789 jkl mno pqr Which makes more sense to me but perhaps it is just as zany... What do people think? Mark Index: def.h =================================================================== RCS file: /cvs/src/usr.bin/mg/def.h,v retrieving revision 1.152 diff -u -p -u -p -r1.152 def.h --- def.h 29 Oct 2015 19:46:47 -0000 1.152 +++ def.h 24 Dec 2015 09:27:00 -0000 @@ -602,6 +602,7 @@ int capword(int, int); int delfword(int, int); int delbword(int, int); int inword(void); +int transposeword(int, int); /* region.c X */ int killregion(int, int); Index: funmap.c =================================================================== RCS file: /cvs/src/usr.bin/mg/funmap.c,v retrieving revision 1.51 diff -u -p -u -p -r1.51 funmap.c --- funmap.c 26 Sep 2015 15:03:15 -0000 1.51 +++ funmap.c 24 Dec 2015 09:27:00 -0000 @@ -201,6 +201,7 @@ static struct funmap functnames[] = { {togglereadonly, "toggle-read-only" }, {twiddle, "transpose-chars",}, {transposepara, "transpose-paragraphs",}, + {transposeword, "transpose-words",}, {undo, "undo",}, {undo_add_boundary, "undo-boundary",}, {undo_boundary_enable, "undo-boundary-toggle",}, Index: keymap.c =================================================================== RCS file: /cvs/src/usr.bin/mg/keymap.c,v retrieving revision 1.57 diff -u -p -u -p -r1.57 keymap.c --- keymap.c 26 Sep 2015 21:51:58 -0000 1.57 +++ keymap.c 24 Dec 2015 09:27:00 -0000 @@ -282,7 +282,7 @@ static PF metal[] = { fillpara, /* q */ backsearch, /* r */ forwsearch, /* s */ - rescan, /* t */ + transposeword, /* t */ upperword, /* u */ backpage, /* v */ copyregion, /* w */ Index: mg.1 =================================================================== RCS file: /cvs/src/usr.bin/mg/mg.1,v retrieving revision 1.98 diff -u -p -u -p -r1.98 mg.1 --- mg.1 24 Dec 2015 09:07:47 -0000 1.98 +++ mg.1 24 Dec 2015 09:27:00 -0000 @@ -306,6 +306,8 @@ fill-paragraph search-backward .It M-s search-forward +.It M-t +transpose-words .It M-u upcase-word .It M-v @@ -881,6 +883,12 @@ If multiple iterations are requested, th be moved .Va n paragraphs forward. +.It transpose-words +Transpose adjacent words. +If multiple iterations are requested, the current word will +be moved +.Va n +words forward. .It undo Undo the most recent action. If invoked again without an intervening command, Index: word.c =================================================================== RCS file: /cvs/src/usr.bin/mg/word.c,v retrieving revision 1.17 diff -u -p -u -p -r1.17 word.c --- word.c 19 Mar 2015 21:22:15 -0000 1.17 +++ word.c 24 Dec 2015 09:27:00 -0000 @@ -10,11 +10,15 @@ #include <sys/queue.h> #include <signal.h> +#include <errno.h> #include <stdio.h> +#include <stdlib.h> +#include <string.h> #include "def.h" RSIZE countfword(void); +int grabword(char **); /* * Move the cursor backward by "n" words. All of the details of motion are @@ -61,6 +65,158 @@ forwword(int f, int n) return (TRUE); } } + return (TRUE); +} + +/* + * Transpose 2 words. + */ +int +transposeword(int f, int n) +{ + struct line *tmp1_w_dotp = NULL; + struct line *tmp2_w_dotp = NULL; + int tmp2_w_doto = 0; + int tmp1_w_dotline = 0; + int tmp2_w_dotline = 0; + int tmp1_w_doto; + int i; /* start-of-line space counter */ + int ret, s; + int newline; + int leave = 0; + int tmp_len; + char *word1 = NULL; + char *word2 = NULL; + char *chr; + + if (n == 0) + return (TRUE); + + if ((s = checkdirty(curbp)) != TRUE) + return (s); + if (curbp->b_flag & BFREADONLY) { + dobeep(); + ewprintf("Buffer is read-only"); + return (FALSE); + } + + undo_boundary_enable(FFRAND, 0); + + /* go backwards to find the start of a word to transpose. */ + (void)backword(FFRAND, 1); + ret = grabword(&word1); + if (ret == ABORT) { + ewprintf("No word to the left to tranpose."); + return (FALSE); + } + if (ret < 0) { + dobeep(); + ewprintf("Error copying word: %s", strerror(ret)); + free(word1); + return (FALSE); + } + tmp_len = strlen(word1); + + while (n-- > 0) { + i = 0; + newline = 0; + + tmp1_w_doto = curwp->w_doto; + tmp1_w_dotline = curwp->w_dotline; + tmp1_w_dotp = curwp->w_dotp; + + /* go forward and find next word. */ + while (inword() == FALSE) { + if (forwchar(FFRAND, 1) == FALSE) { + leave = 1; + if (tmp1_w_dotline < curwp->w_dotline) + curwp->w_dotline--; + ewprintf("Don't have two things to transpose"); + break; + } + if (curwp->w_doto == 0) { + newline = 1; + i = 0; + } else if (newline) + i++; + } + if (leave) { + tmp2_w_doto = tmp1_w_doto; + tmp2_w_dotline = tmp1_w_dotline; + tmp2_w_dotp = tmp1_w_dotp; + break; + } + tmp2_w_doto = curwp->w_doto; + tmp2_w_dotline = curwp->w_dotline; + tmp2_w_dotp = curwp->w_dotp; + + ret = grabword(&word2); + if (ret < 0) { + dobeep(); + ewprintf("Error copying word: %s", strerror(ret)); + free(word1); + return (FALSE); + } + tmp_len = strlen(word2); + tmp2_w_doto += tmp_len; + + curwp->w_doto = tmp1_w_doto; + curwp->w_dotline = tmp1_w_dotline; + curwp->w_dotp = tmp1_w_dotp; + + /* insert shuffled along word */ + for (chr = word2; *chr != '\0'; ++chr) + linsert(1, *chr); + + if (newline) + tmp2_w_doto = i; + + curwp->w_doto = tmp2_w_doto; + curwp->w_dotline = tmp2_w_dotline; + curwp->w_dotp = tmp2_w_dotp; + + word2 = NULL; + } + curwp->w_doto = tmp2_w_doto; + curwp->w_dotline = tmp2_w_dotline; + curwp->w_dotp = tmp2_w_dotp; + + /* insert very first word in its new position */ + for (chr = word1; *chr != '\0'; ++chr) + linsert(1, *chr); + + if (leave) + (void)backword(FFRAND, 1); + + free(word1); + free(word2); + + undo_boundary_enable(FFRAND, 1); + + return (TRUE); +} + +/* + * copy and delete word. +*/ +int +grabword(char **word) +{ + int c; + + while (inword() == TRUE) { + c = lgetc(curwp->w_dotp, curwp->w_doto); + if (*word == NULL) { + if (asprintf(word, "%c", c) == -1) + return (errno); + } else { + if (asprintf(word, "%s%c", *word, c) == -1) + return (errno); + } + (void)forwdel(FFRAND, 1); + } + if (*word == NULL) + return (ABORT); return (TRUE); }