xref: /vim-8.2.3635/src/beval.c (revision b171fb17)
1 /* vi:set ts=8 sts=4 sw=4 noet:
2  *
3  * VIM - Vi IMproved	by Bram Moolenaar
4  *			Visual Workshop integration by Gordon Prieur
5  *
6  * Do ":help uganda"  in Vim to read copying and usage conditions.
7  * Do ":help credits" in Vim to see a list of people who contributed.
8  * See README.txt for an overview of the Vim source code.
9  */
10 
11 #include "vim.h"
12 
13 #if defined(FEAT_BEVAL) || defined(FEAT_PROP_POPUP) || defined(PROTO)
14 /*
15  * Find text under the mouse position "row" / "col".
16  * If "getword" is TRUE the returned text in "*textp" is not the whole line but
17  * the relevant word in allocated memory.
18  * Return OK if found.
19  * Return FAIL if not found, no text at the mouse position.
20  */
21     int
find_word_under_cursor(int mouserow,int mousecol,int getword,int flags,win_T ** winp,linenr_T * lnump,char_u ** textp,int * colp,int * startcolp)22 find_word_under_cursor(
23 	int	    mouserow,
24 	int	    mousecol,
25 	int	    getword,
26 	int	    flags,	// flags for find_ident_at_pos()
27 	win_T	    **winp,	// can be NULL
28 	linenr_T    *lnump,	// can be NULL
29 	char_u	    **textp,
30 	int	    *colp,	// column where mouse hovers, can be NULL
31 	int	    *startcolp) // column where text starts, can be NULL
32 {
33     int		row = mouserow;
34     int		col = mousecol;
35     int		scol;
36     win_T	*wp;
37     char_u	*lbuf;
38     linenr_T	lnum;
39 
40     *textp = NULL;
41     wp = mouse_find_win(&row, &col, FAIL_POPUP);
42     if (wp != NULL && row >= 0 && row < wp->w_height && col < wp->w_width)
43     {
44 	// Found a window and the cursor is in the text.  Now find the line
45 	// number.
46 	if (!mouse_comp_pos(wp, &row, &col, &lnum, NULL))
47 	{
48 	    // Not past end of the file.
49 	    lbuf = ml_get_buf(wp->w_buffer, lnum, FALSE);
50 	    if (col <= win_linetabsize(wp, lbuf, (colnr_T)MAXCOL))
51 	    {
52 		// Not past end of line.
53 		if (getword)
54 		{
55 		    // For Netbeans we get the relevant part of the line
56 		    // instead of the whole line.
57 		    int		len;
58 		    pos_T	*spos = NULL, *epos = NULL;
59 
60 		    if (VIsual_active)
61 		    {
62 			if (LT_POS(VIsual, curwin->w_cursor))
63 			{
64 			    spos = &VIsual;
65 			    epos = &curwin->w_cursor;
66 			}
67 			else
68 			{
69 			    spos = &curwin->w_cursor;
70 			    epos = &VIsual;
71 			}
72 		    }
73 
74 		    col = vcol2col(wp, lnum, col);
75 		    scol = col;
76 
77 		    if (VIsual_active
78 			    && wp->w_buffer == curwin->w_buffer
79 			    && (lnum == spos->lnum
80 				? col >= (int)spos->col
81 				: lnum > spos->lnum)
82 			    && (lnum == epos->lnum
83 				? col <= (int)epos->col
84 				: lnum < epos->lnum))
85 		    {
86 			// Visual mode and pointing to the line with the
87 			// Visual selection: return selected text, with a
88 			// maximum of one line.
89 			if (spos->lnum != epos->lnum || spos->col == epos->col)
90 			    return FAIL;
91 
92 			lbuf = ml_get_buf(curwin->w_buffer, VIsual.lnum, FALSE);
93 			len = epos->col - spos->col;
94 			if (*p_sel != 'e')
95 			    len += mb_ptr2len(lbuf + epos->col);
96 			lbuf = vim_strnsave(lbuf + spos->col, len);
97 			lnum = spos->lnum;
98 			col = spos->col;
99 			scol = col;
100 		    }
101 		    else
102 		    {
103 			// Find the word under the cursor.
104 			++emsg_off;
105 			len = find_ident_at_pos(wp, lnum, (colnr_T)col,
106 							  &lbuf, &scol, flags);
107 			--emsg_off;
108 			if (len == 0)
109 			    return FAIL;
110 			lbuf = vim_strnsave(lbuf, len);
111 		    }
112 		}
113 		else
114 		    scol = col;
115 
116 		if (winp != NULL)
117 		    *winp = wp;
118 		if (lnump != NULL)
119 		    *lnump = lnum;
120 		*textp = lbuf;
121 		if (colp != NULL)
122 		    *colp = col;
123 		if (startcolp != NULL)
124 		    *startcolp = scol;
125 		return OK;
126 	    }
127 	}
128     }
129     return FAIL;
130 }
131 #endif
132 
133 #if defined(FEAT_BEVAL) || defined(PROTO)
134 
135 /*
136  * Get the text and position to be evaluated for "beval".
137  * If "getword" is TRUE the returned text is not the whole line but the
138  * relevant word in allocated memory.
139  * Returns OK or FAIL.
140  */
141     int
get_beval_info(BalloonEval * beval,int getword,win_T ** winp,linenr_T * lnump,char_u ** textp,int * colp)142 get_beval_info(
143 	BalloonEval	*beval,
144 	int		getword,
145 	win_T		**winp,
146 	linenr_T	*lnump,
147 	char_u		**textp,
148 	int		*colp)
149 {
150     int		row = mouse_row;
151     int		col = mouse_col;
152 
153 # ifdef FEAT_GUI
154     if (gui.in_use)
155     {
156 	row = Y_2_ROW(beval->y);
157 	col = X_2_COL(beval->x);
158     }
159 #endif
160     if (find_word_under_cursor(row, col, getword,
161 		FIND_IDENT + FIND_STRING + FIND_EVAL,
162 		winp, lnump, textp, colp, NULL) == OK)
163     {
164 #ifdef FEAT_VARTABS
165 	vim_free(beval->vts);
166 	beval->vts = tabstop_copy((*winp)->w_buffer->b_p_vts_array);
167 	if ((*winp)->w_buffer->b_p_vts_array != NULL && beval->vts == NULL)
168 	{
169 	    if (getword)
170 		vim_free(*textp);
171 	    return FAIL;
172 	}
173 #endif
174 	beval->ts = (*winp)->w_buffer->b_p_ts;
175 	return OK;
176     }
177 
178     return FAIL;
179 }
180 
181 /*
182  * Show a balloon with "mesg" or "list".
183  * Hide the balloon when both are NULL.
184  */
185     void
post_balloon(BalloonEval * beval UNUSED,char_u * mesg,list_T * list UNUSED)186 post_balloon(BalloonEval *beval UNUSED, char_u *mesg, list_T *list UNUSED)
187 {
188 # ifdef FEAT_BEVAL_TERM
189 #  ifdef FEAT_GUI
190     if (!gui.in_use)
191 #  endif
192 	ui_post_balloon(mesg, list);
193 # endif
194 # ifdef FEAT_BEVAL_GUI
195     if (gui.in_use)
196 	// GUI can't handle a list
197 	gui_mch_post_balloon(beval, mesg);
198 # endif
199 }
200 
201 /*
202  * Returns TRUE if the balloon eval has been enabled:
203  * 'ballooneval' for the GUI and 'balloonevalterm' for the terminal.
204  * Also checks if the screen isn't scrolled up.
205  */
206     int
can_use_beval(void)207 can_use_beval(void)
208 {
209     return (0
210 #ifdef FEAT_BEVAL_GUI
211 		|| (gui.in_use && p_beval)
212 #endif
213 #ifdef FEAT_BEVAL_TERM
214 		|| (
215 # ifdef FEAT_GUI
216 		    !gui.in_use &&
217 # endif
218 		    p_bevalterm)
219 #endif
220 	     ) && msg_scrolled == 0;
221 }
222 
223 /*
224  * Common code, invoked when the mouse is resting for a moment.
225  */
226     void
general_beval_cb(BalloonEval * beval,int state UNUSED)227 general_beval_cb(BalloonEval *beval, int state UNUSED)
228 {
229 #ifdef FEAT_EVAL
230     win_T	*wp;
231     int		col;
232     int		use_sandbox;
233     linenr_T	lnum;
234     char_u	*text;
235     static char_u  *result = NULL;
236     long	winnr = 0;
237     char_u	*bexpr;
238     buf_T	*save_curbuf;
239     size_t	len;
240     win_T	*cw;
241 #endif
242     static int	recursive = FALSE;
243 
244     // Don't do anything when 'ballooneval' is off, messages scrolled the
245     // windows up or we have no beval area.
246     if (!can_use_beval() || beval == NULL)
247 	return;
248 
249     // Don't do this recursively.  Happens when the expression evaluation
250     // takes a long time and invokes something that checks for CTRL-C typed.
251     if (recursive)
252 	return;
253     recursive = TRUE;
254 
255 #ifdef FEAT_EVAL
256     if (get_beval_info(beval, TRUE, &wp, &lnum, &text, &col) == OK)
257     {
258 	bexpr = (*wp->w_buffer->b_p_bexpr == NUL) ? p_bexpr
259 						    : wp->w_buffer->b_p_bexpr;
260 	if (*bexpr != NUL)
261 	{
262 	    // Convert window pointer to number.
263 	    for (cw = firstwin; cw != wp; cw = cw->w_next)
264 		++winnr;
265 
266 	    set_vim_var_nr(VV_BEVAL_BUFNR, (long)wp->w_buffer->b_fnum);
267 	    set_vim_var_nr(VV_BEVAL_WINNR, winnr);
268 	    set_vim_var_nr(VV_BEVAL_WINID, wp->w_id);
269 	    set_vim_var_nr(VV_BEVAL_LNUM, (long)lnum);
270 	    set_vim_var_nr(VV_BEVAL_COL, (long)(col + 1));
271 	    set_vim_var_string(VV_BEVAL_TEXT, text, -1);
272 	    vim_free(text);
273 
274 	    /*
275 	     * Temporarily change the curbuf, so that we can determine whether
276 	     * the buffer-local balloonexpr option was set insecurely.
277 	     */
278 	    save_curbuf = curbuf;
279 	    curbuf = wp->w_buffer;
280 	    use_sandbox = was_set_insecurely((char_u *)"balloonexpr",
281 				 *curbuf->b_p_bexpr == NUL ? 0 : OPT_LOCAL);
282 	    curbuf = save_curbuf;
283 	    if (use_sandbox)
284 		++sandbox;
285 	    ++textwinlock;
286 
287 	    vim_free(result);
288 	    result = eval_to_string(bexpr, TRUE);
289 
290 	    // Remove one trailing newline, it is added when the result was a
291 	    // list and it's hardly ever useful.  If the user really wants a
292 	    // trailing newline he can add two and one remains.
293 	    if (result != NULL)
294 	    {
295 		len = STRLEN(result);
296 		if (len > 0 && result[len - 1] == NL)
297 		    result[len - 1] = NUL;
298 	    }
299 
300 	    if (use_sandbox)
301 		--sandbox;
302 	    --textwinlock;
303 
304 	    set_vim_var_string(VV_BEVAL_TEXT, NULL, -1);
305 	    if (result != NULL && result[0] != NUL)
306 		post_balloon(beval, result, NULL);
307 
308 	    // The 'balloonexpr' evaluation may show something on the screen
309 	    // that requires a screen update.
310 	    if (must_redraw)
311 		redraw_after_callback(FALSE);
312 
313 	    recursive = FALSE;
314 	    return;
315 	}
316     }
317 #endif
318 #ifdef FEAT_NETBEANS_INTG
319     if (bevalServers & BEVAL_NETBEANS)
320 	netbeans_beval_cb(beval, state);
321 #endif
322 
323     recursive = FALSE;
324 }
325 
326 #endif
327