0
0
mirror of https://github.com/vim/vim.git synced 2025-09-23 03:43:49 -04:00

patch 8.2.0110: prop_find() is not implemented

Problem:    prop_find() is not implemented.
Solution:   Implement prop_find(). (Ryan Hackett, closes #5421, closes #4970)
This commit is contained in:
Bram Moolenaar
2020-01-10 19:56:46 +01:00
parent 2963456ff2
commit e05a89ac63
6 changed files with 319 additions and 31 deletions

View File

@@ -154,8 +154,6 @@ prop_add({lnum}, {col}, {props})
added to. When not found, the global property types are used. added to. When not found, the global property types are used.
If not found an error is given. If not found an error is given.
See |text-properties| for information about text properties.
Can also be used as a |method|: > Can also be used as a |method|: >
GetLnum()->prop_add(col, props) GetLnum()->prop_add(col, props)
@@ -168,14 +166,11 @@ prop_clear({lnum} [, {lnum-end} [, {props}]]) *prop_clear()*
When {props} contains a "bufnr" item use this buffer, When {props} contains a "bufnr" item use this buffer,
otherwise use the current buffer. otherwise use the current buffer.
See |text-properties| for information about text properties.
Can also be used as a |method|: > Can also be used as a |method|: >
GetLnum()->prop_clear() GetLnum()->prop_clear()
< <
*prop_find()* *prop_find()*
prop_find({props} [, {direction}]) prop_find({props} [, {direction}])
{not implemented yet}
Search for a text property as specified with {props}: Search for a text property as specified with {props}:
id property with this ID id property with this ID
type property with this type name type property with this type name
@@ -198,8 +193,6 @@ prop_find({props} [, {direction}])
as with prop_list(), and additionally an "lnum" entry. as with prop_list(), and additionally an "lnum" entry.
If no match is found then an empty Dict is returned. If no match is found then an empty Dict is returned.
See |text-properties| for information about text properties.
prop_list({lnum} [, {props}]) *prop_list()* prop_list({lnum} [, {props}]) *prop_list()*
Return a List with all text properties in line {lnum}. Return a List with all text properties in line {lnum}.
@@ -223,8 +216,6 @@ prop_list({lnum} [, {props}]) *prop_list()*
When "end" is zero the property continues in the next line. When "end" is zero the property continues in the next line.
The line break after this line is included. The line break after this line is included.
See |text-properties| for information about text properties.
Can also be used as a |method|: > Can also be used as a |method|: >
GetLnum()->prop_list() GetLnum()->prop_list()
< <
@@ -248,8 +239,6 @@ prop_remove({props} [, {lnum} [, {lnum-end}]])
Returns the number of properties that were removed. Returns the number of properties that were removed.
See |text-properties| for information about text properties.
Can also be used as a |method|: > Can also be used as a |method|: >
GetProps()->prop_remove() GetProps()->prop_remove()
@@ -275,8 +264,6 @@ prop_type_add({name}, {props}) *prop_type_add()* *E969* *E970*
end_incl when TRUE inserts at the end position will be end_incl when TRUE inserts at the end position will be
included in the text property included in the text property
See |text-properties| for information about text properties.
Can also be used as a |method|: > Can also be used as a |method|: >
GetPropName()->prop_type_add(props) GetPropName()->prop_type_add(props)
@@ -285,8 +272,6 @@ prop_type_change({name}, {props}) *prop_type_change()*
property with this name does not exist an error is given. property with this name does not exist an error is given.
The {props} argument is just like |prop_type_add()|. The {props} argument is just like |prop_type_add()|.
See |text-properties| for information about text properties.
Can also be used as a |method|: > Can also be used as a |method|: >
GetPropName()->prop_type_change(props) GetPropName()->prop_type_change(props)
@@ -301,8 +286,6 @@ prop_type_delete({name} [, {props}]) *prop_type_delete()*
When text property type {name} is not found there is no error. When text property type {name} is not found there is no error.
See |text-properties| for information about text properties.
Can also be used as a |method|: > Can also be used as a |method|: >
GetPropName()->prop_type_delete() GetPropName()->prop_type_delete()
@@ -316,8 +299,6 @@ prop_type_get([{name} [, {props}]) *prop_type_get()*
{props} can contain a "bufnr" item. When it is given, use {props} can contain a "bufnr" item. When it is given, use
this buffer instead of the global property types. this buffer instead of the global property types.
See |text-properties| for information about text properties.
Can also be used as a |method|: > Can also be used as a |method|: >
GetPropName()->prop_type_get() GetPropName()->prop_type_get()
@@ -327,8 +308,6 @@ prop_type_list([{props}]) *prop_type_list()*
{props} can contain a "bufnr" item. When it is given, use {props} can contain a "bufnr" item. When it is given, use
this buffer instead of the global property types. this buffer instead of the global property types.
See |text-properties| for information about text properties.
============================================================================== ==============================================================================
3. When text changes *text-prop-changes* 3. When text changes *text-prop-changes*

View File

@@ -620,6 +620,7 @@ static funcentry_T global_functions[] =
#ifdef FEAT_PROP_POPUP #ifdef FEAT_PROP_POPUP
{"prop_add", 3, 3, FEARG_1, f_prop_add}, {"prop_add", 3, 3, FEARG_1, f_prop_add},
{"prop_clear", 1, 3, FEARG_1, f_prop_clear}, {"prop_clear", 1, 3, FEARG_1, f_prop_clear},
{"prop_find", 1, 2, FEARG_1, f_prop_find},
{"prop_list", 1, 2, FEARG_1, f_prop_list}, {"prop_list", 1, 2, FEARG_1, f_prop_list},
{"prop_remove", 1, 3, FEARG_1, f_prop_remove}, {"prop_remove", 1, 3, FEARG_1, f_prop_remove},
{"prop_type_add", 2, 2, FEARG_1, f_prop_type_add}, {"prop_type_add", 2, 2, FEARG_1, f_prop_type_add},

View File

@@ -6,6 +6,7 @@ int get_text_props(buf_T *buf, linenr_T lnum, char_u **props, int will_change);
int find_visible_prop(win_T *wp, int type_id, int id, textprop_T *prop, linenr_T *found_lnum); int find_visible_prop(win_T *wp, int type_id, int id, textprop_T *prop, linenr_T *found_lnum);
proptype_T *text_prop_type_by_id(buf_T *buf, int id); proptype_T *text_prop_type_by_id(buf_T *buf, int id);
void f_prop_clear(typval_T *argvars, typval_T *rettv); void f_prop_clear(typval_T *argvars, typval_T *rettv);
void f_prop_find(typval_T *argvars, typval_T *rettv);
void f_prop_list(typval_T *argvars, typval_T *rettv); void f_prop_list(typval_T *argvars, typval_T *rettv);
void f_prop_remove(typval_T *argvars, typval_T *rettv); void f_prop_remove(typval_T *argvars, typval_T *rettv);
void f_prop_type_add(typval_T *argvars, typval_T *rettv); void f_prop_type_add(typval_T *argvars, typval_T *rettv);

View File

@@ -103,6 +103,118 @@ func Get_expected_props()
\ ] \ ]
endfunc endfunc
func Test_prop_find()
new
call setline(1, ['one one one', 'twotwo', 'three', 'fourfour', 'five', 'sixsix'])
" Add two text props on lines 1 and 5, and one spanning lines 2 to 4.
call prop_type_add('prop_name', {'highlight': 'Directory'})
call prop_add(1, 5, {'type': 'prop_name', 'id': 10, 'length': 3})
call prop_add(2, 4, {'type': 'prop_name', 'id': 11, 'end_lnum': 4, 'end_col': 9})
call prop_add(5, 4, {'type': 'prop_name', 'id': 12, 'length': 1})
let expected = [
\ {'lnum': 1, 'col': 5, 'length': 3, 'id': 10, 'type': 'prop_name', 'start': 1, 'end': 1},
\ {'lnum': 2, 'col': 4, 'id': 11, 'type': 'prop_name', 'start': 1, 'end': 0},
\ {'lnum': 5, 'col': 4, 'length': 1, 'id': 12, 'type': 'prop_name', 'start': 1, 'end': 1}
\ ]
" Starting at line 5 col 1 this should find the prop at line 5 col 4.
call cursor(5,1)
let result = prop_find({'type': 'prop_name'}, 'f')
call assert_equal(expected[2], result)
" With skipstart left at false (default), this should find the prop at line
" 5 col 4.
let result = prop_find({'type': 'prop_name', 'lnum': 5, 'col': 4}, 'b')
call assert_equal(expected[2], result)
" With skipstart set to true, this should skip the prop at line 5 col 4.
let result = prop_find({'type': 'prop_name', 'lnum': 5, 'col': 4, 'skipstart': 1}, 'b')
unlet result.length
call assert_equal(expected[1], result)
" Search backwards from line 1 col 10 to find the prop on the same line.
let result = prop_find({'type': 'prop_name', 'lnum': 1, 'col': 10}, 'b')
call assert_equal(expected[0], result)
" with skipstart set to false, if the start position is anywhere between the
" start and end lines of a text prop (searching forward or backward), the
" result should be the prop on the first line (the line with 'start' set to 1).
call cursor(3,1)
let result = prop_find({'type': 'prop_name'}, 'f')
unlet result.length
call assert_equal(expected[1], result)
let result = prop_find({'type': 'prop_name'}, 'b')
unlet result.length
call assert_equal(expected[1], result)
" with skipstart set to true, if the start position is anywhere between the
" start and end lines of a text prop (searching forward or backward), all lines
" of the prop will be skipped.
let result = prop_find({'type': 'prop_name', 'skipstart': 1}, 'b')
call assert_equal(expected[0], result)
let result = prop_find({'type': 'prop_name', 'skipstart': 1}, 'f')
call assert_equal(expected[2], result)
" Use skipstart to search through all props with type name 'prop_name'.
" First forward...
let lnum = 1
let col = 1
let i = 0
for exp in expected
let result = prop_find({'type': 'prop_name', 'lnum': lnum, 'col': col, 'skipstart': 1}, 'f')
if !has_key(exp, "length")
unlet result.length
endif
call assert_equal(exp, result)
let lnum = result.lnum
let col = result.col
let i = i + 1
endfor
" ...then backwards.
let lnum = 6
let col = 4
let i = 2
while i >= 0
let result = prop_find({'type': 'prop_name', 'lnum': lnum, 'col': col, 'skipstart': 1}, 'b')
if !has_key(expected[i], "length")
unlet result.length
endif
call assert_equal(expected[i], result)
let lnum = result.lnum
let col = result.col
let i = i - 1
endwhile
" Starting from line 6 col 1 search backwards for prop with id 10.
call cursor(6,1)
let result = prop_find({'id': 10, 'skipstart': 1}, 'b')
call assert_equal(expected[0], result)
" Starting from line 1 col 1 search forwards for prop with id 12.
call cursor(1,1)
let result = prop_find({'id': 12}, 'f')
call assert_equal(expected[2], result)
" Search for a prop with an unknown id.
let result = prop_find({'id': 999}, 'f')
call assert_equal({}, result)
" Search backwards from the proceeding position of the prop with id 11
" (at line num 2 col 4). This should return an empty dict.
let result = prop_find({'id': 11, 'lnum': 2, 'col': 3}, 'b')
call assert_equal({}, result)
" When lnum is given and col is omitted, use column 1.
let result = prop_find({'type': 'prop_name', 'lnum': 1}, 'f')
call assert_equal(expected[0], result)
call prop_clear(1,6)
call prop_type_delete('prop_name')
endfunc
func Test_prop_add() func Test_prop_add()
new new
call AddPropTypes() call AddPropTypes()

