1 /*
2  * glspi_doc.c - This file is part of the Lua scripting plugin for the Geany IDE
3  * See the file "geanylua.c" for copyright information.
4  */
5 
6 #define NEED_FAIL_ARG_TYPE
7 #define NEED_FAIL_ARG_TYPES
8 #include "glspi.h"
9 
10 
11 #define NOTEBOOK GTK_NOTEBOOK(main_widgets->notebook)
12 
13 
14 #ifdef G_OS_WIN32
15 #define fncmp(a,b) ( a && b && (strcasecmp(a,b)==0))
16 #else
17 #define fncmp(a,b) ( a && b && (strcmp(a,b)==0))
18 #endif
19 
20 
21 
22 /* Return the filename of the currently active Geany document */
glspi_filename(lua_State * L)23 static gint glspi_filename(lua_State* L)
24 {
25 	DOC_REQUIRED
26 	lua_pushstring(L, (const gchar *) doc->file_name);
27 	return 1;
28 }
29 
30 
31 /* Create a new Geany document tab */
glspi_newfile(lua_State * L)32 static gint glspi_newfile(lua_State* L)
33 {
34 	const gchar *fn=NULL;
35 	GeanyFiletype *ft=NULL;
36 	switch (lua_gettop(L)) {
37 	case 0: break;
38 	case 2:
39 		if (!lua_isstring(L, 2))	{ return FAIL_STRING_ARG(2); }
40 		const gchar *tmp=lua_tostring(L, 2);
41 		if ( '\0' == tmp[0] ) {
42 			ft=NULL;
43 		} else {
44 			ft=filetypes_lookup_by_name(tmp);
45 		}
46 	default:
47 		if (!lua_isstring(L, 1))	{ return FAIL_STRING_ARG(1); }
48 		fn=lua_tostring(L, 1);
49 		if ( '\0' == fn[0] ) { fn=NULL; }
50 	}
51 	document_new_file(fn, ft, NULL);
52 	return 0;
53 }
54 
55 
56 
57 /*
58 	Try to find the geany->documents_array index of the specified filename.
59 	Returns -1 if the filename doesn't match any open tabs.
60 */
filename_to_doc_idx(const gchar * fn)61 static gint filename_to_doc_idx(const gchar*fn)
62 {
63 	if (fn && *fn) {
64 		guint i=0;
65 		foreach_document(i)
66 		{
67 			if fncmp(fn,documents[i]->file_name) {return i; }
68 		}
69 	}
70 	return -1;
71 }
72 
73 
74 /* Converts a geany->documents_array index to a notebook tab index */
doc_idx_to_tab_idx(gint idx)75 static gint doc_idx_to_tab_idx(gint idx)
76 {
77 	return (
78 		(idx>=0) && ((guint)idx<geany->documents_array->len) && documents[idx]->is_valid
79 	) ? gtk_notebook_page_num(NOTEBOOK, GTK_WIDGET(documents[idx]->editor->sci)):-1;
80 }
81 
82 
83 
84 /* Returns the filename of the specified document, or NULL on bad index */
doc_idx_to_filename(gint idx)85 static const gchar* doc_idx_to_filename(gint idx) {
86 	if ( (idx >= 0 ) && ( ((guint)idx) < geany->documents_array->len ) ) {
87 		GeanyDocument *doc=g_ptr_array_index(geany->documents_array, idx);
88 		if (doc) { return doc->file_name?doc->file_name:GEANY_STRING_UNTITLED; }
89 	}
90 	return NULL;
91 }
92 
93 
94 
95 /* Actvate and focus the specified document */
glspi_activate(lua_State * L)96 static gint glspi_activate(lua_State* L)
97 {
98 	gint idx=-1;
99 	if (lua_gettop(L)>0) {
100 		if (lua_isnumber(L,1)) {
101 			idx=(lua_tonumber(L,1));
102 			if (idx<0) { /* Negative number refers to (absolute) GtkNotebook index */
103 				idx=(0-idx)-1;
104 				if (idx>=gtk_notebook_get_n_pages(NOTEBOOK)) { idx=-1;}
105 			} else { /* A positive number refers to the geany->documents_array index */
106 				idx=doc_idx_to_tab_idx(idx-1);
107 			}
108 
109 		} else {
110 			if (lua_isstring(L,1)) {
111 				idx=doc_idx_to_tab_idx(filename_to_doc_idx(lua_tostring(L, 1)));
112 			} else {
113 				if (!lua_isnil(L,1)) { return FAIL_STR_OR_NUM_ARG(1); }
114 			}
115 		}
116 	}
117 	if (idx>=0) {
118 		if (idx!=gtk_notebook_get_current_page(NOTEBOOK)) {
119 			gtk_notebook_set_current_page(NOTEBOOK, idx);
120 		}
121 	}
122 	lua_pushboolean(L, (idx>0));
123 	return 1;
124 }
125 
126 
127 
128 /* Lua "closure" function to iterate through the list of open documents */
documents_closure(lua_State * L)129 static gint documents_closure(lua_State *L)
130 {
131 	gint idx=lua_tonumber(L, lua_upvalueindex(1));
132 	int max=geany->documents_array->len;
133 	do {
134 		/* Find next valid index, skipping invalid (closed)  files */
135 		idx++;
136 	} while (( idx < max ) && !documents[idx]->is_valid );
137 	if ( idx < max ){
138 		push_number(L, idx);
139 		lua_pushvalue(L, -1);
140 		lua_replace(L, lua_upvalueindex(1));
141 		lua_pushstring(L,doc_idx_to_filename(idx));
142 		return 1;
143 	} else {
144 		return 0;
145 	}
146 }
147 
148 
149 /* Access the list of open documents */
glspi_documents(lua_State * L)150 static gint glspi_documents(lua_State *L)
151 {
152 	if (lua_gettop(L)==0) {
153 		push_number(L,-1);
154 		lua_pushcclosure(L, &documents_closure, 1);
155 		return 1;
156 	} else {
157 		gint idx;
158 		const gchar *name;
159 		DOC_REQUIRED
160 		if (lua_isnumber(L,1)) {
161 			idx=lua_tonumber(L,1)-1;
162 			name=doc_idx_to_filename(idx);
163 			if (name) {
164 				lua_pushstring(L,name);
165 				return 1;
166 			} else {
167 				return 0;
168 			}
169 		} else {
170 			if (lua_isstring(L,1)) {
171 				name=lua_tostring(L,1);
172 				idx=filename_to_doc_idx(name);
173 				if (idx>=0) {
174 					push_number(L,idx+1);
175 					return 1;
176 				} else { return 0; }
177 			} else { return FAIL_STR_OR_NUM_ARG(1); }
178 		}
179 	}
180 }
181 
182 
183 /* Returns the number of open documents */
glspi_count(lua_State * L)184 static gint glspi_count(lua_State* L)
185 {
186 	guint i=0, n=0;
187 	foreach_document(i)
188 	{
189 		if (documents[i]->is_valid){n++;}
190 	}
191 	push_number(L,n);
192 	return 1;
193 }
194 
195 
196 /* Save a file to disk */
glspi_save(lua_State * L)197 static gint glspi_save(lua_State* L)
198 {
199 	gboolean status=FALSE;
200 	if (lua_gettop(L)==0){
201 		DOC_REQUIRED
202 		status=document_save_file(document_get_current(), TRUE);
203 	} else {
204 		if (lua_isnumber(L,1)) {
205 			gint idx=(gint)lua_tonumber(L,1)-1;
206 			status=document_save_file(documents[idx], TRUE);
207 		} else {
208 			if (lua_isstring(L,1)) {
209 				gint idx=filename_to_doc_idx(lua_tostring(L,1));
210 				status=document_save_file(documents[idx], TRUE);
211 			} else { return FAIL_STR_OR_NUM_ARG(1);	}
212 		}
213 	}
214 	lua_pushboolean(L,status);
215 	return 1;
216 }
217 
218 
219 /* Open or reload a file */
glspi_open(lua_State * L)220 static gint glspi_open(lua_State* L)
221 {
222 	gint status=-1;
223 	const gchar*fn=NULL;
224 	gint idx=-1;
225 
226 	if (lua_gettop(L)==0) {
227 		DOC_REQUIRED
228 		idx=document_get_current()->index;
229 	} else {
230 		if (lua_isnumber(L,1)) {
231 			idx=lua_tonumber(L,1)-1;
232 		} else {
233 			if (lua_isstring(L,1)) {
234 				fn=lua_tostring(L,1);
235 			} else { return FAIL_STR_OR_NUM_ARG(1); }
236 		}
237 	}
238 	if (!fn) {
239 		status=document_reload_force(documents[idx],NULL) ? idx : -1;
240 	} else {
241 		guint len=geany->documents_array->len;
242 		GeanyDocument*doc=document_open_file(fn,FALSE,NULL,NULL);
243 		status=doc?doc->index:-1;
244 		if ( (status>=0) && (len==geany->documents_array->len))
245 		{
246 			/* if len doesn't change, it means we are reloading an already open file */
247 			/* ntrel: actually, len can stay the same when reusing invalid document slots. */
248 			idx=document_get_current()->index;
249 			status=document_reload_force(documents[idx],NULL) ? idx : -1;
250 		}
251 	}
252 	push_number(L,status+1);
253 	return 1;
254 }
255 
256 
257 /* Close a document */
glspi_close(lua_State * L)258 static gint glspi_close(lua_State* L)
259 {
260 	gboolean status=FALSE;
261 	if (lua_gettop(L)==0){
262 		DOC_REQUIRED
263 		status=document_close(document_get_current());
264 	} else {
265 		if (lua_isnumber(L,1)) {
266 			guint idx=(guint)lua_tonumber(L,1)-1;
267 			status=document_close(documents[idx]);
268 		} else {
269 			if (lua_isstring(L,1)) {
270 				guint idx=(guint)filename_to_doc_idx(lua_tostring(L,1));
271 				status=document_close(documents[idx]);
272 			} else { return FAIL_STR_OR_NUM_ARG(1);	}
273 		}
274 	}
275 	lua_pushboolean(L,status);
276 	return 1;
277 }
278 
279 
280 #define StrField(rec,field) rec?rec->field?rec->field:"":""
281 
282 #define FileTypeStr(field) StrField(doc->file_type,field)
283 
284 #define BuildCmdStr(field) \
285 	doc->file_type?StrField(doc->file_type->programs,field):""
286 
287 
288 /* Retrieve Geany's information about an open document */
glspi_fileinfo(lua_State * L)289 static gint glspi_fileinfo(lua_State* L)
290 {
291 	DOC_REQUIRED
292 	lua_newtable(L);
293 	if (doc->file_name) {
294 		gchar*tmp,*p;
295 		tmp=g_path_get_dirname (doc->file_name);
296 		p=strchr(tmp,'\0');
297 		if (p>tmp) {p--;}
298 		lua_pushstring(L, "path");
299 		if (p && (*p==G_DIR_SEPARATOR)){
300 			lua_pushstring(L, tmp);
301 		} else {
302 			lua_pushfstring(L, "%s%s", tmp, G_DIR_SEPARATOR_S);
303 		}
304 		lua_rawset(L,-3);
305 		g_free(tmp);
306 
307 		tmp=g_path_get_basename (doc->file_name);
308 		p=strrchr(tmp,'.');
309 		if (p==tmp) {p=NULL;}
310 		SetTableStr("name", tmp);
311 		SetTableStr("ext", p?p:"");
312 		g_free(tmp);
313 	} else {
314 		SetTableStr("name", "")
315 		SetTableStr("path", "")
316 	}
317 	SetTableStr("type",   FileTypeStr(name));
318 	SetTableStr("desc",   FileTypeStr(title));
319 	SetTableStr("opener", FileTypeStr(comment_open));
320 	SetTableStr("closer", FileTypeStr(comment_close));
321 	SetTableStr("action", FileTypeStr(context_action_cmd));
322 /*
323 	SetTableStr("compiler", BuildCmdStr(compiler));
324 	SetTableStr("linker",   BuildCmdStr(linker));
325 	SetTableStr("exec",     BuildCmdStr(run_cmd));
326 	SetTableStr("exec2",    BuildCmdStr(run_cmd2));
327 */
328 	SetTableNum("ftid", GPOINTER_TO_INT(doc->file_type?doc->file_type->id:GEANY_FILETYPES_NONE));
329 	SetTableStr("encoding", StrField(doc,encoding));
330 	SetTableBool("bom",doc->has_bom);
331 	SetTableBool("changed",doc->changed);
332 	SetTableBool("readonly",doc->readonly);
333 	return 1;
334 }
335 
336 
337 
338 
339 static const struct luaL_reg glspi_doc_funcs[] = {
340 	{"filename",  glspi_filename},
341 	{"fileinfo",  glspi_fileinfo},
342 	{"documents", glspi_documents},
343 	{"count",     glspi_count},
344 	{"activate",  glspi_activate},
345 	{"newfile",   glspi_newfile},
346 	{"save",      glspi_save},
347 	{"open",      glspi_open},
348 	{"close",     glspi_close},
349 	{NULL,NULL}
350 };
351 
glspi_init_doc_funcs(lua_State * L)352 void glspi_init_doc_funcs(lua_State *L) {
353 	luaL_register(L, NULL,glspi_doc_funcs);
354 }
355