# HG changeset patch # User Christian Brabandt # Date 1451858404 -3600 # Node ID a4916368155996f4f2273056ddf7f5f74557134d # Parent cae508519cf64b653412ce1de484633301d3efbc commit https://github.com/vim/vim/commit/ed767a2073ef150971b0439a58e7ee582af6984e Author: Bram Moolenaar Date: Sun Jan 3 22:49:16 2016 +0100 patch 7.4.1042 Problem: g-CTRL-G shows the word count, but there is no way to get the word count in a script. Solution: Add the wordcount() function. (Christian Brabandt) diff --git a/runtime/doc/editing.txt b/runtime/doc/editing.txt --- a/runtime/doc/editing.txt +++ b/runtime/doc/editing.txt @@ -78,7 +78,9 @@ g CTRL-G Prints the current position of than one position on the screen ( or special character), both the "real" column and the screen column are shown, separated with a dash. - See also 'ruler' option. {not in Vi} + Also see the 'ruler' option and the |wordcount()| + function. + {not in Vi} *v_g_CTRL-G* {Visual}g CTRL-G Similar to "g CTRL-G", but Word, Character, Line, and diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -1,4 +1,4 @@ -*eval.txt* For Vim version 7.4. Last change: 2016 Jan 02 +*eval.txt* For Vim version 7.4. Last change: 2016 Jan 03 VIM REFERENCE MANUAL by Bram Moolenaar @@ -2075,6 +2075,7 @@ winrestcmd() String returns command to winrestview( {dict}) none restore view of current window winsaveview() Dict save view of current window winwidth( {nr}) Number width of window {nr} +wordcount() Dict get byte/char/word statistics writefile( {list}, {fname} [, {flags}]) Number write list of lines to file {fname} xor( {expr}, {expr}) Number bitwise XOR @@ -6744,6 +6745,28 @@ winwidth({nr}) *winwidth()* : exe "normal 50\|" :endif < +wordcount() *wordcount()* + The result is a dictionary of byte/chars/word statistics for + the current buffer. This is the same info as provided by + |g_CTRL-G| + The return value includes: + bytes Number of bytes in the buffer + chars Number of chars in the buffer + words Number of words in the buffer + cursor_bytes Number of bytes before cursor position + (not in Visual mode) + cursor_chars Number of chars before cursor position + (not in Visual mode) + cursor_words Number of words before cursor position + (not in Visual mode) + visual_bytes Number of bytes visually selected + (only in Visual mode) + visual_chars Number of chars visually selected + (only in Visual mode) + visual_words Number of chars visually selected + (only in Visual mode) + + *writefile()* writefile({list}, {fname} [, {flags}]) Write |List| {list} to file {fname}. Each list item is diff --git a/runtime/doc/usr_41.txt b/runtime/doc/usr_41.txt --- a/runtime/doc/usr_41.txt +++ b/runtime/doc/usr_41.txt @@ -923,6 +923,7 @@ Various: *various-functions* mzeval() evaluate |MzScheme| expression py3eval() evaluate Python expression (|+python3|) pyeval() evaluate Python expression (|+python|) + wordcount() get byte/word/char count of buffer ============================================================================== *41.7* Defining a function diff --git a/src/eval.c b/src/eval.c --- a/src/eval.c +++ b/src/eval.c @@ -780,6 +780,7 @@ static void f_winrestview __ARGS((typval static void f_winsaveview __ARGS((typval_T *argvars, typval_T *rettv)); static void f_winwidth __ARGS((typval_T *argvars, typval_T *rettv)); static void f_writefile __ARGS((typval_T *argvars, typval_T *rettv)); +static void f_wordcount __ARGS((typval_T *argvars, typval_T *rettv)); static void f_xor __ARGS((typval_T *argvars, typval_T *rettv)); static int list2fpos __ARGS((typval_T *arg, pos_T *posp, int *fnump, colnr_T *curswantp)); @@ -8387,6 +8388,7 @@ static struct fst {"winrestview", 1, 1, f_winrestview}, {"winsaveview", 0, 0, f_winsaveview}, {"winwidth", 1, 1, f_winwidth}, + {"wordcount", 0, 0, f_wordcount}, {"writefile", 2, 3, f_writefile}, {"xor", 2, 2, f_xor}, }; @@ -20220,6 +20222,19 @@ f_winwidth(argvars, rettv) } /* + * "wordcount()" function + */ + static void +f_wordcount(argvars, rettv) + typval_T *argvars UNUSED; + typval_T *rettv; +{ + if (rettv_dict_alloc(rettv) == FAIL) + return; + cursor_pos_info(rettv->vval.v_dict); +} + +/* * Write list of strings to file */ static int diff --git a/src/normal.c b/src/normal.c --- a/src/normal.c +++ b/src/normal.c @@ -8270,7 +8270,7 @@ nv_g_cmd(cap) * "g CTRL-G": display info about cursor position */ case Ctrl_G: - cursor_pos_info(); + cursor_pos_info(NULL); break; /* diff --git a/src/ops.c b/src/ops.c --- a/src/ops.c +++ b/src/ops.c @@ -6963,15 +6963,18 @@ line_count_info(line, wc, cc, limit, eol * Give some info about the position of the cursor (for "g CTRL-G"). * In Visual mode, give some info about the selected region. (In this case, * the *_count_cursor variables store running totals for the selection.) + * When "dict" is not NULL store the info there instead of showing it. */ void -cursor_pos_info() +cursor_pos_info(dict) + dict_T *dict; { char_u *p; char_u buf1[50]; char_u buf2[40]; linenr_T lnum; long byte_count = 0; + long bom_count = 0; long byte_count_cursor = 0; long char_count = 0; long char_count_cursor = 0; @@ -6989,7 +6992,11 @@ cursor_pos_info() */ if (curbuf->b_ml.ml_flags & ML_EMPTY) { - MSG(_(no_lines_msg)); + if (dict == NULL) + { + MSG(_(no_lines_msg)); + return; + } } else { @@ -7122,74 +7129,98 @@ cursor_pos_info() if (!curbuf->b_p_eol && (curbuf->b_p_bin || !curbuf->b_p_fixeol)) byte_count -= eol_size; - if (VIsual_active) + if (dict == NULL) { - if (VIsual_mode == Ctrl_V && curwin->w_curswant < MAXCOL) + if (VIsual_active) { - getvcols(curwin, &min_pos, &max_pos, &min_pos.col, - &max_pos.col); - vim_snprintf((char *)buf1, sizeof(buf1), _("%ld Cols; "), - (long)(oparg.end_vcol - oparg.start_vcol + 1)); + if (VIsual_mode == Ctrl_V && curwin->w_curswant < MAXCOL) + { + getvcols(curwin, &min_pos, &max_pos, &min_pos.col, + &max_pos.col); + vim_snprintf((char *)buf1, sizeof(buf1), _("%ld Cols; "), + (long)(oparg.end_vcol - oparg.start_vcol + 1)); + } + else + buf1[0] = NUL; + + if (char_count_cursor == byte_count_cursor + && char_count == byte_count) + vim_snprintf((char *)IObuff, IOSIZE, + _("Selected %s%ld of %ld Lines; %ld of %ld Words; %ld of %ld Bytes"), + buf1, line_count_selected, + (long)curbuf->b_ml.ml_line_count, + word_count_cursor, word_count, + byte_count_cursor, byte_count); + else + vim_snprintf((char *)IObuff, IOSIZE, + _("Selected %s%ld of %ld Lines; %ld of %ld Words; %ld of %ld Chars; %ld of %ld Bytes"), + buf1, line_count_selected, + (long)curbuf->b_ml.ml_line_count, + word_count_cursor, word_count, + char_count_cursor, char_count, + byte_count_cursor, byte_count); } else - buf1[0] = NUL; - - if (char_count_cursor == byte_count_cursor - && char_count == byte_count) - vim_snprintf((char *)IObuff, IOSIZE, - _("Selected %s%ld of %ld Lines; %ld of %ld Words; %ld of %ld Bytes"), - buf1, line_count_selected, + { + p = ml_get_curline(); + validate_virtcol(); + col_print(buf1, sizeof(buf1), (int)curwin->w_cursor.col + 1, + (int)curwin->w_virtcol + 1); + col_print(buf2, sizeof(buf2), (int)STRLEN(p), + linetabsize(p)); + + if (char_count_cursor == byte_count_cursor + && char_count == byte_count) + vim_snprintf((char *)IObuff, IOSIZE, + _("Col %s of %s; Line %ld of %ld; Word %ld of %ld; Byte %ld of %ld"), + (char *)buf1, (char *)buf2, + (long)curwin->w_cursor.lnum, (long)curbuf->b_ml.ml_line_count, word_count_cursor, word_count, byte_count_cursor, byte_count); - else - vim_snprintf((char *)IObuff, IOSIZE, - _("Selected %s%ld of %ld Lines; %ld of %ld Words; %ld of %ld Chars; %ld of %ld Bytes"), - buf1, line_count_selected, + else + vim_snprintf((char *)IObuff, IOSIZE, + _("Col %s of %s; Line %ld of %ld; Word %ld of %ld; Char %ld of %ld; Byte %ld of %ld"), + (char *)buf1, (char *)buf2, + (long)curwin->w_cursor.lnum, (long)curbuf->b_ml.ml_line_count, word_count_cursor, word_count, char_count_cursor, char_count, byte_count_cursor, byte_count); + } + } + + /* Don't shorten this message, the user asked for it. */ +#ifdef FEAT_MBYTE + bom_count = bomb_size(); + if (bom_count > 0) + sprintf((char *)IObuff + STRLEN(IObuff), _("(+%ld for BOM)"), + bom_count); +#endif + if (dict == NULL) + { + p = p_shm; + p_shm = (char_u *)""; + msg(IObuff); + p_shm = p; + } + } + if (dict != NULL) + { + dict_add_nr_str(dict, "words", (long)word_count, NULL); + dict_add_nr_str(dict, "chars", (long)char_count, NULL); + dict_add_nr_str(dict, "bytes", (long)byte_count + bom_count, NULL); + if (VIsual_active) + { + dict_add_nr_str(dict, "visual_bytes", (long)byte_count_cursor, NULL); + dict_add_nr_str(dict, "visual_chars", (long)char_count_cursor, NULL); + dict_add_nr_str(dict, "visual_words", (long)word_count_cursor, NULL); } else { - p = ml_get_curline(); - validate_virtcol(); - col_print(buf1, sizeof(buf1), (int)curwin->w_cursor.col + 1, - (int)curwin->w_virtcol + 1); - col_print(buf2, sizeof(buf2), (int)STRLEN(p), - linetabsize(p)); - - if (char_count_cursor == byte_count_cursor - && char_count == byte_count) - vim_snprintf((char *)IObuff, IOSIZE, - _("Col %s of %s; Line %ld of %ld; Word %ld of %ld; Byte %ld of %ld"), - (char *)buf1, (char *)buf2, - (long)curwin->w_cursor.lnum, - (long)curbuf->b_ml.ml_line_count, - word_count_cursor, word_count, - byte_count_cursor, byte_count); - else - vim_snprintf((char *)IObuff, IOSIZE, - _("Col %s of %s; Line %ld of %ld; Word %ld of %ld; Char %ld of %ld; Byte %ld of %ld"), - (char *)buf1, (char *)buf2, - (long)curwin->w_cursor.lnum, - (long)curbuf->b_ml.ml_line_count, - word_count_cursor, word_count, - char_count_cursor, char_count, - byte_count_cursor, byte_count); + dict_add_nr_str(dict, "cursor_bytes", (long)byte_count_cursor, NULL); + dict_add_nr_str(dict, "cursor_chars", (long)char_count_cursor, NULL); + dict_add_nr_str(dict, "cursor_words", (long)word_count_cursor, NULL); } - -#ifdef FEAT_MBYTE - byte_count = bomb_size(); - if (byte_count > 0) - sprintf((char *)IObuff + STRLEN(IObuff), _("(+%ld for BOM)"), - byte_count); -#endif - /* Don't shorten this message, the user asked for it. */ - p = p_shm; - p_shm = (char_u *)""; - msg(IObuff); - p_shm = p; } } diff --git a/src/proto/ops.pro b/src/proto/ops.pro --- a/src/proto/ops.pro +++ b/src/proto/ops.pro @@ -58,5 +58,5 @@ void write_reg_contents __ARGS((int name void write_reg_contents_lst __ARGS((int name, char_u **strings, int maxlen, int must_append, int yank_type, long block_len)); void write_reg_contents_ex __ARGS((int name, char_u *str, int maxlen, int must_append, int yank_type, long block_len)); void clear_oparg __ARGS((oparg_T *oap)); -void cursor_pos_info __ARGS((void)); +void cursor_pos_info __ARGS((dict_T *eval)); /* vim: set ft=c : */ diff --git a/src/testdir/Make_all.mak b/src/testdir/Make_all.mak --- a/src/testdir/Make_all.mak +++ b/src/testdir/Make_all.mak @@ -120,6 +120,7 @@ SCRIPTS_ALL = \ test_tagcase.out \ test_textobjects.out \ test_utf8.out \ + test_wordcount.out \ test_writefile.out diff --git a/src/testdir/test_wordcount.in b/src/testdir/test_wordcount.in new file mode 100644 --- /dev/null +++ b/src/testdir/test_wordcount.in @@ -0,0 +1,125 @@ +Test for wordcount() function + +STARTTEST +:so small.vim +:so mbyte.vim +:set enc=utf8 +:new +:fu DoRecordWin(...) +: wincmd k +: if exists("a:1") +: call cursor(a:1) +: endif +: let result=[] +: call add(result, g:test) +: call add(result, getline(1, '$')) +: call add(result, wordcount()) +: wincmd j +: return result +:endfu +:fu PutInWindow(args) +: wincmd k +: %d _ +: call append(1, a:args) +: wincmd j +:endfu +:fu Log() +: $put ='----' +: $put =remove(g:log,0) +: $put =string(g:log) +:endfu +:fu! STL() +: if mode() =~? 'V' +: let g:visual_stat=wordcount() +: endif +: return string(wordcount()) +:endfu +:let g:test="Test 1: empty window" +:let log=DoRecordWin() +:call Log() +:" +:let g:test="Test 2: some words, cursor at start" +:call PutInWindow('one two three') +:let log=DoRecordWin([1,1,0]) +:call Log() +:" +:let g:test="Test 3: some words, cursor at end" +:call PutInWindow('one two three') +:let log=DoRecordWin([2,99,0]) +:call Log() +:" +:let g:test="Test 4: some words, cursor at end, ve=all" +:set ve=all +:call PutInWindow('one two three') +:let log=DoRecordWin([2,99,0]) +:call Log() +:set ve= +:" +:let g:test="Test 5: several lines with words" +:call PutInWindow(['one two three', 'one two three', 'one two three']) +:let log=DoRecordWin([4,99,0]) +:call Log() +:" +:let g:test="Test 6: one line with BOM set" +:call PutInWindow('one two three') +:wincmd k +:set bomb +:w! Xtest +:wincmd j +:let log=DoRecordWin([2,99,0]) +:call Log() +:wincmd k +:set nobomb +:w! +:wincmd j +:" +:let g:test="Test 7: one line with multibyte words" +:call PutInWindow(['Äne M¤ne Müh']) +:let log=DoRecordWin([2,99,0]) +:call Log() +:" +:let g:test="Test 8: several lines with multibyte words" +:call PutInWindow(['Äne M¤ne Müh', 'und raus bist dü!']) +:let log=DoRecordWin([3,99,0]) +:call Log() +:" +:let g:test="Test 9: visual mode, complete buffer" +:call PutInWindow(['Äne M¤ne Müh', 'und raus bist dü!']) +:wincmd k +:set ls=2 stl=%{STL()} +:" start visual mode quickly and select complete buffer +:0 +V2jy +:set stl= ls=1 +:let log=DoRecordWin([3,99,0]) +:let log[2]=g:visual_stat +:call Log() +:" +:let g:test="Test 10: visual mode (empty)" +:call PutInWindow(['Äne M¤ne Müh', 'und raus bist dü!']) +:wincmd k +:set ls=2 stl=%{STL()} +:" start visual mode quickly and select complete buffer +:0 +v$y +:set stl= ls=1 +:let log=DoRecordWin([3,99,0]) +:let log[2]=g:visual_stat +:call Log() +:" +:let g:test="Test 11: visual mode, single line" +:call PutInWindow(['Äne M¤ne Müh', 'und raus bist dü!']) +:wincmd k +:set ls=2 stl=%{STL()} +:" start visual mode quickly and select complete buffer +:2 +0v$y +:set stl= ls=1 +:let log=DoRecordWin([3,99,0]) +:let log[2]=g:visual_stat +:call Log() +:" +:/^RESULT test/,$w! test.out +:qa! +ENDTEST +RESULT test: diff --git a/src/testdir/test_wordcount.ok b/src/testdir/test_wordcount.ok new file mode 100644 --- /dev/null +++ b/src/testdir/test_wordcount.ok @@ -0,0 +1,34 @@ +RESULT test: +---- +Test 1: empty window +[[''], {'chars': 0, 'cursor_chars': 0, 'words': 0, 'cursor_words': 0, 'bytes': 0, 'cursor_bytes': 0}] +---- +Test 2: some words, cursor at start +[['', 'one two three'], {'chars': 15, 'cursor_chars': 1, 'words': 3, 'cursor_words': 0, 'bytes': 15, 'cursor_bytes': 1}] +---- +Test 3: some words, cursor at end +[['', 'one two three'], {'chars': 15, 'cursor_chars': 14, 'words': 3, 'cursor_words': 3, 'bytes': 15, 'cursor_bytes': 14}] +---- +Test 4: some words, cursor at end, ve=all +[['', 'one two three'], {'chars': 15, 'cursor_chars': 15, 'words': 3, 'cursor_words': 3, 'bytes': 15, 'cursor_bytes': 15}] +---- +Test 5: several lines with words +[['', 'one two three', 'one two three', 'one two three'], {'chars': 43, 'cursor_chars': 42, 'words': 9, 'cursor_words': 9, 'bytes': 43, 'cursor_bytes': 42}] +---- +Test 6: one line with BOM set +[['', 'one two three'], {'chars': 15, 'cursor_chars': 14, 'words': 3, 'cursor_words': 3, 'bytes': 18, 'cursor_bytes': 14}] +---- +Test 7: one line with multibyte words +[['', 'Äne M¤ne Müh'], {'chars': 14, 'cursor_chars': 13, 'words': 3, 'cursor_words': 3, 'bytes': 17, 'cursor_bytes': 16}] +---- +Test 8: several lines with multibyte words +[['', 'Äne M¤ne Müh', 'und raus bist dü!'], {'chars': 32, 'cursor_chars': 31, 'words': 7, 'cursor_words': 7, 'bytes': 36, 'cursor_bytes': 35}] +---- +Test 9: visual mode, complete buffer +[['', 'Äne M¤ne Müh', 'und raus bist dü!'], {'chars': 32, 'words': 7, 'bytes': 36, 'visual_chars': 32, 'visual_words': 7, 'visual_bytes': 36}] +---- +Test 10: visual mode (empty) +[['', 'Äne M¤ne Müh', 'und raus bist dü!'], {'chars': 32, 'words': 7, 'bytes': 36, 'visual_chars': 1, 'visual_words': 0, 'visual_bytes': 1}] +---- +Test 11: visual mode, single line +[['', 'Äne M¤ne Müh', 'und raus bist dü!'], {'chars': 32, 'words': 7, 'bytes': 36, 'visual_chars': 13, 'visual_words': 3, 'visual_bytes': 16}] diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -742,6 +742,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 1042, +/**/ 1041, /**/ 1040,