View File

@@ -468,6 +468,24 @@ find_type_by_id(hashtab_T *ht, int id)
return NULL; return NULL;
} }
/*
* Fill 'dict' with text properties in 'prop'.
*/
static void
prop_fill_dict(dict_T *dict, textprop_T *prop, buf_T *buf)
{
proptype_T *pt;
dict_add_number(dict, "col", prop->tp_col);
dict_add_number(dict, "length", prop->tp_len);
dict_add_number(dict, "id", prop->tp_id);
dict_add_number(dict, "start", !(prop->tp_flags & TP_FLAG_CONT_PREV));
dict_add_number(dict, "end", !(prop->tp_flags & TP_FLAG_CONT_NEXT));
pt = text_prop_type_by_id(buf, prop->tp_type);
if (pt != NULL)
dict_add_string(dict, "type", pt->pt_name);
}
/* /*
* Find a property type by ID in "buf" or globally. * Find a property type by ID in "buf" or globally.
* Returns NULL if not found. * Returns NULL if not found.
@@ -536,6 +554,190 @@ f_prop_clear(typval_T *argvars, typval_T *rettv UNUSED)
redraw_buf_later(buf, NOT_VALID); redraw_buf_later(buf, NOT_VALID);
} }
/*
* prop_find({props} [, {direction}])
*/
void
f_prop_find(typval_T *argvars, typval_T *rettv)
{
pos_T *cursor = &curwin->w_cursor;
dict_T *dict;
buf_T *buf = curbuf;
dictitem_T *di;
int lnum_start;
int start_pos_has_prop = 0;
int seen_end = 0;
int id = -1;
int type_id = -1;
int skipstart = 0;
int lnum = -1;
int col = -1;
int dir = 1; // 1 = forward, -1 = backward
if (argvars[0].v_type != VAR_DICT || argvars[0].vval.v_dict == NULL)
{
emsg(_(e_invarg));
return;
}
dict = argvars[0].vval.v_dict;
if (get_bufnr_from_arg(&argvars[0], &buf) == FAIL)
return;
if (buf->b_ml.ml_mfp == NULL)
return;
if (argvars[1].v_type != VAR_UNKNOWN)
{
char_u *dir_s = tv_get_string(&argvars[1]);
if (*dir_s == 'b')
dir = -1;
else if (*dir_s != 'f')
{
emsg(_(e_invarg));
return;
}
}
di = dict_find(dict, (char_u *)"lnum", -1);
if (di != NULL)
lnum = tv_get_number(&di->di_tv);
di = dict_find(dict, (char_u *)"col", -1);
if (di != NULL)
col = tv_get_number(&di->di_tv);
if (lnum == -1)
{
lnum = cursor->lnum;
col = cursor->col + 1;
}
else if (col == -1)
col = 1;
if (lnum < 1 || lnum > buf->b_ml.ml_line_count)
{
emsg(_(e_invrange));
return;
}
di = dict_find(dict, (char_u *)"skipstart", -1);
if (di != NULL)
skipstart = tv_get_number(&di->di_tv);
if (dict_find(dict, (char_u *)"id", -1) != NULL)
id = dict_get_number(dict, (char_u *)"id");
if (dict_find(dict, (char_u *)"type", -1))
{
char_u *name = dict_get_string(dict, (char_u *)"type", FALSE);
proptype_T *type = lookup_prop_type(name, buf);
if (type == NULL)
return;
type_id = type->pt_id;
}
if (id == -1 && type_id == -1)
{
emsg(_("E968: Need at least one of 'id' or 'type'"));
return;
}
lnum_start = lnum;
if (rettv_dict_alloc(rettv) == FAIL)
return;
while (1)
{
char_u *text = ml_get_buf(buf, lnum, FALSE);
size_t textlen = STRLEN(text) + 1;
int count = (int)((buf->b_ml.ml_line_len - textlen)
/ sizeof(textprop_T));
int i;
textprop_T prop;
int prop_start;
int prop_end;
for (i = 0; i < count; ++i)
{
mch_memmove(&prop, text + textlen + i * sizeof(textprop_T),
sizeof(textprop_T));
if (prop.tp_id == id || prop.tp_type == type_id)
{
// Check if the starting position has text props.
if (lnum_start == lnum)
{
if (col >= prop.tp_col
&& (col <= prop.tp_col + prop.tp_len-1))
start_pos_has_prop = 1;
}
else
{
// Not at the first line of the search so adjust col to
// indicate that we're continuing from prev/next line.
if (dir < 0)
col = buf->b_ml.ml_line_len;
else
col = 1;
}
prop_start = !(prop.tp_flags & TP_FLAG_CONT_PREV);
prop_end = !(prop.tp_flags & TP_FLAG_CONT_NEXT);
if (!prop_start && prop_end && dir > 0)
seen_end = 1;
// Skip lines without the start flag.
if (!prop_start)
{
// Always search backwards for start when search started
// on a prop and we're not skipping.
if (start_pos_has_prop && !skipstart)
dir = -1;
break;
}
// If skipstart is true, skip the prop at start pos (even if
// continued from another line).
if (start_pos_has_prop && skipstart && !seen_end)
{
start_pos_has_prop = 0;
break;
}
if (dir < 0)
{
if (col < prop.tp_col)
break;
}
else
{
if (col > prop.tp_col + prop.tp_len-1)
break;
}
prop_fill_dict(rettv->vval.v_dict, &prop, buf);
dict_add_number(rettv->vval.v_dict, "lnum", lnum);
return;
}
}
if (dir > 0)
{
if (lnum >= buf->b_ml.ml_line_count)
break;
lnum++;
}
else
{
if (lnum <= 1)
break;
lnum--;
}
}
}
/* /*
* prop_list({lnum} [, {bufnr}]) * prop_list({lnum} [, {bufnr}])
*/ */
@@ -564,7 +766,6 @@ f_prop_list(typval_T *argvars, typval_T *rettv)
/ sizeof(textprop_T)); / sizeof(textprop_T));
int i; int i;
textprop_T prop; textprop_T prop;
proptype_T *pt;
for (i = 0; i < count; ++i) for (i = 0; i < count; ++i)
{ {
@@ -574,15 +775,7 @@ f_prop_list(typval_T *argvars, typval_T *rettv)
break; break;
mch_memmove(&prop, text + textlen + i * sizeof(textprop_T), mch_memmove(&prop, text + textlen + i * sizeof(textprop_T),
sizeof(textprop_T)); sizeof(textprop_T));
dict_add_number(d, "col", prop.tp_col); prop_fill_dict(d, &prop, buf);
dict_add_number(d, "length", prop.tp_len);
dict_add_number(d, "id", prop.tp_id);
dict_add_number(d, "start", !(prop.tp_flags & TP_FLAG_CONT_PREV));
dict_add_number(d, "end", !(prop.tp_flags & TP_FLAG_CONT_NEXT));
pt = text_prop_type_by_id(buf, prop.tp_type);
if (pt != NULL)
dict_add_string(d, "type", pt->pt_name);
list_append_dict(rettv->vval.v_list, d); list_append_dict(rettv->vval.v_list, d);
} }
} }

View File

@@ -742,6 +742,8 @@ static char *(features[]) =
static int included_patches[] = static int included_patches[] =
{ /* Add new patch number below this line */ { /* Add new patch number below this line */
/**/
110,
/**/ /**/
109, 109,
/**/ /**/