Guest User

gb vim parser

a guest
Jul 1st, 2015
358
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 420.32 KB | None | 0 0
  1. From 2473d3505c88fededc373e666cec660acb93d7f8 Mon Sep 17 00:00:00 2001
  2. From: Sebastien Lafargue <slafargue@gnome.org>
  3. Date: Thu, 30 Apr 2015 18:14:22 +0200
  4. Subject: \vim: new parser for command line
  5.  
  6. ---
  7. data/keybindings/default.css | 4 +-
  8. data/keybindings/emacs.css | 6 +-
  9. data/theme/shared.css | 23 +
  10. libide/Makefile.am | 28 +-
  11. libide/ide-source-view.c | 19 +-
  12. libide/ide-source-view.h | 3 +-
  13. libide/ide-vim-iter.c | 457 -----
  14. libide/ide-vim-iter.h | 39 -
  15. libide/ide.h | 7 +
  16. libide/vim/ide-vim-complete-item.c | 261 +++
  17. libide/vim/ide-vim-complete-item.h | 58 +
  18. libide/vim/ide-vim-iter.c | 457 +++++
  19. libide/vim/ide-vim-iter.h | 39 +
  20. libide/vim/ide-vim-parser-command.c | 633 +++++++
  21. libide/vim/ide-vim-parser-command.h | 66 +
  22. libide/vim/ide-vim-parser-commands.c | 29 +
  23. libide/vim/ide-vim-parser-commands.h | 41 +
  24. libide/vim/ide-vim-parser-debug.c | 151 ++
  25. libide/vim/ide-vim-parser-debug.h | 36 +
  26. libide/vim/ide-vim-parser-error.c | 340 ++++
  27. libide/vim/ide-vim-parser-error.h | 93 +
  28. libide/vim/ide-vim-parser-objects-pool.c | 496 ++++++
  29. libide/vim/ide-vim-parser-objects-pool.h | 63 +
  30. libide/vim/ide-vim-parser-private.h | 37 +
  31. libide/vim/ide-vim-parser-settable.c | 559 ++++++
  32. libide/vim/ide-vim-parser-settable.h | 93 +
  33. libide/vim/ide-vim-parser-token.c | 124 ++
  34. libide/vim/ide-vim-parser-token.h | 92 +
  35. libide/vim/ide-vim-parser-types.h | 28 +
  36. libide/vim/ide-vim-parser.c | 1926 +++++++++++++++++++++
  37. libide/vim/ide-vim-parser.h | 125 ++
  38. libide/vim/ide-vim-state-machine.c | 500 ++++++
  39. libide/vim/ide-vim-state-machine.h | 63 +
  40. libide/vim/ide-vim-state-private.h | 37 +
  41. libide/vim/ide-vim-state-result.c | 73 +
  42. libide/vim/ide-vim-state-result.h | 39 +
  43. libide/vim/ide-vim-state.c | 162 ++
  44. libide/vim/ide-vim-state.h | 71 +
  45. plugins/command-bar/Makefile.am | 4 +
  46. plugins/command-bar/gb-command-bar-item.c | 257 +++
  47. plugins/command-bar/gb-command-bar-item.h | 39 +
  48. plugins/command-bar/gb-command-bar-item.ui | 83 +
  49. plugins/command-bar/gb-command-bar.c | 493 +++++-
  50. plugins/command-bar/gb-command-bar.gresource.xml | 1 +
  51. plugins/command-bar/gb-command-bar.ui | 67 +-
  52. plugins/command-bar/gb-command-complete-item.c | 113 ++
  53. plugins/command-bar/gb-command-complete-item.h | 53 +
  54. plugins/command-bar/gb-command-gaction-provider.c | 35 +-
  55. plugins/command-bar/gb-command-gaction-provider.h | 1 +
  56. plugins/command-bar/gb-command-manager.c | 61 +-
  57. plugins/command-bar/gb-command-manager.h | 17 +-
  58. plugins/command-bar/gb-command-provider.c | 77 +-
  59. plugins/command-bar/gb-command-provider.h | 41 +-
  60. plugins/command-bar/gb-command-vim-provider.c | 195 ++-
  61. plugins/command-bar/gb-command-vim-provider.h | 1 +
  62. plugins/command-bar/gb-command-vim.c | 111 +-
  63. plugins/command-bar/gb-command-vim.h | 6 +
  64. plugins/command-bar/gb-command.c | 171 +-
  65. plugins/command-bar/gb-command.h | 9 +-
  66. plugins/command-bar/gb-vim.c | 591 ++++---
  67. plugins/command-bar/gb-vim.h | 98 +-
  68. src/editor/gb-editor-frame.c | 2 +
  69. src/resources/gnome-builder.gresource.xml | 1 -
  70. src/views/gb-view-stack-actions.c | 81 +-
  71. src/views/gb-view-stack-private.h | 1 -
  72. src/views/gb-view-stack.c | 105 +-
  73. tests/Makefile.am | 9 +
  74. tests/test-ide-vim-parser-settable.c | 280 +++
  75. tests/test-ide-vim-parser.c | 283 +++
  76. 69 files changed, 9542 insertions(+), 1022 deletions(-)
  77. delete mode 100644 libide/ide-vim-iter.c
  78. delete mode 100644 libide/ide-vim-iter.h
  79. create mode 100644 libide/vim/ide-vim-complete-item.c
  80. create mode 100644 libide/vim/ide-vim-complete-item.h
  81. create mode 100644 libide/vim/ide-vim-iter.c
  82. create mode 100644 libide/vim/ide-vim-iter.h
  83. create mode 100644 libide/vim/ide-vim-parser-command.c
  84. create mode 100644 libide/vim/ide-vim-parser-command.h
  85. create mode 100644 libide/vim/ide-vim-parser-commands.c
  86. create mode 100644 libide/vim/ide-vim-parser-commands.h
  87. create mode 100644 libide/vim/ide-vim-parser-debug.c
  88. create mode 100644 libide/vim/ide-vim-parser-debug.h
  89. create mode 100644 libide/vim/ide-vim-parser-error.c
  90. create mode 100644 libide/vim/ide-vim-parser-error.h
  91. create mode 100644 libide/vim/ide-vim-parser-objects-pool.c
  92. create mode 100644 libide/vim/ide-vim-parser-objects-pool.h
  93. create mode 100644 libide/vim/ide-vim-parser-private.h
  94. create mode 100644 libide/vim/ide-vim-parser-settable.c
  95. create mode 100644 libide/vim/ide-vim-parser-settable.h
  96. create mode 100644 libide/vim/ide-vim-parser-token.c
  97. create mode 100644 libide/vim/ide-vim-parser-token.h
  98. create mode 100644 libide/vim/ide-vim-parser-types.h
  99. create mode 100644 libide/vim/ide-vim-parser.c
  100. create mode 100644 libide/vim/ide-vim-parser.h
  101. create mode 100644 libide/vim/ide-vim-state-machine.c
  102. create mode 100644 libide/vim/ide-vim-state-machine.h
  103. create mode 100644 libide/vim/ide-vim-state-private.h
  104. create mode 100644 libide/vim/ide-vim-state-result.c
  105. create mode 100644 libide/vim/ide-vim-state-result.h
  106. create mode 100644 libide/vim/ide-vim-state.c
  107. create mode 100644 libide/vim/ide-vim-state.h
  108. create mode 100644 plugins/command-bar/gb-command-bar-item.c
  109. create mode 100644 plugins/command-bar/gb-command-bar-item.h
  110. create mode 100644 plugins/command-bar/gb-command-bar-item.ui
  111. create mode 100644 plugins/command-bar/gb-command-complete-item.c
  112. create mode 100644 plugins/command-bar/gb-command-complete-item.h
  113. create mode 100644 tests/test-ide-vim-parser-settable.c
  114. create mode 100644 tests/test-ide-vim-parser.c
  115.  
  116. diff --git a/data/keybindings/default.css b/data/keybindings/default.css
  117. index 2563d71..47466cf 100644
  118. --- a/data/keybindings/default.css
  119. +++ b/data/keybindings/default.css
  120. @@ -38,8 +38,8 @@
  121. bind "F6" { "action" ("view", "preview", "") };
  122. bind "F9" { "action" ("workspace", "toggle-sidebar", "") };
  123.  
  124. - bind "<alt>n" { "move-error" (down) };
  125. - bind "<alt>p" { "move-error" (up) };
  126. + bind "<alt>n" { "move-error" (down, 1) };
  127. + bind "<alt>p" { "move-error" (up, 1) };
  128.  
  129. bind "<ctrl>Return" { "action" ("win", "show-command-bar", "") };
  130. bind "<ctrl>KP_Enter" { "action" ("win", "show-command-bar", "") };
  131. diff --git a/data/keybindings/emacs.css b/data/keybindings/emacs.css
  132. index 5f537a1..d0cd58c 100644
  133. --- a/data/keybindings/emacs.css
  134. +++ b/data/keybindings/emacs.css
  135. @@ -55,8 +55,8 @@
  136. bind "<ctrl>period" { "action" ("workbench", "global-search", "") };
  137. bind "<alt>period" { "goto-definition" () };
  138. bind "<ctrl>comma" { "action" ("app", "preferences", "") };
  139. - bind "<alt>n" { "move-error" (down) };
  140. - bind "<alt>p" { "move-error" (up) };
  141. + bind "<alt>n" { "move-error" (down, 1) };
  142. + bind "<alt>p" { "move-error" (up, 1) };
  143. bind "<ctrl>j" { "action" ("view-grid", "focus-neighbor", "3") };
  144. bind "<shift><ctrl>j" { "action" ("view-stack", "split-down", "") };
  145. bind "F2" { "clear-selection" ()
  146. @@ -111,7 +111,7 @@
  147. bind "2" { "action" ("view-stack", "split-down", "") };
  148. bind "3" { "action" ("view-stack", "split-right", "") };
  149. bind "o" { "action" ("view-grid", "focus-neighbor", "0") };
  150. - bind "grave" { "move-error" (down) };
  151. + bind "grave" { "move-error" (down), 1 };
  152. bind "h" { "select-all" (1) };
  153. }
  154.  
  155. diff --git a/data/theme/shared.css b/data/theme/shared.css
  156. index 53e2958..9934f53 100644
  157. --- a/data/theme/shared.css
  158. +++ b/data/theme/shared.css
  159. @@ -78,6 +78,29 @@ GtkEntry.gb-command-bar-entry {
  160. color: #eeeeec;
  161. }
  162.  
  163. +.gb-command-bar-results,
  164. +.gb-command-bar-results GtkListBox {
  165. + background-color: #2e3436;
  166. +}
  167. +.gb-command-bar-results GtkListBoxRow:hover {
  168. + background-color: #454a47;
  169. +}
  170. +.gb-command-bar-results GtkListBoxRow:selected {
  171. + background-color: #2e5797;
  172. +}
  173. +.gb-command-bar-results GtkListBoxRow:hover:selected {
  174. + background-color: #3a669f;
  175. +}
  176. +
  177. +.gb-command-bar-box GtkButton {
  178. + border-color: #43484A;
  179. + box-shadow: none;
  180. + background-image: inherit;
  181. +}
  182. +.gb-command-bar-box GtkButton:hover {
  183. + outline-color: rgba(238, 238, 236, 0.3);
  184. + background-image: linear-gradient(to bottom, #2e3436, #555753 10%);
  185. +}
  186.  
  187. /*
  188. * Style selector widget.
  189. diff --git a/libide/Makefile.am b/libide/Makefile.am
  190. index b658b24..cd8f2c4 100644
  191. --- a/libide/Makefile.am
  192. +++ b/libide/Makefile.am
  193. @@ -184,6 +184,21 @@ libide_1_0_la_public_sources = \
  194. theatrics/ide-animation.h \
  195. vala/ide-vala-language.c \
  196. vala/ide-vala-language.h \
  197. + vim/ide-vim-parser.c \
  198. + vim/ide-vim-parser.h \
  199. + vim/ide-vim-parser-command.c \
  200. + vim/ide-vim-parser-command.h \
  201. + vim/ide-vim-parser-debug.c \
  202. + vim/ide-vim-parser-error.c \
  203. + vim/ide-vim-parser-error.h \
  204. + vim/ide-vim-parser-objects-pool.c \
  205. + vim/ide-vim-parser-objects-pool.h \
  206. + vim/ide-vim-parser-settable.c \
  207. + vim/ide-vim-parser-settable.h \
  208. + vim/ide-vim-parser-token.c \
  209. + vim/ide-vim-parser-token.h \
  210. + vim/ide-vim-complete-item.c \
  211. + vim/ide-vim-complete-item.h \
  212. xml/ide-xml-language.c \
  213. xml/ide-xml-language.h \
  214. $(NULL)
  215. @@ -292,8 +307,6 @@ libide_1_0_la_SOURCES = \
  216. ide-source-view-capture.h \
  217. ide-source-view-movements.c \
  218. ide-source-view-movements.h \
  219. - ide-vim-iter.c \
  220. - ide-vim-iter.h \
  221. mingw/ide-mingw-device-provider.c \
  222. mingw/ide-mingw-device-provider.h \
  223. modelines/ide-modelines-file-settings.c \
  224. @@ -324,6 +337,16 @@ libide_1_0_la_SOURCES = \
  225. util/ide-rgba.h \
  226. util/ide-xml.c \
  227. util/ide-xml.h \
  228. + vim/ide-vim-iter.c \
  229. + vim/ide-vim-iter.h \
  230. + vim/ide-vim-parser-commands.c \
  231. + vim/ide-vim-parser-commands.h \
  232. + vim/ide-vim-state.c \
  233. + vim/ide-vim-state.h \
  234. + vim/ide-vim-state-machine.c \
  235. + vim/ide-vim-state-machine.h \
  236. + vim/ide-vim-state-result.c \
  237. + vim/ide-vim-state-result.h \
  238. xml/ide-xml-highlighter.c \
  239. xml/ide-xml-highlighter.h \
  240. xml/ide-xml-indenter.c \
  241. @@ -362,6 +385,7 @@ libide_1_0_la_includes = \
  242. -I$(srcdir)/theatrics \
  243. -I$(srcdir)/util \
  244. -I$(srcdir)/vala \
  245. + -I$(srcdir)/vim \
  246. -I$(srcdir)/xml \
  247. $(NULL)
  248.  
  249. diff --git a/libide/ide-source-view.c b/libide/ide-source-view.c
  250. index 1e31f16..d0d4f90 100644
  251. --- a/libide/ide-source-view.c
  252. +++ b/libide/ide-source-view.c
  253. @@ -3219,7 +3219,8 @@ ide_source_view_real_move_search (IdeSourceView *self,
  254.  
  255. static void
  256. ide_source_view_real_move_error (IdeSourceView *self,
  257. - GtkDirectionType dir)
  258. + GtkDirectionType dir,
  259. + gint count)
  260. {
  261. IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
  262. GtkTextBuffer *buffer;
  263. @@ -3272,11 +3273,13 @@ ide_source_view_real_move_error (IdeSourceView *self,
  264. break;
  265.  
  266. gtk_text_buffer_select_range (buffer, &iter, &iter);
  267. - ide_source_view_scroll_mark_onscreen (self, insert, TRUE, 0.5, 0.5);
  268. - return;
  269. + g_signal_emit_by_name (self, "save-insert-mark");
  270. + if (--count == 0)
  271. + {
  272. + ide_source_view_scroll_mark_onscreen (self, insert, TRUE, 0.5, 0.5);
  273. + return;
  274. + }
  275. }
  276. -
  277. - break;
  278. }
  279. }
  280. }
  281. @@ -5525,6 +5528,7 @@ ide_source_view_class_init (IdeSourceViewClass *klass)
  282. * IdeSourceView::move-error:
  283. * @self: An #IdeSourceView.
  284. * @dir: The direction to move.
  285. + * @count: The nth error to go.
  286. *
  287. * Moves to the next search result either forwards or backwards.
  288. */
  289. @@ -5535,8 +5539,9 @@ ide_source_view_class_init (IdeSourceViewClass *klass)
  290. G_STRUCT_OFFSET (IdeSourceViewClass, move_error),
  291. NULL, NULL, NULL,
  292. G_TYPE_NONE,
  293. - 1,
  294. - GTK_TYPE_DIRECTION_TYPE);
  295. + 2,
  296. + GTK_TYPE_DIRECTION_TYPE,
  297. + G_TYPE_INT);
  298.  
  299. gSignals [MOVE_SEARCH] =
  300. g_signal_new ("move-search",
  301. diff --git a/libide/ide-source-view.h b/libide/ide-source-view.h
  302. index 2e6cac9..40de334 100644
  303. --- a/libide/ide-source-view.h
  304. +++ b/libide/ide-source-view.h
  305. @@ -222,7 +222,8 @@ struct _IdeSourceViewClass
  306. gboolean exclusive,
  307. gboolean apply_count);
  308. void (*move_error) (IdeSourceView *self,
  309. - GtkDirectionType dir);
  310. + GtkDirectionType dir,
  311. + gint count);
  312. void (*move_search) (IdeSourceView *self,
  313. GtkDirectionType dir,
  314. gboolean extend_selection,
  315. diff --git a/libide/ide-vim-iter.c b/libide/ide-vim-iter.c
  316. deleted file mode 100644
  317. index d03b829..0000000
  318. --- a/libide/ide-vim-iter.c
  319. +++ /dev/null
  320. @@ -1,457 +0,0 @@
  321. -/* ide-vim-iter.c
  322. - *
  323. - * Copyright (C) 2015 Christian Hergert <christian@hergert.me>
  324. - *
  325. - * This program is free software: you can redistribute it and/or modify
  326. - * it under the terms of the GNU General Public License as published by
  327. - * the Free Software Foundation, either version 3 of the License, or
  328. - * (at your option) any later version.
  329. - *
  330. - * This program is distributed in the hope that it will be useful,
  331. - * but WITHOUT ANY WARRANTY; without even the implied warranty of
  332. - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  333. - * GNU General Public License for more details.
  334. - *
  335. - * You should have received a copy of the GNU General Public License
  336. - * along with this program. If not, see <http://www.gnu.org/licenses/>.
  337. - */
  338. -
  339. -#include <gtk/gtk.h>
  340. -
  341. -#include "ide-debug.h"
  342. -#include "ide-vim-iter.h"
  343. -
  344. -typedef enum
  345. -{
  346. - SENTENCE_OK,
  347. - SENTENCE_PARA,
  348. - SENTENCE_FAILED,
  349. -} SentenceStatus;
  350. -
  351. -enum
  352. -{
  353. - CLASS_0,
  354. - CLASS_SPACE,
  355. - CLASS_SPECIAL,
  356. - CLASS_WORD,
  357. -};
  358. -
  359. -static int
  360. -_ide_vim_word_classify (gunichar ch)
  361. -{
  362. - switch (ch)
  363. - {
  364. - case ' ':
  365. - case '\t':
  366. - case '\n':
  367. - return CLASS_SPACE;
  368. -
  369. - case '"': case '\'':
  370. - case '(': case ')':
  371. - case '{': case '}':
  372. - case '[': case ']':
  373. - case '<': case '>':
  374. - case '-': case '+': case '*': case '/':
  375. - case '!': case '@': case '#': case '$': case '%':
  376. - case '^': case '&': case ':': case ';': case '?':
  377. - case '|': case '=': case '\\': case '.': case ',':
  378. - return CLASS_SPECIAL;
  379. -
  380. - case '_':
  381. - default:
  382. - return CLASS_WORD;
  383. - }
  384. -}
  385. -
  386. -static int
  387. -_ide_vim_WORD_classify (gunichar ch)
  388. -{
  389. - if (g_unichar_isspace (ch))
  390. - return CLASS_SPACE;
  391. - return CLASS_WORD;
  392. -}
  393. -
  394. -static gboolean
  395. -_ide_vim_iter_line_is_empty (GtkTextIter *iter)
  396. -{
  397. - return gtk_text_iter_starts_line (iter) && gtk_text_iter_ends_line (iter);
  398. -}
  399. -
  400. -/**
  401. - * _ide_vim_iter_backward_paragraph_start:
  402. - * @iter: A #GtkTextIter
  403. - *
  404. - * Searches backwards until we find the beginning of a paragraph.
  405. - *
  406. - * Returns: %TRUE if we are not at the beginning of the buffer; otherwise %FALSE.
  407. - */
  408. -gboolean
  409. -_ide_vim_iter_backward_paragraph_start (GtkTextIter *iter)
  410. -{
  411. - g_return_val_if_fail (iter, FALSE);
  412. -
  413. - /* Work our way past the current empty lines */
  414. - if (_ide_vim_iter_line_is_empty (iter))
  415. - while (_ide_vim_iter_line_is_empty (iter))
  416. - if (!gtk_text_iter_backward_line (iter))
  417. - return FALSE;
  418. -
  419. - /* Now find first line that is empty */
  420. - while (!_ide_vim_iter_line_is_empty (iter))
  421. - if (!gtk_text_iter_backward_line (iter))
  422. - return FALSE;
  423. -
  424. - return TRUE;
  425. -}
  426. -
  427. -/**
  428. - * _ide_vim_iter_forward_paragraph_end:
  429. - * @iter: A #GtkTextIter
  430. - *
  431. - * Searches forward until the end of a paragraph has been hit.
  432. - *
  433. - * Returns: %TRUE if we are not at the end of the buffer; otherwise %FALSE.
  434. - */
  435. -gboolean
  436. -_ide_vim_iter_forward_paragraph_end (GtkTextIter *iter)
  437. -{
  438. - g_return_val_if_fail (iter, FALSE);
  439. -
  440. - /* Work our way past the current empty lines */
  441. - if (_ide_vim_iter_line_is_empty (iter))
  442. - while (_ide_vim_iter_line_is_empty (iter))
  443. - if (!gtk_text_iter_forward_line (iter))
  444. - return FALSE;
  445. -
  446. - /* Now find first line that is empty */
  447. - while (!_ide_vim_iter_line_is_empty (iter))
  448. - if (!gtk_text_iter_forward_line (iter))
  449. - return FALSE;
  450. -
  451. - return TRUE;
  452. -}
  453. -
  454. -static gboolean
  455. -sentence_end_chars (gunichar ch,
  456. - gpointer user_data)
  457. -{
  458. - switch (ch)
  459. - {
  460. - case '!':
  461. - case '.':
  462. - case '?':
  463. - return TRUE;
  464. -
  465. - default:
  466. - return FALSE;
  467. - }
  468. -}
  469. -
  470. -static SentenceStatus
  471. -_ide_vim_iter_backward_sentence_end (GtkTextIter *iter)
  472. -{
  473. - GtkTextIter end_bounds;
  474. - GtkTextIter start_bounds;
  475. - gboolean found_para;
  476. -
  477. - g_return_val_if_fail (iter, FALSE);
  478. -
  479. - end_bounds = *iter;
  480. - start_bounds = *iter;
  481. - found_para = _ide_vim_iter_backward_paragraph_start (&start_bounds);
  482. -
  483. - if (!found_para)
  484. - gtk_text_buffer_get_start_iter (gtk_text_iter_get_buffer (iter), &start_bounds);
  485. -
  486. - while ((gtk_text_iter_compare (iter, &start_bounds) > 0) && gtk_text_iter_backward_char (iter))
  487. - {
  488. - if (gtk_text_iter_backward_find_char (iter, sentence_end_chars, NULL, &end_bounds))
  489. - {
  490. - GtkTextIter copy = *iter;
  491. -
  492. - while (gtk_text_iter_forward_char (&copy) && (gtk_text_iter_compare (&copy, &end_bounds) < 0))
  493. - {
  494. - gunichar ch;
  495. -
  496. - ch = gtk_text_iter_get_char (&copy);
  497. -
  498. - switch (ch)
  499. - {
  500. - case ']':
  501. - case ')':
  502. - case '"':
  503. - case '\'':
  504. - continue;
  505. -
  506. - case ' ':
  507. - case '\n':
  508. - *iter = copy;
  509. - return SENTENCE_OK;
  510. -
  511. - default:
  512. - break;
  513. - }
  514. - }
  515. - }
  516. - }
  517. -
  518. - *iter = start_bounds;
  519. -
  520. - if (found_para)
  521. - return SENTENCE_PARA;
  522. -
  523. - return SENTENCE_FAILED;
  524. -}
  525. -
  526. -gboolean
  527. -_ide_vim_iter_forward_sentence_end (GtkTextIter *iter)
  528. -{
  529. - GtkTextIter end_bounds;
  530. - gboolean found_para;
  531. -
  532. - g_return_val_if_fail (iter, FALSE);
  533. -
  534. - end_bounds = *iter;
  535. - found_para = _ide_vim_iter_forward_paragraph_end (&end_bounds);
  536. -
  537. - if (!found_para)
  538. - gtk_text_buffer_get_end_iter (gtk_text_iter_get_buffer (iter), &end_bounds);
  539. -
  540. - while ((gtk_text_iter_compare (iter, &end_bounds) < 0) && gtk_text_iter_forward_char (iter))
  541. - {
  542. - if (gtk_text_iter_forward_find_char (iter, sentence_end_chars, NULL, &end_bounds))
  543. - {
  544. - GtkTextIter copy = *iter;
  545. -
  546. - while (gtk_text_iter_forward_char (&copy) && (gtk_text_iter_compare (&copy, &end_bounds) < 0))
  547. - {
  548. - gunichar ch;
  549. - gboolean invalid = FALSE;
  550. -
  551. - ch = gtk_text_iter_get_char (&copy);
  552. -
  553. - switch (ch)
  554. - {
  555. - case ']':
  556. - case ')':
  557. - case '"':
  558. - case '\'':
  559. - continue;
  560. -
  561. - case ' ':
  562. - case '\n':
  563. - *iter = copy;
  564. - return SENTENCE_OK;
  565. -
  566. - default:
  567. - invalid = TRUE;
  568. - break;
  569. - }
  570. -
  571. - if (invalid)
  572. - break;
  573. - }
  574. - }
  575. - }
  576. -
  577. - *iter = end_bounds;
  578. -
  579. - if (found_para)
  580. - return SENTENCE_PARA;
  581. -
  582. - return SENTENCE_FAILED;
  583. -}
  584. -
  585. -gboolean
  586. -_ide_vim_iter_backward_sentence_start (GtkTextIter *iter)
  587. -{
  588. - GtkTextIter tmp;
  589. - SentenceStatus status;
  590. -
  591. - g_return_val_if_fail (iter, FALSE);
  592. -
  593. - tmp = *iter;
  594. - status = _ide_vim_iter_backward_sentence_end (&tmp);
  595. -
  596. - switch (status)
  597. - {
  598. - case SENTENCE_PARA:
  599. - case SENTENCE_OK:
  600. - {
  601. - GtkTextIter copy = tmp;
  602. -
  603. - /*
  604. - * try to work forward to first non-whitespace char.
  605. - * if we land where we started, discard the walk.
  606. - */
  607. - while (g_unichar_isspace (gtk_text_iter_get_char (&copy)))
  608. - if (!gtk_text_iter_forward_char (&copy))
  609. - break;
  610. - if (gtk_text_iter_compare (&copy, iter) < 0)
  611. - tmp = copy;
  612. - *iter = tmp;
  613. -
  614. - return TRUE;
  615. - }
  616. -
  617. - case SENTENCE_FAILED:
  618. - default:
  619. - gtk_text_buffer_get_start_iter (gtk_text_iter_get_buffer (iter), iter);
  620. - return FALSE;
  621. - }
  622. -}
  623. -
  624. -static gboolean
  625. -_ide_vim_iter_forward_classified_start (GtkTextIter *iter,
  626. - gint (*classify) (gunichar))
  627. -{
  628. - gint begin_class;
  629. - gint cur_class;
  630. - gunichar ch;
  631. -
  632. - g_assert (iter);
  633. -
  634. - ch = gtk_text_iter_get_char (iter);
  635. - begin_class = classify (ch);
  636. -
  637. - /* Move to the first non-whitespace character if necessary. */
  638. - if (begin_class == CLASS_SPACE)
  639. - {
  640. - for (;;)
  641. - {
  642. - if (!gtk_text_iter_forward_char (iter))
  643. - return FALSE;
  644. -
  645. - ch = gtk_text_iter_get_char (iter);
  646. - cur_class = classify (ch);
  647. - if (cur_class != CLASS_SPACE)
  648. - return TRUE;
  649. - }
  650. - }
  651. -
  652. - /* move to first character not at same class level. */
  653. - while (gtk_text_iter_forward_char (iter))
  654. - {
  655. - ch = gtk_text_iter_get_char (iter);
  656. - cur_class = classify (ch);
  657. -
  658. - if (cur_class == CLASS_SPACE)
  659. - {
  660. - begin_class = CLASS_0;
  661. - continue;
  662. - }
  663. -
  664. - if (cur_class != begin_class)
  665. - return TRUE;
  666. - }
  667. -
  668. - return FALSE;
  669. -}
  670. -
  671. -gboolean
  672. -_ide_vim_iter_forward_word_start (GtkTextIter *iter)
  673. -{
  674. - return _ide_vim_iter_forward_classified_start (iter, _ide_vim_word_classify);
  675. -}
  676. -
  677. -gboolean
  678. -_ide_vim_iter_forward_WORD_start (GtkTextIter *iter)
  679. -{
  680. - return _ide_vim_iter_forward_classified_start (iter, _ide_vim_WORD_classify);
  681. -}
  682. -
  683. -gboolean
  684. -_ide_vim_iter_forward_classified_end (GtkTextIter *iter,
  685. - gint (*classify) (gunichar))
  686. -{
  687. - gunichar ch;
  688. - gint begin_class;
  689. - gint cur_class;
  690. -
  691. - g_assert (iter);
  692. -
  693. - if (!gtk_text_iter_forward_char (iter))
  694. - return FALSE;
  695. -
  696. - /* If we are on space, walk to the start of the next word. */
  697. - ch = gtk_text_iter_get_char (iter);
  698. - if (classify (ch) == CLASS_SPACE)
  699. - if (!_ide_vim_iter_forward_classified_start (iter, classify))
  700. - return FALSE;
  701. -
  702. - ch = gtk_text_iter_get_char (iter);
  703. - begin_class = classify (ch);
  704. -
  705. - for (;;)
  706. - {
  707. - if (!gtk_text_iter_forward_char (iter))
  708. - return FALSE;
  709. -
  710. - ch = gtk_text_iter_get_char (iter);
  711. - cur_class = classify (ch);
  712. -
  713. - if (cur_class != begin_class)
  714. - {
  715. - gtk_text_iter_backward_char (iter);
  716. - return TRUE;
  717. - }
  718. - }
  719. -
  720. - return FALSE;
  721. -}
  722. -
  723. -gboolean
  724. -_ide_vim_iter_forward_word_end (GtkTextIter *iter)
  725. -{
  726. - return _ide_vim_iter_forward_classified_end (iter, _ide_vim_word_classify);
  727. -}
  728. -
  729. -gboolean
  730. -_ide_vim_iter_forward_WORD_end (GtkTextIter *iter)
  731. -{
  732. - return _ide_vim_iter_forward_classified_end (iter, _ide_vim_WORD_classify);
  733. -}
  734. -
  735. -static gboolean
  736. -_ide_vim_iter_backward_classified_end (GtkTextIter *iter,
  737. - gint (*classify) (gunichar))
  738. -{
  739. - gunichar ch;
  740. - gint begin_class;
  741. - gint cur_class;
  742. -
  743. - g_assert (iter);
  744. -
  745. - ch = gtk_text_iter_get_char (iter);
  746. - begin_class = classify (ch);
  747. -
  748. - for (;;)
  749. - {
  750. - if (!gtk_text_iter_backward_char (iter))
  751. - return FALSE;
  752. -
  753. - ch = gtk_text_iter_get_char (iter);
  754. - cur_class = classify (ch);
  755. -
  756. - /* reset begin_class if we hit space, we can take anything after that */
  757. - if (cur_class == CLASS_SPACE)
  758. - begin_class = CLASS_SPACE;
  759. -
  760. - if (cur_class != begin_class && cur_class != CLASS_SPACE)
  761. - return TRUE;
  762. - }
  763. -
  764. - return FALSE;
  765. -}
  766. -
  767. -gboolean
  768. -_ide_vim_iter_backward_word_end (GtkTextIter *iter)
  769. -{
  770. - return _ide_vim_iter_backward_classified_end (iter, _ide_vim_word_classify);
  771. -}
  772. -
  773. -gboolean
  774. -_ide_vim_iter_backward_WORD_end (GtkTextIter *iter)
  775. -{
  776. - return _ide_vim_iter_backward_classified_end (iter, _ide_vim_WORD_classify);
  777. -}
  778. diff --git a/libide/ide-vim-iter.h b/libide/ide-vim-iter.h
  779. deleted file mode 100644
  780. index 3777238..0000000
  781. --- a/libide/ide-vim-iter.h
  782. +++ /dev/null
  783. @@ -1,39 +0,0 @@
  784. -/* ide-vim-iter.h
  785. - *
  786. - * Copyright (C) 2015 Christian Hergert <christian@hergert.me>
  787. - *
  788. - * This program is free software: you can redistribute it and/or modify
  789. - * it under the terms of the GNU General Public License as published by
  790. - * the Free Software Foundation, either version 3 of the License, or
  791. - * (at your option) any later version.
  792. - *
  793. - * This program is distributed in the hope that it will be useful,
  794. - * but WITHOUT ANY WARRANTY; without even the implied warranty of
  795. - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  796. - * GNU General Public License for more details.
  797. - *
  798. - * You should have received a copy of the GNU General Public License
  799. - * along with this program. If not, see <http://www.gnu.org/licenses/>.
  800. - */
  801. -
  802. -#ifndef IDE_VIM_ITER_H
  803. -#define IDE_VIM_ITER_H
  804. -
  805. -#include <gtk/gtk.h>
  806. -
  807. -G_BEGIN_DECLS
  808. -
  809. -gboolean _ide_vim_iter_forward_word_start (GtkTextIter *iter);
  810. -gboolean _ide_vim_iter_forward_WORD_start (GtkTextIter *iter);
  811. -gboolean _ide_vim_iter_forward_word_end (GtkTextIter *iter);
  812. -gboolean _ide_vim_iter_forward_WORD_end (GtkTextIter *iter);
  813. -gboolean _ide_vim_iter_backward_paragraph_start (GtkTextIter *iter);
  814. -gboolean _ide_vim_iter_forward_paragraph_end (GtkTextIter *iter);
  815. -gboolean _ide_vim_iter_backward_sentence_start (GtkTextIter *iter);
  816. -gboolean _ide_vim_iter_forward_sentence_end (GtkTextIter *iter);
  817. -gboolean _ide_vim_iter_backward_WORD_end (GtkTextIter *iter);
  818. -gboolean _ide_vim_iter_backward_word_end (GtkTextIter *iter);
  819. -
  820. -G_END_DECLS
  821. -
  822. -#endif /* IDE_VIM_ITER_H */
  823. diff --git a/libide/ide.h b/libide/ide.h
  824. index b5e6a17..127e5d6 100644
  825. --- a/libide/ide.h
  826. +++ b/libide/ide.h
  827. @@ -108,6 +108,13 @@ G_BEGIN_DECLS
  828. #include "python/ide-python-language.h"
  829. #include "theatrics/ide-animation.h"
  830. #include "vala/ide-vala-language.h"
  831. +#include "vim/ide-vim-parser.h"
  832. +#include "vim/ide-vim-parser-debug.h"
  833. +#include "vim/ide-vim-parser-error.h"
  834. +#include "vim/ide-vim-parser-token.h"
  835. +#include "vim/ide-vim-parser-command.h"
  836. +#include "vim/ide-vim-parser-objects-pool.h"
  837. +#include "vim/ide-vim-parser-settable.h"
  838. #include "xml/ide-xml-language.h"
  839.  
  840. #undef IDE_INSIDE
  841. diff --git a/libide/vim/ide-vim-complete-item.c b/libide/vim/ide-vim-complete-item.c
  842. new file mode 100644
  843. index 0000000..f0dde0e
  844. --- /dev/null
  845. +++ b/libide/vim/ide-vim-complete-item.c
  846. @@ -0,0 +1,261 @@
  847. +/* ide-vim-complete-item.c
  848. + *
  849. + * Copyright (C) 2015 Sébastien Lafargue <slafargue@gnome.org>
  850. + *
  851. + * This program is free software: you can redistribute it and/or modify
  852. + * it under the terms of the GNU General Public License as published by
  853. + * the Free Software Foundation, either version 3 of the License, or
  854. + * (at your option) any later version.
  855. + *
  856. + * This program is distributed in the hope that it will be useful,
  857. + * but WITHOUT ANY WARRANTY; without even the implied warranty of
  858. + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  859. + * GNU General Public License for more details.
  860. + *
  861. + * You should have received a copy of the GNU General Public License
  862. + * along with this program. If not, see <http://www.gnu.org/licenses/>.
  863. + */
  864. +#include <string.h>
  865. +#include <errno.h>
  866. +#include <glib/gi18n.h>
  867. +#include <glib/gprintf.h>
  868. +
  869. +#include "ide-debug.h"
  870. +#include "ide-macros.h"
  871. +
  872. +#include <glib/gi18n.h>
  873. +
  874. +#include "ide-vim-complete-item.h"
  875. +
  876. +typedef struct
  877. +{
  878. + IdeVimCompleteItemKind kind;
  879. + gchar *name;
  880. + gchar *shortname;
  881. +} IdeVimCompleteItemPrivate;
  882. +
  883. +G_DEFINE_TYPE_WITH_PRIVATE (IdeVimCompleteItem, ide_vim_complete_item, G_TYPE_OBJECT)
  884. +
  885. +enum {
  886. + PROP_0,
  887. + PROP_KIND,
  888. + PROP_NAME,
  889. + PROP_SHORTNAME,
  890. + LAST_PROP
  891. +};
  892. +
  893. +static GParamSpec *gParamSpecs [LAST_PROP];
  894. +
  895. +/**
  896. + * ide_vim_complete_item_get_kind:
  897. + * @self: (in): #IdeClangCompletionItem instance.
  898. + *
  899. + * Get the item's kind.
  900. + *
  901. + * Returns: the item's kind.
  902. + */
  903. +IdeVimCompleteItemKind
  904. +ide_vim_complete_item_get_kind (IdeVimCompleteItem *self)
  905. +{
  906. + IdeVimCompleteItemPrivate *priv = ide_vim_complete_item_get_instance_private (self);
  907. +
  908. + g_return_val_if_fail (IDE_IS_VIM_COMPLETE_ITEM (self), IDE_VIM_COMPLETE_ITEM_KIND_NONE);
  909. +
  910. + return priv->kind;
  911. +}
  912. +
  913. +/**
  914. + * ide_vim_complete_item_get_name:
  915. + * @self: (in): #IdeClangCompletionItem instance.
  916. + *
  917. + * Return the item's name.
  918. + *
  919. + * Returns: the item's name.
  920. + */
  921. +const gchar *
  922. +ide_vim_complete_item_get_name (IdeVimCompleteItem *self)
  923. +{
  924. + IdeVimCompleteItemPrivate *priv = ide_vim_complete_item_get_instance_private (self);
  925. +
  926. + g_return_val_if_fail (IDE_IS_VIM_COMPLETE_ITEM (self), NULL);
  927. +
  928. + return priv->name;
  929. +}
  930. +
  931. +/**
  932. + * ide_vim_complete_item_get_shortname:
  933. + * @self: (in): #IdeClangCompletionItem instance.
  934. + *
  935. + * Return the item's shortname.
  936. + *
  937. + * Returns: the item's shortname.
  938. + */
  939. +const gchar *
  940. +ide_vim_complete_item_get_shortname (IdeVimCompleteItem *self)
  941. +{
  942. + IdeVimCompleteItemPrivate *priv = ide_vim_complete_item_get_instance_private (self);
  943. +
  944. + g_return_val_if_fail (IDE_IS_VIM_COMPLETE_ITEM (self), NULL);
  945. +
  946. + return priv->shortname;
  947. +}
  948. +
  949. +IdeVimCompleteItem *
  950. +ide_vim_complete_item_new (IdeVimCompleteItemKind kind,
  951. + gchar *name,
  952. + gchar *shortname)
  953. +{
  954. + return g_object_new (IDE_TYPE_VIM_COMPLETE_ITEM,
  955. + "kind", kind,
  956. + "name", name,
  957. + "shortname", shortname,
  958. + NULL);
  959. +}
  960. +
  961. +static void
  962. +ide_vim_complete_item_finalize (GObject *object)
  963. +{
  964. + IdeVimCompleteItem *self = (IdeVimCompleteItem *)object;
  965. + IdeVimCompleteItemPrivate *priv = ide_vim_complete_item_get_instance_private (self);
  966. +
  967. + g_clear_pointer (&priv->name, g_free);
  968. + g_clear_pointer (&priv->shortname, g_free);
  969. +
  970. + G_OBJECT_CLASS (ide_vim_complete_item_parent_class)->finalize (object);
  971. +}
  972. +
  973. +static void
  974. +ide_vim_complete_item_get_property (GObject *object,
  975. + guint prop_id,
  976. + GValue *value,
  977. + GParamSpec *pspec)
  978. +{
  979. + IdeVimCompleteItem *self = IDE_VIM_COMPLETE_ITEM (object);
  980. +
  981. + switch (prop_id)
  982. + {
  983. + case PROP_KIND:
  984. + g_value_set_enum (value, ide_vim_complete_item_get_kind (self));
  985. + break;
  986. +
  987. + case PROP_NAME:
  988. + g_value_set_string (value, ide_vim_complete_item_get_name (self));
  989. + break;
  990. +
  991. + case PROP_SHORTNAME:
  992. + g_value_set_string (value, ide_vim_complete_item_get_shortname (self));
  993. + break;
  994. +
  995. + default:
  996. + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
  997. + }
  998. +}
  999. +
  1000. +static void
  1001. +ide_vim_complete_item_set_property (GObject *object,
  1002. + guint prop_id,
  1003. + const GValue *value,
  1004. + GParamSpec *pspec)
  1005. +{
  1006. + IdeVimCompleteItem *self = IDE_VIM_COMPLETE_ITEM (object);
  1007. + IdeVimCompleteItemPrivate *priv = ide_vim_complete_item_get_instance_private (self);
  1008. +
  1009. + switch (prop_id)
  1010. + {
  1011. + case PROP_KIND:
  1012. + priv->kind = g_value_get_enum (value);
  1013. + break;
  1014. +
  1015. + case PROP_NAME:
  1016. + priv->name = g_value_dup_string (value);
  1017. + break;
  1018. +
  1019. + case PROP_SHORTNAME:
  1020. + priv->shortname = g_value_dup_string (value);
  1021. + break;
  1022. +
  1023. + default:
  1024. + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
  1025. + }
  1026. +}
  1027. +
  1028. +static void
  1029. +ide_vim_complete_item_init (IdeVimCompleteItem *self)
  1030. +{
  1031. +}
  1032. +
  1033. +/**
  1034. + * ide_vim_complete_item_kind_get_type:
  1035. + *
  1036. + * Retrieves the GType for #IdeVimCompleteItemKind.
  1037. + *
  1038. + * Returns: A GType.
  1039. + * Side effects: GType registered on first call.
  1040. + */
  1041. +GType
  1042. +ide_vim_complete_item_kind_get_type (void)
  1043. +{
  1044. + static GType type_id = 0;
  1045. + static const GEnumValue values[] = {
  1046. + { IDE_VIM_COMPLETE_ITEM_KIND_NONE, "IDE_VIM_COMPLETE_ITEM_KIND_NONE", "NONE" },
  1047. + { IDE_VIM_COMPLETE_ITEM_KIND_SET, "IDE_VIM_COMPLETE_ITEM_KIND_SET", "SET" },
  1048. + { IDE_VIM_COMPLETE_ITEM_KIND_COLORSCHEME, "IDE_VIM_COMPLETE_ITEM_KIND_COLORSCHEME", "COLORSCHEME" },
  1049. + { IDE_VIM_COMPLETE_ITEM_KIND_COMMAND, "IDE_VIM_COMPLETE_ITEM_KIND_COMMAND", "COMMAND" },
  1050. + { IDE_VIM_COMPLETE_ITEM_KIND_PATH, "IDE_VIM_COMPLETE_ITEM_KIND_PATH", "PATH" },
  1051. + { 0 }
  1052. + };
  1053. +
  1054. + if (G_UNLIKELY (!type_id))
  1055. + type_id = g_enum_register_static ("IdeVimCompleteItemKind", values);
  1056. +
  1057. + return type_id;
  1058. +}
  1059. +
  1060. +static void
  1061. +ide_vim_complete_item_class_init (IdeVimCompleteItemClass *klass)
  1062. +{
  1063. + GObjectClass *object_class = G_OBJECT_CLASS (klass);
  1064. +
  1065. + object_class->finalize = ide_vim_complete_item_finalize;
  1066. + object_class->get_property = ide_vim_complete_item_get_property;
  1067. + object_class->set_property = ide_vim_complete_item_set_property;
  1068. +
  1069. +/**
  1070. + * IdeVimCompleteItem:kind:
  1071. + *
  1072. + * The kind of content the Complete item represent.
  1073. + */
  1074. + gParamSpecs[PROP_KIND] =
  1075. + g_param_spec_enum ("kind",
  1076. + _("Item kind"),
  1077. + _("The kind of the Item"),
  1078. + IDE_TYPE_VIM_COMPLETE_ITEM_KIND,
  1079. + IDE_VIM_COMPLETE_ITEM_KIND_NONE,
  1080. + (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
  1081. +
  1082. + /**
  1083. + * IdeVimCompleteItem:name:
  1084. + *
  1085. + * A string holding the name of the item.
  1086. + */
  1087. + gParamSpecs[PROP_NAME] =
  1088. + g_param_spec_string ("name",
  1089. + _("item name"),
  1090. + _("The name of the item."),
  1091. + NULL,
  1092. + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
  1093. +
  1094. + /**
  1095. + * IdeVimCompleteItem:shortname:
  1096. + *
  1097. + * A string holding the shortname of the item.
  1098. + */
  1099. + gParamSpecs[PROP_SHORTNAME] =
  1100. + g_param_spec_string ("shortname",
  1101. + _("item shortname"),
  1102. + _("The shortname of the item."),
  1103. + NULL,
  1104. + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
  1105. +
  1106. + g_object_class_install_properties (object_class, LAST_PROP, gParamSpecs);
  1107. +}
  1108. diff --git a/libide/vim/ide-vim-complete-item.h b/libide/vim/ide-vim-complete-item.h
  1109. new file mode 100644
  1110. index 0000000..160653d
  1111. --- /dev/null
  1112. +++ b/libide/vim/ide-vim-complete-item.h
  1113. @@ -0,0 +1,58 @@
  1114. +/* ide-vim-complete-item.h
  1115. + *
  1116. + * Copyright (C) 2015 Sébastien Lafargue <slafargue@gnome.org>
  1117. + *
  1118. + * This program is free software: you can redistribute it and/or modify
  1119. + * it under the terms of the GNU General Public License as published by
  1120. + * the Free Software Foundation, either version 3 of the License, or
  1121. + * (at your option) any later version.
  1122. + *
  1123. + * This program is distributed in the hope that it will be useful,
  1124. + * but WITHOUT ANY WARRANTY; without even the implied warranty of
  1125. + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  1126. + * GNU General Public License for more details.
  1127. + *
  1128. + * You should have received a copy of the GNU General Public License
  1129. + * along with this program. If not, see <http://www.gnu.org/licenses/>.
  1130. + */
  1131. +
  1132. +#ifndef IDE_VIM_COMPLETE_ITEM_H
  1133. +#define IDE_VIM_COMPLETE_ITEM_H
  1134. +
  1135. +#include <glib.h>
  1136. +
  1137. +G_BEGIN_DECLS
  1138. +
  1139. +#define IDE_TYPE_VIM_COMPLETE_ITEM (ide_vim_complete_item_get_type())
  1140. +#define IDE_TYPE_VIM_COMPLETE_ITEM_KIND (ide_vim_complete_item_kind_get_type())
  1141. +
  1142. +G_DECLARE_FINAL_TYPE (IdeVimCompleteItem , ide_vim_complete_item, IDE, VIM_COMPLETE_ITEM, GObject)
  1143. +
  1144. +struct _IdeVimCompleteItem
  1145. +{
  1146. + GObject parent;
  1147. +};
  1148. +
  1149. +typedef enum _IdeVimCompleteItemKind IdeVimCompleteItemKind;
  1150. +
  1151. +enum _IdeVimCompleteItemKind
  1152. +{
  1153. + IDE_VIM_COMPLETE_ITEM_KIND_NONE,
  1154. + IDE_VIM_COMPLETE_ITEM_KIND_SET,
  1155. + IDE_VIM_COMPLETE_ITEM_KIND_COLORSCHEME,
  1156. + IDE_VIM_COMPLETE_ITEM_KIND_COMMAND,
  1157. + IDE_VIM_COMPLETE_ITEM_KIND_PATH,
  1158. +
  1159. + IDE_VIM_COMPLETE_ITEM_KIND_LAST
  1160. +};
  1161. +
  1162. +IdeVimCompleteItem *ide_vim_complete_item_new (IdeVimCompleteItemKind kind,
  1163. + gchar *name,
  1164. + gchar *shortname);
  1165. +IdeVimCompleteItemKind ide_vim_complete_item_get_kind (IdeVimCompleteItem *self);
  1166. +const gchar *ide_vim_complete_item_get_name (IdeVimCompleteItem *self);
  1167. +const gchar *ide_vim_complete_item_get_shortname (IdeVimCompleteItem *self);
  1168. +
  1169. +G_END_DECLS
  1170. +
  1171. +#endif /* IDE_VIM_COMPLETE_ITEM_H */
  1172. diff --git a/libide/vim/ide-vim-iter.c b/libide/vim/ide-vim-iter.c
  1173. new file mode 100644
  1174. index 0000000..d03b829
  1175. --- /dev/null
  1176. +++ b/libide/vim/ide-vim-iter.c
  1177. @@ -0,0 +1,457 @@
  1178. +/* ide-vim-iter.c
  1179. + *
  1180. + * Copyright (C) 2015 Christian Hergert <christian@hergert.me>
  1181. + *
  1182. + * This program is free software: you can redistribute it and/or modify
  1183. + * it under the terms of the GNU General Public License as published by
  1184. + * the Free Software Foundation, either version 3 of the License, or
  1185. + * (at your option) any later version.
  1186. + *
  1187. + * This program is distributed in the hope that it will be useful,
  1188. + * but WITHOUT ANY WARRANTY; without even the implied warranty of
  1189. + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  1190. + * GNU General Public License for more details.
  1191. + *
  1192. + * You should have received a copy of the GNU General Public License
  1193. + * along with this program. If not, see <http://www.gnu.org/licenses/>.
  1194. + */
  1195. +
  1196. +#include <gtk/gtk.h>
  1197. +
  1198. +#include "ide-debug.h"
  1199. +#include "ide-vim-iter.h"
  1200. +
  1201. +typedef enum
  1202. +{
  1203. + SENTENCE_OK,
  1204. + SENTENCE_PARA,
  1205. + SENTENCE_FAILED,
  1206. +} SentenceStatus;
  1207. +
  1208. +enum
  1209. +{
  1210. + CLASS_0,
  1211. + CLASS_SPACE,
  1212. + CLASS_SPECIAL,
  1213. + CLASS_WORD,
  1214. +};
  1215. +
  1216. +static int
  1217. +_ide_vim_word_classify (gunichar ch)
  1218. +{
  1219. + switch (ch)
  1220. + {
  1221. + case ' ':
  1222. + case '\t':
  1223. + case '\n':
  1224. + return CLASS_SPACE;
  1225. +
  1226. + case '"': case '\'':
  1227. + case '(': case ')':
  1228. + case '{': case '}':
  1229. + case '[': case ']':
  1230. + case '<': case '>':
  1231. + case '-': case '+': case '*': case '/':
  1232. + case '!': case '@': case '#': case '$': case '%':
  1233. + case '^': case '&': case ':': case ';': case '?':
  1234. + case '|': case '=': case '\\': case '.': case ',':
  1235. + return CLASS_SPECIAL;
  1236. +
  1237. + case '_':
  1238. + default:
  1239. + return CLASS_WORD;
  1240. + }
  1241. +}
  1242. +
  1243. +static int
  1244. +_ide_vim_WORD_classify (gunichar ch)
  1245. +{
  1246. + if (g_unichar_isspace (ch))
  1247. + return CLASS_SPACE;
  1248. + return CLASS_WORD;
  1249. +}
  1250. +
  1251. +static gboolean
  1252. +_ide_vim_iter_line_is_empty (GtkTextIter *iter)
  1253. +{
  1254. + return gtk_text_iter_starts_line (iter) && gtk_text_iter_ends_line (iter);
  1255. +}
  1256. +
  1257. +/**
  1258. + * _ide_vim_iter_backward_paragraph_start:
  1259. + * @iter: A #GtkTextIter
  1260. + *
  1261. + * Searches backwards until we find the beginning of a paragraph.
  1262. + *
  1263. + * Returns: %TRUE if we are not at the beginning of the buffer; otherwise %FALSE.
  1264. + */
  1265. +gboolean
  1266. +_ide_vim_iter_backward_paragraph_start (GtkTextIter *iter)
  1267. +{
  1268. + g_return_val_if_fail (iter, FALSE);
  1269. +
  1270. + /* Work our way past the current empty lines */
  1271. + if (_ide_vim_iter_line_is_empty (iter))
  1272. + while (_ide_vim_iter_line_is_empty (iter))
  1273. + if (!gtk_text_iter_backward_line (iter))
  1274. + return FALSE;
  1275. +
  1276. + /* Now find first line that is empty */
  1277. + while (!_ide_vim_iter_line_is_empty (iter))
  1278. + if (!gtk_text_iter_backward_line (iter))
  1279. + return FALSE;
  1280. +
  1281. + return TRUE;
  1282. +}
  1283. +
  1284. +/**
  1285. + * _ide_vim_iter_forward_paragraph_end:
  1286. + * @iter: A #GtkTextIter
  1287. + *
  1288. + * Searches forward until the end of a paragraph has been hit.
  1289. + *
  1290. + * Returns: %TRUE if we are not at the end of the buffer; otherwise %FALSE.
  1291. + */
  1292. +gboolean
  1293. +_ide_vim_iter_forward_paragraph_end (GtkTextIter *iter)
  1294. +{
  1295. + g_return_val_if_fail (iter, FALSE);
  1296. +
  1297. + /* Work our way past the current empty lines */
  1298. + if (_ide_vim_iter_line_is_empty (iter))
  1299. + while (_ide_vim_iter_line_is_empty (iter))
  1300. + if (!gtk_text_iter_forward_line (iter))
  1301. + return FALSE;
  1302. +
  1303. + /* Now find first line that is empty */
  1304. + while (!_ide_vim_iter_line_is_empty (iter))
  1305. + if (!gtk_text_iter_forward_line (iter))
  1306. + return FALSE;
  1307. +
  1308. + return TRUE;
  1309. +}
  1310. +
  1311. +static gboolean
  1312. +sentence_end_chars (gunichar ch,
  1313. + gpointer user_data)
  1314. +{
  1315. + switch (ch)
  1316. + {
  1317. + case '!':
  1318. + case '.':
  1319. + case '?':
  1320. + return TRUE;
  1321. +
  1322. + default:
  1323. + return FALSE;
  1324. + }
  1325. +}
  1326. +
  1327. +static SentenceStatus
  1328. +_ide_vim_iter_backward_sentence_end (GtkTextIter *iter)
  1329. +{
  1330. + GtkTextIter end_bounds;
  1331. + GtkTextIter start_bounds;
  1332. + gboolean found_para;
  1333. +
  1334. + g_return_val_if_fail (iter, FALSE);
  1335. +
  1336. + end_bounds = *iter;
  1337. + start_bounds = *iter;
  1338. + found_para = _ide_vim_iter_backward_paragraph_start (&start_bounds);
  1339. +
  1340. + if (!found_para)
  1341. + gtk_text_buffer_get_start_iter (gtk_text_iter_get_buffer (iter), &start_bounds);
  1342. +
  1343. + while ((gtk_text_iter_compare (iter, &start_bounds) > 0) && gtk_text_iter_backward_char (iter))
  1344. + {
  1345. + if (gtk_text_iter_backward_find_char (iter, sentence_end_chars, NULL, &end_bounds))
  1346. + {
  1347. + GtkTextIter copy = *iter;
  1348. +
  1349. + while (gtk_text_iter_forward_char (&copy) && (gtk_text_iter_compare (&copy, &end_bounds) < 0))
  1350. + {
  1351. + gunichar ch;
  1352. +
  1353. + ch = gtk_text_iter_get_char (&copy);
  1354. +
  1355. + switch (ch)
  1356. + {
  1357. + case ']':
  1358. + case ')':
  1359. + case '"':
  1360. + case '\'':
  1361. + continue;
  1362. +
  1363. + case ' ':
  1364. + case '\n':
  1365. + *iter = copy;
  1366. + return SENTENCE_OK;
  1367. +
  1368. + default:
  1369. + break;
  1370. + }
  1371. + }
  1372. + }
  1373. + }
  1374. +
  1375. + *iter = start_bounds;
  1376. +
  1377. + if (found_para)
  1378. + return SENTENCE_PARA;
  1379. +
  1380. + return SENTENCE_FAILED;
  1381. +}
  1382. +
  1383. +gboolean
  1384. +_ide_vim_iter_forward_sentence_end (GtkTextIter *iter)
  1385. +{
  1386. + GtkTextIter end_bounds;
  1387. + gboolean found_para;
  1388. +
  1389. + g_return_val_if_fail (iter, FALSE);
  1390. +
  1391. + end_bounds = *iter;
  1392. + found_para = _ide_vim_iter_forward_paragraph_end (&end_bounds);
  1393. +
  1394. + if (!found_para)
  1395. + gtk_text_buffer_get_end_iter (gtk_text_iter_get_buffer (iter), &end_bounds);
  1396. +
  1397. + while ((gtk_text_iter_compare (iter, &end_bounds) < 0) && gtk_text_iter_forward_char (iter))
  1398. + {
  1399. + if (gtk_text_iter_forward_find_char (iter, sentence_end_chars, NULL, &end_bounds))
  1400. + {
  1401. + GtkTextIter copy = *iter;
  1402. +
  1403. + while (gtk_text_iter_forward_char (&copy) && (gtk_text_iter_compare (&copy, &end_bounds) < 0))
  1404. + {
  1405. + gunichar ch;
  1406. + gboolean invalid = FALSE;
  1407. +
  1408. + ch = gtk_text_iter_get_char (&copy);
  1409. +
  1410. + switch (ch)
  1411. + {
  1412. + case ']':
  1413. + case ')':
  1414. + case '"':
  1415. + case '\'':
  1416. + continue;
  1417. +
  1418. + case ' ':
  1419. + case '\n':
  1420. + *iter = copy;
  1421. + return SENTENCE_OK;
  1422. +
  1423. + default:
  1424. + invalid = TRUE;
  1425. + break;
  1426. + }
  1427. +
  1428. + if (invalid)
  1429. + break;
  1430. + }
  1431. + }
  1432. + }
  1433. +
  1434. + *iter = end_bounds;
  1435. +
  1436. + if (found_para)
  1437. + return SENTENCE_PARA;
  1438. +
  1439. + return SENTENCE_FAILED;
  1440. +}
  1441. +
  1442. +gboolean
  1443. +_ide_vim_iter_backward_sentence_start (GtkTextIter *iter)
  1444. +{
  1445. + GtkTextIter tmp;
  1446. + SentenceStatus status;
  1447. +
  1448. + g_return_val_if_fail (iter, FALSE);
  1449. +
  1450. + tmp = *iter;
  1451. + status = _ide_vim_iter_backward_sentence_end (&tmp);
  1452. +
  1453. + switch (status)
  1454. + {
  1455. + case SENTENCE_PARA:
  1456. + case SENTENCE_OK:
  1457. + {
  1458. + GtkTextIter copy = tmp;
  1459. +
  1460. + /*
  1461. + * try to work forward to first non-whitespace char.
  1462. + * if we land where we started, discard the walk.
  1463. + */
  1464. + while (g_unichar_isspace (gtk_text_iter_get_char (&copy)))
  1465. + if (!gtk_text_iter_forward_char (&copy))
  1466. + break;
  1467. + if (gtk_text_iter_compare (&copy, iter) < 0)
  1468. + tmp = copy;
  1469. + *iter = tmp;
  1470. +
  1471. + return TRUE;
  1472. + }
  1473. +
  1474. + case SENTENCE_FAILED:
  1475. + default:
  1476. + gtk_text_buffer_get_start_iter (gtk_text_iter_get_buffer (iter), iter);
  1477. + return FALSE;
  1478. + }
  1479. +}
  1480. +
  1481. +static gboolean
  1482. +_ide_vim_iter_forward_classified_start (GtkTextIter *iter,
  1483. + gint (*classify) (gunichar))
  1484. +{
  1485. + gint begin_class;
  1486. + gint cur_class;
  1487. + gunichar ch;
  1488. +
  1489. + g_assert (iter);
  1490. +
  1491. + ch = gtk_text_iter_get_char (iter);
  1492. + begin_class = classify (ch);
  1493. +
  1494. + /* Move to the first non-whitespace character if necessary. */
  1495. + if (begin_class == CLASS_SPACE)
  1496. + {
  1497. + for (;;)
  1498. + {
  1499. + if (!gtk_text_iter_forward_char (iter))
  1500. + return FALSE;
  1501. +
  1502. + ch = gtk_text_iter_get_char (iter);
  1503. + cur_class = classify (ch);
  1504. + if (cur_class != CLASS_SPACE)
  1505. + return TRUE;
  1506. + }
  1507. + }
  1508. +
  1509. + /* move to first character not at same class level. */
  1510. + while (gtk_text_iter_forward_char (iter))
  1511. + {
  1512. + ch = gtk_text_iter_get_char (iter);
  1513. + cur_class = classify (ch);
  1514. +
  1515. + if (cur_class == CLASS_SPACE)
  1516. + {
  1517. + begin_class = CLASS_0;
  1518. + continue;
  1519. + }
  1520. +
  1521. + if (cur_class != begin_class)
  1522. + return TRUE;
  1523. + }
  1524. +
  1525. + return FALSE;
  1526. +}
  1527. +
  1528. +gboolean
  1529. +_ide_vim_iter_forward_word_start (GtkTextIter *iter)
  1530. +{
  1531. + return _ide_vim_iter_forward_classified_start (iter, _ide_vim_word_classify);
  1532. +}
  1533. +
  1534. +gboolean
  1535. +_ide_vim_iter_forward_WORD_start (GtkTextIter *iter)
  1536. +{
  1537. + return _ide_vim_iter_forward_classified_start (iter, _ide_vim_WORD_classify);
  1538. +}
  1539. +
  1540. +gboolean
  1541. +_ide_vim_iter_forward_classified_end (GtkTextIter *iter,
  1542. + gint (*classify) (gunichar))
  1543. +{
  1544. + gunichar ch;
  1545. + gint begin_class;
  1546. + gint cur_class;
  1547. +
  1548. + g_assert (iter);
  1549. +
  1550. + if (!gtk_text_iter_forward_char (iter))
  1551. + return FALSE;
  1552. +
  1553. + /* If we are on space, walk to the start of the next word. */
  1554. + ch = gtk_text_iter_get_char (iter);
  1555. + if (classify (ch) == CLASS_SPACE)
  1556. + if (!_ide_vim_iter_forward_classified_start (iter, classify))
  1557. + return FALSE;
  1558. +
  1559. + ch = gtk_text_iter_get_char (iter);
  1560. + begin_class = classify (ch);
  1561. +
  1562. + for (;;)
  1563. + {
  1564. + if (!gtk_text_iter_forward_char (iter))
  1565. + return FALSE;
  1566. +
  1567. + ch = gtk_text_iter_get_char (iter);
  1568. + cur_class = classify (ch);
  1569. +
  1570. + if (cur_class != begin_class)
  1571. + {
  1572. + gtk_text_iter_backward_char (iter);
  1573. + return TRUE;
  1574. + }
  1575. + }
  1576. +
  1577. + return FALSE;
  1578. +}
  1579. +
  1580. +gboolean
  1581. +_ide_vim_iter_forward_word_end (GtkTextIter *iter)
  1582. +{
  1583. + return _ide_vim_iter_forward_classified_end (iter, _ide_vim_word_classify);
  1584. +}
  1585. +
  1586. +gboolean
  1587. +_ide_vim_iter_forward_WORD_end (GtkTextIter *iter)
  1588. +{
  1589. + return _ide_vim_iter_forward_classified_end (iter, _ide_vim_WORD_classify);
  1590. +}
  1591. +
  1592. +static gboolean
  1593. +_ide_vim_iter_backward_classified_end (GtkTextIter *iter,
  1594. + gint (*classify) (gunichar))
  1595. +{
  1596. + gunichar ch;
  1597. + gint begin_class;
  1598. + gint cur_class;
  1599. +
  1600. + g_assert (iter);
  1601. +
  1602. + ch = gtk_text_iter_get_char (iter);
  1603. + begin_class = classify (ch);
  1604. +
  1605. + for (;;)
  1606. + {
  1607. + if (!gtk_text_iter_backward_char (iter))
  1608. + return FALSE;
  1609. +
  1610. + ch = gtk_text_iter_get_char (iter);
  1611. + cur_class = classify (ch);
  1612. +
  1613. + /* reset begin_class if we hit space, we can take anything after that */
  1614. + if (cur_class == CLASS_SPACE)
  1615. + begin_class = CLASS_SPACE;
  1616. +
  1617. + if (cur_class != begin_class && cur_class != CLASS_SPACE)
  1618. + return TRUE;
  1619. + }
  1620. +
  1621. + return FALSE;
  1622. +}
  1623. +
  1624. +gboolean
  1625. +_ide_vim_iter_backward_word_end (GtkTextIter *iter)
  1626. +{
  1627. + return _ide_vim_iter_backward_classified_end (iter, _ide_vim_word_classify);
  1628. +}
  1629. +
  1630. +gboolean
  1631. +_ide_vim_iter_backward_WORD_end (GtkTextIter *iter)
  1632. +{
  1633. + return _ide_vim_iter_backward_classified_end (iter, _ide_vim_WORD_classify);
  1634. +}
  1635. diff --git a/libide/vim/ide-vim-iter.h b/libide/vim/ide-vim-iter.h
  1636. new file mode 100644
  1637. index 0000000..3777238
  1638. --- /dev/null
  1639. +++ b/libide/vim/ide-vim-iter.h
  1640. @@ -0,0 +1,39 @@
  1641. +/* ide-vim-iter.h
  1642. + *
  1643. + * Copyright (C) 2015 Christian Hergert <christian@hergert.me>
  1644. + *
  1645. + * This program is free software: you can redistribute it and/or modify
  1646. + * it under the terms of the GNU General Public License as published by
  1647. + * the Free Software Foundation, either version 3 of the License, or
  1648. + * (at your option) any later version.
  1649. + *
  1650. + * This program is distributed in the hope that it will be useful,
  1651. + * but WITHOUT ANY WARRANTY; without even the implied warranty of
  1652. + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  1653. + * GNU General Public License for more details.
  1654. + *
  1655. + * You should have received a copy of the GNU General Public License
  1656. + * along with this program. If not, see <http://www.gnu.org/licenses/>.
  1657. + */
  1658. +
  1659. +#ifndef IDE_VIM_ITER_H
  1660. +#define IDE_VIM_ITER_H
  1661. +
  1662. +#include <gtk/gtk.h>
  1663. +
  1664. +G_BEGIN_DECLS
  1665. +
  1666. +gboolean _ide_vim_iter_forward_word_start (GtkTextIter *iter);
  1667. +gboolean _ide_vim_iter_forward_WORD_start (GtkTextIter *iter);
  1668. +gboolean _ide_vim_iter_forward_word_end (GtkTextIter *iter);
  1669. +gboolean _ide_vim_iter_forward_WORD_end (GtkTextIter *iter);
  1670. +gboolean _ide_vim_iter_backward_paragraph_start (GtkTextIter *iter);
  1671. +gboolean _ide_vim_iter_forward_paragraph_end (GtkTextIter *iter);
  1672. +gboolean _ide_vim_iter_backward_sentence_start (GtkTextIter *iter);
  1673. +gboolean _ide_vim_iter_forward_sentence_end (GtkTextIter *iter);
  1674. +gboolean _ide_vim_iter_backward_WORD_end (GtkTextIter *iter);
  1675. +gboolean _ide_vim_iter_backward_word_end (GtkTextIter *iter);
  1676. +
  1677. +G_END_DECLS
  1678. +
  1679. +#endif /* IDE_VIM_ITER_H */
  1680. diff --git a/libide/vim/ide-vim-parser-command.c b/libide/vim/ide-vim-parser-command.c
  1681. new file mode 100644
  1682. index 0000000..0ce86d4
  1683. --- /dev/null
  1684. +++ b/libide/vim/ide-vim-parser-command.c
  1685. @@ -0,0 +1,633 @@
  1686. +/* ide-vim-parser-command.c
  1687. + *
  1688. + * Copyright (C) 2015 Sébastien Lafargue <slafargue@gnome.org>
  1689. + *
  1690. + * This program is free software: you can redistribute it and/or modify
  1691. + * it under the terms of the GNU General Public License as published by
  1692. + * the Free Software Foundation, either version 3 of the License, or
  1693. + * (at your option) any later version.
  1694. + *
  1695. + * This program is distributed in the hope that it will be useful,
  1696. + * but WITHOUT ANY WARRANTY; without even the implied warranty of
  1697. + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  1698. + * GNU General Public License for more details.
  1699. + *
  1700. + * You should have received a copy of the GNU General Public License
  1701. + * along with this program. If not, see <http://www.gnu.org/licenses/>.
  1702. + */
  1703. +
  1704. +#include <string.h>
  1705. +
  1706. +#include <glib/gprintf.h>
  1707. +#include <glib/gi18n.h>
  1708. +
  1709. +#include "ide-debug.h"
  1710. +#include "ide-macros.h"
  1711. +
  1712. +#include "ide-vim-parser-command.h"
  1713. +
  1714. +typedef struct
  1715. +{
  1716. + gchar *name;
  1717. + gchar *shortname;
  1718. + IdeVimParserCommandFunc func;
  1719. + guint id;
  1720. + GPtrArray *args;
  1721. + gboolean has_bang : 1;
  1722. +} IdeVimParserCommandPrivate;
  1723. +
  1724. +G_DEFINE_TYPE_WITH_PRIVATE (IdeVimParserCommand, ide_vim_parser_command, G_TYPE_OBJECT)
  1725. +
  1726. +enum {
  1727. + PROP_0,
  1728. + PROP_ARGS,
  1729. + PROP_NAME,
  1730. + PROP_SHORTNAME,
  1731. + PROP_ID,
  1732. + PROP_FUNC,
  1733. + PROP_SYNTAX,
  1734. + LAST_PROP
  1735. +};
  1736. +
  1737. +static GParamSpec *gParamSpecs [LAST_PROP];
  1738. +
  1739. +/**
  1740. + * ide_vim_parser_command_has_bang:
  1741. + * @self: (in): a #IdeVimParserCommand instance.
  1742. + *
  1743. + * Return TRUE if the parsed command is ended by a bang, FALSE otherwise.
  1744. + *
  1745. + * Returns: TRUE if the parsed command is ended by a bang, FALSE otherwise.
  1746. + */
  1747. +gboolean
  1748. +ide_vim_parser_command_has_bang (IdeVimParserCommand *self)
  1749. +{
  1750. + IdeVimParserCommandPrivate *priv = ide_vim_parser_command_get_instance_private (self);
  1751. +
  1752. + g_return_val_if_fail (IDE_IS_VIM_PARSER_COMMAND (self), FALSE);
  1753. +
  1754. + return priv->has_bang;
  1755. +}
  1756. +
  1757. +static IdeVimParserToken *
  1758. +get_token_from_command (IdeVimParserCommand *self,
  1759. + gint position,
  1760. + IdeVimParserTokenKind kind)
  1761. +{
  1762. + const GPtrArray *args;
  1763. + IdeVimParserToken *token;
  1764. +
  1765. + args = ide_vim_parser_command_get_args (self);
  1766. + if (position >= args->len)
  1767. + {
  1768. + g_critical (_("The arguments position %i is outside of arguments array bounds\n"), position);
  1769. + return FALSE;
  1770. + }
  1771. +
  1772. + token = g_ptr_array_index (args, position);
  1773. + if (token->kind != kind)
  1774. + {
  1775. + g_critical (_("The argument at position %i is not a string\n"), position);
  1776. + return FALSE;
  1777. + }
  1778. +
  1779. + return token;
  1780. +}
  1781. +
  1782. +/**
  1783. + * ide_vim_parser_command_get_string_arg:
  1784. + * @self: (in): a #IdeVimParserCommand instance.
  1785. + * @position: (in): the argument position, starting from 0.
  1786. + * @string: (out) (transfer none): an adress of a pointer to a gchar variable.
  1787. + *
  1788. + * Get the location of the string contained in an argument of kind string.
  1789. + * If the argument is not set then FALSE is returned.
  1790. + *
  1791. + * Returns: TRUE on succes, FALSE otherwise.
  1792. + */
  1793. +gboolean
  1794. +ide_vim_parser_command_get_string_arg (IdeVimParserCommand *self,
  1795. + guint position,
  1796. + const gchar **string)
  1797. +{
  1798. + IdeVimParserToken *token;
  1799. +
  1800. + g_return_val_if_fail (IDE_IS_VIM_PARSER_COMMAND (self), FALSE);
  1801. + g_return_val_if_fail (string != NULL, FALSE);
  1802. +
  1803. +
  1804. + token = get_token_from_command (self, position, IDE_VIM_PARSER_TOKEN_STRING);
  1805. + *string = token->content;
  1806. +
  1807. + return token->is_set;
  1808. +}
  1809. +
  1810. +/**
  1811. + * ide_vim_parser_command_get_number_arg:
  1812. + * @self: (in): a #IdeVimParserCommand instance.
  1813. + * @position: (in): the argument position, starting from 0.
  1814. + * @number: (out): an adress of a gint variable.
  1815. + *
  1816. + * Get the value of an argument of kind number.
  1817. + * If the argument is not set then FALSE is returned.
  1818. + *
  1819. + * Returns: TRUE on succes, FALSE otherwise.
  1820. + */
  1821. +gboolean
  1822. +ide_vim_parser_command_get_number_arg (IdeVimParserCommand *self,
  1823. + guint position,
  1824. + gint *number)
  1825. +{
  1826. + IdeVimParserToken *token;
  1827. +
  1828. + g_return_val_if_fail (IDE_IS_VIM_PARSER_COMMAND (self), FALSE);
  1829. + g_return_val_if_fail (number != NULL, FALSE);
  1830. +
  1831. + token = get_token_from_command (self, position, IDE_VIM_PARSER_TOKEN_NUMBER);
  1832. + *number = token->number;
  1833. +
  1834. + return token->is_set;
  1835. +}
  1836. +
  1837. +/**
  1838. + * ide_vim_parser_command_get_func:
  1839. + * @self: (in): a #IdeVimParserCommand instance.
  1840. + *
  1841. + * Get a #IdeVimParserCommandFunc pointer to the command function.
  1842. + *
  1843. + * Returns: (skip): a #IdeVimParserCommandFunc pointer.
  1844. + */
  1845. +IdeVimParserCommandFunc
  1846. +ide_vim_parser_command_get_func (IdeVimParserCommand *self)
  1847. +{
  1848. + IdeVimParserCommandPrivate *priv = ide_vim_parser_command_get_instance_private (self);
  1849. +
  1850. + g_return_val_if_fail (IDE_IS_VIM_PARSER_COMMAND (self), NULL);
  1851. +
  1852. + return priv->func;
  1853. +}
  1854. +
  1855. +/**
  1856. + * ide_vim_parser_command_get_name:
  1857. + * @self: (in): a #IdeVimParserCommand instance.
  1858. + *
  1859. + * Get the command name.
  1860. + *
  1861. + * Returns: (transfer none): a string holding the command name.
  1862. + */
  1863. +const gchar *
  1864. +ide_vim_parser_command_get_name (IdeVimParserCommand *self)
  1865. +{
  1866. + IdeVimParserCommandPrivate *priv = ide_vim_parser_command_get_instance_private (self);
  1867. +
  1868. + g_return_val_if_fail (IDE_IS_VIM_PARSER_COMMAND (self), NULL);
  1869. +
  1870. + return priv->name;
  1871. +}
  1872. +
  1873. +/**
  1874. + * ide_vim_parser_command_get_shortname:
  1875. + * @self: (in): a #IdeVimParserCommand instance.
  1876. + *
  1877. + * Get the command shortname.
  1878. + *
  1879. + * Returns: (transfer none) (nullable): a string holding the command shortname.
  1880. + */
  1881. +const gchar *
  1882. +ide_vim_parser_command_get_shortname (IdeVimParserCommand *self)
  1883. +{
  1884. + IdeVimParserCommandPrivate *priv = ide_vim_parser_command_get_instance_private (self);
  1885. +
  1886. + g_return_val_if_fail (IDE_IS_VIM_PARSER_COMMAND (self), NULL);
  1887. +
  1888. + return priv->shortname;
  1889. +}
  1890. +
  1891. +/**
  1892. + * ide_vim_parser_command_set_shortname:
  1893. + * @self: (in): a #IdeVimParserCommand instance.
  1894. + * @shortname: (in): the command shortname.
  1895. + *
  1896. + * Set the command shortname. The shortname should be a prefix of the name,
  1897. + * at least one char and shorter than the complete name.
  1898. + */
  1899. +static void
  1900. +ide_vim_parser_command_set_shortname (IdeVimParserCommand *self,
  1901. + const gchar *shortname)
  1902. +{
  1903. + IdeVimParserCommandPrivate *priv = ide_vim_parser_command_get_instance_private (self);
  1904. + guint shortname_len;
  1905. +
  1906. + g_return_if_fail (IDE_IS_VIM_PARSER_COMMAND (self));
  1907. +
  1908. + if (ide_str_empty0 (shortname))
  1909. + {
  1910. + priv->shortname = NULL;
  1911. + return;
  1912. + }
  1913. + shortname_len = strlen (shortname);
  1914. +
  1915. + if (shortname_len > 0 && shortname_len < strlen (priv->name) && g_str_has_prefix (priv->name, shortname))
  1916. + priv->shortname = g_strdup (shortname);
  1917. + else
  1918. + g_warning ("The shortname must be a prefix of the name and its length between 1 and name length - 1");
  1919. +}
  1920. +
  1921. +/**
  1922. + * ide_vim_parser_command_get_syntax:
  1923. + * @self: (in): a #IdeVimParserCommand instance.
  1924. + *
  1925. + * Get the command syntax.
  1926. + *
  1927. + * Returns: (transfer none): a string holding the command syntax.
  1928. + */
  1929. +const gchar *
  1930. +ide_vim_parser_command_get_syntax (IdeVimParserCommand *self)
  1931. +{
  1932. + gchar *str = NULL;
  1933. +
  1934. + g_return_val_if_fail (IDE_IS_VIM_PARSER_COMMAND (self), NULL);
  1935. +
  1936. + /* TODO: not yet implemented */
  1937. +
  1938. + return str;
  1939. +}
  1940. +
  1941. +/**
  1942. + * ide_vim_parser_command_get_id:
  1943. + * @self: (in): a #IdeVimParserCommand instance.
  1944. + *
  1945. + * Get the command id.
  1946. + *
  1947. + * Returns: a guint holding the command id.
  1948. + */
  1949. +guint
  1950. +ide_vim_parser_command_get_id (IdeVimParserCommand *self)
  1951. +{
  1952. + IdeVimParserCommandPrivate *priv = ide_vim_parser_command_get_instance_private (self);
  1953. +
  1954. + g_return_val_if_fail (IDE_IS_VIM_PARSER_COMMAND (self), 0);
  1955. +
  1956. + return priv->id;
  1957. +}
  1958. +
  1959. +/**
  1960. + * ide_vim_parser_command_get_args:
  1961. + * @self: (in): a #IdeVimParserCommand instance.
  1962. + *
  1963. + * Get a pointer to a #GPtrArray holding the #IdeVimParserToken command arguments.
  1964. + *
  1965. + * Returns: (transfer none) (element-type IdeVimParserToken): a #GPtrArray pointer
  1966. + */
  1967. +const GPtrArray *
  1968. +ide_vim_parser_command_get_args (IdeVimParserCommand *self)
  1969. +{
  1970. + IdeVimParserCommandPrivate *priv = ide_vim_parser_command_get_instance_private (self);
  1971. +
  1972. + g_return_val_if_fail (IDE_IS_VIM_PARSER_COMMAND (self), NULL);
  1973. +
  1974. + return priv->args;
  1975. +}
  1976. +
  1977. +/**
  1978. + * ide_vim_parser_command_add_arg:
  1979. + * @self: (in): a #IdeVimParserCommand instance.
  1980. + * @kind: (in): a #IdeVimParserTokenKind kind.
  1981. + * @flag: (in): one or more (ored) #IdeVimParserTokenFlag flags.
  1982. + *
  1983. + * Add an argument to the #IdeVimParserCommand command.
  1984. + */
  1985. +void
  1986. +ide_vim_parser_command_add_arg (IdeVimParserCommand *self,
  1987. + IdeVimParserTokenKind kind,
  1988. + IdeVimParserTokenFlag flag)
  1989. +{
  1990. + IdeVimParserCommandPrivate *priv = ide_vim_parser_command_get_instance_private (self);
  1991. + IdeVimParserToken *token;
  1992. +
  1993. + g_assert (IDE_IS_VIM_PARSER_COMMAND (self));
  1994. +
  1995. + token = ide_vim_parser_token_new ();
  1996. + token->kind = kind;
  1997. + token->flag = flag;
  1998. +
  1999. + g_ptr_array_add (priv->args, token);
  2000. +}
  2001. +
  2002. +static void
  2003. +ide_vim_parser_command_add_arg_full (IdeVimParserCommand *self,
  2004. + IdeVimParserToken *token)
  2005. +{
  2006. + IdeVimParserCommandPrivate *priv = ide_vim_parser_command_get_instance_private (self);
  2007. +
  2008. + g_assert (IDE_IS_VIM_PARSER_COMMAND (self));
  2009. +
  2010. + token->is_set = TRUE;
  2011. + g_ptr_array_add (priv->args, token);
  2012. +}
  2013. +
  2014. +/**
  2015. + * ide_vim_parser_command_parsed_new:
  2016. + * @self: (in): a #IdeVimParserCommand instance.
  2017. + * @args: (element-type IdeVimParserToken): a #GList of parsed #IdeVimParserToken.
  2018. + * @error: an adress of a #IdeVimParserError error pointer.
  2019. + *
  2020. + * Inject a #IdeVimParserToken list into a copy of
  2021. + * a #IdeVimParserCommand. The list and the command args
  2022. + * must match and only differ by optional arguments.
  2023. + *
  2024. + * Returns: (transfer full): A parsed #IdeVimParserCommand.
  2025. + */
  2026. +IdeVimParserCommand *
  2027. +ide_vim_parser_command_parsed_new (IdeVimParserCommand *self,
  2028. + GList *args,
  2029. + IdeVimParserError **error)
  2030. +{
  2031. + IdeVimParserCommand *new_command;
  2032. + IdeVimParserCommandPrivate *new_command_priv;
  2033. + IdeVimParserToken *src_token;
  2034. + IdeVimParserToken *dst_token;
  2035. + const GPtrArray *src_args;
  2036. + gboolean is_matching;
  2037. +
  2038. + g_return_val_if_fail (IDE_IS_VIM_PARSER_COMMAND (self), NULL);
  2039. + g_return_val_if_fail (args != NULL, NULL);
  2040. +
  2041. + /* src_args is a list of registered arguments for the given command,
  2042. + * dst_args is a list of parsed arguments.
  2043. + * Both must match according to the kinds and flags.
  2044. + */
  2045. + new_command = ide_vim_parser_command_new (ide_vim_parser_command_get_name (self),
  2046. + ide_vim_parser_command_get_shortname (self),
  2047. + ide_vim_parser_command_get_func (self));
  2048. +
  2049. + new_command_priv = ide_vim_parser_command_get_instance_private (new_command);
  2050. + src_args = ide_vim_parser_command_get_args (self);
  2051. + g_return_val_if_fail (src_args->len > 0, NULL);
  2052. +
  2053. + for (gint i = 0; i < src_args->len; i++)
  2054. + {
  2055. + src_token = g_ptr_array_index (src_args, i);
  2056. + if (args == NULL)
  2057. + {
  2058. + if (src_token->flag == IDE_VIM_PARSER_TOKEN_FLAG_OPTIONNAL)
  2059. + {
  2060. + ide_vim_parser_command_add_arg (new_command, src_token->kind, src_token->flag);
  2061. + continue;
  2062. + }
  2063. + else
  2064. + {
  2065. + ide_vim_parser_error_literal_set (error, IDE_VIM_PARSER_ERROR_MISMATCH_LESS_ARGS,
  2066. + _("The %s command syntax need at least one more argument of kind %s\n"),
  2067. + ide_vim_parser_command_get_name (self),
  2068. + ide_vim_parser_token_get_kind_name (src_token));
  2069. + return NULL;
  2070. + }
  2071. + }
  2072. +
  2073. + dst_token = args->data;
  2074. + is_matching = (src_token->kind == dst_token->kind ||
  2075. + (src_token->kind == IDE_VIM_PARSER_TOKEN_NUMBER && dst_token->kind == IDE_VIM_PARSER_TOKEN_RANGE));
  2076. +
  2077. + if (is_matching )
  2078. + {
  2079. + if (src_token->kind == IDE_VIM_PARSER_TOKEN_COMMAND_NAME)
  2080. + new_command_priv->has_bang = dst_token->command_has_bang;
  2081. +
  2082. + dst_token->flag = src_token->flag;
  2083. + ide_vim_parser_command_add_arg_full (new_command, dst_token);
  2084. + args = args->next;
  2085. + }
  2086. + else if (src_token->flag == IDE_VIM_PARSER_TOKEN_FLAG_OPTIONNAL)
  2087. + {
  2088. + ide_vim_parser_command_add_arg (new_command, src_token->kind, src_token->flag);
  2089. + }
  2090. + else
  2091. + {
  2092. + ide_vim_parser_error_literal_set (error, IDE_VIM_PARSER_ERROR_MISMATCH_KIND,
  2093. + _("%s should be of kind %s (%s found)\n"),
  2094. + ide_vim_parser_token_get_content (dst_token),
  2095. + ide_vim_parser_token_get_kind_name (dst_token),
  2096. + ide_vim_parser_token_get_kind_name (src_token));
  2097. + return NULL;
  2098. + }
  2099. + }
  2100. +
  2101. + if (args != NULL)
  2102. + {
  2103. + dst_token = args->data;
  2104. + ide_vim_parser_error_literal_set (error, IDE_VIM_PARSER_ERROR_MISMATCH_MORE_ARGS,
  2105. + _("You provide too much arguments for the %s command syntax, starting with:%s\n"),
  2106. + ide_vim_parser_command_get_name (self),
  2107. + ide_vim_parser_token_get_content (dst_token));
  2108. + return NULL;
  2109. + }
  2110. +
  2111. + return new_command;
  2112. +}
  2113. +
  2114. +static void
  2115. +ide_vim_parser_command_get_property (GObject *object,
  2116. + guint prop_id,
  2117. + GValue *value,
  2118. + GParamSpec *pspec)
  2119. +{
  2120. + IdeVimParserCommand *self = IDE_VIM_PARSER_COMMAND (object);
  2121. + IdeVimParserCommandPrivate *priv = ide_vim_parser_command_get_instance_private (self);
  2122. +
  2123. + switch (prop_id)
  2124. + {
  2125. + case PROP_ARGS:
  2126. + g_value_set_boxed (value, priv->args);
  2127. + break;
  2128. +
  2129. + case PROP_ID:
  2130. + g_value_set_uint (value, priv->id);
  2131. + break;
  2132. +
  2133. + case PROP_FUNC:
  2134. + g_value_set_pointer (value, priv->func);
  2135. + break;
  2136. +
  2137. + case PROP_NAME:
  2138. + g_value_set_string (value, priv->name);
  2139. + break;
  2140. +
  2141. + case PROP_SHORTNAME:
  2142. + g_value_set_string (value, priv->shortname);
  2143. + break;
  2144. +
  2145. + case PROP_SYNTAX:
  2146. + g_value_set_string (value, ide_vim_parser_command_get_syntax (self));
  2147. + break;
  2148. +
  2149. + default:
  2150. + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
  2151. + }
  2152. +}
  2153. +
  2154. +static void
  2155. +ide_vim_parser_command_set_property (GObject *object,
  2156. + guint prop_id,
  2157. + const GValue *value,
  2158. + GParamSpec *pspec)
  2159. +{
  2160. + IdeVimParserCommand *self = IDE_VIM_PARSER_COMMAND (object);
  2161. + IdeVimParserCommandPrivate *priv = ide_vim_parser_command_get_instance_private (self);
  2162. +
  2163. + switch (prop_id)
  2164. + {
  2165. + case PROP_FUNC:
  2166. + priv->func = g_value_get_pointer (value);
  2167. + break;
  2168. +
  2169. + case PROP_NAME:
  2170. + priv->name = g_value_dup_string (value);
  2171. + break;
  2172. +
  2173. + case PROP_SHORTNAME:
  2174. + ide_vim_parser_command_set_shortname (self, g_value_get_string (value));
  2175. + break;
  2176. +
  2177. + default:
  2178. + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
  2179. + }
  2180. +}
  2181. +
  2182. +static void
  2183. +ide_vim_parser_command_finalize (GObject *object)
  2184. +{
  2185. + IdeVimParserCommand *self = (IdeVimParserCommand *)object;
  2186. + IdeVimParserCommandPrivate *priv = ide_vim_parser_command_get_instance_private (self);
  2187. +
  2188. + g_clear_pointer (&priv->name, g_free);
  2189. + g_clear_pointer (&priv->shortname, g_free);
  2190. + g_ptr_array_free (priv->args, TRUE);
  2191. +
  2192. + G_OBJECT_CLASS (ide_vim_parser_command_parent_class)->finalize (object);
  2193. +}
  2194. +
  2195. +/**
  2196. + * ide_vim_parser_command_new:
  2197. + * @name: (in): the command name.
  2198. + * @shortname: (in): the command shortname.
  2199. + * @func: (in) (skip): the #IdeVimParserCommandFunc command function pointer.
  2200. + *
  2201. + * Create a new #IdeVimParserCommand command.
  2202. + * The shortname should be a prefix of the name,
  2203. + * at least one char and shorter than the complete name.
  2204. + *
  2205. + * Returns: (transfer full): A new #IdeVimParserCommand command.
  2206. + */
  2207. +IdeVimParserCommand *
  2208. +ide_vim_parser_command_new (const gchar *name,
  2209. + const gchar *shortname,
  2210. + IdeVimParserCommandFunc func)
  2211. +{
  2212. + return g_object_new (IDE_TYPE_VIM_PARSER_COMMAND,
  2213. + "name", name,
  2214. + "shortname", shortname,
  2215. + "func", func,
  2216. + NULL);
  2217. +}
  2218. +
  2219. +static void
  2220. +ide_vim_parser_command_init (IdeVimParserCommand *self)
  2221. +{
  2222. + IdeVimParserCommandPrivate *priv = ide_vim_parser_command_get_instance_private (self);
  2223. + static guint id_count = 1;
  2224. +
  2225. + priv->id = id_count++;
  2226. + priv->args = g_ptr_array_new_full (8, (GDestroyNotify)ide_vim_parser_token_unref);
  2227. +}
  2228. +
  2229. +static void
  2230. +ide_vim_parser_command_class_init (IdeVimParserCommandClass *klass)
  2231. +{
  2232. + GObjectClass *object_class = G_OBJECT_CLASS (klass);
  2233. +
  2234. + object_class->finalize = ide_vim_parser_command_finalize;
  2235. + object_class->get_property = ide_vim_parser_command_get_property;
  2236. + object_class->set_property = ide_vim_parser_command_set_property;
  2237. +
  2238. + /**
  2239. + * IdeVimParserCommand:args:
  2240. + *
  2241. + * A #GPtrArray holding the #IdeVimParserToken args of the command.
  2242. + *
  2243. + */
  2244. + gParamSpecs[PROP_ARGS] =
  2245. + g_param_spec_boxed ("args",
  2246. + _("Command arg array"),
  2247. + _("The arg array of the command."),
  2248. + G_TYPE_PTR_ARRAY,
  2249. + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
  2250. +
  2251. + /**
  2252. + * IdeVimParserCommand:func:
  2253. + *
  2254. + * A IdeVimParserCommandFunc pointer to the function use to execute the command.
  2255. + *
  2256. + */
  2257. + gParamSpecs[PROP_FUNC] =
  2258. + g_param_spec_pointer ("func",
  2259. + _("Command function pointer"),
  2260. + _("A pointer to the command function."),
  2261. + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
  2262. +
  2263. + /**
  2264. + * IdeVimParserCommand:id:
  2265. + *
  2266. + * A uint holding the id of the command.
  2267. + *
  2268. + */
  2269. + gParamSpecs[PROP_ID] =
  2270. + g_param_spec_uint ("id",
  2271. + _("Command id"),
  2272. + _("The id of the command."),
  2273. + 0,
  2274. + G_MAXUINT,
  2275. + 0,
  2276. + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
  2277. +
  2278. + /**
  2279. + * IdeVimParserCommand:name:
  2280. + *
  2281. + * A string holding the name of the command.
  2282. + */
  2283. + gParamSpecs[PROP_NAME] =
  2284. + g_param_spec_string ("name",
  2285. + _("Command name"),
  2286. + _("The name of the command."),
  2287. + NULL,
  2288. + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
  2289. +
  2290. + /**
  2291. + * IdeVimParserCommand:shortname:
  2292. + *
  2293. + * A string holding the shortname of the command.
  2294. + * By design, it is ensured that the shortname string
  2295. + * is a prefix of the name string.
  2296. + */
  2297. + gParamSpecs[PROP_SHORTNAME] =
  2298. + g_param_spec_string ("shortname",
  2299. + _("Command shortname"),
  2300. + _("The shortname of the command."),
  2301. + NULL,
  2302. + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
  2303. +
  2304. + /**
  2305. + * IdeVimParserCommand:syntax:
  2306. + *
  2307. + * A string holding the syntax of the command,
  2308. + * mostly used for helping purpose.
  2309. + */
  2310. + gParamSpecs[PROP_SYNTAX] =
  2311. + g_param_spec_string ("syntax",
  2312. + _("Command syntax"),
  2313. + _("The syntax of the command."),
  2314. + NULL,
  2315. + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
  2316. +
  2317. + g_object_class_install_properties (object_class, LAST_PROP, gParamSpecs);
  2318. +}
  2319. diff --git a/libide/vim/ide-vim-parser-command.h b/libide/vim/ide-vim-parser-command.h
  2320. new file mode 100644
  2321. index 0000000..292cb13
  2322. --- /dev/null
  2323. +++ b/libide/vim/ide-vim-parser-command.h
  2324. @@ -0,0 +1,66 @@
  2325. +/* ide-vim-parser-command.h
  2326. + *
  2327. + * Copyright (C) 2015 Sébastien Lafargue <slafargue@gnome.org>
  2328. + *
  2329. + * This program is free software: you can redistribute it and/or modify
  2330. + * it under the terms of the GNU General Public License as published by
  2331. + * the Free Software Foundation, either version 3 of the License, or
  2332. + * (at your option) any later version.
  2333. + *
  2334. + * This program is distributed in the hope that it will be useful,
  2335. + * but WITHOUT ANY WARRANTY; without even the implied warranty of
  2336. + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  2337. + * GNU General Public License for more details.
  2338. + *
  2339. + * You should have received a copy of the GNU General Public License
  2340. + * along with this program. If not, see <http://www.gnu.org/licenses/>.
  2341. + */
  2342. +
  2343. +#ifndef IDE_VIM_PARSER_COMMAND_H
  2344. +#define IDE_VIM_PARSER_COMMAND_H
  2345. +
  2346. +#include <glib.h>
  2347. +
  2348. +#include "ide-vim-parser-error.h"
  2349. +#include "ide-vim-parser-token.h"
  2350. +#include "ide-vim-parser-commands.h"
  2351. +
  2352. +G_BEGIN_DECLS
  2353. +
  2354. +#define IDE_TYPE_VIM_PARSER_COMMAND (ide_vim_parser_command_get_type())
  2355. +
  2356. +G_DECLARE_FINAL_TYPE (IdeVimParserCommand , ide_vim_parser_command, IDE, VIM_PARSER_COMMAND, GObject)
  2357. +
  2358. +struct _IdeVimParserCommand
  2359. +{
  2360. + GObject parent;
  2361. +};
  2362. +
  2363. +
  2364. +
  2365. +IdeVimParserCommand *ide_vim_parser_command_new (const gchar *name,
  2366. + const gchar *shortname,
  2367. + IdeVimParserCommandFunc func);
  2368. +const GPtrArray *ide_vim_parser_command_get_args (IdeVimParserCommand *self);
  2369. +gboolean ide_vim_parser_command_get_number_arg (IdeVimParserCommand *self,
  2370. + guint position,
  2371. + gint *number);
  2372. +gboolean ide_vim_parser_command_get_string_arg (IdeVimParserCommand *self,
  2373. + guint position,
  2374. + const gchar **string);
  2375. +IdeVimParserCommandFunc ide_vim_parser_command_get_func (IdeVimParserCommand *self);
  2376. +const gchar *ide_vim_parser_command_get_name (IdeVimParserCommand *self);
  2377. +const gchar *ide_vim_parser_command_get_shortname (IdeVimParserCommand *self);
  2378. +const gchar *ide_vim_parser_command_get_syntax (IdeVimParserCommand *self);
  2379. +guint ide_vim_parser_command_get_id (IdeVimParserCommand *self);
  2380. +void ide_vim_parser_command_add_arg (IdeVimParserCommand *self,
  2381. + IdeVimParserTokenKind kind,
  2382. + IdeVimParserTokenFlag flag);
  2383. +gboolean ide_vim_parser_command_has_bang (IdeVimParserCommand *self);
  2384. +/* TODO: should be private */
  2385. +IdeVimParserCommand *ide_vim_parser_command_parsed_new (IdeVimParserCommand *self,
  2386. + GList *args,
  2387. + IdeVimParserError **error);
  2388. +G_END_DECLS
  2389. +
  2390. +#endif /* IDE_VIM_PARSER_COMMAND_H */
  2391. diff --git a/libide/vim/ide-vim-parser-commands.c b/libide/vim/ide-vim-parser-commands.c
  2392. new file mode 100644
  2393. index 0000000..68023e6
  2394. --- /dev/null
  2395. +++ b/libide/vim/ide-vim-parser-commands.c
  2396. @@ -0,0 +1,29 @@
  2397. +/* ide-vim-parser-commands.c
  2398. + *
  2399. + * Copyright (C) 2015 Sébastien Lafargue <slafargue@gnome.org>
  2400. + *
  2401. + * This program is free software: you can redistribute it and/or modify
  2402. + * it under the terms of the GNU General Public License as published by
  2403. + * the Free Software Foundation, either version 3 of the License, or
  2404. + * (at your option) any later version.
  2405. + *
  2406. + * This program is distributed in the hope that it will be useful,
  2407. + * but WITHOUT ANY WARRANTY; without even the implied warranty of
  2408. + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  2409. + * GNU General Public License for more details.
  2410. + *
  2411. + * You should have received a copy of the GNU General Public License
  2412. + * along with this program. If not, see <http://www.gnu.org/licenses/>.
  2413. + */
  2414. +
  2415. +#include <string.h>
  2416. +
  2417. +#include <glib/gprintf.h>
  2418. +#include <glib/gi18n.h>
  2419. +
  2420. +#include "ide-debug.h"
  2421. +#include "ide-macros.h"
  2422. +
  2423. +#include "ide-vim-parser-command.h"
  2424. +#include "ide-vim-parser-commands.h"
  2425. +
  2426. diff --git a/libide/vim/ide-vim-parser-commands.h b/libide/vim/ide-vim-parser-commands.h
  2427. new file mode 100644
  2428. index 0000000..381084c
  2429. --- /dev/null
  2430. +++ b/libide/vim/ide-vim-parser-commands.h
  2431. @@ -0,0 +1,41 @@
  2432. +/* ide-vim-parser-commands.h
  2433. + *
  2434. + * Copyright (C) 2015 Sébastien Lafargue <slafargue@gnome.org>
  2435. + *
  2436. + * This program is free software: you can redistribute it and/or modify
  2437. + * it under the terms of the GNU General Public License as published by
  2438. + * the Free Software Foundation, either version 3 of the License, or
  2439. + * (at your option) any later version.
  2440. + *
  2441. + * This program is distributed in the hope that it will be useful,
  2442. + * but WITHOUT ANY WARRANTY; without even the implied warranty of
  2443. + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  2444. + * GNU General Public License for more details.
  2445. + *
  2446. + * You should have received a copy of the GNU General Public License
  2447. + * along with this program. If not, see <http://www.gnu.org/licenses/>.
  2448. + */
  2449. +
  2450. +#ifndef IDE_VIM_PARSER_COMMANDS_H
  2451. +#define IDE_VIM_PARSER_COMMANDS_H
  2452. +
  2453. +#include <glib.h>
  2454. +#include <gtk/gtk.h>
  2455. +#include <gtksourceview/gtksource.h>
  2456. +
  2457. +#include "ide-vim-parser-error.h"
  2458. +#include "ide-vim-parser-token.h"
  2459. +
  2460. +G_BEGIN_DECLS
  2461. +
  2462. +typedef struct _IdeVimParserCommand IdeVimParserCommand;
  2463. +typedef struct _IdeVimParser IdeVimParser;
  2464. +
  2465. +typedef gboolean (*IdeVimParserCommandFunc) (GtkSourceView *source_view,
  2466. + IdeVimParser *parser,
  2467. + IdeVimParserCommand *command,
  2468. + IdeVimParserError **error);
  2469. +
  2470. +G_END_DECLS
  2471. +
  2472. +#endif /* IDE_VIM_PARSER_COMMANDS_H */
  2473. diff --git a/libide/vim/ide-vim-parser-debug.c b/libide/vim/ide-vim-parser-debug.c
  2474. new file mode 100644
  2475. index 0000000..d54b287
  2476. --- /dev/null
  2477. +++ b/libide/vim/ide-vim-parser-debug.c
  2478. @@ -0,0 +1,151 @@
  2479. +/* ide-vim-parser-debug.c
  2480. + *
  2481. + * Copyright (C) 2015 Sébastien Lafargue <slafargue@gnome.org>
  2482. + *
  2483. + * This program is free software: you can redistribute it and/or modify
  2484. + * it under the terms of the GNU General Public License as published by
  2485. + * the Free Software Foundation, either version 3 of the License, or
  2486. + * (at your option) any later version.
  2487. + *
  2488. + * This program is distributed in the hope that it will be useful,
  2489. + * but WITHOUT ANY WARRANTY; without even the implied warranty of
  2490. + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  2491. + * GNU General Public License for more details.
  2492. + *
  2493. + * You should have received a copy of the GNU General Public License
  2494. + * along with this program. If not, see <http://www.gnu.org/licenses/>.
  2495. + */
  2496. +
  2497. +#include <glib/gprintf.h>
  2498. +#include <glib/gi18n.h>
  2499. +
  2500. +#include "ide-macros.h"
  2501. +
  2502. +#include "ide-vim-parser-debug.h"
  2503. +
  2504. +#pragma GCC diagnostic push
  2505. +#pragma GCC diagnostic ignored "-Wswitch-enum"
  2506. +
  2507. +gchar *
  2508. +ide_vim_parser_debug_token_to_string (IdeVimParserToken *token)
  2509. +{
  2510. + GString *string = g_string_new (NULL);
  2511. +
  2512. + string = g_string_append (string, ide_vim_parser_token_get_kind_name (token));
  2513. + if (!token->is_set)
  2514. + {
  2515. + g_string_append_printf (string, ": not set");
  2516. + return g_string_free (string, FALSE);
  2517. + }
  2518. +
  2519. + switch (token->kind)
  2520. + {
  2521. + case IDE_VIM_PARSER_TOKEN_MARK:
  2522. + case IDE_VIM_PARSER_TOKEN_NUMBER:
  2523. + case IDE_VIM_PARSER_TOKEN_RANGE_SPECIFIER:
  2524. + case IDE_VIM_PARSER_TOKEN_RANGE_SEPARATOR:
  2525. + case IDE_VIM_PARSER_TOKEN_REGISTER:
  2526. + case IDE_VIM_PARSER_TOKEN_STRING:
  2527. + g_string_append_printf (string, ":%s", ide_vim_parser_token_get_content (token));
  2528. + break;
  2529. +
  2530. + case IDE_VIM_PARSER_TOKEN_PATTERN:
  2531. + g_string_append_printf (string, "type:%c, %s", token->pattern_type, ide_vim_parser_token_get_content (token));
  2532. + break;
  2533. +
  2534. + case IDE_VIM_PARSER_TOKEN_RANGE:
  2535. + g_string_append_printf (string, ":[%i,%i]", token->range_start, token->range_end);
  2536. + break;
  2537. +
  2538. + case IDE_VIM_PARSER_TOKEN_UNKNOW:
  2539. + case IDE_VIM_PARSER_TOKEN_END_OF_STRING:
  2540. + break;
  2541. +
  2542. + case IDE_VIM_PARSER_TOKEN_COMMAND_NAME:
  2543. + g_string_append_printf (string, ":%s%c",
  2544. + ide_vim_parser_command_get_name (token->command_entry),
  2545. + token->command_has_bang ? '!' : '\0');
  2546. + break;
  2547. +
  2548. + default:
  2549. + g_assert_not_reached ();
  2550. + break;
  2551. + }
  2552. +
  2553. + return g_string_free (string, FALSE);
  2554. +}
  2555. +
  2556. +#pragma GCC diagnostic pop
  2557. +
  2558. +void
  2559. +ide_vim_parser_debug_show_stack (GList *token_list)
  2560. +{
  2561. + GList *l = NULL;
  2562. + gchar *str;
  2563. +
  2564. + if (token_list != NULL)
  2565. + g_printf ("--------------------------------------------\n"
  2566. + "Debug - Show stack :\n");
  2567. +
  2568. + for (l = token_list; l != NULL; l = l->next)
  2569. + {
  2570. + str = ide_vim_parser_debug_token_to_string (l->data);
  2571. + g_printf ("%s\n", str);
  2572. + g_free (str);
  2573. + }
  2574. +}
  2575. +
  2576. +/**
  2577. + * ide_vim_parser_debug_show_parsed:
  2578. + * @parsed_command: (in): A #IdeVimParserCommand command.
  2579. + *
  2580. + * Show the tokens of a parsed command line.
  2581. + */
  2582. +void
  2583. +ide_vim_parser_debug_show_parsed (IdeVimParserCommand *parsed_command)
  2584. +{
  2585. + const GPtrArray *args;
  2586. + gchar *str;
  2587. +
  2588. + args = ide_vim_parser_command_get_args (parsed_command);
  2589. + if (args == NULL || args->len == 0)
  2590. + {
  2591. + g_printf ("no tokens in list\n");
  2592. + return;
  2593. + }
  2594. +
  2595. + for (gint i = 0; i < args->len; i++)
  2596. + {
  2597. + str = ide_vim_parser_debug_token_to_string (g_ptr_array_index (args, i));
  2598. + g_printf ("%s\n", str);
  2599. + g_free (str);
  2600. + }
  2601. +}
  2602. +
  2603. +void
  2604. +ide_vim_parser_debug_show_info (const IdeVimParserInfo *info)
  2605. +{
  2606. + const gchar *state_names [] =
  2607. + {
  2608. + "Not parsed",
  2609. + "Complete",
  2610. + "Incomplete",
  2611. + "Error"
  2612. + };
  2613. +
  2614. + const gchar *state_name;
  2615. + const gchar *error_code_name;
  2616. +
  2617. + state_name = state_names [info->state];
  2618. + error_code_name = ide_vim_parser_error_get_message (info->error_code);
  2619. +
  2620. + g_printf ("state:%s, error code:%i %s\n", state_name, info->error_code, error_code_name);
  2621. + g_printf ("last token kind:%s, last good token kind:%s\n",
  2622. + ide_vim_parser_token_kind_get_name (info->last_token_kind),
  2623. + ide_vim_parser_token_kind_get_name (info->last_good_token_kind));
  2624. +
  2625. + g_printf ("last token content:%s\n", info->last_token_content);
  2626. + g_printf ("command found:%s\n\n", info->command_name_found);
  2627. +}
  2628. +
  2629. +
  2630. diff --git a/libide/vim/ide-vim-parser-debug.h b/libide/vim/ide-vim-parser-debug.h
  2631. new file mode 100644
  2632. index 0000000..259c0fc
  2633. --- /dev/null
  2634. +++ b/libide/vim/ide-vim-parser-debug.h
  2635. @@ -0,0 +1,36 @@
  2636. +/* ide-vim-parser-debug.h
  2637. + *
  2638. + * Copyright (C) 2015 Sébastien Lafargue <slafargue@gnome.org>
  2639. + *
  2640. + * This program is free software: you can redistribute it and/or modify
  2641. + * it under the terms of the GNU General Public License as published by
  2642. + * the Free Software Foundation, either version 3 of the License, or
  2643. + * (at your option) any later version.
  2644. + *
  2645. + * This program is distributed in the hope that it will be useful,
  2646. + * but WITHOUT ANY WARRANTY; without even the implied warranty of
  2647. + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  2648. + * GNU General Public License for more details.
  2649. + *
  2650. + * You should have received a copy of the GNU General Public License
  2651. + * along with this program. If not, see <http://www.gnu.org/licenses/>.
  2652. + */
  2653. +
  2654. +#ifndef IDE_VIM_PARSER_DEBUG_H
  2655. +#define IDE_VIM_PARSER_DEBUG_H
  2656. +
  2657. +#include <glib-object.h>
  2658. +
  2659. +#include "ide-vim-parser.h"
  2660. +#include "ide-vim-parser-command.h"
  2661. +#include "ide-vim-parser-token.h"
  2662. +
  2663. +G_BEGIN_DECLS
  2664. +
  2665. +void ide_vim_parser_debug_show_parsed (IdeVimParserCommand *parsed_command);
  2666. +gchar *ide_vim_parser_debug_token_to_string (IdeVimParserToken *token);
  2667. +void ide_vim_parser_debug_show_stack (GList *token_list);
  2668. +void ide_vim_parser_debug_show_info (const IdeVimParserInfo *info);
  2669. +G_END_DECLS
  2670. +
  2671. +#endif /* IDE_VIM_PARSER_DEBUG_H */
  2672. diff --git a/libide/vim/ide-vim-parser-error.c b/libide/vim/ide-vim-parser-error.c
  2673. new file mode 100644
  2674. index 0000000..5d2e541
  2675. --- /dev/null
  2676. +++ b/libide/vim/ide-vim-parser-error.c
  2677. @@ -0,0 +1,340 @@
  2678. +/* ide-vim-parser-error.c
  2679. + *
  2680. + * Copyright (C) 2015 Sébastien Lafargue <slafargue@gnome.org>
  2681. + *
  2682. + * This program is free software: you can redistribute it and/or modify
  2683. + * it under the terms of the GNU General Public License as published by
  2684. + * the Free Software Foundation, either version 3 of the License, or
  2685. + * (at your option) any later version.
  2686. + *
  2687. + * This program is distributed in the hope that it will be useful,
  2688. + * but WITHOUT ANY WARRANTY; without even the implied warranty of
  2689. + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  2690. + * GNU General Public License for more details.
  2691. + *
  2692. + * You should have received a copy of the GNU General Public License
  2693. + * along with this program. If not, see <http://www.gnu.org/licenses/>.
  2694. + */
  2695. +#include <glib/gprintf.h>
  2696. +#include <glib/gi18n.h>
  2697. +
  2698. +#include "ide-macros.h"
  2699. +
  2700. +#include "ide-vim-parser-error.h"
  2701. +
  2702. +/* Keep it in sync with IdeVimParserErrorCode enum */
  2703. +static const gchar *error_messages [] = {
  2704. + N_("No error"),
  2705. + N_("Unknow parser error"),
  2706. + N_("Feature not yet implemented"),
  2707. + N_("Vim mode requires GtkSourceView"),
  2708. + N_("Failed to locate working directory"),
  2709. + N_("There's no buffer referenced by the view"),
  2710. + N_("Not found"),
  2711. + N_("This is not a number"),
  2712. + N_("The Number is out of range"),
  2713. + N_("There's no command found in the command line text"),
  2714. + N_("The command is empty or NULL"),
  2715. + N_("This is an unknow option"),
  2716. + N_("Your command line syntax doesn't match the registered command syntax"),
  2717. + N_("You provide less arguments than the registered command syntax need"),
  2718. + N_("You provide more arguments than the registered command syntax need"),
  2719. + N_("The set value you provide is invalid"),
  2720. + N_("The set command can't be parsed."),
  2721. + N_("Unknow range specifier."),
  2722. + N_("Unknow mark."),
  2723. + N_("Unknow register."),
  2724. + N_("You can't use this token here.")
  2725. +};
  2726. +
  2727. +/**
  2728. + * ide_vim_parser_error_get_detail:
  2729. + * @error: (in): a #IdeVimParserError pointer.
  2730. + *
  2731. + * Return the detail string of the IdeVimParserError error.
  2732. + *
  2733. + * Returns: (transfer full): the detail string.
  2734. + */
  2735. +gchar *
  2736. +ide_vim_parser_error_get_detail (IdeVimParserError *error)
  2737. +{
  2738. + g_autofree gchar *error_cursor = NULL;
  2739. +
  2740. + g_assert (error->pos >= -1 || error->pos < strlen (error->detail_text));
  2741. +
  2742. + if (!ide_str_empty0 (error->detail_text))
  2743. + {
  2744. + if (error->pos == -1)
  2745. + return g_strdup (error->detail_text);
  2746. +
  2747. + if (error->pos == 0)
  2748. + return g_strdup_printf ("%s\n^", error->detail_text);
  2749. +
  2750. + error_cursor = g_strnfill (error->pos, ' ');
  2751. + return g_strdup_printf ("%s\n%s^", error->detail_text, error_cursor);
  2752. + }
  2753. +
  2754. + return NULL;
  2755. +}
  2756. +
  2757. +/**
  2758. + * ide_vim_parser_error_print:
  2759. + * @error: (in): a #IdeVimParserError pointer.
  2760. + *
  2761. + * Print the message and detail of the #IdeVimParserError error.
  2762. + * Mainly used for debug stuffs.
  2763. + */
  2764. +void
  2765. +ide_vim_parser_error_print (IdeVimParserError *error)
  2766. +{
  2767. + g_autofree gchar *detail = NULL;
  2768. +
  2769. + g_assert (error != NULL);
  2770. +
  2771. + g_printf ("%s\n", error->code_text);
  2772. + detail = ide_vim_parser_error_get_detail (error);
  2773. + g_printf ("%s", detail);
  2774. +}
  2775. +
  2776. +const gchar *
  2777. +ide_vim_parser_error_get_message (IdeVimParserErrorCode error_code)
  2778. +{
  2779. + return error_messages [error_code];
  2780. +}
  2781. +
  2782. +IdeVimParserErrorCode
  2783. +ide_vim_parser_error_get_error_code (IdeVimParserError *error)
  2784. +{
  2785. + if (error == NULL)
  2786. + return IDE_VIM_PARSER_ERROR_NONE;
  2787. + else
  2788. + return error->code;
  2789. +}
  2790. +
  2791. +/**
  2792. + * ide_vim_parser_error_propagate:
  2793. + * @dest: (in): an adress of the destination #IdeVimParserError error pointer.
  2794. + * @src: (in): a source #IdeVimParserError error pointer.
  2795. + *
  2796. + * If src error is set, then tranfer it to the dest error,
  2797. + * otherwise, do nothing.
  2798. + */
  2799. +void
  2800. +ide_vim_parser_error_propagate (IdeVimParserError **dest,
  2801. + IdeVimParserError *src)
  2802. +{
  2803. + if (dest == NULL)
  2804. + {
  2805. + if (src)
  2806. + ide_vim_parser_error_free (src);
  2807. + return;
  2808. + }
  2809. + else
  2810. + {
  2811. + if (*dest != NULL)
  2812. + {
  2813. + g_warning ("You are overwriting an already set IdeVimParserError");
  2814. + if (src)
  2815. + ide_vim_parser_error_free (src);
  2816. + }
  2817. + else
  2818. + {
  2819. + *dest = src;
  2820. + }
  2821. + }
  2822. +}
  2823. +
  2824. +#pragma GCC diagnostic push
  2825. +#pragma GCC diagnostic ignored "-Wformat-nonliteral"
  2826. +static IdeVimParserError *
  2827. +ide_vim_parser_error_new_valist (IdeVimParserErrorCode code,
  2828. + const gchar *message,
  2829. + va_list args)
  2830. +{
  2831. + IdeVimParserError *self;
  2832. +
  2833. + self = g_new0 (IdeVimParserError, 1);
  2834. +
  2835. + self->code = code;
  2836. + self->code_text = g_strdup (gettext (error_messages [code]));
  2837. + self->detail_text = (message) ? g_strdup_vprintf (message, args) : NULL;
  2838. + self->pos = -1;
  2839. +
  2840. + return self;
  2841. +}
  2842. +#pragma GCC diagnostic pop
  2843. +
  2844. +/**
  2845. + * ide_vim_parser_error_new:
  2846. + * @code: a #IdeVimParserErrorCode error code.
  2847. + * @message: a text error string.
  2848. + *
  2849. + * Returns: (skip) (transfer full): a new @IdeVimParserError error.
  2850. + */
  2851. +IdeVimParserError *
  2852. +ide_vim_parser_error_new (IdeVimParserErrorCode code,
  2853. + const gchar *message)
  2854. +{
  2855. + return ide_vim_parser_error_cursor_new (code, message, -1);
  2856. +}
  2857. +
  2858. +/**
  2859. + * ide_vim_parser_error_cursor_new:
  2860. + * @code: a #IdeVimParserErrorCode error code.
  2861. + * @message: a text error string (command line for example).
  2862. + * @pos: the position, starting from 0, of an error indicator for the #cmdline text.
  2863. + *
  2864. + * Returns: (skip) (transfer full): a new @IdeVimParserError error.
  2865. + */
  2866. +IdeVimParserError *
  2867. +ide_vim_parser_error_cursor_new (IdeVimParserErrorCode code,
  2868. + const gchar *message,
  2869. + gint pos)
  2870. +{
  2871. + IdeVimParserError *self;
  2872. +
  2873. + self = g_new0 (IdeVimParserError, 1);
  2874. +
  2875. + self->code = code;
  2876. + self->code_text = g_strdup (gettext (error_messages [code]));
  2877. + self->detail_text = (message) ? g_strdup (message) : NULL;
  2878. + self->pos = pos;
  2879. +
  2880. + return self;
  2881. +}
  2882. +
  2883. +/**
  2884. + * ide_vim_parser_error_literal_new:
  2885. + * @code: (in): a #IdeVimParserErrorCode error code.
  2886. + * @message: (in): a printf style string.
  2887. + * @...: (in): arguments for the printf style string.
  2888. + *
  2889. + * Returns: (skip) (transfer full): a new @IdeVimParserError error.
  2890. + */
  2891. +IdeVimParserError *
  2892. +ide_vim_parser_error_literal_new (IdeVimParserErrorCode code,
  2893. + const gchar *message,
  2894. + ...)
  2895. +{
  2896. + IdeVimParserError *self;
  2897. + va_list args;
  2898. +
  2899. + self = g_new0 (IdeVimParserError, 1);
  2900. +
  2901. + va_start(args, message);
  2902. + self = ide_vim_parser_error_new_valist (code, message, args);
  2903. + va_end (args);
  2904. +
  2905. + return self;
  2906. +}
  2907. +
  2908. +/**
  2909. + * ide_vim_parser_error_set:
  2910. + * @error_p: (in): an adress of the destination #IdeVimParserError error pointer.
  2911. + * @code: (in): a #IdeVimParserErrorCode error code.
  2912. + * @message: (in): a text error string (command line for example).
  2913. + *
  2914. + * Set the content of a @IdeVimParserError error.
  2915. + */
  2916. +void
  2917. +ide_vim_parser_error_set (IdeVimParserError **error_p,
  2918. + IdeVimParserErrorCode code,
  2919. + const gchar *message)
  2920. +{
  2921. + if (error_p == NULL)
  2922. + return;
  2923. +
  2924. + if (*error_p == NULL)
  2925. + *error_p = ide_vim_parser_error_cursor_new (code, message, -1);
  2926. + else
  2927. + g_warning ("You are overwriting an already set IdeVimParserError");
  2928. +}
  2929. +
  2930. +/**
  2931. + * ide_vim_parser_error_cursor_set:
  2932. + * @error_p: (in): an adress of the destination #IdeVimParserError error pointer.
  2933. + * @code: (in): a #IdeVimParserErrorCode error code.
  2934. + * @message: (in): a text error string (command line for example).
  2935. + * @pos: (in): the position, starting from 0, of an error indicator for the #cmdline text.
  2936. + *
  2937. + * Set the content of a @IdeVimParserError error.
  2938. + */
  2939. +void
  2940. +ide_vim_parser_error_cursor_set (IdeVimParserError **error_p,
  2941. + IdeVimParserErrorCode code,
  2942. + const gchar *message,
  2943. + guint pos)
  2944. +{
  2945. + if (error_p == NULL)
  2946. + return;
  2947. +
  2948. + if (*error_p == NULL)
  2949. + *error_p = ide_vim_parser_error_cursor_new (code, message, pos);
  2950. + else
  2951. + g_warning ("You are overwriting an already set IdeVimParserError");
  2952. +}
  2953. +
  2954. +/**
  2955. + * ide_vim_parser_error_literal_set:
  2956. + * @error_p: (in): an adress of the destination #IdeVimParserError error pointer.
  2957. + * @code: (in): a #IdeVimParserErrorCode error code.
  2958. + * @message: (in): a printf style string.
  2959. + * @...: (in): arguments for the printf style string.
  2960. + *
  2961. + * Set the content of a @IdeVimParserError error.
  2962. + */
  2963. +void
  2964. +ide_vim_parser_error_literal_set (IdeVimParserError **error_p,
  2965. + IdeVimParserErrorCode code,
  2966. + const gchar *message,
  2967. + ...)
  2968. +{
  2969. + va_list args;
  2970. +
  2971. + if (error_p == NULL)
  2972. + return;
  2973. +
  2974. + if (*error_p == NULL)
  2975. + {
  2976. + va_start(args, message);
  2977. + *error_p = ide_vim_parser_error_new_valist (code, message, args);
  2978. + va_end (args);
  2979. + }
  2980. + else
  2981. + {
  2982. + g_warning ("You are overwriting an already set IdeVimParserError");
  2983. + }
  2984. +}
  2985. +
  2986. +/**
  2987. + * ide_vim_parser_error_clear:
  2988. + * @error: (in): an adress of the destination #IdeVimParserError error pointer.
  2989. + *
  2990. + * Free the @IdeVimParserError pointed by error and set it to NULL.
  2991. + */
  2992. +void
  2993. +ide_vim_parser_error_clear (IdeVimParserError **error)
  2994. +{
  2995. + if (error && *error)
  2996. + {
  2997. + ide_vim_parser_error_free (*error);
  2998. + *error = NULL;
  2999. + }
  3000. +}
  3001. +
  3002. +/**
  3003. + * ide_vim_parser_error_free:
  3004. + * @self: (in): a IdeVimParserError error.
  3005. + *
  3006. + * Free the @IdeVimParserError error.
  3007. + */
  3008. +void
  3009. +ide_vim_parser_error_free (IdeVimParserError *self)
  3010. +{
  3011. + g_assert (self != NULL);
  3012. +
  3013. + g_clear_pointer (&self->code_text, g_free);
  3014. + g_clear_pointer (&self->detail_text, g_free);
  3015. +
  3016. + g_free (self);
  3017. +}
  3018. diff --git a/libide/vim/ide-vim-parser-error.h b/libide/vim/ide-vim-parser-error.h
  3019. new file mode 100644
  3020. index 0000000..7fd466c
  3021. --- /dev/null
  3022. +++ b/libide/vim/ide-vim-parser-error.h
  3023. @@ -0,0 +1,93 @@
  3024. +/* ide-vim-parser-error.h
  3025. + *
  3026. + * Copyright (C) 2015 Sébastien Lafargue <slafargue@gnome.org>
  3027. + *
  3028. + * This program is free software: you can redistribute it and/or modify
  3029. + * it under the terms of the GNU General Public License as published by
  3030. + * the Free Software Foundation, either version 3 of the License, or
  3031. + * (at your option) any later version.
  3032. + *
  3033. + * This program is distributed in the hope that it will be useful,
  3034. + * but WITHOUT ANY WARRANTY; without even the implied warranty of
  3035. + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  3036. + * GNU General Public License for more details.
  3037. + *
  3038. + * You should have received a copy of the GNU General Public License
  3039. + * along with this program. If not, see <http://www.gnu.org/licenses/>.
  3040. + */
  3041. +
  3042. +#ifndef IDE_VIM_PARSER_ERROR_H
  3043. +#define IDE_VIM_PARSER_ERROR_H
  3044. +
  3045. +#include <glib-object.h>
  3046. +
  3047. +G_BEGIN_DECLS
  3048. +
  3049. +#define IDE_VIM_PARSER_ERROR (ide_vim_parser_error_quark())
  3050. +
  3051. +typedef struct _IdeVimParserError IdeVimParserError;
  3052. +
  3053. +/* Keep it in sync with error_messages array */
  3054. +typedef enum
  3055. +{
  3056. + IDE_VIM_PARSER_ERROR_NONE,
  3057. + IDE_VIM_PARSER_ERROR_FAILED,
  3058. + IDE_VIM_PARSER_ERROR_NOT_IMPLEMENTED,
  3059. + IDE_VIM_PARSER_ERROR_NOT_SOURCE_VIEW,
  3060. + IDE_VIM_PARSER_ERROR_NO_WORKING_DIR,
  3061. + IDE_VIM_PARSER_ERROR_NO_BUFFER,
  3062. + IDE_VIM_PARSER_ERROR_NOT_FOUND,
  3063. + IDE_VIM_PARSER_ERROR_NOT_NUMBER,
  3064. + IDE_VIM_PARSER_ERROR_NUMBER_OUT_OF_RANGE,
  3065. + IDE_VIM_PARSER_ERROR_COMMAND_NOT_FOUND,
  3066. + IDE_VIM_PARSER_ERROR_CMDLINE_EMPTY,
  3067. + IDE_VIM_PARSER_ERROR_UNKNOWN_OPTION,
  3068. + IDE_VIM_PARSER_ERROR_MISMATCH_KIND,
  3069. + IDE_VIM_PARSER_ERROR_MISMATCH_LESS_ARGS,
  3070. + IDE_VIM_PARSER_ERROR_MISMATCH_MORE_ARGS,
  3071. + IDE_VIM_PARSER_ERROR_SET_VALUE_INVALID,
  3072. + IDE_VIM_PARSER_ERROR_SET_CANT_PARSE,
  3073. + IDE_VIM_PARSER_ERROR_UNKNOWN_RANGE_SPECIFIER,
  3074. + IDE_VIM_PARSER_ERROR_UNKNOWN_MARK,
  3075. + IDE_VIM_PARSER_ERROR_UNKNOWN_REGISTER,
  3076. + IDE_VIM_PARSER_ERROR_UNWANTED_TOKEN
  3077. +} IdeVimParserErrorCode;
  3078. +
  3079. +struct _IdeVimParserError
  3080. +{
  3081. + IdeVimParserErrorCode code;
  3082. + const gchar *code_text;
  3083. + const gchar *detail_text;
  3084. + gint pos;
  3085. +};
  3086. +
  3087. +void ide_vim_parser_error_clear (IdeVimParserError **error);
  3088. +gchar *ide_vim_parser_error_get_detail (IdeVimParserError *error);
  3089. +void ide_vim_parser_error_free (IdeVimParserError *self);
  3090. +IdeVimParserError *ide_vim_parser_error_literal_new (IdeVimParserErrorCode code,
  3091. + const gchar *message,
  3092. + ...) G_GNUC_PRINTF (2,3);
  3093. +IdeVimParserError *ide_vim_parser_error_new (IdeVimParserErrorCode code,
  3094. + const gchar *message);
  3095. +IdeVimParserError *ide_vim_parser_error_cursor_new (IdeVimParserErrorCode code,
  3096. + const gchar *message,
  3097. + gint pos);
  3098. +void ide_vim_parser_error_literal_set (IdeVimParserError **error_p,
  3099. + IdeVimParserErrorCode code,
  3100. + const gchar *message,
  3101. + ...) G_GNUC_PRINTF (3,4);
  3102. +void ide_vim_parser_error_cursor_set (IdeVimParserError **error_p,
  3103. + IdeVimParserErrorCode code,
  3104. + const gchar *message,
  3105. + guint pos);
  3106. +void ide_vim_parser_error_set (IdeVimParserError **error_p,
  3107. + IdeVimParserErrorCode code,
  3108. + const gchar *message);
  3109. +void ide_vim_parser_error_print (IdeVimParserError *error);
  3110. +void ide_vim_parser_error_propagate (IdeVimParserError **dest,
  3111. + IdeVimParserError *src);
  3112. +IdeVimParserErrorCode ide_vim_parser_error_get_error_code (IdeVimParserError *error);
  3113. +const gchar *ide_vim_parser_error_get_message (IdeVimParserErrorCode error_code);
  3114. +G_END_DECLS
  3115. +
  3116. +#endif /* IDE_VIM_PARSER_ERROR_H */
  3117. diff --git a/libide/vim/ide-vim-parser-objects-pool.c b/libide/vim/ide-vim-parser-objects-pool.c
  3118. new file mode 100644
  3119. index 0000000..7423ef0
  3120. --- /dev/null
  3121. +++ b/libide/vim/ide-vim-parser-objects-pool.c
  3122. @@ -0,0 +1,496 @@
  3123. +/* ide-vim-parser-objects-pool.c
  3124. + *
  3125. + * Copyright (C) 2015 Sébastien Lafargue <slafargue@gnome.org>
  3126. + *
  3127. + * This program is free software: you can redistribute it and/or modify
  3128. + * it under the terms of the GNU General Public License as published by
  3129. + * the Free Software Foundation, either version 3 of the License, or
  3130. + * (at your option) any later version.
  3131. + *
  3132. + * This program is distributed in the hope that it will be useful,
  3133. + * but WITHOUT ANY WARRANTY; without even the implied warranty of
  3134. + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  3135. + * GNU General Public License for more details.
  3136. + *
  3137. + * You should have received a copy of the GNU General Public License
  3138. + * along with this program. If not, see <http://www.gnu.org/licenses/>.
  3139. + */
  3140. +
  3141. +#include <string.h>
  3142. +
  3143. +#include <glib/gprintf.h>
  3144. +#include <glib/gi18n.h>
  3145. +
  3146. +#include "ide-debug.h"
  3147. +#include "ide-macros.h"
  3148. +
  3149. +#include "ide-vim-parser-objects-pool.h"
  3150. +#include "ide-vim-complete-item.h"
  3151. +
  3152. +typedef struct
  3153. +{
  3154. + GHashTable *command_name_table;
  3155. + GHashTable *command_shortname_table;
  3156. +
  3157. + GHashTable *settable_name_table;
  3158. + GHashTable *settable_shortname_table;
  3159. +} IdeVimParserObjectsPoolPrivate;
  3160. +
  3161. +G_DEFINE_TYPE_WITH_PRIVATE (IdeVimParserObjectsPool, ide_vim_parser_objects_pool, G_TYPE_OBJECT)
  3162. +
  3163. +enum {
  3164. + PROP_0,
  3165. + LAST_PROP
  3166. +};
  3167. +
  3168. +static GParamSpec *gParamSpecs [LAST_PROP];
  3169. +
  3170. +IdeVimParserSettable *
  3171. +ide_vim_parser_objects_pool_lookup_settable (IdeVimParserObjectsPool *self,
  3172. + const gchar *string)
  3173. +{
  3174. + gpointer value;
  3175. + IdeVimParserObjectsPoolPrivate *priv = ide_vim_parser_objects_pool_get_instance_private (self);
  3176. +
  3177. + value = g_hash_table_lookup (priv->settable_name_table, string);
  3178. + if (value == NULL)
  3179. + value = g_hash_table_lookup (priv->settable_shortname_table, string);
  3180. +
  3181. + return IDE_VIM_PARSER_SETTABLE (value);
  3182. +}
  3183. +
  3184. +IdeVimParserCommand *
  3185. +ide_vim_parser_objects_pool_lookup_command (IdeVimParserObjectsPool *self,
  3186. + const gchar *string)
  3187. +{
  3188. + gpointer value;
  3189. + IdeVimParserObjectsPoolPrivate *priv = ide_vim_parser_objects_pool_get_instance_private (self);
  3190. +
  3191. + value = g_hash_table_lookup (priv->command_name_table, string);
  3192. + if (value == NULL)
  3193. + value = g_hash_table_lookup (priv->command_shortname_table, string);
  3194. +
  3195. + return IDE_VIM_PARSER_COMMAND (value);
  3196. +}
  3197. +
  3198. +/*
  3199. + * ar is a GPtrArray of IdeVimCompleteItem corresponding to
  3200. + * the commands with prefix match_name.
  3201. + *
  3202. + * Returns: FALSE if there is no element retrived, TRUE otherwise.
  3203. + */
  3204. +gboolean
  3205. +ide_vim_parser_objects_pool_complete_command (IdeVimParserObjectsPool *self,
  3206. + GPtrArray *ar,
  3207. + const gchar *match_text)
  3208. +{
  3209. + IdeVimParserObjectsPoolPrivate *priv;
  3210. + GHashTable *dup_table = NULL;
  3211. + GHashTableIter iter;
  3212. + gpointer key, value;
  3213. + IdeVimCompleteItem *complete_entry = NULL;
  3214. + const gchar *name;
  3215. + const gchar *shortname;
  3216. + gboolean is_get_all = FALSE;
  3217. + gboolean has_entry = FALSE;
  3218. +
  3219. + g_return_val_if_fail (IDE_IS_VIM_PARSER_OBJECTS_POOL (self), FALSE);
  3220. + priv = ide_vim_parser_objects_pool_get_instance_private (self);
  3221. +
  3222. + /* If the matching text is empty, we want all the existing commands */
  3223. + if (ide_str_empty0 (match_text))
  3224. + is_get_all = TRUE;
  3225. + else
  3226. + dup_table = g_hash_table_new ((GHashFunc)g_str_hash, (GEqualFunc)g_str_equal);
  3227. +
  3228. + g_hash_table_iter_init (&iter, priv->command_name_table);
  3229. + while (g_hash_table_iter_next (&iter, &key, &value))
  3230. + {
  3231. + name = (gchar *)key;
  3232. + if (is_get_all || g_str_has_prefix (name, match_text))
  3233. + {
  3234. + shortname = ide_vim_parser_command_get_shortname (IDE_VIM_PARSER_COMMAND (value));
  3235. + if (!is_get_all && !ide_str_empty0 (shortname))
  3236. + g_hash_table_add (dup_table, (gpointer)shortname);
  3237. +
  3238. + complete_entry = ide_vim_complete_item_new (IDE_VIM_COMPLETE_ITEM_KIND_COMMAND,
  3239. + g_strdup (name),
  3240. + g_strdup (shortname));
  3241. + g_ptr_array_add (ar, complete_entry);
  3242. + has_entry = TRUE;
  3243. + }
  3244. + }
  3245. +
  3246. + if (!is_get_all)
  3247. + {
  3248. + g_hash_table_iter_init (&iter, priv->command_shortname_table);
  3249. + while (g_hash_table_iter_next (&iter, &key, &value))
  3250. + {
  3251. + shortname = (gchar *)key;
  3252. + if (g_str_has_prefix (shortname, match_text) && !g_hash_table_contains (dup_table, shortname))
  3253. + {
  3254. + name = ide_vim_parser_command_get_name (IDE_VIM_PARSER_COMMAND (value));
  3255. + complete_entry = ide_vim_complete_item_new (IDE_VIM_COMPLETE_ITEM_KIND_COMMAND,
  3256. + g_strdup (name),
  3257. + g_strdup (shortname));
  3258. + g_ptr_array_add (ar, complete_entry);
  3259. + has_entry = TRUE;
  3260. + }
  3261. + }
  3262. + }
  3263. +
  3264. + if (!is_get_all)
  3265. + g_hash_table_destroy (dup_table);
  3266. +
  3267. + return has_entry;
  3268. +}
  3269. +
  3270. +/*
  3271. + * ar is a GPtrArray of IdeVimCompleteItem corresponding to
  3272. + * the settables with prefix match_name.
  3273. + *
  3274. + * Returns: FALSE if there is no element retrived, TRUE otherwise.
  3275. + */
  3276. +gboolean
  3277. +ide_vim_parser_objects_pool_complete_settable (IdeVimParserObjectsPool *self,
  3278. + GPtrArray *ar,
  3279. + const gchar *match_text)
  3280. +{
  3281. + IdeVimParserObjectsPoolPrivate *priv;
  3282. + GHashTable *dup_table = NULL;
  3283. + GHashTableIter iter;
  3284. + gpointer key, value;
  3285. + IdeVimCompleteItem *complete_entry = NULL;
  3286. + const gchar *name;
  3287. + const gchar *shortname;
  3288. + gboolean is_get_all = FALSE;
  3289. + gboolean has_entry = FALSE;
  3290. +
  3291. + g_return_val_if_fail (IDE_IS_VIM_PARSER_OBJECTS_POOL (self), FALSE);
  3292. + priv = ide_vim_parser_objects_pool_get_instance_private (self);
  3293. +
  3294. + /* If the matching text is empty, we want all the existing commands */
  3295. + if (ide_str_empty0 (match_text))
  3296. + is_get_all = TRUE;
  3297. + else
  3298. + dup_table = g_hash_table_new ((GHashFunc)g_str_hash, (GEqualFunc)g_str_equal);
  3299. +
  3300. + g_hash_table_iter_init (&iter, priv->settable_name_table);
  3301. + while (g_hash_table_iter_next (&iter, &key, &value))
  3302. + {
  3303. + name = (gchar *)key;
  3304. + if (is_get_all || g_str_has_prefix (name, match_text))
  3305. + {
  3306. + shortname = ide_vim_parser_settable_get_shortname (IDE_VIM_PARSER_SETTABLE (value));
  3307. + if (!is_get_all && !ide_str_empty0 (shortname))
  3308. + g_hash_table_add (dup_table, (gpointer)shortname);
  3309. +
  3310. + complete_entry = ide_vim_complete_item_new (IDE_VIM_COMPLETE_ITEM_KIND_SET,
  3311. + g_strdup (name),
  3312. + g_strdup (shortname));
  3313. + g_ptr_array_add (ar, complete_entry);
  3314. + has_entry = TRUE;
  3315. + }
  3316. + }
  3317. +
  3318. + if (!is_get_all)
  3319. + {
  3320. + g_hash_table_iter_init (&iter, priv->settable_shortname_table);
  3321. + while (g_hash_table_iter_next (&iter, &key, &value))
  3322. + {
  3323. + shortname = (gchar *)key;
  3324. + if (g_str_has_prefix (shortname, match_text) && !g_hash_table_contains (dup_table, shortname))
  3325. + {
  3326. + name = ide_vim_parser_settable_get_name (IDE_VIM_PARSER_SETTABLE (value));
  3327. + complete_entry = ide_vim_complete_item_new (IDE_VIM_COMPLETE_ITEM_KIND_SET,
  3328. + g_strdup (name),
  3329. + g_strdup (shortname));
  3330. + g_ptr_array_add (ar, complete_entry);
  3331. + has_entry = TRUE;
  3332. + }
  3333. + }
  3334. + }
  3335. +
  3336. + if (!is_get_all)
  3337. + g_hash_table_destroy (dup_table);
  3338. +
  3339. + return has_entry;
  3340. +}
  3341. +
  3342. +gboolean
  3343. +ide_vim_parser_objects_pool_add_settable (IdeVimParserObjectsPool *self,
  3344. + IdeVimParserSettable *settable,
  3345. + gboolean replace)
  3346. +{
  3347. + const gchar *name;
  3348. + const gchar *shortname;
  3349. + const gchar *found_settable_shortname;
  3350. + IdeVimParserSettable *found_settable;
  3351. + gboolean is_conflict;
  3352. + IdeVimParserObjectsPoolPrivate *priv;
  3353. +
  3354. + g_return_val_if_fail (IDE_IS_VIM_PARSER_OBJECTS_POOL (self), FALSE);
  3355. + g_return_val_if_fail (IDE_IS_VIM_PARSER_SETTABLE (settable), FALSE);
  3356. +
  3357. + priv = ide_vim_parser_objects_pool_get_instance_private (self);
  3358. + name = ide_vim_parser_settable_get_name (settable);
  3359. + shortname = ide_vim_parser_settable_get_shortname (settable);
  3360. +
  3361. + found_settable = IDE_VIM_PARSER_SETTABLE (g_hash_table_lookup (priv->settable_name_table, name));
  3362. +
  3363. + is_conflict = (g_hash_table_lookup (priv->settable_shortname_table, name) != NULL ||
  3364. + g_hash_table_lookup (priv->settable_name_table, shortname) != NULL ||
  3365. + g_hash_table_lookup (priv->settable_shortname_table, shortname) != NULL);
  3366. +
  3367. + if (is_conflict)
  3368. + {
  3369. + g_warning (_("This Settable can't be added because of a conflict with an existing name and/or shortname"));
  3370. + return FALSE;
  3371. + }
  3372. +
  3373. + if (found_settable == NULL)
  3374. + {
  3375. + g_object_ref (settable);
  3376. + g_hash_table_insert (priv->settable_name_table, g_strdup (name), settable);
  3377. + g_hash_table_insert (priv->settable_shortname_table, g_strdup (shortname), settable);
  3378. +
  3379. + return TRUE;
  3380. + }
  3381. +
  3382. + if (settable == found_settable)
  3383. + {
  3384. + g_warning (_("This Settable is already in the pool"));
  3385. + return FALSE;
  3386. + }
  3387. +
  3388. + if (replace)
  3389. + {
  3390. + found_settable_shortname = ide_vim_parser_settable_get_shortname (found_settable);
  3391. + g_hash_table_remove (priv->settable_shortname_table, found_settable_shortname);
  3392. +
  3393. + g_object_ref (settable);
  3394. + g_hash_table_replace (priv->settable_name_table, g_strdup (name), settable);
  3395. + g_hash_table_insert (priv->settable_shortname_table, g_strdup (shortname), settable);
  3396. +
  3397. + return TRUE;
  3398. + }
  3399. +
  3400. + return FALSE;
  3401. +}
  3402. +
  3403. +gboolean
  3404. +ide_vim_parser_objects_pool_add_command (IdeVimParserObjectsPool *self,
  3405. + IdeVimParserCommand *command,
  3406. + gboolean replace)
  3407. +{
  3408. + const gchar *name;
  3409. + const gchar *shortname;
  3410. + const gchar *found_command_shortname;
  3411. + IdeVimParserCommand *found_command;
  3412. + gboolean is_conflict;
  3413. + gboolean is_shortname_empty;
  3414. + IdeVimParserObjectsPoolPrivate *priv;
  3415. +
  3416. + g_return_val_if_fail (IDE_IS_VIM_PARSER_OBJECTS_POOL (self), FALSE);
  3417. + g_return_val_if_fail (IDE_IS_VIM_PARSER_COMMAND (command), FALSE);
  3418. +
  3419. + priv = ide_vim_parser_objects_pool_get_instance_private (self);
  3420. + name = ide_vim_parser_command_get_name (command);
  3421. + shortname = ide_vim_parser_command_get_shortname (command);
  3422. + is_shortname_empty = ide_str_empty0 (shortname);
  3423. +
  3424. + found_command = IDE_VIM_PARSER_COMMAND (g_hash_table_lookup (priv->command_name_table, name));
  3425. +
  3426. + is_conflict = (g_hash_table_lookup (priv->command_shortname_table, name) != NULL ||
  3427. + (!is_shortname_empty && (g_hash_table_lookup (priv->command_name_table, shortname) != NULL ||
  3428. + g_hash_table_lookup (priv->command_shortname_table, shortname) != NULL)));
  3429. +
  3430. + if (is_conflict)
  3431. + {
  3432. + g_warning (_("This command can't be added because of a conflict with an existing name and/or shortname"));
  3433. + return FALSE;
  3434. + }
  3435. +
  3436. + if (found_command == NULL)
  3437. + {
  3438. + g_object_ref (command);
  3439. + g_hash_table_insert (priv->command_name_table, g_strdup (name), command);
  3440. + if (!is_shortname_empty)
  3441. + g_hash_table_insert (priv->command_shortname_table, g_strdup (shortname), command);
  3442. +
  3443. + return TRUE;
  3444. + }
  3445. +
  3446. + if (command == found_command)
  3447. + {
  3448. + g_warning (_("This command is already in the pool"));
  3449. + return FALSE;
  3450. + }
  3451. +
  3452. + if (replace)
  3453. + {
  3454. + found_command_shortname = ide_vim_parser_command_get_shortname (found_command);
  3455. + if (found_command_shortname != NULL)
  3456. + g_hash_table_remove (priv->command_shortname_table, found_command_shortname);
  3457. +
  3458. + g_object_ref (command);
  3459. + g_hash_table_replace (priv->command_name_table, g_strdup (name), command);
  3460. + if (!is_shortname_empty)
  3461. + g_hash_table_insert (priv->command_shortname_table, g_strdup (shortname), command);
  3462. +
  3463. + return TRUE;
  3464. + }
  3465. +
  3466. + return FALSE;
  3467. +}
  3468. +
  3469. +/* @string can be the name or the shortname */
  3470. +gboolean
  3471. +ide_vim_parser_objects_pool_remove_settable (IdeVimParserObjectsPool *self,
  3472. + const gchar *string)
  3473. +{
  3474. + IdeVimParserSettable *settable;
  3475. + const gchar *name;
  3476. + const gchar *shortname;
  3477. + IdeVimParserObjectsPoolPrivate *priv;
  3478. +
  3479. + g_return_val_if_fail (IDE_IS_VIM_PARSER_OBJECTS_POOL (self), FALSE);
  3480. + g_return_val_if_fail (!ide_str_empty0 (string), FALSE);
  3481. +
  3482. + priv = ide_vim_parser_objects_pool_get_instance_private (self);
  3483. + settable = ide_vim_parser_objects_pool_lookup_settable (self, string);
  3484. + if (settable != NULL)
  3485. + {
  3486. + shortname = ide_vim_parser_settable_get_shortname (settable);
  3487. + g_hash_table_remove (priv->settable_shortname_table, shortname);
  3488. + name = ide_vim_parser_settable_get_name (settable);
  3489. + g_hash_table_remove (priv->settable_name_table, name);
  3490. + return TRUE;
  3491. + }
  3492. +
  3493. + return FALSE;
  3494. +}
  3495. +
  3496. +/* @string can be the name or the shortname */
  3497. +gboolean
  3498. +ide_vim_parser_objects_pool_remove_command (IdeVimParserObjectsPool *self,
  3499. + const gchar *string)
  3500. +{
  3501. + IdeVimParserCommand *command;
  3502. + const gchar *name;
  3503. + const gchar *shortname;
  3504. + IdeVimParserObjectsPoolPrivate *priv;
  3505. +
  3506. + g_return_val_if_fail (IDE_IS_VIM_PARSER_OBJECTS_POOL (self), FALSE);
  3507. + g_return_val_if_fail (!ide_str_empty0 (string), FALSE);
  3508. +
  3509. + priv = ide_vim_parser_objects_pool_get_instance_private (self);
  3510. + command = ide_vim_parser_objects_pool_lookup_command (self, string);
  3511. + if (command != NULL)
  3512. + {
  3513. + shortname = ide_vim_parser_command_get_shortname (command);
  3514. + g_hash_table_remove (priv->command_shortname_table, shortname);
  3515. + name = ide_vim_parser_command_get_name (command);
  3516. + g_hash_table_remove (priv->command_name_table, name);
  3517. + return TRUE;
  3518. + }
  3519. +
  3520. + return FALSE;
  3521. +}
  3522. +
  3523. +static void
  3524. +ide_vim_parser_objects_pool_get_property (GObject *object,
  3525. + guint prop_id,
  3526. + GValue *value,
  3527. + GParamSpec *pspec)
  3528. +{
  3529. + IdeVimParserObjectsPool *self = IDE_VIM_PARSER_OBJECTS_POOL (object);
  3530. +
  3531. + switch (prop_id)
  3532. + {
  3533. + default:
  3534. + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
  3535. + }
  3536. +}
  3537. +
  3538. +static void
  3539. +ide_vim_parser_objects_pool_set_property (GObject *object,
  3540. + guint prop_id,
  3541. + const GValue *value,
  3542. + GParamSpec *pspec)
  3543. +{
  3544. + IdeVimParserObjectsPool *self = IDE_VIM_PARSER_OBJECTS_POOL (object);
  3545. + IdeVimParserObjectsPoolPrivate *priv = ide_vim_parser_objects_pool_get_instance_private (self);
  3546. +
  3547. + switch (prop_id)
  3548. + {
  3549. + default:
  3550. + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
  3551. + }
  3552. +}
  3553. +
  3554. +static void
  3555. +ide_vim_parser_objects_pool_finalize (GObject *object)
  3556. +{
  3557. + IdeVimParserObjectsPool *self = IDE_VIM_PARSER_OBJECTS_POOL (object);
  3558. + IdeVimParserObjectsPoolPrivate *priv = ide_vim_parser_objects_pool_get_instance_private (self);
  3559. +
  3560. + g_hash_table_destroy (priv->command_name_table);
  3561. + g_hash_table_destroy (priv->command_shortname_table);
  3562. +
  3563. + g_hash_table_destroy (priv->settable_name_table);
  3564. + g_hash_table_destroy (priv->settable_shortname_table);
  3565. +
  3566. + G_OBJECT_CLASS (ide_vim_parser_objects_pool_parent_class)->finalize (object);
  3567. +}
  3568. +
  3569. +/**
  3570. + * ide_vim_parser_objects_pool_new:
  3571. + *
  3572. + * Create a new #IdeVimParserSObjectsPool instance.
  3573. + *
  3574. + * Returns: (transfer full): A new #IdeVimParserObjectsPool instance.
  3575. + */
  3576. +IdeVimParserObjectsPool *
  3577. +ide_vim_parser_objects_pool_new (void)
  3578. +{
  3579. + return g_object_new (IDE_TYPE_VIM_PARSER_OBJECTS_POOL, NULL);
  3580. +}
  3581. +
  3582. +static void
  3583. +ide_vim_parser_objects_pool_init (IdeVimParserObjectsPool *self)
  3584. +{
  3585. + IdeVimParserObjectsPoolPrivate *priv = ide_vim_parser_objects_pool_get_instance_private (self);
  3586. +
  3587. + priv->command_name_table = g_hash_table_new_full ((GHashFunc)g_str_hash,
  3588. + (GEqualFunc)g_str_equal,
  3589. + (GDestroyNotify)g_free,
  3590. + (GDestroyNotify)g_object_unref);
  3591. +
  3592. + priv->command_shortname_table = g_hash_table_new_full ((GHashFunc)g_str_hash,
  3593. + (GEqualFunc)g_str_equal,
  3594. + (GDestroyNotify)g_free,
  3595. + NULL);
  3596. +
  3597. + priv->settable_name_table = g_hash_table_new_full ((GHashFunc)g_str_hash,
  3598. + (GEqualFunc)g_str_equal,
  3599. + (GDestroyNotify)g_free,
  3600. + (GDestroyNotify)g_object_unref);
  3601. +
  3602. + priv->settable_shortname_table = g_hash_table_new_full ((GHashFunc)g_str_hash,
  3603. + (GEqualFunc)g_str_equal,
  3604. + (GDestroyNotify)g_free,
  3605. + NULL);
  3606. +}
  3607. +
  3608. +static void
  3609. +ide_vim_parser_objects_pool_class_init (IdeVimParserObjectsPoolClass *klass)
  3610. +{
  3611. + GObjectClass *object_class = G_OBJECT_CLASS (klass);
  3612. +
  3613. + object_class->finalize = ide_vim_parser_objects_pool_finalize;
  3614. + object_class->get_property = ide_vim_parser_objects_pool_get_property;
  3615. + object_class->set_property = ide_vim_parser_objects_pool_set_property;
  3616. +
  3617. + //g_object_class_install_properties (object_class, LAST_PROP, gParamSpecs);
  3618. +}
  3619. diff --git a/libide/vim/ide-vim-parser-objects-pool.h b/libide/vim/ide-vim-parser-objects-pool.h
  3620. new file mode 100644
  3621. index 0000000..f6f3e17
  3622. --- /dev/null
  3623. +++ b/libide/vim/ide-vim-parser-objects-pool.h
  3624. @@ -0,0 +1,63 @@
  3625. +/* ide-vim-parser-objects-pool.h
  3626. + *
  3627. + * Copyright (C) 2015 Sébastien Lafargue <slafargue@gnome.org>
  3628. + *
  3629. + * This program is free software: you can redistribute it and/or modify
  3630. + * it under the terms of the GNU General Public License as published by
  3631. + * the Free Software Foundation, either version 3 of the License, or
  3632. + * (at your option) any later version.
  3633. + *
  3634. + * This program is distributed in the hope that it will be useful,
  3635. + * but WITHOUT ANY WARRANTY; without even the implied warranty of
  3636. + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  3637. + * GNU General Public License for more details.
  3638. + *
  3639. + * You should have received a copy of the GNU General Public License
  3640. + * along with this program. If not, see <http://www.gnu.org/licenses/>.
  3641. + */
  3642. +
  3643. +#ifndef IDE_VIM_PARSER_OBJECTS_POOL_H
  3644. +#define IDE_VIM_PARSER_OBJECTS_POOL_H
  3645. +
  3646. +#include <glib.h>
  3647. +
  3648. +#include "ide-vim-parser-error.h"
  3649. +#include "ide-vim-parser-settable.h"
  3650. +#include "ide-vim-parser-command.h"
  3651. +
  3652. +G_BEGIN_DECLS
  3653. +
  3654. +#define IDE_TYPE_VIM_PARSER_OBJECTS_POOL (ide_vim_parser_objects_pool_get_type())
  3655. +
  3656. +G_DECLARE_FINAL_TYPE (IdeVimParserObjectsPool, ide_vim_parser_objects_pool, IDE, VIM_PARSER_OBJECTS_POOL, GObject)
  3657. +
  3658. +struct _IdeVimParserObjectsPool
  3659. +{
  3660. + GObject parent;
  3661. +};
  3662. +
  3663. +IdeVimParserObjectsPool *ide_vim_parser_objects_pool_new (void);
  3664. +gboolean ide_vim_parser_objects_pool_add_settable (IdeVimParserObjectsPool *self,
  3665. + IdeVimParserSettable *settable,
  3666. + gboolean replace);
  3667. +gboolean ide_vim_parser_objects_pool_add_command (IdeVimParserObjectsPool *self,
  3668. + IdeVimParserCommand *command,
  3669. + gboolean replace);
  3670. +gboolean ide_vim_parser_objects_pool_complete_command (IdeVimParserObjectsPool *self,
  3671. + GPtrArray *ar,
  3672. + const gchar *match_text);
  3673. +gboolean ide_vim_parser_objects_pool_complete_settable (IdeVimParserObjectsPool *self,
  3674. + GPtrArray *ar,
  3675. + const gchar *match_text);
  3676. +IdeVimParserSettable *ide_vim_parser_objects_pool_lookup_settable (IdeVimParserObjectsPool *self,
  3677. + const gchar *string);
  3678. +IdeVimParserCommand *ide_vim_parser_objects_pool_lookup_command (IdeVimParserObjectsPool *self,
  3679. + const gchar *string);
  3680. +gboolean ide_vim_parser_objects_pool_remove_settable (IdeVimParserObjectsPool *self,
  3681. + const gchar *string);
  3682. +gboolean ide_vim_parser_objects_pool_remove_command (IdeVimParserObjectsPool *self,
  3683. + const gchar *string);
  3684. +
  3685. +G_END_DECLS
  3686. +
  3687. +#endif /* IDE_VIM_PARSER_OBJECTS_H */
  3688. diff --git a/libide/vim/ide-vim-parser-private.h b/libide/vim/ide-vim-parser-private.h
  3689. new file mode 100644
  3690. index 0000000..8128365
  3691. --- /dev/null
  3692. +++ b/libide/vim/ide-vim-parser-private.h
  3693. @@ -0,0 +1,37 @@
  3694. +/* ide-vim-parser-private.h
  3695. + *
  3696. + * Copyright (C) 2015 Sébastien Lafargue <slafargue@gnome.org>
  3697. + *
  3698. + * This program is free software: you can redistribute it and/or modify
  3699. + * it under the terms of the GNU General Public License as published by
  3700. + * the Free Software Foundation, either version 3 of the License, or
  3701. + * (at your option) any later version.
  3702. + *
  3703. + * This program is distributed in the hope that it will be useful,
  3704. + * but WITHOUT ANY WARRANTY; without even the implied warranty of
  3705. + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  3706. + * GNU General Public License for more details.
  3707. + *
  3708. + * You should have received a copy of the GNU General Public License
  3709. + * along with this program. If not, see <http://www.gnu.org/licenses/>.
  3710. + */
  3711. +
  3712. +#ifndef IDE_VIM_PARSER_PRIVATE_H
  3713. +#define IDE_VIM_PARSER_PRIVATE_H
  3714. +
  3715. +#include "ide-vim-parser.h"
  3716. +#include "ide-vim-state-machine.h"
  3717. +
  3718. +G_BEGIN_DECLS
  3719. +
  3720. +typedef enum
  3721. +{
  3722. + FSM_STATE_INITIAL,
  3723. + FSM_STATE_RANGE,
  3724. + FSM_STATE_PATTERN,
  3725. + FSM_STATE_COMMAND
  3726. +} FsmState;
  3727. +
  3728. +G_END_DECLS
  3729. +
  3730. +#endif /* IDE_VIM_PARSER_PRIVATE_H */
  3731. diff --git a/libide/vim/ide-vim-parser-settable.c b/libide/vim/ide-vim-parser-settable.c
  3732. new file mode 100644
  3733. index 0000000..319b7f5
  3734. --- /dev/null
  3735. +++ b/libide/vim/ide-vim-parser-settable.c
  3736. @@ -0,0 +1,559 @@
  3737. +/* ide-vim-parser-settable.c
  3738. + *
  3739. + * Copyright (C) 2015 Sébastien Lafargue <slafargue@gnome.org>
  3740. + *
  3741. + * This program is free software: you can redistribute it and/or modify
  3742. + * it under the terms of the GNU General Public License as published by
  3743. + * the Free Software Foundation, either version 3 of the License, or
  3744. + * (at your option) any later version.
  3745. + *
  3746. + * This program is distributed in the hope that it will be useful,
  3747. + * but WITHOUT ANY WARRANTY; without even the implied warranty of
  3748. + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  3749. + * GNU General Public License for more details.
  3750. + *
  3751. + * You should have received a copy of the GNU General Public License
  3752. + * along with this program. If not, see <http://www.gnu.org/licenses/>.
  3753. + */
  3754. +
  3755. +#include <string.h>
  3756. +
  3757. +#include <glib/gprintf.h>
  3758. +#include <glib/gi18n.h>
  3759. +
  3760. +#include "ide-debug.h"
  3761. +#include "ide-macros.h"
  3762. +
  3763. +#include "ide-vim-parser-settable.h"
  3764. +
  3765. +typedef struct
  3766. +{
  3767. + GParamSpec *value_spec;
  3768. + GValue value;
  3769. + GValue default_value;
  3770. + IdeVimParserSettableFunc func;
  3771. + IdeVimParserSettableValuesFunc values_func;
  3772. + gboolean constructed : 1;
  3773. +} IdeVimParserSettablePrivate;
  3774. +
  3775. +G_DEFINE_TYPE_WITH_PRIVATE (IdeVimParserSettable, ide_vim_parser_settable, G_TYPE_OBJECT)
  3776. +
  3777. +enum {
  3778. + PROP_0,
  3779. + PROP_NAME,
  3780. + PROP_SHORTNAME,
  3781. + PROP_HELP,
  3782. + PROP_FUNC,
  3783. + PROP_VALUES_FUNC,
  3784. + PROP_VALUE_SPEC,
  3785. + PROP_VALUE,
  3786. + PROP_DEFAULT_VALUE,
  3787. + LAST_PROP
  3788. +};
  3789. +
  3790. +static GParamSpec *gParamSpecs [LAST_PROP];
  3791. +
  3792. +/**
  3793. + * ide_vim_parser_settable_set_value:
  3794. + * @self: a #IdeVimParserSettable instance.
  3795. + * @value: a #GValue value.
  3796. + *
  3797. + * Set the settable to a new value.
  3798. + * The #IdeVimParserSettableFunc function is called.
  3799. + *
  3800. + * Returns: TRUE on succes, FALSE otherwise
  3801. + * (wrong Value type or out of bounds value)
  3802. + */
  3803. +gboolean
  3804. +ide_vim_parser_settable_set_value (IdeVimParserSettable *self,
  3805. + GValue *value,
  3806. + IdeVimParserError **error)
  3807. +{
  3808. + IdeVimParserSettableFunc func;
  3809. + g_autofree gchar *msg = NULL;
  3810. + IdeVimParserError *tmp_error = NULL;
  3811. + IdeVimParserSettablePrivate *priv = ide_vim_parser_settable_get_instance_private (self);
  3812. +
  3813. + g_return_val_if_fail (IDE_IS_VIM_PARSER_SETTABLE (self), FALSE);
  3814. +
  3815. + if (!g_param_value_validate (priv->value_spec, value))
  3816. + {
  3817. + func = priv->func;
  3818. + if (!func (self, value, IDE_VIM_PARSER_SETTABLE_OP_SET, &tmp_error))
  3819. + {
  3820. + ide_vim_parser_error_propagate (error, tmp_error);
  3821. + return FALSE;
  3822. + }
  3823. + }
  3824. + else
  3825. + {
  3826. + msg = g_strdup_value_contents (value);
  3827. + ide_vim_parser_error_set (error, IDE_VIM_PARSER_ERROR_SET_VALUE_INVALID, msg);
  3828. + return FALSE;
  3829. + }
  3830. +
  3831. + return TRUE;
  3832. +}
  3833. +
  3834. +/**
  3835. + * ide_vim_parser_settable_reset_value:
  3836. + * @self: a #IdeVimParserSettable instance.
  3837. + *
  3838. + * Reset the settable to its default value.
  3839. + */
  3840. +void
  3841. +ide_vim_parser_settable_reset_value (IdeVimParserSettable *self)
  3842. +{
  3843. + IdeVimParserSettableFunc func;
  3844. + IdeVimParserSettablePrivate *priv = ide_vim_parser_settable_get_instance_private (self);
  3845. +
  3846. + g_return_if_fail (IDE_IS_VIM_PARSER_SETTABLE (self));
  3847. +
  3848. + g_param_value_set_default (priv->value_spec, &priv->value);
  3849. +
  3850. + func = priv->func;
  3851. + func (self, &priv->value, IDE_VIM_PARSER_SETTABLE_OP_RESET, NULL);
  3852. +}
  3853. +
  3854. +/**
  3855. + * ide_vim_parser_settable_get_name:
  3856. + * @self: (in): a #IdeVimParserSettable instance.
  3857. + *
  3858. + * Get the settable name.
  3859. + *
  3860. + * Returns: (transfer none): a string holding the settable name.
  3861. + */
  3862. +const gchar *
  3863. +ide_vim_parser_settable_get_name (IdeVimParserSettable *self)
  3864. +{
  3865. + IdeVimParserSettablePrivate *priv = ide_vim_parser_settable_get_instance_private (self);
  3866. +
  3867. + g_return_val_if_fail (IDE_IS_VIM_PARSER_SETTABLE (self), NULL);
  3868. +
  3869. + return g_param_spec_get_name (priv->value_spec);
  3870. +}
  3871. +
  3872. +/**
  3873. + * ide_vim_parser_settable_get_shortname:
  3874. + * @self: (in): a #IdeVimParserSettable instance.
  3875. + *
  3876. + * Get the settable shortname.
  3877. + *
  3878. + * Returns: (transfer none) (nullable): a string holding the settable shortname.
  3879. + */
  3880. +const gchar *
  3881. +ide_vim_parser_settable_get_shortname (IdeVimParserSettable *self)
  3882. +{
  3883. + IdeVimParserSettablePrivate *priv = ide_vim_parser_settable_get_instance_private (self);
  3884. +
  3885. + g_return_val_if_fail (IDE_IS_VIM_PARSER_SETTABLE (self), NULL);
  3886. +
  3887. + return g_param_spec_get_nick (priv->value_spec);
  3888. +}
  3889. +
  3890. +/**
  3891. + * ide_vim_parser_settable_get_help:
  3892. + * @self: (in): a #IdeVimParserSettable instance.
  3893. + *
  3894. + * Get the settable help string.
  3895. + *
  3896. + * Returns: (transfer none): a string holding the settable help.
  3897. + */
  3898. +const gchar *
  3899. +ide_vim_parser_settable_get_help (IdeVimParserSettable *self)
  3900. +{
  3901. + IdeVimParserSettablePrivate *priv = ide_vim_parser_settable_get_instance_private (self);
  3902. +
  3903. + g_return_val_if_fail (IDE_IS_VIM_PARSER_SETTABLE (self), NULL);
  3904. +
  3905. + return g_param_spec_get_blurb (priv->value_spec);
  3906. +}
  3907. +
  3908. +/**
  3909. + * ide_vim_parser_settable_get_value:
  3910. + * @self: (in): a #IdeVimParserSettable instance.
  3911. + *
  3912. + * Get the settable value.
  3913. + *
  3914. + * Returns: (transfer none): a #GValue holding the settable current value.
  3915. + */
  3916. +const GValue *
  3917. +ide_vim_parser_settable_get_value (IdeVimParserSettable *self)
  3918. +{
  3919. + IdeVimParserSettableFunc func;
  3920. + IdeVimParserSettablePrivate *priv = ide_vim_parser_settable_get_instance_private (self);
  3921. +
  3922. + g_return_val_if_fail (IDE_IS_VIM_PARSER_SETTABLE (self), NULL);
  3923. +
  3924. + func = priv->func;
  3925. + func (self, &priv->value, IDE_VIM_PARSER_SETTABLE_OP_GET, NULL);
  3926. + return &priv->value;
  3927. +}
  3928. +
  3929. +/**
  3930. + * ide_vim_parser_settable_get_default_value:
  3931. + * @self: (in): a #IdeVimParserSettable instance.
  3932. + *
  3933. + * Get the settable default value.
  3934. + *
  3935. + * Returns: (transfer none): a #GValue holding the settable default value.
  3936. + */
  3937. +const GValue *
  3938. +ide_vim_parser_settable_get_default_value (IdeVimParserSettable *self)
  3939. +{
  3940. + IdeVimParserSettablePrivate *priv = ide_vim_parser_settable_get_instance_private (self);
  3941. +
  3942. + g_return_val_if_fail (IDE_IS_VIM_PARSER_SETTABLE (self), NULL);
  3943. +
  3944. + return g_param_spec_get_default_value (priv->value_spec);
  3945. +}
  3946. +
  3947. +static void
  3948. +ide_vim_parser_settable_get_property (GObject *object,
  3949. + guint prop_id,
  3950. + GValue *value,
  3951. + GParamSpec *pspec)
  3952. +{
  3953. + IdeVimParserSettable *self = IDE_VIM_PARSER_SETTABLE (object);
  3954. +
  3955. + switch (prop_id)
  3956. + {
  3957. + case PROP_NAME:
  3958. + g_value_set_string (value, ide_vim_parser_settable_get_name (self));
  3959. + break;
  3960. +
  3961. + case PROP_SHORTNAME:
  3962. + g_value_set_string (value, ide_vim_parser_settable_get_shortname (self));
  3963. + break;
  3964. +
  3965. + case PROP_HELP:
  3966. + g_value_set_string (value, ide_vim_parser_settable_get_help (self));
  3967. + break;
  3968. +
  3969. + case PROP_VALUE:
  3970. + g_value_set_pointer (value, (gpointer)ide_vim_parser_settable_get_value (self));
  3971. + break;
  3972. +
  3973. + case PROP_DEFAULT_VALUE:
  3974. + g_value_set_pointer (value, (gpointer)ide_vim_parser_settable_get_default_value (self));
  3975. + break;
  3976. +
  3977. + default:
  3978. + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
  3979. + }
  3980. +}
  3981. +
  3982. +static void
  3983. +ide_vim_parser_settable_set_property (GObject *object,
  3984. + guint prop_id,
  3985. + const GValue *value,
  3986. + GParamSpec *pspec)
  3987. +{
  3988. + IdeVimParserSettable *self = IDE_VIM_PARSER_SETTABLE (object);
  3989. + IdeVimParserSettablePrivate *priv = ide_vim_parser_settable_get_instance_private (self);
  3990. +
  3991. + switch (prop_id)
  3992. + {
  3993. + case PROP_FUNC:
  3994. + priv->func = g_value_get_pointer (value);
  3995. + break;
  3996. +
  3997. + case PROP_VALUES_FUNC:
  3998. + priv->values_func = g_value_get_pointer (value);
  3999. + break;
  4000. +
  4001. + case PROP_VALUE:
  4002. + /* To be sure value_spec and funcs are set, we finish init in ide_vim_parser_settable_constructed */
  4003. + if (priv->constructed)
  4004. + {
  4005. + ide_vim_parser_settable_set_value (self, g_value_get_pointer (value), NULL);
  4006. + }
  4007. + else
  4008. + {
  4009. + GValue *value_p = g_value_get_pointer (value);
  4010. +
  4011. + g_value_init (&priv->value, G_VALUE_TYPE (value_p));
  4012. + g_value_copy (value_p, &priv->value);
  4013. + }
  4014. + break;
  4015. +
  4016. + case PROP_VALUE_SPEC:
  4017. + priv->value_spec = g_param_spec_ref (g_value_get_param (value));
  4018. + break;
  4019. +
  4020. + default:
  4021. + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
  4022. + }
  4023. +}
  4024. +
  4025. +static void
  4026. +ide_vim_parser_settable_constructed (GObject *object)
  4027. +{
  4028. + IdeVimParserSettable *self = IDE_VIM_PARSER_SETTABLE (object);
  4029. + IdeVimParserSettablePrivate *priv = ide_vim_parser_settable_get_instance_private (self);
  4030. +
  4031. + if (!G_VALUE_HOLDS (&priv->value, G_PARAM_SPEC_VALUE_TYPE(priv->value_spec)))
  4032. + g_critical (_("Error during setting of IdeVimParserSettable value at creation time : wrong type"));
  4033. + else if (!ide_vim_parser_settable_set_value (self, &priv->value, NULL))
  4034. + g_warning (_("Error during setting of IdeVimParserSettable value at creation time : wrong value"));
  4035. +
  4036. + priv->constructed = TRUE;
  4037. +}
  4038. +
  4039. +static void
  4040. +ide_vim_parser_settable_finalize (GObject *object)
  4041. +{
  4042. + IdeVimParserSettable *self = IDE_VIM_PARSER_SETTABLE (object);
  4043. + IdeVimParserSettablePrivate *priv = ide_vim_parser_settable_get_instance_private (self);
  4044. +
  4045. + g_clear_pointer (&priv->value_spec, g_param_spec_unref);
  4046. +
  4047. + G_OBJECT_CLASS (ide_vim_parser_settable_parent_class)->finalize (object);
  4048. +}
  4049. +
  4050. +/**
  4051. + * ide_vim_parser_settable_new:
  4052. + * @g_value_spec:
  4053. + * @g_value:
  4054. + * @func: (scope call): the #IdeVimParserSettableFunc settable function pointer.
  4055. + * @values_func: (scope call)
  4056. + *
  4057. + * Create a new #IdeVimParserSettable settable.
  4058. + *
  4059. + * Returns: (transfer full): A new #IdeVimParserSettable settable.
  4060. + */
  4061. +IdeVimParserSettable *
  4062. +ide_vim_parser_settable_new (GParamSpec *g_value_spec,
  4063. + GValue g_value,
  4064. + IdeVimParserSettableFunc func,
  4065. + IdeVimParserSettableValuesFunc values_func)
  4066. +{
  4067. + g_assert (G_PARAM_SPEC_VALUE_TYPE (g_value_spec) == G_VALUE_TYPE (&g_value));
  4068. +
  4069. + return g_object_new (IDE_TYPE_VIM_PARSER_SETTABLE,
  4070. + "value-spec", g_value_spec,
  4071. + "value", &g_value,
  4072. + "func", func,
  4073. + "values-func", values_func,
  4074. + NULL);
  4075. +}
  4076. +
  4077. +/**
  4078. + * ide_vim_parser_settable_boolean_new:
  4079. + * @name: the settable name.
  4080. + * @shortname: the settable shortname.
  4081. + * @help:
  4082. + * @value:
  4083. + * @default_value:
  4084. + * @func: (scope call): the #IdeVimParserSettableFunc settable function pointer.
  4085. + * @values_func: (scope call)
  4086. + *
  4087. + * Create a new #IdeVimParserSettable boolean settable.
  4088. + *
  4089. + * Returns: (transfer full): A new #IdeVimParserSettable boolean settable.
  4090. + */
  4091. +IdeVimParserSettable *
  4092. +ide_vim_parser_settable_boolean_new (const gchar *name,
  4093. + const gchar *shortname,
  4094. + const gchar *help,
  4095. + gboolean value,
  4096. + gboolean default_value,
  4097. + IdeVimParserSettableFunc func,
  4098. + IdeVimParserSettableValuesFunc values_func)
  4099. +{
  4100. + GParamSpec *g_value_spec;
  4101. + GValue g_value = G_VALUE_INIT;
  4102. +
  4103. + g_value_init (&g_value, G_TYPE_BOOLEAN);
  4104. + g_value_set_boolean (&g_value, value);
  4105. +
  4106. + g_value_spec = g_param_spec_boolean (name, shortname, help, default_value,
  4107. + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
  4108. +
  4109. + return ide_vim_parser_settable_new (g_value_spec, g_value, func, values_func);
  4110. +}
  4111. +
  4112. +/**
  4113. + * ide_vim_parser_settable_integer_new:
  4114. + * @name: the settable name.
  4115. + * @shortname: the settable shortname.
  4116. + * @help:
  4117. + * @value:
  4118. + * @default_value:
  4119. + * @func: (scope call): the #IdeVimParserSettableFunc settable function pointer.
  4120. + * @values_func: (scope call)
  4121. + *
  4122. + * Create a new #IdeVimParserSettable integer settable.
  4123. + *
  4124. + * Returns: (transfer full): A new #IdeVimParserSettable integer settable.
  4125. + */
  4126. +IdeVimParserSettable *
  4127. +ide_vim_parser_settable_integer_new (const gchar *name,
  4128. + const gchar *shortname,
  4129. + const gchar *help,
  4130. + gint value,
  4131. + gint default_value,
  4132. + gint minimum,
  4133. + gint maximum,
  4134. + IdeVimParserSettableFunc func,
  4135. + IdeVimParserSettableValuesFunc values_func)
  4136. +{
  4137. + GParamSpec *g_value_spec;
  4138. + GValue g_value = G_VALUE_INIT;
  4139. +
  4140. + g_value_init (&g_value, G_TYPE_INT64);
  4141. + g_value_set_int64 (&g_value, value);
  4142. +
  4143. + g_value_spec = g_param_spec_int64 (name, shortname, help, minimum, maximum, default_value,
  4144. + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
  4145. +
  4146. + return ide_vim_parser_settable_new (g_value_spec, g_value, func, values_func);
  4147. +}
  4148. +
  4149. +/**
  4150. + * ide_vim_parser_settable_string_new:
  4151. + * @name: the settable name.
  4152. + * @shortname: the settable shortname.
  4153. + * @help:
  4154. + * @value:
  4155. + * @default_value:
  4156. + * @func: (scope call): the #IdeVimParserSettableFunc settable function pointer.
  4157. + * @values_func: (scope call)
  4158. + *
  4159. + * Create a new #IdeVimParserSettable string settable.
  4160. + *
  4161. + * Returns: (transfer full): A new #IdeVimParserSettable string settable.
  4162. + */
  4163. +IdeVimParserSettable *
  4164. +ide_vim_parser_settable_string_new (const gchar *name,
  4165. + const gchar *shortname,
  4166. + const gchar *help,
  4167. + const gchar *value,
  4168. + const gchar *default_value,
  4169. + IdeVimParserSettableFunc func,
  4170. + IdeVimParserSettableValuesFunc values_func)
  4171. +{
  4172. + GParamSpec *g_value_spec;
  4173. + GValue g_value = G_VALUE_INIT;
  4174. +
  4175. + g_value_init (&g_value, G_TYPE_STRING);
  4176. + g_value_set_string (&g_value, value);
  4177. +
  4178. + g_value_spec = g_param_spec_string (name, shortname, help, default_value,
  4179. + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
  4180. +
  4181. + return ide_vim_parser_settable_new (g_value_spec, g_value, func, values_func);
  4182. +}
  4183. +
  4184. +static void
  4185. +ide_vim_parser_settable_init (IdeVimParserSettable *self)
  4186. +{
  4187. + IdeVimParserSettablePrivate *priv = ide_vim_parser_settable_get_instance_private (self);
  4188. +}
  4189. +
  4190. +static void
  4191. +ide_vim_parser_settable_class_init (IdeVimParserSettableClass *klass)
  4192. +{
  4193. + GObjectClass *object_class = G_OBJECT_CLASS (klass);
  4194. +
  4195. + object_class->constructed = ide_vim_parser_settable_constructed;
  4196. + object_class->finalize = ide_vim_parser_settable_finalize;
  4197. + object_class->get_property = ide_vim_parser_settable_get_property;
  4198. + object_class->set_property = ide_vim_parser_settable_set_property;
  4199. +
  4200. + /**
  4201. + * IdeVimParserSettable:func:
  4202. + *
  4203. + * A IdeVimParserSettableFunc pointer to the function use to execute the settable.
  4204. + *
  4205. + */
  4206. + gParamSpecs[PROP_FUNC] =
  4207. + g_param_spec_pointer ("func",
  4208. + _("Settable function pointer"),
  4209. + _("A pointer to the settable function."),
  4210. + G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
  4211. +
  4212. + /**
  4213. + * IdeVimParserSettable:values-func:
  4214. + *
  4215. + * A IdeVimParserSettableValuesFunc pointer to the function use to collect the possibles values of the settable.
  4216. + *
  4217. + */
  4218. + gParamSpecs[PROP_VALUES_FUNC] =
  4219. + g_param_spec_pointer ("values-func",
  4220. + _("Settable collect values function pointer"),
  4221. + _("A pointer to the settable collect values function."),
  4222. + G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
  4223. +
  4224. + /**
  4225. + * IdeVimParserSettable:name:
  4226. + *
  4227. + * A string holding the name of the settable.
  4228. + */
  4229. + gParamSpecs[PROP_NAME] =
  4230. + g_param_spec_string ("name",
  4231. + _("Settable name"),
  4232. + _("The name of the settable."),
  4233. + NULL,
  4234. + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
  4235. +
  4236. + /**
  4237. + * IdeVimParserSettable:shortname:
  4238. + *
  4239. + * A string holding the shortname of the settable.
  4240. + */
  4241. + gParamSpecs[PROP_SHORTNAME] =
  4242. + g_param_spec_string ("shortname",
  4243. + _("Settable shortname"),
  4244. + _("The shortname of the settable."),
  4245. + NULL,
  4246. + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
  4247. +
  4248. + /**
  4249. + * IdeVimParserSettable:help:
  4250. + *
  4251. + * A string holding the help text of the settable.
  4252. + */
  4253. + gParamSpecs[PROP_HELP] =
  4254. + g_param_spec_string ("help",
  4255. + _("Settable help text"),
  4256. + _("The help text of the settable."),
  4257. + NULL,
  4258. + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
  4259. +
  4260. + /**
  4261. + * IdeVimParserSettable:value-spec:
  4262. + *
  4263. + * A #GParamSpec holding the spec of the settable value.
  4264. + */
  4265. + gParamSpecs[PROP_VALUE_SPEC] =
  4266. + g_param_spec_param ("value-spec",
  4267. + _("Settable GParamSpec"),
  4268. + _("The settable GParamSpec."),
  4269. + G_TYPE_PARAM,
  4270. + G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
  4271. +
  4272. + /**
  4273. + * IdeVimParserSettable:value:
  4274. + *
  4275. + * A #GValue holding the value of the settable.
  4276. + */
  4277. + gParamSpecs[PROP_VALUE] =
  4278. + g_param_spec_pointer ("value",
  4279. + _("Settable value"),
  4280. + _("The value of the settable."),
  4281. + G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS);
  4282. +
  4283. + /**
  4284. + * IdeVimParserSettable:default-value:
  4285. + *
  4286. + * A #GValue holding the default value of the settable.
  4287. + */
  4288. + gParamSpecs[PROP_DEFAULT_VALUE] =
  4289. + g_param_spec_pointer ("default-value",
  4290. + _("Settable default value"),
  4291. + _("The default value of the settable."),
  4292. + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
  4293. +
  4294. + g_object_class_install_properties (object_class, LAST_PROP, gParamSpecs);
  4295. +}
  4296. diff --git a/libide/vim/ide-vim-parser-settable.h b/libide/vim/ide-vim-parser-settable.h
  4297. new file mode 100644
  4298. index 0000000..483c6dd
  4299. --- /dev/null
  4300. +++ b/libide/vim/ide-vim-parser-settable.h
  4301. @@ -0,0 +1,93 @@
  4302. +/* ide-vim-parser-settable.h
  4303. + *
  4304. + * Copyright (C) 2015 Sébastien Lafargue <slafargue@gnome.org>
  4305. + *
  4306. + * This program is free software: you can redistribute it and/or modify
  4307. + * it under the terms of the GNU General Public License as published by
  4308. + * the Free Software Foundation, either version 3 of the License, or
  4309. + * (at your option) any later version.
  4310. + *
  4311. + * This program is distributed in the hope that it will be useful,
  4312. + * but WITHOUT ANY WARRANTY; without even the implied warranty of
  4313. + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  4314. + * GNU General Public License for more details.
  4315. + *
  4316. + * You should have received a copy of the GNU General Public License
  4317. + * along with this program. If not, see <http://www.gnu.org/licenses/>.
  4318. + */
  4319. +
  4320. +#ifndef IDE_VIM_PARSER_SETTABLE_H
  4321. +#define IDE_VIM_PARSER_SETTABLE_H
  4322. +
  4323. +#include <glib.h>
  4324. +
  4325. +#include "ide-vim-parser-error.h"
  4326. +
  4327. +G_BEGIN_DECLS
  4328. +
  4329. +#define IDE_TYPE_VIM_PARSER_SETTABLE (ide_vim_parser_settable_get_type())
  4330. +
  4331. +G_DECLARE_FINAL_TYPE (IdeVimParserSettable, ide_vim_parser_settable, IDE, VIM_PARSER_SETTABLE, GObject)
  4332. +
  4333. +struct _IdeVimParserSettable
  4334. +{
  4335. + GObject parent;
  4336. +};
  4337. +
  4338. +typedef enum
  4339. +{
  4340. + IDE_VIM_PARSER_SETTABLE_OP_SET,
  4341. + IDE_VIM_PARSER_SETTABLE_OP_GET,
  4342. + IDE_VIM_PARSER_SETTABLE_OP_RESET
  4343. +} IdeVimParserSettableOp;
  4344. +
  4345. +typedef gboolean (*IdeVimParserSettableFunc) (IdeVimParserSettable *settable,
  4346. + GValue *value,
  4347. + IdeVimParserSettableOp op,
  4348. + IdeVimParserError **error);
  4349. +
  4350. +typedef GArray * (*IdeVimParserSettableValuesFunc) (IdeVimParserSettable *settable,
  4351. + IdeVimParserError **error);
  4352. +
  4353. +const gchar *ide_vim_parser_settable_get_help (IdeVimParserSettable *self);
  4354. +const gchar *ide_vim_parser_settable_get_name (IdeVimParserSettable *self);
  4355. +const gchar *ide_vim_parser_settable_get_shortname (IdeVimParserSettable *self);
  4356. +const GValue *ide_vim_parser_settable_get_value (IdeVimParserSettable *self);
  4357. +const GValue *ide_vim_parser_settable_get_default_value (IdeVimParserSettable *self);
  4358. +
  4359. +gboolean ide_vim_parser_settable_set_value (IdeVimParserSettable *self,
  4360. + GValue *value,
  4361. + IdeVimParserError **error);
  4362. +void ide_vim_parser_settable_reset_value (IdeVimParserSettable *self);
  4363. +
  4364. +IdeVimParserSettable *ide_vim_parser_settable_new (GParamSpec *g_value_spec,
  4365. + GValue g_value,
  4366. + IdeVimParserSettableFunc func,
  4367. + IdeVimParserSettableValuesFunc values_func);
  4368. +IdeVimParserSettable *ide_vim_parser_settable_boolean_new (const gchar *name,
  4369. + const gchar *shortname,
  4370. + const gchar *help,
  4371. + gboolean value,
  4372. + gboolean default_value,
  4373. + IdeVimParserSettableFunc func,
  4374. + IdeVimParserSettableValuesFunc values_func);
  4375. +IdeVimParserSettable *ide_vim_parser_settable_integer_new (const gchar *name,
  4376. + const gchar *shortname,
  4377. + const gchar *help,
  4378. + gint value,
  4379. + gint default_value,
  4380. + gint minimum,
  4381. + gint maximum,
  4382. + IdeVimParserSettableFunc func,
  4383. + IdeVimParserSettableValuesFunc values_func);
  4384. +IdeVimParserSettable *ide_vim_parser_settable_string_new (const gchar *name,
  4385. + const gchar *shortname,
  4386. + const gchar *help,
  4387. + const gchar *value,
  4388. + const gchar *default_value,
  4389. + IdeVimParserSettableFunc func,
  4390. + IdeVimParserSettableValuesFunc values_func);
  4391. +
  4392. +G_END_DECLS
  4393. +
  4394. +#endif /* IDE_VIM_PARSER_SETTABLE_H */
  4395. diff --git a/libide/vim/ide-vim-parser-token.c b/libide/vim/ide-vim-parser-token.c
  4396. new file mode 100644
  4397. index 0000000..94ad3dd
  4398. --- /dev/null
  4399. +++ b/libide/vim/ide-vim-parser-token.c
  4400. @@ -0,0 +1,124 @@
  4401. +/* ide-vim-parser-token.c
  4402. + *
  4403. + * Copyright (C) 2015 Sébastien Lafargue <slafargue@gnome.org>
  4404. + *
  4405. + * This program is free software: you can redistribute it and/or modify
  4406. + * it under the terms of the GNU General Public License as published by
  4407. + * the Free Software Foundation, either version 3 of the License, or
  4408. + * (at your option) any later version.
  4409. + *
  4410. + * This program is distributed in the hope that it will be useful,
  4411. + * but WITHOUT ANY WARRANTY; without even the implied warranty of
  4412. + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  4413. + * GNU General Public License for more details.
  4414. + *
  4415. + * You should have received a copy of the GNU General Public License
  4416. + * along with this program. If not, see <http://www.gnu.org/licenses/>.
  4417. + */
  4418. +
  4419. +#include <string.h>
  4420. +#include <errno.h>
  4421. +#include <glib/gi18n.h>
  4422. +#include <glib/gprintf.h>
  4423. +
  4424. +#include "ide-debug.h"
  4425. +#include "ide-macros.h"
  4426. +
  4427. +#include "ide-vim-parser-token.h"
  4428. +
  4429. +G_DEFINE_BOXED_TYPE (IdeVimParserToken, ide_vim_parser_token, ide_vim_parser_token_ref, ide_vim_parser_token_unref)
  4430. +
  4431. +const gchar *
  4432. +ide_vim_parser_token_get_content (IdeVimParserToken *token)
  4433. +{
  4434. + return token->content;
  4435. +}
  4436. +
  4437. +static gchar *token_kind_name [] =
  4438. +{
  4439. + "UNKNOW",
  4440. + "COMMAND_NAME",
  4441. + "END OF STRING",
  4442. + "MARK",
  4443. + "NUMBER",
  4444. + "PATTERN",
  4445. + "RANGE",
  4446. + "RANGE SEPARATOR",
  4447. + "RANGE SPECIFIER",
  4448. + "REGISTER",
  4449. + "STRING",
  4450. + "PATH",
  4451. + "SET ARGUMENT"
  4452. +};
  4453. +
  4454. +const gchar *
  4455. +ide_vim_parser_token_get_kind_name (IdeVimParserToken *token)
  4456. +{
  4457. + return token_kind_name [token->kind];
  4458. +}
  4459. +
  4460. +const gchar *
  4461. +ide_vim_parser_token_kind_get_name (IdeVimParserTokenKind kind)
  4462. +{
  4463. + return token_kind_name [kind];
  4464. +}
  4465. +
  4466. +static void
  4467. +ide_vim_parser_token_finalize (IdeVimParserToken *self)
  4468. +{
  4469. + g_clear_pointer (&self->content, g_free);
  4470. + g_free (self);
  4471. +}
  4472. +
  4473. +/**
  4474. + * ide_vim_parser_token_ref:
  4475. + * @self: (in): An #IdeVimParserToken instance.
  4476. + *
  4477. + * Increment #IdeVimParserToken ref count.
  4478. + *
  4479. + * Returns: (transfer none): a #IdeVimParserToken ref.
  4480. + */
  4481. +IdeVimParserToken *
  4482. +ide_vim_parser_token_ref (IdeVimParserToken *self)
  4483. +{
  4484. + g_return_val_if_fail (self, NULL);
  4485. + g_return_val_if_fail (self->ref_count > 0, NULL);
  4486. +
  4487. + g_atomic_int_inc (&self->ref_count);
  4488. +
  4489. + return self;
  4490. +}
  4491. +
  4492. +/**
  4493. + * ide_vim_parser_token_unref:
  4494. + * @self: (in): #IdeVimParserToken instance.
  4495. + *
  4496. + * Decrement #IdeVimParserToken ref count.
  4497. + */
  4498. +void
  4499. +ide_vim_parser_token_unref (IdeVimParserToken *self)
  4500. +{
  4501. + g_return_if_fail (self);
  4502. + g_return_if_fail (self->ref_count > 0);
  4503. +
  4504. + if (g_atomic_int_dec_and_test (&self->ref_count))
  4505. + ide_vim_parser_token_finalize (self);
  4506. +}
  4507. +
  4508. +/**
  4509. + * ide_vim_parser_token_new:
  4510. + *
  4511. + * Return a new #IdeVimParserToken instance.
  4512. + *
  4513. + * Returns: (transfer full) (skip): A new #IdeVimParserToken.
  4514. + */
  4515. +IdeVimParserToken *
  4516. +ide_vim_parser_token_new (void)
  4517. +{
  4518. + IdeVimParserToken *self;
  4519. +
  4520. + self = g_new0 (IdeVimParserToken, 1);
  4521. + self->ref_count = 1;
  4522. +
  4523. + return self;
  4524. +}
  4525. diff --git a/libide/vim/ide-vim-parser-token.h b/libide/vim/ide-vim-parser-token.h
  4526. new file mode 100644
  4527. index 0000000..1c78ac3
  4528. --- /dev/null
  4529. +++ b/libide/vim/ide-vim-parser-token.h
  4530. @@ -0,0 +1,92 @@
  4531. +/* ide-vim-parser-token.h
  4532. + *
  4533. + * Copyright (C) 2015 Sébastien Lafargue <slafargue@gnome.org>
  4534. + *
  4535. + * This program is free software: you can redistribute it and/or modify
  4536. + * it under the terms of the GNU General Public License as published by
  4537. + * the Free Software Foundation, either version 3 of the License, or
  4538. + * (at your option) any later version.
  4539. + *
  4540. + * This program is distributed in the hope that it will be useful,
  4541. + * but WITHOUT ANY WARRANTY; without even the implied warranty of
  4542. + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  4543. + * GNU General Public License for more details.
  4544. + *
  4545. + * You should have received a copy of the GNU General Public License
  4546. + * along with this program. If not, see <http://www.gnu.org/licenses/>.
  4547. + */
  4548. +
  4549. +#ifndef IDE_VIM_PARSER_TOKEN_H
  4550. +#define IDE_VIM_PARSER_TOKEN_H
  4551. +
  4552. +#include <glib-object.h>
  4553. +
  4554. +G_BEGIN_DECLS
  4555. +
  4556. +#define IDE_TYPE_VIM_PARSER_TOKEN (ide_vim_parser_token_get_type())
  4557. +
  4558. +typedef struct _IdeVimParserCommand IdeVimParserCommand;
  4559. +
  4560. +typedef enum _IdeVimParserTokenKind
  4561. +{
  4562. + IDE_VIM_PARSER_TOKEN_UNKNOW,
  4563. + IDE_VIM_PARSER_TOKEN_COMMAND_NAME,
  4564. + IDE_VIM_PARSER_TOKEN_END_OF_STRING,
  4565. + IDE_VIM_PARSER_TOKEN_MARK,
  4566. + IDE_VIM_PARSER_TOKEN_NUMBER,
  4567. + IDE_VIM_PARSER_TOKEN_PATTERN,
  4568. + IDE_VIM_PARSER_TOKEN_RANGE,
  4569. + IDE_VIM_PARSER_TOKEN_RANGE_SEPARATOR,
  4570. + IDE_VIM_PARSER_TOKEN_RANGE_SPECIFIER,
  4571. + IDE_VIM_PARSER_TOKEN_REGISTER,
  4572. + IDE_VIM_PARSER_TOKEN_STRING,
  4573. + IDE_VIM_PARSER_TOKEN_PATH,
  4574. + IDE_VIM_PARSER_TOKEN_SET_ARG
  4575. +} IdeVimParserTokenKind;
  4576. +
  4577. +typedef enum _IdeVimParserTokenFlag
  4578. +{
  4579. + /* Don't use value 0, it's used for error checking */
  4580. + IDE_VIM_PARSER_TOKEN_FLAG_NONE = 1,
  4581. + /* FIXME: optional, one n */
  4582. + IDE_VIM_PARSER_TOKEN_FLAG_OPTIONNAL,
  4583. +} IdeVimParserTokenFlag;
  4584. +
  4585. +/* TODO: drop error and use return status ? */
  4586. +typedef enum _IdeVimParserTokenError
  4587. +{
  4588. + IDE_VIM_PARSER_TOKEN_ERROR_NO_ERROR,
  4589. + IDE_VIM_PARSER_TOKEN_ERROR_INCOMPLETE,
  4590. + IDE_VIM_PARSER_TOKEN_ERROR_WRONG_KIND
  4591. +} IdeVimParserTokenError;
  4592. +
  4593. +struct _IdeVimParserToken
  4594. +{
  4595. + volatile gint ref_count;
  4596. +
  4597. + IdeVimParserTokenKind kind;
  4598. + IdeVimParserTokenFlag flag;
  4599. + //IdeVimParserTokenError error;
  4600. + const gchar *content;
  4601. + gint number;
  4602. + gint range_start;
  4603. + gint range_end;
  4604. + IdeVimParserCommand *command_entry;
  4605. + gchar pattern_type;
  4606. + gboolean command_has_bang : 1;
  4607. + gboolean range_is_native : 1;
  4608. + gboolean is_set : 1;
  4609. +};
  4610. +
  4611. +typedef struct _IdeVimParserToken IdeVimParserToken;
  4612. +
  4613. +const gchar *ide_vim_parser_token_get_content (IdeVimParserToken *token);
  4614. +const gchar *ide_vim_parser_token_get_kind_name (IdeVimParserToken *token);
  4615. +const gchar *ide_vim_parser_token_kind_get_name (IdeVimParserTokenKind kind);
  4616. +IdeVimParserToken *ide_vim_parser_token_new (void);
  4617. +IdeVimParserToken *ide_vim_parser_token_ref (IdeVimParserToken *self);
  4618. +void ide_vim_parser_token_unref (IdeVimParserToken *self);
  4619. +
  4620. +G_END_DECLS
  4621. +
  4622. +#endif /* IDE_VIM_PARSER_TOKEN_H */
  4623. diff --git a/libide/vim/ide-vim-parser-types.h b/libide/vim/ide-vim-parser-types.h
  4624. new file mode 100644
  4625. index 0000000..db5f14c
  4626. --- /dev/null
  4627. +++ b/libide/vim/ide-vim-parser-types.h
  4628. @@ -0,0 +1,28 @@
  4629. +/* ide-vim-parser-types.h
  4630. + *
  4631. + * Copyright (C) 2015 Sébastien Lafargue <slafargue@gnome.org>
  4632. + *
  4633. + * This program is free software: you can redistribute it and/or modify
  4634. + * it under the terms of the GNU General Public License as published by
  4635. + * the Free Software Foundation, either version 3 of the License, or
  4636. + * (at your option) any later version.
  4637. + *
  4638. + * This program is distributed in the hope that it will be useful,
  4639. + * but WITHOUT ANY WARRANTY; without even the implied warranty of
  4640. + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  4641. + * GNU General Public License for more details.
  4642. + *
  4643. + * You should have received a copy of the GNU General Public License
  4644. + * along with this program. If not, see <http://www.gnu.org/licenses/>.
  4645. + */
  4646. +
  4647. +#ifndef IDE_VIM_PARSER_TYPES_H
  4648. +#define IDE_VIM_PARSER_TYPES_H
  4649. +
  4650. +#include <glib.h>
  4651. +
  4652. +G_BEGIN_DECLS
  4653. +
  4654. +G_END_DECLS
  4655. +
  4656. +#endif /* IDE_VIM_PARSER_TYPES_H */
  4657. diff --git a/libide/vim/ide-vim-parser.c b/libide/vim/ide-vim-parser.c
  4658. new file mode 100644
  4659. index 0000000..cc192f5
  4660. --- /dev/null
  4661. +++ b/libide/vim/ide-vim-parser.c
  4662. @@ -0,0 +1,1926 @@
  4663. +/* ide-vim-parser.c
  4664. + *
  4665. + * Copyright (C) 2015 Sébastien Lafargue <slafargue@gnome.org>
  4666. + *
  4667. + * This program is free software: you can redistribute it and/or modify
  4668. + * it under the terms of the GNU General Public License as published by
  4669. + * the Free Software Foundation, either version 3 of the License, or
  4670. + * (at your option) any later version.
  4671. + *
  4672. + * This program is distributed in the hope that it will be useful,
  4673. + * but WITHOUT ANY WARRANTY; without even the implied warranty of
  4674. + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  4675. + * GNU General Public License for more details.
  4676. + *
  4677. + * You should have received a copy of the GNU General Public License
  4678. + * along with this program. If not, see <http://www.gnu.org/licenses/>.
  4679. + */
  4680. +
  4681. +#define G_LOG_DOMAIN "ide-vim-parser"
  4682. +
  4683. +#include <string.h>
  4684. +#include <stdio.h>
  4685. +#include <errno.h>
  4686. +
  4687. +#include <glib/gi18n.h>
  4688. +#include <glib/gprintf.h>
  4689. +#include <gtksourceview/gtksource.h>
  4690. +
  4691. +#include "ide-debug.h"
  4692. +#include "ide-macros.h"
  4693. +
  4694. +#include "ide-vim-parser.h"
  4695. +#include "ide-vim-parser-private.h"
  4696. +#include "ide-vim-parser-debug.h"
  4697. +#include "ide-vim-parser-objects-pool.h"
  4698. +#include "ide-vim-parser-error.h"
  4699. +
  4700. +#pragma GCC diagnostic push
  4701. +#pragma GCC diagnostic ignored "-Wswitch-enum"
  4702. +
  4703. +typedef struct
  4704. +{
  4705. + IdeVimParserObjectsPool *objects_pool;
  4706. + IdeVimStateMachine *machine;
  4707. + GQueue *stack;
  4708. + IdeVimParserInfo *info;
  4709. +
  4710. +} IdeVimParserPrivate;
  4711. +
  4712. +G_DEFINE_TYPE_WITH_PRIVATE (IdeVimParser, ide_vim_parser, G_TYPE_OBJECT)
  4713. +
  4714. +enum {
  4715. + PROP_0,
  4716. + LAST_PROP
  4717. +};
  4718. +
  4719. +static GParamSpec *gParamSpecs [LAST_PROP];
  4720. +
  4721. +#define ide_vim_str_equal0(s1,s2) \
  4722. + (((s1) == (s2)) || ((s1) && (s2) && g_str_equal(s1,s2)))
  4723. +
  4724. +#define BUFFER_TO_VIM_LINE_COORDS (1)
  4725. +#define VALUE_NOT_SET (-1)
  4726. +
  4727. +static gint
  4728. +_ide_vim_parser_add_command_entry (IdeVimParser *self,
  4729. + const gchar *name,
  4730. + const gchar *shortname,
  4731. + IdeVimParserCommandFunc func,
  4732. + IdeVimParserCommand **command_entry)
  4733. +{
  4734. + IdeVimParserPrivate *priv = ide_vim_parser_get_instance_private (self);
  4735. + IdeVimParserCommand *entry;
  4736. +
  4737. + entry = g_object_new (IDE_TYPE_VIM_PARSER_COMMAND,
  4738. + "name", name,
  4739. + "shortname", shortname,
  4740. + "func", func,
  4741. + NULL);
  4742. +
  4743. + ide_vim_parser_objects_pool_add_command (priv->objects_pool, entry, TRUE);
  4744. + *command_entry = entry;
  4745. +
  4746. + return ide_vim_parser_command_get_id (entry);
  4747. +}
  4748. +
  4749. +/* TODO: Add a _v() function for introspection */
  4750. +
  4751. +/**
  4752. + * ide_vim_parser_add_command:
  4753. + * @self: (in): a #IdeVimParser instance.
  4754. + * @name: (in): the name of the command to add.
  4755. + * @shortname: (in) (nullable): the shortname of the command, must be null
  4756. + * or a prefix of the command.
  4757. + * @func: (in) (skip): a #IdeVimParserCommand command function.
  4758. + * @...: (in) (nullable): a succession of #IdeVimParserTokenKind and #IdeVimParserTokenFlag,
  4759. + * ended by 0 to describe the command arguments.
  4760. + *
  4761. + * Add a command to the parser :
  4762. + *
  4763. + * the variadic list is a sequence of
  4764. + * IdeVimParserTokenKind, IdeVimParserTokenFlags
  4765. + * pairs, ended with 0.
  4766. + *
  4767. + * A command with a name already registred replace the old command
  4768. + *
  4769. + * Returns: id of the command
  4770. + */
  4771. +gint
  4772. +ide_vim_parser_add_command (IdeVimParser *self,
  4773. + const gchar *name,
  4774. + const gchar *shortname,
  4775. + IdeVimParserCommandFunc func,
  4776. + ...)
  4777. +{
  4778. + IdeVimParserPrivate *priv = ide_vim_parser_get_instance_private (self);
  4779. + va_list args;
  4780. + IdeVimParserCommand *command_entry;
  4781. + gint command_id;
  4782. + IdeVimParserTokenKind kind;
  4783. + IdeVimParserTokenFlag flag;
  4784. + gint nb_args = 0;
  4785. + gint name_arg_count = 0;
  4786. +
  4787. + g_assert (IDE_IS_VIM_PARSER (self));
  4788. + g_return_val_if_fail (!ide_str_empty0 (name), 0);
  4789. + g_return_val_if_fail (func != NULL, 0);
  4790. +
  4791. + command_id = _ide_vim_parser_add_command_entry (self, name, shortname, func, &command_entry);
  4792. +
  4793. + va_start(args, func);
  4794. +
  4795. + while ((kind = va_arg(args, gint)) != 0)
  4796. + {
  4797. + if ((flag = va_arg(args, gint)) != 0)
  4798. + {
  4799. + ++nb_args;
  4800. + if (kind == IDE_VIM_PARSER_TOKEN_COMMAND_NAME)
  4801. + {
  4802. + ++name_arg_count;
  4803. + if (name_arg_count > 1)
  4804. + {
  4805. + g_critical ("Wrong entry format: one and only one argument must be of type command_name");
  4806. + goto error;
  4807. + }
  4808. + }
  4809. +
  4810. + ide_vim_parser_command_add_arg (command_entry, kind, flag);
  4811. + continue;
  4812. + }
  4813. +
  4814. + g_critical ("Wrong entry format: a flag is missing");
  4815. + goto error;
  4816. + }
  4817. +
  4818. + va_end(args);
  4819. +
  4820. + if (nb_args == 0)
  4821. + {
  4822. + g_critical ("Wrong entry format: no arguments");
  4823. + goto error;
  4824. + }
  4825. +
  4826. + return command_id;
  4827. +
  4828. +error:
  4829. + ide_vim_parser_objects_pool_remove_command (priv->objects_pool, name);
  4830. + return 0;
  4831. +}
  4832. +
  4833. +static gchar *
  4834. +get_last_word (const gchar *str)
  4835. +{
  4836. + const gchar *str_end;
  4837. + const gchar *cursor;
  4838. + const gchar *prev;
  4839. + gint len;
  4840. +
  4841. + if (ide_str_empty0 (str))
  4842. + return NULL;
  4843. +
  4844. + prev = cursor = str_end = str + strlen (str);
  4845. + while ((cursor = g_utf8_find_prev_char (str, cursor)))
  4846. + {
  4847. + if (g_unichar_isspace (g_utf8_get_char (cursor)))
  4848. + break;
  4849. +
  4850. + prev = cursor;
  4851. + }
  4852. +
  4853. + if (cursor == NULL)
  4854. + return g_strdup (str);
  4855. +
  4856. + len = str_end - prev;
  4857. + if (len == 0)
  4858. + return NULL;
  4859. +
  4860. + return g_strndup (prev, len);
  4861. +}
  4862. +
  4863. +/**
  4864. + * ide_vim_parser_complete:
  4865. + * @self: (in): a #IdeVimParser instance.
  4866. + * @match_name: (in) (nullable): a prefix for the items to find.
  4867. + *
  4868. + * Return all the items matching match_name:
  4869. + * This can be commands, set command variables, colorscheme names
  4870. + * or paths used with file commands.
  4871. + *
  4872. + * Returns: (transfer container) (nullable) (element-type IdeVimCompleteItem)
  4873. + * A GPtrArray of #IdeVimCompleteItem items matching match_name.
  4874. + */
  4875. +GPtrArray *
  4876. +ide_vim_parser_complete (IdeVimParser *self,
  4877. + GtkTextBuffer *buffer,
  4878. + const gchar *match_name)
  4879. +{
  4880. + IdeVimParserPrivate *priv;
  4881. + IdeVimParserCommand *command = NULL;
  4882. + IdeVimParserError *tmp_error = NULL;
  4883. + const IdeVimParserInfo *info;
  4884. + GPtrArray *ar;
  4885. + const gchar *match_word = NULL;
  4886. +
  4887. + g_assert (IDE_IS_VIM_PARSER (self));
  4888. +
  4889. + priv = ide_vim_parser_get_instance_private (self);
  4890. + ar = g_ptr_array_new_full (32, g_object_unref);
  4891. +
  4892. + /* TODO: parse to find command
  4893. + if (ide_str_equal0 (name, "set"))
  4894. + {
  4895. + g_printf ("not implemented yet\n");
  4896. + return NULL;
  4897. + }
  4898. +
  4899. + if (ide_str_equal0 (name, "colorscheme"))
  4900. + {
  4901. + g_printf ("not implemented yet\n");
  4902. + return NULL;
  4903. + }
  4904. +
  4905. + if (ide_str_equal0 (name, "edit"))
  4906. + {
  4907. + g_printf ("not implemented yet\n");
  4908. + return NULL;
  4909. + }
  4910. + */
  4911. +
  4912. + //match_word = get_last_word (match_name);
  4913. + if (!ide_str_empty0 (match_name))
  4914. + {
  4915. + command = ide_vim_parser_parse (self, buffer, match_name, &tmp_error);
  4916. + info = ide_vim_parser_get_info (self);
  4917. + if (command != NULL)
  4918. + {
  4919. + g_printf ("No parsing Error\n");
  4920. + ide_vim_parser_debug_show_parsed (command);
  4921. + //ide_vim_parser_error_propagate (error, tmp_error);
  4922. + g_ptr_array_free (ar, TRUE);
  4923. + ar = NULL;
  4924. + }
  4925. + else if (info->last_token_kind == IDE_VIM_PARSER_TOKEN_STRING)
  4926. + {
  4927. + match_word = info->last_token_content;
  4928. + }
  4929. + }
  4930. +
  4931. + g_printf ("match word:%s\n", match_word);
  4932. + if (!ide_vim_parser_objects_pool_complete_command (priv->objects_pool, ar, match_word))
  4933. + {
  4934. + g_ptr_array_free (ar, TRUE);
  4935. + ar = NULL;
  4936. + }
  4937. +
  4938. + return ar;
  4939. +}
  4940. +
  4941. +static gboolean
  4942. +token_range_resolve_mark (IdeVimParserContext *context,
  4943. + IdeVimParserToken *token,
  4944. + gint *line)
  4945. +{
  4946. + g_printf ("not implemented yet\n");
  4947. + return FALSE;
  4948. +}
  4949. +
  4950. +static gboolean
  4951. +token_range_resolve_pattern (IdeVimParserContext *context,
  4952. + IdeVimParserToken *token,
  4953. + gint start_line,
  4954. + gint *end_of_pattern_line)
  4955. +{
  4956. + GtkSourceSearchContext *search_context;
  4957. + GtkSourceSearchSettings *search_settings;
  4958. + g_autofree gchar *content;
  4959. + GtkTextIter begin;
  4960. + GtkTextIter match_begin;
  4961. + GtkTextIter match_end;
  4962. + gint len;
  4963. + gboolean ret;
  4964. +
  4965. + len = strlen (token->content);
  4966. + g_assert (len >= 2);
  4967. +
  4968. + if (len == 2)
  4969. + {
  4970. + /*TODO: get previous pattern */
  4971. + }
  4972. +
  4973. + content = g_strndup (token->content + 1, len - 1);
  4974. +
  4975. + search_settings = gtk_source_search_settings_new ();
  4976. + gtk_source_search_settings_set_search_text (search_settings, content);
  4977. + gtk_source_search_settings_set_case_sensitive (search_settings, TRUE);
  4978. + gtk_source_search_settings_set_regex_enabled (search_settings, TRUE);
  4979. +
  4980. + /* TODO: handle wrap around ? */
  4981. + gtk_text_buffer_get_iter_at_line (context->buffer, &begin, start_line);
  4982. +
  4983. + search_context = gtk_source_search_context_new (GTK_SOURCE_BUFFER (context->buffer), search_settings);
  4984. + gtk_source_search_context_set_highlight (search_context, FALSE);
  4985. +
  4986. + if (token->pattern_type == '/')
  4987. + ret = gtk_source_search_context_forward (search_context, &begin, &match_begin, &match_end);
  4988. + else
  4989. + ret = gtk_source_search_context_backward (search_context, &begin, &match_begin, &match_end);
  4990. +
  4991. + g_clear_object (&search_settings);
  4992. + g_clear_object (&search_context);
  4993. +
  4994. + if (ret)
  4995. + *end_of_pattern_line = gtk_text_iter_get_line (&match_end) + BUFFER_TO_VIM_LINE_COORDS;
  4996. +
  4997. + return ret;
  4998. +}
  4999. +
  5000. +static gboolean
  5001. +token_range_resolve_specifier (IdeVimParserContext *context,
  5002. + IdeVimParserToken *token,
  5003. + gint *line_start,
  5004. + gint *line_end)
  5005. +{
  5006. + const gchar *content = token->content;
  5007. +
  5008. + g_assert (line_start !=NULL);
  5009. + g_assert (line_end !=NULL);
  5010. + g_assert (!ide_str_empty0 (content));
  5011. +
  5012. + if (ide_str_equal0 (content, "."))
  5013. + {
  5014. + *line_start = context->insert_line;
  5015. + }
  5016. + else if (ide_str_equal0 (content, "$"))
  5017. + {
  5018. + *line_start = context->end_line;
  5019. + }
  5020. + else if (ide_str_equal0 (content, "%"))
  5021. + {
  5022. + *line_start = 1;
  5023. + *line_end = context->end_line;
  5024. + }
  5025. + else if (ide_str_equal0 (content, "\\/"))
  5026. + {
  5027. + /* TODO: get from previous search forward */
  5028. + g_printf ("not implemented yet\n");
  5029. + }
  5030. + else if (ide_str_equal0 (content, "\\?"))
  5031. + {
  5032. + /* TODO: get from previous search backward */
  5033. + g_printf ("not implemented yet\n");
  5034. + }
  5035. + else if (ide_str_equal0 (content, "\\&"))
  5036. + {
  5037. + /* TODO: get from previous substitute forward */
  5038. + g_printf ("not implemented yet\n");
  5039. + }
  5040. +
  5041. + return TRUE;
  5042. +}
  5043. +
  5044. +/* Line values are resolved in term of Vim coordinates,
  5045. + * so that lines begin at 1 but as the work always start
  5046. + * on the next line, 0 can be use to take line 1 into account.
  5047. + * -1 is used for not set values. */
  5048. +static gboolean
  5049. +token_range_resolve (IdeVimParserContext *context,
  5050. + IdeVimParserToken *token)
  5051. +{
  5052. + gint line_start = VALUE_NOT_SET;
  5053. + gint line_end = VALUE_NOT_SET;
  5054. + gint begin_line = 0;
  5055. + gboolean ret = FALSE;
  5056. +
  5057. + g_assert (context !=NULL);
  5058. + g_assert (token !=NULL);
  5059. +
  5060. + switch (token->kind)
  5061. + {
  5062. + case IDE_VIM_PARSER_TOKEN_MARK:
  5063. + ret = token_range_resolve_mark (context, token, &line_start);
  5064. + break;
  5065. +
  5066. + case IDE_VIM_PARSER_TOKEN_NUMBER:
  5067. + line_start = token->number;
  5068. + ret = TRUE;
  5069. + break;
  5070. +
  5071. + case IDE_VIM_PARSER_TOKEN_PATTERN:
  5072. + if (context->previous_token_is_separator)
  5073. + begin_line = context->insert_line;
  5074. + else
  5075. + begin_line = context->range_end_line;
  5076. +
  5077. + ret = token_range_resolve_pattern (context, token, begin_line, &line_start);
  5078. + break;
  5079. +
  5080. + case IDE_VIM_PARSER_TOKEN_RANGE_SPECIFIER:
  5081. + ret = token_range_resolve_specifier (context, token, &line_start, &line_end);
  5082. + break;
  5083. +
  5084. + case IDE_VIM_PARSER_TOKEN_RANGE_SEPARATOR:
  5085. + if (context->previous_token_is_separator)
  5086. + return FALSE;
  5087. +
  5088. + context->previous_token_is_separator = TRUE;
  5089. + if (*token->content == ';' && context->range_end_line != VALUE_NOT_SET)
  5090. + context->insert_line += context->range_end_line;
  5091. +
  5092. + context->range_start_line = context->range_end_line;
  5093. + context->range_end_line = VALUE_NOT_SET;
  5094. + return TRUE;
  5095. + break;
  5096. +
  5097. + case IDE_VIM_PARSER_TOKEN_UNKNOW:
  5098. + case IDE_VIM_PARSER_TOKEN_COMMAND_NAME:
  5099. + case IDE_VIM_PARSER_TOKEN_END_OF_STRING:
  5100. + case IDE_VIM_PARSER_TOKEN_RANGE:
  5101. + case IDE_VIM_PARSER_TOKEN_REGISTER:
  5102. + case IDE_VIM_PARSER_TOKEN_STRING:
  5103. + context->range_start_line = VALUE_NOT_SET;
  5104. + context->range_end_line = VALUE_NOT_SET;
  5105. + ret = FALSE;
  5106. + break;
  5107. +
  5108. + default:
  5109. + g_assert_not_reached ();
  5110. + break;
  5111. + }
  5112. +
  5113. + if (line_end != VALUE_NOT_SET)
  5114. + {
  5115. + context->range_start_line = line_start;
  5116. + context->range_end_line = line_end;
  5117. + }
  5118. + else if (line_start != VALUE_NOT_SET)
  5119. + {
  5120. + if (context->range_end_line != VALUE_NOT_SET)
  5121. + context->range_end_line += line_start;
  5122. + else
  5123. + context->range_end_line = line_start;
  5124. + }
  5125. +
  5126. + context->previous_token_is_separator = FALSE;
  5127. + return ret;
  5128. +}
  5129. +
  5130. +static gboolean
  5131. +token_check_rules (IdeVimParserContext *context,
  5132. + IdeVimParserToken *new_token)
  5133. +{
  5134. + GQueue *stack;
  5135. + IdeVimParserTokenKind previous_kind;
  5136. + IdeVimParserTokenKind new_kind;
  5137. + IdeVimParserToken *previous_token;
  5138. +
  5139. + stack = context->stack;
  5140. + previous_token = g_queue_peek_head (stack);
  5141. + if (previous_token == NULL)
  5142. + return TRUE;
  5143. +
  5144. + previous_kind = previous_token->kind;
  5145. + new_kind = new_token->kind;
  5146. +
  5147. + /* In addition to the registered command arguments, these rules
  5148. + * help catching errors before finding a command name.
  5149. + */
  5150. +
  5151. + /* A mark must be at the start of the command line or preceded by a range separator or a command name */
  5152. + if (new_kind == IDE_VIM_PARSER_TOKEN_MARK &&
  5153. + !(context->previous_token_is_separator || previous_kind == IDE_VIM_PARSER_TOKEN_COMMAND_NAME))
  5154. + return FALSE;
  5155. +
  5156. + /* A number can't be preceded by a register */
  5157. + if (new_kind == IDE_VIM_PARSER_TOKEN_NUMBER && previous_kind == IDE_VIM_PARSER_TOKEN_REGISTER)
  5158. + return FALSE;
  5159. +
  5160. + /* A pattern can't be preceded by a register */
  5161. + if (new_kind == IDE_VIM_PARSER_TOKEN_PATTERN && previous_kind == IDE_VIM_PARSER_TOKEN_REGISTER)
  5162. + return FALSE;
  5163. +
  5164. + /* A range specifier must be at the start of the command line or preceded by a range separator */
  5165. + if (new_kind == IDE_VIM_PARSER_TOKEN_RANGE_SPECIFIER && !context->previous_token_is_separator)
  5166. + return FALSE;
  5167. +
  5168. + /* A register can only be preceded by a command name */
  5169. + if (new_kind == IDE_VIM_PARSER_TOKEN_REGISTER && !(previous_kind == IDE_VIM_PARSER_TOKEN_COMMAND_NAME))
  5170. + return FALSE;
  5171. +
  5172. + /* TODO: new rule: no more than one successive separator
  5173. + * see and fix token_range_resolve*/
  5174. +
  5175. + return TRUE;
  5176. +}
  5177. +
  5178. +static gboolean
  5179. +token_range_try_compact (IdeVimParserContext *context,
  5180. + IdeVimParserToken *new_token)
  5181. +{
  5182. + GQueue *stack;
  5183. + IdeVimParserTokenKind previous_kind;
  5184. + IdeVimParserTokenKind new_kind;
  5185. + IdeVimParserToken *previous_token;
  5186. + IdeVimParserToken *token;
  5187. + gboolean previous_eligible;
  5188. + gboolean new_eligible;
  5189. +
  5190. + stack = context->stack;
  5191. + previous_token = g_queue_peek_head (stack);
  5192. + if (previous_token == NULL)
  5193. + return FALSE;
  5194. +
  5195. + previous_kind = previous_token->kind;
  5196. + new_kind = new_token->kind;
  5197. +
  5198. + previous_eligible = (previous_kind == IDE_VIM_PARSER_TOKEN_MARK ||
  5199. + previous_kind == IDE_VIM_PARSER_TOKEN_NUMBER ||
  5200. + previous_kind == IDE_VIM_PARSER_TOKEN_PATTERN ||
  5201. + previous_kind == IDE_VIM_PARSER_TOKEN_RANGE ||
  5202. + previous_kind == IDE_VIM_PARSER_TOKEN_RANGE_SPECIFIER);
  5203. +
  5204. + new_eligible = (new_kind == IDE_VIM_PARSER_TOKEN_MARK ||
  5205. + new_kind == IDE_VIM_PARSER_TOKEN_NUMBER ||
  5206. + new_kind == IDE_VIM_PARSER_TOKEN_PATTERN ||
  5207. + new_kind == IDE_VIM_PARSER_TOKEN_RANGE_SPECIFIER);
  5208. +
  5209. + if (previous_eligible && new_eligible)
  5210. + {
  5211. + token = ide_vim_parser_token_new ();
  5212. +
  5213. + token->kind = IDE_VIM_PARSER_TOKEN_RANGE;
  5214. + /* TODO: we miss the separator in the content string ? */
  5215. + token->content = g_strconcat (previous_token->content, " ", new_token->content, NULL);
  5216. + token->range_start = context->range_start_line;
  5217. + token->range_end = context->range_end_line;
  5218. +
  5219. + g_printf ("=> Stack Compaction: %s + %s = %s\n",
  5220. + ide_vim_parser_token_get_kind_name (previous_token),
  5221. + ide_vim_parser_token_get_kind_name (new_token),
  5222. + ide_vim_parser_token_get_kind_name (token));
  5223. +
  5224. + ide_vim_parser_token_unref (g_queue_pop_head (stack));
  5225. + g_queue_push_head (stack, token);
  5226. +
  5227. + return TRUE;
  5228. + }
  5229. +
  5230. + return FALSE;
  5231. +}
  5232. +
  5233. +/* TODO: handle errors */
  5234. +static gboolean
  5235. +ide_vim_parser_push_to_stack (IdeVimParserContext *context,
  5236. + IdeVimParserToken *token)
  5237. +{
  5238. + IdeVimParserPrivate *parser_priv = ide_vim_parser_get_instance_private (context->parser);
  5239. + IdeVimParserInfo *info = parser_priv->info;
  5240. +
  5241. + info->last_token_kind = token->kind;
  5242. + if (token->content)
  5243. + info->last_token_content = strdup (token->content);
  5244. +
  5245. + if (!token_check_rules (context, token))
  5246. + {
  5247. + /* TODO: free context->token or later ? */
  5248. + /* error code and update info ? */
  5249. + return FALSE;
  5250. + }
  5251. +
  5252. + token_range_resolve (context, token);
  5253. +
  5254. + if (token_range_try_compact (context, token))
  5255. + g_clear_pointer (&context->token, ide_vim_parser_token_unref);
  5256. + else if (token->kind != IDE_VIM_PARSER_TOKEN_RANGE_SEPARATOR)
  5257. + g_queue_push_head (context->stack, token);
  5258. +
  5259. + info->last_good_token_kind = token->kind;
  5260. + return TRUE;
  5261. +}
  5262. +
  5263. +/* get char at *ptr */
  5264. +static inline gchar
  5265. +get_char (const gchar **ptr)
  5266. +{
  5267. + return **ptr;
  5268. +}
  5269. +
  5270. +/* move *ptr to next char then return it.
  5271. + * If the current char == 0 then *ptr don't move
  5272. + */
  5273. +static inline gchar
  5274. +get_next_char (const gchar **ptr)
  5275. +{
  5276. + if (get_char (ptr))
  5277. + (*ptr)++;
  5278. +
  5279. + return **ptr;
  5280. +}
  5281. +
  5282. +/* get char at *ptr and move next char, unless char == 0 */
  5283. +static inline gchar
  5284. +get_char_and_forward (const gchar **ptr)
  5285. +{
  5286. + gchar c = get_char (ptr);
  5287. +
  5288. + if (c)
  5289. + (*ptr)++;
  5290. +
  5291. + return c;
  5292. +}
  5293. +
  5294. +/* Skip spaces and return the first char after */
  5295. +static gchar
  5296. +skip_spaces (const gchar **ptr)
  5297. +{
  5298. + gchar c = get_char (ptr);
  5299. +
  5300. + while (g_ascii_isspace(c))
  5301. + {
  5302. + c = get_next_char (ptr);
  5303. + };
  5304. +
  5305. + return c;
  5306. +}
  5307. +
  5308. +/* Skip all up to a space. An escaped space is also skiped.
  5309. + * We return in case of a space at the start of the string,
  5310. + * so that you should perhaps g_strchug your string before.
  5311. + * We also return if we reach the end of the string ( \0 ).
  5312. + * Update *ptr to the new position and return the char it point to */
  5313. +static gchar
  5314. +skip_till_spaces (const gchar **ptr)
  5315. +{
  5316. + const gchar *cursor = *ptr;
  5317. + gchar previous = '\0';
  5318. + gchar c = get_char (&cursor);
  5319. +
  5320. + while (c != '\0')
  5321. + {
  5322. + while (!g_ascii_isspace(c) && c != '\0')
  5323. + {
  5324. + previous = c;
  5325. + c = get_next_char (&cursor);
  5326. + };
  5327. +
  5328. + if (c == '\0' || cursor == *ptr)
  5329. + break;
  5330. +
  5331. + if (previous != '\\')
  5332. + break;
  5333. + }
  5334. +
  5335. + *ptr = cursor;
  5336. + return c;
  5337. +}
  5338. +
  5339. +static gboolean
  5340. +get_number (const gchar **ptr,
  5341. + gint *number,
  5342. + IdeVimParserErrorCode *error_code)
  5343. +{
  5344. + gchar c;
  5345. + const gchar *real_ptr = *ptr;
  5346. + gint sign = 1;
  5347. + guint number_digits = 0;
  5348. + gboolean sign_set = FALSE;
  5349. +
  5350. + /* TODO: use parser error */
  5351. + c = get_char (&real_ptr);
  5352. + if (strchr ("+-", c))
  5353. + {
  5354. + sign = (c == '+') ? 1 : -1;
  5355. + sign_set = TRUE;
  5356. +
  5357. + c = get_next_char (&real_ptr);
  5358. + }
  5359. +
  5360. + if (g_ascii_isdigit (c))
  5361. + {
  5362. + do
  5363. + {
  5364. + number_digits += 1;
  5365. + if (number_digits > 9)
  5366. + {
  5367. + /* Number rangge limit. Approximation a digit less
  5368. + * than 32bits int32 but crazy enough for an editor
  5369. + */
  5370. + get_next_char (&real_ptr);
  5371. + *ptr = real_ptr;
  5372. + *error_code = IDE_VIM_PARSER_ERROR_NUMBER_OUT_OF_RANGE;
  5373. + return FALSE;
  5374. + }
  5375. +
  5376. + *number = *number * 10 + g_ascii_digit_value (c);
  5377. + } while ((c = get_next_char (&real_ptr)) && g_ascii_isdigit (c));
  5378. +
  5379. + *number *= sign;
  5380. + }
  5381. + else if (sign_set)
  5382. + {
  5383. + *number = sign;
  5384. + }
  5385. + else
  5386. + {
  5387. + /* Not a number */
  5388. + return FALSE;
  5389. + }
  5390. +
  5391. + *ptr = real_ptr;
  5392. + return TRUE;
  5393. +}
  5394. +
  5395. +static gboolean
  5396. +update_state (IdeVimParserContext *context,
  5397. + IdeVimParserToken *token,
  5398. + IdeVimParserErrorCode error_code,
  5399. + const gchar *ptr)
  5400. +{
  5401. + IdeVimParserInfo *info;
  5402. + IdeVimParserPrivate *parser_priv;
  5403. +
  5404. + parser_priv = ide_vim_parser_get_instance_private (context->parser);
  5405. + info = parser_priv->info;
  5406. +
  5407. + context->token = token;
  5408. + context->token_size = (gint)(ptr - context->position);
  5409. + info ->last_token_kind = token->kind;
  5410. + info->cmdline_error_offset = (ptr - context->cmdline);
  5411. +
  5412. + if (context->token_size)
  5413. + {
  5414. + token->content = g_strndup (context->position, context->token_size);
  5415. + info->last_token_content = strdup (token->content);
  5416. + }
  5417. +
  5418. + if (error_code == IDE_VIM_PARSER_ERROR_NONE)
  5419. + {
  5420. + context->position += context->token_size;
  5421. + context->status = TRUE;
  5422. + }
  5423. + else
  5424. + {
  5425. + info->state = IDE_VIM_PARSER_STATE_ERROR;
  5426. + /* TODO: check if not set elsewhere */
  5427. + ide_vim_parser_error_cursor_set (&context->error, error_code, context->position, context->token_size);
  5428. + info->error_code = error_code;
  5429. + context->status = FALSE;
  5430. + }
  5431. +
  5432. + return context->status;
  5433. +}
  5434. +
  5435. +/* From a string, return a token type and it's size.
  5436. + * On error, size contain the error position.
  5437. + */
  5438. +static gboolean
  5439. +tokenize (IdeVimParserContext *context)
  5440. +{
  5441. + IdeVimParserObjectsPool *objects_pool;
  5442. + IdeVimParserCommand *command_entry = NULL;
  5443. + IdeVimParserInfo *info;
  5444. + IdeVimParserPrivate *parser_priv;
  5445. + IdeVimParserToken *token;
  5446. + IdeVimParserErrorCode error_code = IDE_VIM_PARSER_ERROR_NONE;
  5447. + g_autofree gchar *command = NULL;
  5448. + const gchar *ptr;
  5449. + const gchar *tmp_ptr;
  5450. + gchar c;
  5451. + gboolean has_bang = FALSE;
  5452. +
  5453. + parser_priv = ide_vim_parser_get_instance_private (context->parser);
  5454. + info = parser_priv->info;
  5455. +
  5456. + /* Skip any preceding spaces */
  5457. + c = skip_spaces (&context->position);
  5458. + ptr = context->position;
  5459. +
  5460. + token = ide_vim_parser_token_new ();
  5461. + token->kind = IDE_VIM_PARSER_TOKEN_UNKNOW;
  5462. +
  5463. + if (c == '\0')
  5464. + {
  5465. + /* End of string */
  5466. + token->kind = IDE_VIM_PARSER_TOKEN_END_OF_STRING;
  5467. + goto out;
  5468. + }
  5469. +
  5470. + /* TODO: Work on a slightly more large copy of the string so we don't need
  5471. + * to worry about reading beyond the end of the string
  5472. + * then destroy it (g_auto free on exit) because we only need the size
  5473. + * Not sure this is still aplying.
  5474. + */
  5475. +
  5476. + /* Numbers */
  5477. + if (get_number (&ptr, &token->number, &error_code) ||
  5478. + error_code != IDE_VIM_PARSER_ERROR_NONE)
  5479. + {
  5480. + token->kind = IDE_VIM_PARSER_TOKEN_NUMBER;
  5481. + goto out;
  5482. + }
  5483. +
  5484. + /* Range separator */
  5485. +
  5486. + if (strchr (",;", c))
  5487. + {
  5488. + token->kind = IDE_VIM_PARSER_TOKEN_RANGE_SEPARATOR;
  5489. + get_next_char (&ptr);
  5490. + goto out;
  5491. + }
  5492. +
  5493. + /* Range specifier and range */
  5494. +
  5495. + if (strchr ("%", c))
  5496. + {
  5497. + token->kind = IDE_VIM_PARSER_TOKEN_RANGE;
  5498. + token->range_is_native = TRUE;
  5499. + get_next_char (&ptr);
  5500. + goto out;
  5501. + }
  5502. +
  5503. + if (strchr (".$", c))
  5504. + {
  5505. + token->kind = IDE_VIM_PARSER_TOKEN_RANGE_SPECIFIER;
  5506. + get_next_char (&ptr);
  5507. + goto out;
  5508. + }
  5509. +
  5510. + if (c == '\\')
  5511. + {
  5512. + token->kind = IDE_VIM_PARSER_TOKEN_RANGE_SPECIFIER;
  5513. + c = get_next_char (&ptr);
  5514. + get_next_char (&ptr);
  5515. + if (strchr ("/?&", c))
  5516. + goto out;
  5517. +
  5518. + error_code = IDE_VIM_PARSER_ERROR_UNKNOWN_RANGE_SPECIFIER;
  5519. + goto out;
  5520. + }
  5521. +
  5522. + /* Marks */
  5523. +
  5524. + if (c == '\'')
  5525. + {
  5526. + token->kind = IDE_VIM_PARSER_TOKEN_MARK;
  5527. + c = get_next_char (&ptr);
  5528. + get_next_char (&ptr);
  5529. +
  5530. + if (c != '\0' && (g_ascii_isalnum (c) || strchr ("'\"^.[](){}<>", c)))
  5531. + goto out;
  5532. +
  5533. + error_code = IDE_VIM_PARSER_ERROR_UNKNOWN_MARK;
  5534. + goto out;
  5535. + }
  5536. +
  5537. + /* Registers */
  5538. +
  5539. + if (c == '"')
  5540. + {
  5541. + token->kind = IDE_VIM_PARSER_TOKEN_REGISTER;
  5542. + c = get_next_char (&ptr);
  5543. + if (c != '\0')
  5544. + {
  5545. + if (g_ascii_isalnum (c) || strchr ("\"-=+~_/%#", c))
  5546. + {
  5547. + get_next_char (&ptr);
  5548. + goto out;
  5549. + }
  5550. +
  5551. + if (c == ':' || c == '.')
  5552. + {
  5553. + c = get_next_char (&ptr);
  5554. + if (c == ',')
  5555. + goto out;
  5556. + }
  5557. +
  5558. + get_next_char (&ptr);
  5559. + error_code = IDE_VIM_PARSER_ERROR_UNKNOWN_REGISTER;
  5560. + goto out;
  5561. + }
  5562. +
  5563. + error_code = IDE_VIM_PARSER_ERROR_UNKNOWN_REGISTER;
  5564. + goto out;
  5565. + }
  5566. +
  5567. + /* Range Patterns */
  5568. +
  5569. + if (strchr ("/?", c))
  5570. + {
  5571. + gchar c_end = c;
  5572. + gchar prev = c;
  5573. +
  5574. + tmp_ptr = ptr;
  5575. + token->pattern_type = prev;
  5576. + token->kind = IDE_VIM_PARSER_TOKEN_PATTERN;
  5577. +
  5578. + /* A pattern end on the matching / or ? so can't be incomplete
  5579. + * end run till end of the line in worst case but we need to
  5580. + * take care of the matching / or ? in the pattern itself.
  5581. + */
  5582. + while ((c = get_next_char (&ptr)))
  5583. + {
  5584. + if (c == c_end && prev != '\\')
  5585. + goto out;
  5586. +
  5587. + prev = c;
  5588. + }
  5589. +
  5590. + /* Unclosed pattern : search command / OR ?
  5591. + * the rest of the line will be a raw pattern */
  5592. + /* TODO: fill a search command entry */
  5593. + token->kind = IDE_VIM_PARSER_TOKEN_COMMAND_NAME;
  5594. + ptr = tmp_ptr;
  5595. + goto out;
  5596. + }
  5597. +
  5598. + /* Various strings */
  5599. + /* TODO: Command name is only letters and we can find number just after without spaces,
  5600. + * is string more than letters can be find before command name ? */
  5601. + tmp_ptr = ptr;
  5602. + while ((c = get_next_char (&ptr)) && !g_ascii_isspace (c))
  5603. + tmp_ptr = ptr;
  5604. +
  5605. + /* Check for a bang just after a possible command name */
  5606. + if (*tmp_ptr == '!')
  5607. + {
  5608. + command = g_strndup (context->position, (gint)(tmp_ptr - context->position));
  5609. + has_bang = TRUE;
  5610. + }
  5611. + else
  5612. + {
  5613. + command = g_strndup (context->position, (gint)(ptr - context->position));
  5614. + }
  5615. +
  5616. + /* Check for an existing command name */
  5617. + objects_pool = parser_priv->objects_pool;
  5618. + command_entry = ide_vim_parser_objects_pool_lookup_command (objects_pool, command);
  5619. + if (command_entry != NULL)
  5620. + {
  5621. + token->command_has_bang = has_bang;
  5622. + token->command_entry = command_entry;
  5623. + token->kind = IDE_VIM_PARSER_TOKEN_COMMAND_NAME;
  5624. +
  5625. + context->command_found = token->command_entry;
  5626. + info->command_name_found = g_strdup (ide_vim_parser_command_get_name (context->command_found));
  5627. + }
  5628. + else
  5629. + {
  5630. + token->kind = IDE_VIM_PARSER_TOKEN_STRING;
  5631. + }
  5632. +
  5633. +out:
  5634. +
  5635. + return update_state (context, token, error_code, ptr);
  5636. +}
  5637. +
  5638. +/* From a string, return a token type and it's size.
  5639. + * On error, size contain the error position.
  5640. + */
  5641. +static gboolean
  5642. +tokenize_after_command (IdeVimParserContext *context,
  5643. + IdeVimParserToken *arg)
  5644. +{
  5645. + IdeVimParserToken *token;
  5646. + IdeVimParserErrorCode error_code = IDE_VIM_PARSER_ERROR_NONE;
  5647. + const gchar *ptr;
  5648. + gchar c;
  5649. +
  5650. + /* Skip any preceding spaces */
  5651. + c = skip_spaces (&context->position);
  5652. + ptr = context->position;
  5653. +
  5654. + token = ide_vim_parser_token_new ();
  5655. + token->kind = IDE_VIM_PARSER_TOKEN_UNKNOW;
  5656. +
  5657. + if (c == '\0')
  5658. + {
  5659. + /* End of string */
  5660. + token->kind = IDE_VIM_PARSER_TOKEN_END_OF_STRING;
  5661. + error_code = IDE_VIM_PARSER_ERROR_MISMATCH_KIND;
  5662. + goto out;
  5663. + }
  5664. +
  5665. + if (arg->kind == IDE_VIM_PARSER_TOKEN_NUMBER)
  5666. + {
  5667. + /* TODO: Negative number used in post command ? */
  5668. + if (get_number (&ptr, &token->number, &error_code) ||
  5669. + error_code != IDE_VIM_PARSER_ERROR_NONE)
  5670. + {
  5671. + token->kind = IDE_VIM_PARSER_TOKEN_NUMBER;
  5672. + goto out;
  5673. + }
  5674. +
  5675. + error_code = IDE_VIM_PARSER_ERROR_MISMATCH_KIND;
  5676. + goto out;
  5677. + }
  5678. +
  5679. + if (arg->kind == IDE_VIM_PARSER_TOKEN_STRING)
  5680. + {
  5681. + while ((c = get_next_char (&ptr)) && !g_ascii_isspace (c));
  5682. +
  5683. + token->kind = IDE_VIM_PARSER_TOKEN_STRING;
  5684. + goto out;
  5685. + }
  5686. +
  5687. + error_code = IDE_VIM_PARSER_ERROR_MISMATCH_KIND;
  5688. +
  5689. +out:
  5690. +
  5691. + return update_state (context, token, error_code, ptr);
  5692. +}
  5693. +
  5694. +static gboolean
  5695. +ide_vim_parser_validate_pre_command_args (IdeVimParserContext *context)
  5696. +{
  5697. + IdeVimParserCommand *command = context->command_found;
  5698. + IdeVimParserToken *arg;
  5699. + IdeVimParserToken *token;
  5700. + const GPtrArray *args;
  5701. + const GList *l_token;
  5702. + gboolean is_matching;
  5703. +
  5704. + g_return_val_if_fail (command != NULL, FALSE);
  5705. +
  5706. + l_token = context->stack->tail;
  5707. + args = ide_vim_parser_command_get_args (command);
  5708. +
  5709. + for (gint i = 0; i < args->len; i++)
  5710. + {
  5711. + context->command_arg_list_pos = i;
  5712. + arg = g_ptr_array_index (args, i);
  5713. + token = l_token->data;
  5714. +
  5715. + /* We also cover the case where we way for a number but
  5716. + * we have a range due to separators
  5717. + */
  5718. + is_matching = arg->kind == token->kind ||
  5719. + (arg->kind == IDE_VIM_PARSER_TOKEN_NUMBER && token->kind == IDE_VIM_PARSER_TOKEN_RANGE);
  5720. +
  5721. + if (!is_matching)
  5722. + {
  5723. + if (arg->flag == IDE_VIM_PARSER_TOKEN_FLAG_OPTIONNAL)
  5724. + continue;
  5725. + else
  5726. + break;
  5727. + }
  5728. +
  5729. + g_printf ("match: kind:%i, flag:%i\n", arg->kind, arg->flag);
  5730. +
  5731. + if (arg->kind == IDE_VIM_PARSER_TOKEN_COMMAND_NAME)
  5732. + return TRUE;
  5733. +
  5734. + l_token = l_token->prev;
  5735. + if (l_token == NULL)
  5736. + break;
  5737. + }
  5738. +
  5739. + g_printf ("missmatch: kind:%i != %i, flag:%i\n", arg->kind, token->kind, arg->flag);
  5740. + return FALSE;
  5741. +}
  5742. +
  5743. +static gboolean
  5744. +ide_vim_parser_validate_post_command_args (IdeVimParserContext *context)
  5745. +{
  5746. + IdeVimParserCommand *command = context->command_found;
  5747. + IdeVimParserError *tmp_error = NULL;
  5748. + const GPtrArray *args;
  5749. + IdeVimParserToken *arg;
  5750. + gboolean ret;
  5751. +
  5752. + g_return_val_if_fail (command != NULL, FALSE);
  5753. +
  5754. + /* We continue validate args after command name */
  5755. + args = ide_vim_parser_command_get_args (command);
  5756. + for (gint i = context->command_arg_list_pos + 1; i < args->len; i++)
  5757. + {
  5758. + arg = g_ptr_array_index (args, i);
  5759. +
  5760. + ret = tokenize_after_command (context, arg);
  5761. + /* TODO: errors stuff to fix too, can be an incomplete token or
  5762. + * no token at all */
  5763. + if (!ret)
  5764. + {
  5765. + if (arg->flag != IDE_VIM_PARSER_TOKEN_FLAG_OPTIONNAL)
  5766. + {
  5767. + g_printf ("post missmatch: kind:%i != %i, flag:%i\n", arg->kind, context->token->kind, arg->flag);
  5768. + return FALSE;
  5769. + }
  5770. +
  5771. + return FALSE;
  5772. + }
  5773. +
  5774. + g_queue_push_head (context->stack, context->token);
  5775. + }
  5776. +
  5777. + return TRUE;
  5778. +}
  5779. +
  5780. +static void
  5781. +ide_vim_parser_destroy_context (IdeVimParserContext *context)
  5782. +{
  5783. + g_free (context->cmdline);
  5784. + g_queue_clear (context->stack);
  5785. + g_free (context);
  5786. +}
  5787. +
  5788. +/* Line values are in Vim coordinates, starting from 1 */
  5789. +static IdeVimParserContext *
  5790. +ide_vim_parser_create_context (IdeVimParser *self,
  5791. + GtkTextBuffer *buffer,
  5792. + const gchar *cmdline)
  5793. +{
  5794. + IdeVimParserPrivate *priv = ide_vim_parser_get_instance_private (self);
  5795. + IdeVimParserContext *context;
  5796. + GtkTextIter start;
  5797. + GtkTextIter end;
  5798. + GtkTextIter insert;
  5799. + GtkTextIter select;
  5800. + gboolean has_selection;
  5801. +
  5802. + g_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), NULL);
  5803. + g_return_val_if_fail (!ide_str_empty0 (cmdline), NULL);
  5804. +
  5805. + context = g_new0 (IdeVimParserContext, 1);
  5806. + context->buffer = buffer;
  5807. +
  5808. + gtk_text_buffer_get_bounds (buffer, &start, &end);
  5809. + context->start_line = gtk_text_iter_get_line (&start) + BUFFER_TO_VIM_LINE_COORDS;
  5810. + context->end_line = gtk_text_iter_get_line (&end) + BUFFER_TO_VIM_LINE_COORDS;
  5811. +
  5812. + has_selection = gtk_text_buffer_get_selection_bounds (buffer, &insert, &select);
  5813. + context->insert_line = gtk_text_iter_get_line (&insert) + BUFFER_TO_VIM_LINE_COORDS;
  5814. + /* TODO: set select_line to 0 if has_selection FALSE ? */
  5815. + context->select_line = gtk_text_iter_get_line (&select) + BUFFER_TO_VIM_LINE_COORDS;
  5816. +
  5817. + context->range_start_line = VALUE_NOT_SET;
  5818. + context->range_end_line = VALUE_NOT_SET;
  5819. +
  5820. + context->previous_token_is_separator = TRUE;
  5821. +
  5822. + context->parser = self;
  5823. + context->cmdline = g_strdup (cmdline);
  5824. + context->position = context->cmdline;
  5825. + context->objects_pool = priv->objects_pool;
  5826. + context->stack = priv->stack;
  5827. + context->info = priv->info;
  5828. +
  5829. + return context;
  5830. +}
  5831. +
  5832. +static void
  5833. +set_token_error (IdeVimParserContext *context)
  5834. +{
  5835. + IdeVimParserInfo *info = context->info;
  5836. +
  5837. + g_assert (info->error_code != IDE_VIM_PARSER_ERROR_NONE);
  5838. +
  5839. + ide_vim_parser_error_cursor_set (&context->error,
  5840. + info->error_code,
  5841. + context->cmdline,
  5842. + info->cmdline_error_offset);
  5843. +}
  5844. +
  5845. +static void
  5846. +set_token_unwanted_error (IdeVimParserContext *context)
  5847. +{
  5848. + IdeVimParserInfo *info = context->info;
  5849. +
  5850. + info->state = IDE_VIM_PARSER_STATE_ERROR;
  5851. + info->error_code = IDE_VIM_PARSER_ERROR_UNWANTED_TOKEN;
  5852. + info->cmdline_error_offset = (context->position - context->token_size - context->cmdline);
  5853. +
  5854. + ide_vim_parser_error_cursor_set (&context->error,
  5855. + IDE_VIM_PARSER_ERROR_UNWANTED_TOKEN,
  5856. + context->cmdline,
  5857. + info->cmdline_error_offset);
  5858. +}
  5859. +
  5860. +/* Commom enter and leave functions */
  5861. +static void
  5862. +state_enter (IdeVimStateMachine *self,
  5863. + gpointer data)
  5864. +{
  5865. + IdeVimParserContext *context = (IdeVimParserContext *)data;
  5866. +
  5867. + //printf ("%s state enter\n", ide_vim_state_machine_get_state_name (self));
  5868. +}
  5869. +
  5870. +static void
  5871. +state_leave (IdeVimStateMachine *self,
  5872. + gpointer data)
  5873. +{
  5874. + IdeVimParserContext *context = (IdeVimParserContext *)data;
  5875. +
  5876. + //g_printf ("%s state leave\n", ide_vim_state_machine_get_state_name (self));
  5877. +}
  5878. +
  5879. +/* 'init' state functions */
  5880. +static IdeVimStateId
  5881. +state_init_transition (IdeVimStateMachine *self,
  5882. + gpointer data)
  5883. +{
  5884. + IdeVimParserContext *context = (IdeVimParserContext *)data;
  5885. + IdeVimParserToken *token;
  5886. + const gchar *state_name;
  5887. + gboolean status;
  5888. +
  5889. + g_printf ("%s state transition\n", ide_vim_state_machine_get_state_name (self));
  5890. +
  5891. + status = tokenize (context);
  5892. + if (status)
  5893. + {
  5894. + token = context->token;
  5895. + g_printf ("%s\n", ide_vim_parser_debug_token_to_string (context->token));
  5896. +
  5897. + switch (token->kind)
  5898. + {
  5899. + case IDE_VIM_PARSER_TOKEN_PATTERN:
  5900. + case IDE_VIM_PARSER_TOKEN_RANGE_SPECIFIER:
  5901. + case IDE_VIM_PARSER_TOKEN_MARK:
  5902. + case IDE_VIM_PARSER_TOKEN_NUMBER:
  5903. + case IDE_VIM_PARSER_TOKEN_RANGE:
  5904. + state_name = ide_vim_parser_push_to_stack (context, token) ? "range" : "error";
  5905. + break;
  5906. +
  5907. + case IDE_VIM_PARSER_TOKEN_REGISTER:
  5908. + state_name = ide_vim_parser_push_to_stack (context, token) ? "register" : "error";
  5909. + break;
  5910. +
  5911. + case IDE_VIM_PARSER_TOKEN_COMMAND_NAME:
  5912. + state_name = ide_vim_parser_push_to_stack (context, token) ? "command" : "error";
  5913. + break;
  5914. +
  5915. + case IDE_VIM_PARSER_TOKEN_RANGE_SEPARATOR:
  5916. + state_name = ide_vim_parser_push_to_stack (context, token) ? "separator" : "error";
  5917. + break;
  5918. +
  5919. + case IDE_VIM_PARSER_TOKEN_UNKNOW:
  5920. + case IDE_VIM_PARSER_TOKEN_STRING:
  5921. + case IDE_VIM_PARSER_TOKEN_END_OF_STRING:
  5922. + /* TODO: goto line command */
  5923. + set_token_unwanted_error (context);
  5924. + state_name = "error";
  5925. + break;
  5926. +
  5927. + default:
  5928. + g_assert_not_reached ();
  5929. + break;
  5930. + }
  5931. +
  5932. + return ide_vim_state_machine_get_id_from_name (self, state_name);
  5933. + }
  5934. +
  5935. + set_token_error (context);
  5936. + return ide_vim_state_machine_get_id_from_name (self, "error");
  5937. +}
  5938. +
  5939. +static gboolean
  5940. +state_init_action (IdeVimStateMachine *self,
  5941. + gpointer data)
  5942. +{
  5943. + IdeVimParserContext *context = (IdeVimParserContext *)data;
  5944. +
  5945. + //g_printf ("%s state action\n", ide_vim_state_machine_get_state_name (self));
  5946. +
  5947. + return TRUE;
  5948. +}
  5949. +
  5950. +/* 'range' state functions */
  5951. +static IdeVimStateId
  5952. +state_range_transition (IdeVimStateMachine *self,
  5953. + gpointer data)
  5954. +{
  5955. + IdeVimParserContext *context = (IdeVimParserContext *)data;
  5956. + IdeVimParserToken *token;
  5957. + const gchar *state_name;
  5958. + gboolean status;
  5959. +
  5960. + g_printf ("%s state transition\n", ide_vim_state_machine_get_state_name (self));
  5961. +
  5962. + status = tokenize (context);
  5963. + if (status)
  5964. + {
  5965. + token = context->token;
  5966. + g_printf ("%s\n", ide_vim_parser_debug_token_to_string (context->token));
  5967. +
  5968. + switch (token->kind)
  5969. + {
  5970. + case IDE_VIM_PARSER_TOKEN_COMMAND_NAME:
  5971. + state_name = ide_vim_parser_push_to_stack (context, token) ? "command" : "error";
  5972. + break;
  5973. +
  5974. + case IDE_VIM_PARSER_TOKEN_NUMBER:
  5975. + case IDE_VIM_PARSER_TOKEN_PATTERN:
  5976. + state_name = ide_vim_parser_push_to_stack (context, token) ? "range" : "error";
  5977. + break;
  5978. +
  5979. + case IDE_VIM_PARSER_TOKEN_RANGE_SEPARATOR:
  5980. + state_name = ide_vim_parser_push_to_stack (context, token) ? "separator" : "error";
  5981. + break;
  5982. +
  5983. + case IDE_VIM_PARSER_TOKEN_RANGE_SPECIFIER:
  5984. + case IDE_VIM_PARSER_TOKEN_MARK:
  5985. + case IDE_VIM_PARSER_TOKEN_REGISTER:
  5986. + case IDE_VIM_PARSER_TOKEN_UNKNOW:
  5987. + case IDE_VIM_PARSER_TOKEN_STRING:
  5988. + case IDE_VIM_PARSER_TOKEN_END_OF_STRING:
  5989. + case IDE_VIM_PARSER_TOKEN_RANGE:
  5990. + set_token_unwanted_error (context);
  5991. + state_name = "error";
  5992. + break;
  5993. +
  5994. + default:
  5995. + g_assert_not_reached ();
  5996. + break;
  5997. + }
  5998. +
  5999. + return ide_vim_state_machine_get_id_from_name (self, state_name);
  6000. + }
  6001. +
  6002. + set_token_error (context);
  6003. + return ide_vim_state_machine_get_id_from_name (self, "error");
  6004. +}
  6005. +
  6006. +static gboolean
  6007. +state_range_action (IdeVimStateMachine *self,
  6008. + gpointer data)
  6009. +{
  6010. + IdeVimParserContext *context = (IdeVimParserContext *)data;
  6011. +
  6012. + //g_printf ("%s state action\n", ide_vim_state_machine_get_state_name (self));
  6013. +
  6014. + return TRUE;
  6015. +}
  6016. +
  6017. +/* 'separator' state functions */
  6018. +static IdeVimStateId
  6019. +state_separator_transition (IdeVimStateMachine *self,
  6020. + gpointer data)
  6021. +{
  6022. + IdeVimParserContext *context = (IdeVimParserContext *)data;
  6023. + IdeVimParserToken *token;
  6024. + const gchar *state_name;
  6025. + gboolean status;
  6026. +
  6027. + g_printf ("%s state transition\n", ide_vim_state_machine_get_state_name (self));
  6028. +
  6029. + status = tokenize (context);
  6030. + if (status)
  6031. + {
  6032. + token = context->token;
  6033. + g_printf ("%s\n", ide_vim_parser_debug_token_to_string (context->token));
  6034. +
  6035. + switch (token->kind)
  6036. + {
  6037. + case IDE_VIM_PARSER_TOKEN_COMMAND_NAME:
  6038. + state_name = ide_vim_parser_push_to_stack (context, token) ? "command" : "error";
  6039. + break;
  6040. +
  6041. + case IDE_VIM_PARSER_TOKEN_PATTERN:
  6042. + case IDE_VIM_PARSER_TOKEN_RANGE_SPECIFIER:
  6043. + case IDE_VIM_PARSER_TOKEN_MARK:
  6044. + case IDE_VIM_PARSER_TOKEN_NUMBER:
  6045. + state_name = ide_vim_parser_push_to_stack (context, token) ? "range" : "error";
  6046. + break;
  6047. +
  6048. + case IDE_VIM_PARSER_TOKEN_RANGE_SEPARATOR:
  6049. + case IDE_VIM_PARSER_TOKEN_REGISTER:
  6050. + case IDE_VIM_PARSER_TOKEN_UNKNOW:
  6051. + case IDE_VIM_PARSER_TOKEN_STRING:
  6052. + case IDE_VIM_PARSER_TOKEN_END_OF_STRING:
  6053. + case IDE_VIM_PARSER_TOKEN_RANGE:
  6054. + set_token_unwanted_error (context);
  6055. + state_name = "error";
  6056. + break;
  6057. +
  6058. + default:
  6059. + g_assert_not_reached ();
  6060. + break;
  6061. + }
  6062. +
  6063. + return ide_vim_state_machine_get_id_from_name (self, state_name);
  6064. + }
  6065. +
  6066. + set_token_error (context);
  6067. + return ide_vim_state_machine_get_id_from_name (self, "error");
  6068. +}
  6069. +
  6070. +static gboolean
  6071. +state_separator_action (IdeVimStateMachine *self,
  6072. + gpointer data)
  6073. +{
  6074. + IdeVimParserContext *context = (IdeVimParserContext *)data;
  6075. +
  6076. + //g_printf ("%s state action\n", ide_vim_state_machine_get_state_name (self));
  6077. +
  6078. + return TRUE;
  6079. +}
  6080. +
  6081. +/* 'register' state functions */
  6082. +static IdeVimStateId
  6083. +state_register_transition (IdeVimStateMachine *self,
  6084. + gpointer data)
  6085. +{
  6086. + IdeVimParserContext *context = (IdeVimParserContext *)data;
  6087. + IdeVimParserToken *token;
  6088. + const gchar *state_name;
  6089. + gboolean status;
  6090. +
  6091. + g_printf ("%s state transition\n", ide_vim_state_machine_get_state_name (self));
  6092. +
  6093. + status = tokenize (context);
  6094. + if (status)
  6095. + {
  6096. + token = context->token;
  6097. + g_printf ("%s\n", ide_vim_parser_debug_token_to_string (context->token));
  6098. +
  6099. + switch (token->kind)
  6100. + {
  6101. + case IDE_VIM_PARSER_TOKEN_COMMAND_NAME:
  6102. + state_name = ide_vim_parser_push_to_stack (context, token) ? "command" : "error";
  6103. + break;
  6104. +
  6105. + case IDE_VIM_PARSER_TOKEN_RANGE_SEPARATOR:
  6106. + case IDE_VIM_PARSER_TOKEN_UNKNOW:
  6107. + case IDE_VIM_PARSER_TOKEN_STRING:
  6108. + case IDE_VIM_PARSER_TOKEN_END_OF_STRING:
  6109. + case IDE_VIM_PARSER_TOKEN_RANGE:
  6110. + case IDE_VIM_PARSER_TOKEN_PATTERN:
  6111. + case IDE_VIM_PARSER_TOKEN_RANGE_SPECIFIER:
  6112. + case IDE_VIM_PARSER_TOKEN_MARK:
  6113. + case IDE_VIM_PARSER_TOKEN_NUMBER:
  6114. + case IDE_VIM_PARSER_TOKEN_REGISTER:
  6115. + set_token_unwanted_error (context);
  6116. + state_name = "error";
  6117. + break;
  6118. +
  6119. + default:
  6120. + g_assert_not_reached ();
  6121. + break;
  6122. + }
  6123. +
  6124. + return ide_vim_state_machine_get_id_from_name (self, state_name);
  6125. + }
  6126. +
  6127. + set_token_error (context);
  6128. + return ide_vim_state_machine_get_id_from_name (self, "error");
  6129. +}
  6130. +
  6131. +static gboolean
  6132. +state_register_action (IdeVimStateMachine *self,
  6133. + gpointer data)
  6134. +{
  6135. + IdeVimParserContext *context = (IdeVimParserContext *)data;
  6136. +
  6137. + //g_printf ("%s state action\n", ide_vim_state_machine_get_state_name (self));
  6138. +
  6139. + return TRUE;
  6140. +}
  6141. +
  6142. +/* 'command' state functions */
  6143. +static IdeVimStateId
  6144. +state_command_transition (IdeVimStateMachine *self,
  6145. + gpointer data)
  6146. +{
  6147. + IdeVimParserContext *context = (IdeVimParserContext *)data;
  6148. +
  6149. + g_printf ("%s state transition\n", ide_vim_state_machine_get_state_name (self));
  6150. +
  6151. + if (!ide_vim_parser_validate_pre_command_args (context))
  6152. + return ide_vim_state_machine_get_id_from_name (self, "error");
  6153. +
  6154. + if (!ide_vim_parser_validate_post_command_args (context))
  6155. + return ide_vim_state_machine_get_id_from_name (self, "error");
  6156. +
  6157. + return ide_vim_state_machine_get_id_from_name (self, "valid");
  6158. +}
  6159. +
  6160. +static gboolean
  6161. +state_command_action (IdeVimStateMachine *self,
  6162. + gpointer data)
  6163. +{
  6164. + IdeVimParserContext *context = (IdeVimParserContext *)data;
  6165. +
  6166. + //g_printf ("%s state action\n", ide_vim_state_machine_get_state_name (self));
  6167. +
  6168. + return TRUE;
  6169. +}
  6170. +
  6171. +/* 'valid' state functions */
  6172. +static gboolean
  6173. +state_valid_action (IdeVimStateMachine *self,
  6174. + gpointer data)
  6175. +{
  6176. + IdeVimParserContext *context = (IdeVimParserContext *)data;
  6177. +
  6178. + g_printf ("%s state action\n", ide_vim_state_machine_get_state_name (self));
  6179. +
  6180. + return TRUE;
  6181. +}
  6182. +
  6183. +/* 'incomplete' state functions */
  6184. +static gboolean
  6185. +state_incomplete_action (IdeVimStateMachine *self,
  6186. + gpointer data)
  6187. +{
  6188. + IdeVimParserContext *context = (IdeVimParserContext *)data;
  6189. +
  6190. + g_printf ("%s state action\n", ide_vim_state_machine_get_state_name (self));
  6191. +
  6192. + return TRUE;
  6193. +}
  6194. +
  6195. +/* 'error' state functions */
  6196. +
  6197. +static gboolean
  6198. +state_error_action (IdeVimStateMachine *self,
  6199. + gpointer data)
  6200. +{
  6201. + IdeVimParserContext *context = (IdeVimParserContext *)data;
  6202. +
  6203. + g_printf ("%s state action\n", ide_vim_state_machine_get_state_name (self));
  6204. + /* TODO: save the token and clear context->token */
  6205. +
  6206. +
  6207. + return TRUE;
  6208. +}
  6209. +
  6210. +#pragma GCC diagnostic push
  6211. +
  6212. +static void
  6213. +_ide_vim_parser_fill_state_table (IdeVimParser *self)
  6214. +{
  6215. + IdeVimParserPrivate *priv = ide_vim_parser_get_instance_private (self);
  6216. +
  6217. + /* The init state is also use for range separators */
  6218. + ide_vim_state_machine_add_state (priv->machine, "init",
  6219. + state_enter, state_leave, state_init_transition, state_init_action);
  6220. +
  6221. + /* The range state is also use for counts */
  6222. + ide_vim_state_machine_add_state (priv->machine, "range",
  6223. + state_enter, state_leave, state_range_transition, state_range_action);
  6224. +
  6225. + ide_vim_state_machine_add_state (priv->machine, "separator",
  6226. + state_enter, state_leave, state_separator_transition, state_separator_action);
  6227. +
  6228. + ide_vim_state_machine_add_state (priv->machine, "register",
  6229. + state_enter, state_leave, state_register_transition, state_register_action);
  6230. +
  6231. + ide_vim_state_machine_add_state (priv->machine, "command",
  6232. + state_enter, state_leave, state_command_transition, state_command_action);
  6233. +
  6234. + ide_vim_state_machine_add_state (priv->machine, "error",
  6235. + state_enter, state_leave, NULL, state_error_action);
  6236. +
  6237. + ide_vim_state_machine_add_state (priv->machine, "valid",
  6238. + state_enter, state_leave, NULL, state_valid_action);
  6239. +
  6240. + ide_vim_state_machine_add_state (priv->machine, "incomplete",
  6241. + state_enter, state_leave, NULL, state_incomplete_action);
  6242. +}
  6243. +
  6244. +static void
  6245. +_ide_vim_parser_fill_command_table (IdeVimParser *self)
  6246. +{
  6247. +
  6248. +/*
  6249. + static IdeVimParserTokenCommandItem commands [] = {
  6250. + {"buffer", IDE_VIM_PARSER_TOKEN_COMMAND_NAME_BUFFER },
  6251. + {"cd", IDE_VIM_PARSER_TOKEN_COMMAND_NAME_CD },
  6252. + {"clist", IDE_VIM_PARSER_TOKEN_COMMAND_NAME_CLIST },
  6253. + {"excecute", IDE_VIM_PARSER_TOKEN_COMMAND_NAME_EXCECUTE },
  6254. + {"history", IDE_VIM_PARSER_TOKEN_COMMAND_NAME_HISTORY },
  6255. + {"marks", IDE_VIM_PARSER_TOKEN_COMMAND_NAME_MARKS },
  6256. + {"fold", IDE_VIM_PARSER_TOKEN_COMMAND_NAME_FOLD },
  6257. + {"global", IDE_VIM_PARSER_TOKEN_COMMAND_NAME_GLOBAL },
  6258. + {"nohl", IDE_VIM_PARSER_TOKEN_COMMAND_NAME_NOHL },
  6259. + {"qall", IDE_VIM_PARSER_TOKEN_COMMAND_NAME_QALL },
  6260. + {"quit", IDE_VIM_PARSER_TOKEN_COMMAND_NAME_QUIT },
  6261. + {"quitall", IDE_VIM_PARSER_TOKEN_COMMAND_NAME_QUITALL },
  6262. + {"reg", IDE_VIM_PARSER_TOKEN_COMMAND_NAME_REG },
  6263. + {"retab", IDE_VIM_PARSER_TOKEN_COMMAND_NAME_RETAB },
  6264. + {"set", IDE_VIM_PARSER_TOKEN_COMMAND_NAME_SET },
  6265. + {"shell", IDE_VIM_PARSER_TOKEN_COMMAND_NAME_SHELL },
  6266. + {"sort", IDE_VIM_PARSER_TOKEN_COMMAND_NAME_SORT },
  6267. + {"split", IDE_VIM_PARSER_TOKEN_COMMAND_NAME_SPLIT },
  6268. + {"substitute", IDE_VIM_PARSER_TOKEN_COMMAND_NAME_SUBSTITUTE },
  6269. + {"syntax", IDE_VIM_PARSER_TOKEN_COMMAND_NAME_SYNTAX },
  6270. + {"tags", IDE_VIM_PARSER_TOKEN_COMMAND_NAME_TAGS },
  6271. + {"version", IDE_VIM_PARSER_TOKEN_COMMAND_NAME_VERSION },
  6272. + {"vsplit", IDE_VIM_PARSER_TOKEN_COMMAND_NAME_VSPLIT },
  6273. + {"wq", IDE_VIM_PARSER_TOKEN_COMMAND_NAME_WQ },
  6274. + {"write", IDE_VIM_PARSER_TOKEN_COMMAND_NAME_WRITE }
  6275. + };
  6276. +*/
  6277. +
  6278. +}
  6279. +
  6280. +static inline gboolean
  6281. +set_substring_parse (const gchar *str,
  6282. + gchar **var,
  6283. + gchar **val,
  6284. + IdeVimParserSetOp *op,
  6285. + IdeVimParserError **error)
  6286. +{
  6287. + gchar *set_separator;
  6288. + const gchar *str_end;
  6289. + g_autofree gchar *val_tmp = NULL;
  6290. + guint error_position = 0;
  6291. + gint len;
  6292. +
  6293. + len = strlen (str);
  6294. + str_end = str + len - 1;
  6295. + set_separator = strchr (str, '=');
  6296. + if (set_separator == NULL)
  6297. + {
  6298. + set_separator = strchr (str, ':');
  6299. + }
  6300. +
  6301. + if (set_separator == NULL)
  6302. + {
  6303. + *val = NULL;
  6304. + if (*str_end == '!')
  6305. + {
  6306. + *var = g_strndup (str, len - 1);
  6307. + *op = IDE_VIM_PARSER_SET_OP_INVERT;
  6308. + }
  6309. + else if (*str_end == '?')
  6310. + {
  6311. + *var = g_strndup (str, len - 1);
  6312. + *op = IDE_VIM_PARSER_SET_OP_SHOW;
  6313. + }
  6314. + else if (g_str_has_prefix (str, "no"))
  6315. + {
  6316. + *var = g_strdup (str + 2);
  6317. + *op = IDE_VIM_PARSER_SET_OP_RESET;
  6318. + }
  6319. + else
  6320. + {
  6321. + *var = g_strdup (str);
  6322. + *op = IDE_VIM_PARSER_SET_OP_NONE;
  6323. + }
  6324. +
  6325. + /* TODO: validate *var as isalpha */
  6326. + }
  6327. + else
  6328. + {
  6329. + if (set_separator == str || set_separator == str_end)
  6330. + {
  6331. + error_position = set_separator -str;
  6332. + goto error;
  6333. + }
  6334. +
  6335. + /* TODO: validate *var as isalpha */
  6336. + *var = g_strndup (str, set_separator - str);
  6337. + val_tmp = g_strndup (set_separator + 1, str_end - set_separator);
  6338. + *val = g_strcompress (val_tmp);
  6339. + }
  6340. +
  6341. + return TRUE;
  6342. +
  6343. +error:
  6344. + ide_vim_parser_error_cursor_set (error, IDE_VIM_PARSER_ERROR_SET_CANT_PARSE, str, error_position);
  6345. + return FALSE;
  6346. +}
  6347. +
  6348. +/**
  6349. + * ide_vim_parser_parse_set:
  6350. + * @self: (in): a #IdeVimParser instance.
  6351. + * @string: (in): The cset string to parse.
  6352. + * @error: (out): The #IdeVimParserError used for error reporting.
  6353. + *
  6354. + * Parse the a set string.
  6355. + *
  6356. + * Returns: (transfer full) (nullable): A #GArray of ?.
  6357. + */
  6358. +GArray *
  6359. +ide_vim_parser_parse_settable (IdeVimParser *self,
  6360. + const gchar *string,
  6361. + IdeVimParserError **error)
  6362. +{
  6363. + g_autofree const gchar *str = NULL;
  6364. + const gchar *cursor;
  6365. + const gchar *base;
  6366. + gchar *set_str, *set_var, *set_val;
  6367. + IdeVimParserSetOp op = IDE_VIM_PARSER_SET_OP_NONE;
  6368. + IdeVimParserError *tmp_error = NULL;
  6369. + GArray *array = NULL;
  6370. + gchar c;
  6371. + IdeVimParserPrivate *priv;
  6372. +
  6373. + g_assert (IDE_IS_VIM_PARSER (self));
  6374. +
  6375. + priv = ide_vim_parser_get_instance_private (self);
  6376. + if (ide_str_empty0 (string))
  6377. + {
  6378. + /* TODO: show changed */
  6379. + return NULL;
  6380. + }
  6381. +
  6382. + str = g_strchomp (g_strchug (g_strdup (string)));
  6383. + base = cursor = str;
  6384. + while (1)
  6385. + {
  6386. + c = skip_till_spaces (&cursor);
  6387. + set_str = g_strndup (base, cursor - base);
  6388. + if (set_substring_parse (set_str, &set_var, &set_val, &op, &tmp_error))
  6389. + g_printf ("set var:'%s', val:'%s' op:%i\n", set_var, set_val, op);
  6390. + else
  6391. + {
  6392. + g_printf ("can't parse:'%s'", set_str);
  6393. + ide_vim_parser_error_propagate (error, tmp_error);
  6394. + return NULL;
  6395. + }
  6396. +
  6397. + if (c == '\0')\
  6398. + break;
  6399. +
  6400. + skip_spaces (&cursor);
  6401. + base = cursor;
  6402. + g_free (set_str);
  6403. + }
  6404. +
  6405. + return array;
  6406. +}
  6407. +
  6408. +/**
  6409. + * ide_vim_parser_parse:
  6410. + * @self: (in): a #IdeVimParser instance.
  6411. + * @buffer: (in): The #GtkTextBuffer used when parsing. We need it to calculate the range part.
  6412. + * @cmdline: (in): The command line to parse.
  6413. + * @error: (out): The #IdeVimParserError used for error reporting.
  6414. + *
  6415. + * Parse the cmdline string.
  6416. + *
  6417. + * Returns: (transfer full) (nullable): A #IdeVimParserCommand.
  6418. + */
  6419. +IdeVimParserCommand *
  6420. +ide_vim_parser_parse (IdeVimParser *self,
  6421. + GtkTextBuffer *buffer,
  6422. + const gchar *cmdline,
  6423. + IdeVimParserError **error)
  6424. +{
  6425. + IdeVimParserPrivate *priv = ide_vim_parser_get_instance_private (self);
  6426. + IdeVimParserCommand *new_command = NULL;
  6427. + GList *list = NULL;
  6428. + IdeVimParserContext *context;
  6429. + IdeVimParserError *tmp_error = NULL;
  6430. + IdeVimParserInfo *info = priv->info;
  6431. +
  6432. + g_assert (IDE_IS_VIM_PARSER (self));
  6433. + g_assert (GTK_IS_TEXT_BUFFER (buffer));
  6434. +
  6435. + if (ide_str_empty0 (cmdline))
  6436. + {
  6437. + info->state = IDE_VIM_PARSER_STATE_ERROR;
  6438. + info->error_code = IDE_VIM_PARSER_ERROR_CMDLINE_EMPTY;
  6439. + ide_vim_parser_error_set (error, IDE_VIM_PARSER_ERROR_CMDLINE_EMPTY, NULL);
  6440. + return NULL;
  6441. + }
  6442. +
  6443. + context = ide_vim_parser_create_context (self, buffer, cmdline);
  6444. +
  6445. + /* TODO: allocate str on the stack but no more than 256 bytes
  6446. + * added or more \0 to keep ptr in. not sure it's needed now
  6447. + */
  6448. +
  6449. + ide_vim_state_machine_set_state_by_name (priv->machine, "init");
  6450. + while (ide_vim_state_machine_run_once (priv->machine, context))
  6451. + ;
  6452. +
  6453. + if (context->error == NULL)
  6454. + {
  6455. + list = g_list_reverse (g_list_copy (context->stack->head));
  6456. +
  6457. + if (context->command_found)
  6458. + {
  6459. + new_command = ide_vim_parser_command_parsed_new (context->command_found, list, &tmp_error);
  6460. + info->command_name_found = ide_vim_parser_command_get_name (new_command);
  6461. +
  6462. + /* TODO: to remove after debuging */
  6463. + if (tmp_error != NULL)
  6464. + {
  6465. + /* FIXME: incomplete or error ? */
  6466. + info->state = IDE_VIM_PARSER_STATE_INCOMPLETE;
  6467. + info->error_code = ide_vim_parser_error_get_error_code (tmp_error);
  6468. + ide_vim_parser_error_propagate (error, tmp_error);
  6469. +
  6470. + ide_vim_parser_debug_show_stack (list);
  6471. + }
  6472. + else
  6473. + {
  6474. + info->state = IDE_VIM_PARSER_STATE_COMPLETE;
  6475. + }
  6476. + }
  6477. + else
  6478. + {
  6479. + info->state = IDE_VIM_PARSER_STATE_ERROR;
  6480. + /* TODO: cursor error ? */
  6481. + ide_vim_parser_error_set (error, IDE_VIM_PARSER_ERROR_COMMAND_NOT_FOUND, cmdline);
  6482. + info->error_code = ide_vim_parser_error_get_error_code (*error);
  6483. + }
  6484. + }
  6485. + else
  6486. + {
  6487. + ide_vim_parser_error_propagate (error, context->error);
  6488. + }
  6489. +
  6490. + /* TODO: free the tokens list if there's no command */
  6491. + g_list_free (list);
  6492. + ide_vim_parser_destroy_context (context);
  6493. +
  6494. + ide_vim_parser_debug_show_info (ide_vim_parser_get_info (self));
  6495. +
  6496. + return new_command;
  6497. +}
  6498. +
  6499. +const IdeVimParserInfo *
  6500. +ide_vim_parser_get_info (IdeVimParser *self)
  6501. +{
  6502. + IdeVimParserPrivate *priv;
  6503. +
  6504. + g_assert (IDE_IS_VIM_PARSER (self));
  6505. +
  6506. + priv = ide_vim_parser_get_instance_private (self);
  6507. +
  6508. + return priv->info;
  6509. +}
  6510. +
  6511. +IdeVimParser *
  6512. +ide_vim_parser_new (void)
  6513. +{
  6514. + return g_object_new (IDE_TYPE_VIM_PARSER, NULL);
  6515. +}
  6516. +
  6517. +static void
  6518. +ide_vim_parser_finalize (GObject *object)
  6519. +{
  6520. + IdeVimParser *self = (IdeVimParser *)object;
  6521. + IdeVimParserPrivate *priv = ide_vim_parser_get_instance_private (self);
  6522. +
  6523. + g_clear_object (&priv->machine);
  6524. + g_clear_object (&priv->objects_pool);
  6525. + g_clear_pointer (&priv->stack, g_queue_free);
  6526. + if (priv->info)
  6527. + {
  6528. + g_free ((gpointer)priv->info->command_name_found);
  6529. + g_clear_object (&priv->info);
  6530. + }
  6531. +
  6532. + G_OBJECT_CLASS (ide_vim_parser_parent_class)->finalize (object);
  6533. +}
  6534. +
  6535. +static void
  6536. +ide_vim_parser_get_property (GObject *object,
  6537. + guint prop_id,
  6538. + GValue *value,
  6539. + GParamSpec *pspec)
  6540. +{
  6541. + //IdeVimParser *self = IDE_VIM_PARSER (object);
  6542. +
  6543. + switch (prop_id)
  6544. + {
  6545. + default:
  6546. + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
  6547. + }
  6548. +}
  6549. +
  6550. +static void
  6551. +ide_vim_parser_set_property (GObject *object,
  6552. + guint prop_id,
  6553. + const GValue *value,
  6554. + GParamSpec *pspec)
  6555. +{
  6556. + //IdeVimParser *self = IDE_VIM_PARSER (object);
  6557. +
  6558. + switch (prop_id)
  6559. + {
  6560. + default:
  6561. + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
  6562. + }
  6563. +}
  6564. +
  6565. +static void
  6566. +ide_vim_parser_init (IdeVimParser *self)
  6567. +{
  6568. + IdeVimParserPrivate *priv = ide_vim_parser_get_instance_private (self);
  6569. +
  6570. + priv->stack = g_queue_new ();
  6571. + priv->machine = ide_vim_state_machine_new ();
  6572. + priv->objects_pool = ide_vim_parser_objects_pool_new ();
  6573. + priv->info = g_new0 (IdeVimParserInfo, 1);
  6574. +
  6575. + _ide_vim_parser_fill_command_table (self);
  6576. + _ide_vim_parser_fill_state_table (self);
  6577. +}
  6578. +
  6579. +static void
  6580. +ide_vim_parser_class_init (IdeVimParserClass *klass)
  6581. +{
  6582. + GObjectClass *object_class = G_OBJECT_CLASS (klass);
  6583. +
  6584. + object_class->finalize = ide_vim_parser_finalize;
  6585. + object_class->get_property = ide_vim_parser_get_property;
  6586. + object_class->set_property = ide_vim_parser_set_property;
  6587. +}
  6588. +
  6589. diff --git a/libide/vim/ide-vim-parser.h b/libide/vim/ide-vim-parser.h
  6590. new file mode 100644
  6591. index 0000000..14b9a89
  6592. --- /dev/null
  6593. +++ b/libide/vim/ide-vim-parser.h
  6594. @@ -0,0 +1,125 @@
  6595. +/* ide-vim-parser.h
  6596. + *
  6597. + * Copyright (C) 2015 Sébastien Lafargue <slafargue@gnome.org>
  6598. + *
  6599. + * This program is free software: you can redistribute it and/or modify
  6600. + * it under the terms of the GNU General Public License as published by
  6601. + * the Free Software Foundation, either version 3 of the License, or
  6602. + * (at your option) any later version.
  6603. + *
  6604. + * This program is distributed in the hope that it will be useful,
  6605. + * but WITHOUT ANY WARRANTY; without even the implied warranty of
  6606. + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  6607. + * GNU General Public License for more details.
  6608. + *
  6609. + * You should have received a copy of the GNU General Public License
  6610. + * along with this program. If not, see <http://www.gnu.org/licenses/>.
  6611. + */
  6612. +
  6613. +#ifndef IDE_VIM_PARSER_H
  6614. +#define IDE_VIM_PARSER_H
  6615. +
  6616. +#include <gtk/gtk.h>
  6617. +
  6618. +#include "ide-vim-parser-command.h"
  6619. +#include "ide-vim-parser-commands.h"
  6620. +#include "ide-vim-parser-objects-pool.h"
  6621. +#include "ide-vim-parser-token.h"
  6622. +#include "ide-vim-complete-item.h"
  6623. +
  6624. +G_BEGIN_DECLS
  6625. +
  6626. +#define IDE_TYPE_VIM_PARSER (ide_vim_parser_get_type())
  6627. +
  6628. +G_DECLARE_FINAL_TYPE (IdeVimParser, ide_vim_parser, IDE, VIM_PARSER, GObject)
  6629. +
  6630. +struct _IdeVimParser
  6631. +{
  6632. + GObject parent;
  6633. +};
  6634. +
  6635. +//typedef struct _IdeVimParserCommand IdeVimParserCommand;
  6636. +
  6637. +/********** PARSER **********/
  6638. +
  6639. +typedef enum
  6640. +{
  6641. + IDE_VIM_PARSER_KIND_RANGE,
  6642. + IDE_VIM_PARSER_KIND_COMMAND,
  6643. + IDE_VIM_PARSER_KIND_PATTERN,
  6644. + IDE_VIM_PARSER_KIND_OPTION
  6645. +} IdeVimParserKind;
  6646. +
  6647. +typedef enum
  6648. +{
  6649. + IDE_VIM_PARSER_SET_OP_NONE,
  6650. + IDE_VIM_PARSER_SET_OP_RESET,
  6651. + IDE_VIM_PARSER_SET_OP_SHOW,
  6652. + IDE_VIM_PARSER_SET_OP_INVERT
  6653. +} IdeVimParserSetOp;
  6654. +
  6655. +typedef enum
  6656. +{
  6657. + IDE_VIM_PARSER_STATE_NOT_PARSED,
  6658. + IDE_VIM_PARSER_STATE_COMPLETE,
  6659. + IDE_VIM_PARSER_STATE_INCOMPLETE,
  6660. + IDE_VIM_PARSER_STATE_ERROR
  6661. +} IdeVimParserState;
  6662. +
  6663. +typedef struct
  6664. +{
  6665. + IdeVimParserState state;
  6666. + IdeVimParserErrorCode error_code;
  6667. + IdeVimParserTokenKind last_token_kind;
  6668. + IdeVimParserTokenKind last_good_token_kind;
  6669. + gint cmdline_error_offset;
  6670. + const gchar *last_token_content;
  6671. + const gchar *command_name_found;
  6672. +} IdeVimParserInfo;
  6673. +
  6674. +typedef struct
  6675. +{
  6676. + IdeVimParser *parser;
  6677. + GtkTextBuffer *buffer;
  6678. + IdeVimParserObjectsPool *objects_pool;
  6679. + GQueue *stack;
  6680. + IdeVimParserInfo *info;
  6681. + gint start_line;
  6682. + gint end_line;
  6683. + gint insert_line;
  6684. + gint select_line;
  6685. + gint range_start_line;
  6686. + gint range_end_line;
  6687. + gchar *cmdline;
  6688. + const gchar *position;
  6689. + IdeVimParserToken *token;
  6690. + gint token_size;
  6691. + IdeVimParserCommand *command_found;
  6692. + gint command_arg_list_pos;
  6693. + IdeVimParserError *error;
  6694. + gboolean previous_token_is_separator : 1;
  6695. + gboolean status : 1;
  6696. +} IdeVimParserContext;
  6697. +
  6698. +gint ide_vim_parser_add_command (IdeVimParser *self,
  6699. + const gchar *name,
  6700. + const gchar *shortname,
  6701. + IdeVimParserCommandFunc func,
  6702. + ...) G_GNUC_NULL_TERMINATED;
  6703. +GPtrArray *ide_vim_parser_complete (IdeVimParser *self,
  6704. + GtkTextBuffer *buffer,
  6705. + const gchar *match_name);
  6706. +IdeVimParser *ide_vim_parser_new (void);
  6707. +IdeVimParserCommand *ide_vim_parser_parse (IdeVimParser *self,
  6708. + GtkTextBuffer *buffer,
  6709. + const gchar *cmdline,
  6710. + IdeVimParserError **error);
  6711. +GArray *ide_vim_parser_parse_settable (IdeVimParser *self,
  6712. + const gchar *string,
  6713. + IdeVimParserError **error);
  6714. +const IdeVimParserInfo *ide_vim_parser_get_info (IdeVimParser *self);
  6715. +
  6716. +
  6717. +G_END_DECLS
  6718. +
  6719. +#endif /* IDE_VIM_PARSER_H */
  6720. diff --git a/libide/vim/ide-vim-state-machine.c b/libide/vim/ide-vim-state-machine.c
  6721. new file mode 100644
  6722. index 0000000..4e2341f
  6723. --- /dev/null
  6724. +++ b/libide/vim/ide-vim-state-machine.c
  6725. @@ -0,0 +1,500 @@
  6726. +/* ide-vim-state-machine.c
  6727. + *
  6728. + * Copyright (C) 2015 Sébastien Lafargue <slafargue@gnome.org>
  6729. + *
  6730. + * This program is free software: you can redistribute it and/or modify
  6731. + * it under the terms of the GNU General Public License as published by
  6732. + * the Free Software Foundation, either version 3 of the License, or
  6733. + * (at your option) any later version.
  6734. + *
  6735. + * This program is distributed in the hope that it will be useful,
  6736. + * but WITHOUT ANY WARRANTY; without even the implied warranty of
  6737. + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  6738. + * GNU General Public License for more details.
  6739. + *
  6740. + * You should have received a copy of the GNU General Public License
  6741. + * along with this program. If not, see <http://www.gnu.org/licenses/>.
  6742. + */
  6743. +
  6744. +#include <string.h>
  6745. +#include <errno.h>
  6746. +#include <glib/gi18n.h>
  6747. +#include <glib/gprintf.h>
  6748. +
  6749. +#include "ide-debug.h"
  6750. +#include "ide-macros.h"
  6751. +
  6752. +#include <glib/gi18n.h>
  6753. +
  6754. +#include "ide-vim-state-machine.h"
  6755. +#include "ide-vim-state-result.h"
  6756. +
  6757. +typedef struct
  6758. +{
  6759. + IdeVimStateId state_id;
  6760. + IdeVimStateId previous_id;
  6761. + GHashTable *state_table;
  6762. +} IdeVimStateMachinePrivate;
  6763. +
  6764. +G_DEFINE_TYPE_WITH_PRIVATE (IdeVimStateMachine, ide_vim_state_machine, G_TYPE_OBJECT)
  6765. +
  6766. +enum {
  6767. + PROP_0,
  6768. + PROP_STATE_NAME,
  6769. + PROP_STATE_ID,
  6770. + LAST_PROP
  6771. +};
  6772. +
  6773. +static inline IdeVimStateId
  6774. +try_state_id (const gchar *name)
  6775. +{
  6776. + g_autofree const gchar *fullname = NULL;
  6777. +
  6778. + g_return_val_if_fail (!ide_str_empty0 (name), 0);
  6779. +
  6780. + fullname = g_strconcat (STATE_NAME_PREFIX, name, NULL);
  6781. +
  6782. + return g_quark_try_string (fullname);
  6783. +}
  6784. +
  6785. +static GParamSpec *gParamSpecs [LAST_PROP];
  6786. +
  6787. +/**
  6788. + * ide_vim_state_machine_add_state:
  6789. + * @self: (in): #IdeVimStateMachine instance.
  6790. + * @name: (in): the name of the state
  6791. + * @state_enter_func: a pointer to a StateEnterFunc for the enter phase.
  6792. + * @state_leave_func: a pointer to a StateLeaveFunc for the leave phase.
  6793. + * @state_transition_func: a pointer to a StateCondFunc for the transition phase.
  6794. + * @state_action_func: a pointer to a StateActionFunc for the action phase.
  6795. + *
  6796. + * Return the state IdeVimStateId.
  6797. + *
  6798. + * Returns: the corresponding IdeVimStateId or 0 if
  6799. + * the state already exist.
  6800. + */
  6801. +IdeVimStateId
  6802. +ide_vim_state_machine_add_state (IdeVimStateMachine *self,
  6803. + const gchar *name,
  6804. + StateEnterFunc enter_func,
  6805. + StateLeaveFunc leave_func,
  6806. + StateTransitionFunc transition_func,
  6807. + StateActionFunc action_func)
  6808. +{
  6809. + IdeVimStateMachinePrivate *priv = ide_vim_state_machine_get_instance_private (self);
  6810. + IdeVimStateId id;
  6811. + IdeVimState *state;
  6812. +
  6813. + g_return_val_if_fail (IDE_IS_VIM_STATE_MACHINE (self), 0);
  6814. + g_return_val_if_fail (!ide_str_empty0 (name), 0);
  6815. +
  6816. +
  6817. + id = try_state_id (name);
  6818. + if (id && (state = (IdeVimState *)g_hash_table_lookup (priv->state_table, GINT_TO_POINTER (id))) != NULL)
  6819. + {
  6820. + ide_vim_state_change_funcs (state, enter_func, leave_func, transition_func, action_func);
  6821. + }
  6822. + else
  6823. + {
  6824. + state = ide_vim_state_new (name, enter_func, leave_func, transition_func, action_func);
  6825. + id = ide_vim_state_get_id (state);
  6826. + g_hash_table_insert (priv->state_table, GINT_TO_POINTER (id), state);
  6827. + }
  6828. +
  6829. + return id;
  6830. +}
  6831. +
  6832. +/**
  6833. + * ide_vim_state_machine_remove_state_by_name:
  6834. + * @self: (in): #IdeVimStateMachine instance.
  6835. + * @name: (in): the name of the state
  6836. + *
  6837. + * Return the succes status of the operation
  6838. + *
  6839. + * Returns: TRUE if removed, FALSE overwise.
  6840. + */
  6841. +gboolean
  6842. +ide_vim_state_machine_remove_state_by_name (IdeVimStateMachine *self,
  6843. + const gchar *name)
  6844. +{
  6845. + IdeVimStateMachinePrivate *priv = ide_vim_state_machine_get_instance_private (self);
  6846. + IdeVimStateId id;
  6847. +
  6848. + g_return_val_if_fail (IDE_IS_VIM_STATE_MACHINE (self), FALSE);
  6849. + g_return_val_if_fail (!ide_str_empty0 (name), FALSE);
  6850. +
  6851. + id = try_state_id (name);
  6852. + if (id)
  6853. + {
  6854. + if (g_hash_table_remove (priv->state_table, GINT_TO_POINTER (id)))
  6855. + {
  6856. + if (priv->state_id == id)
  6857. + {
  6858. + priv->state_id = 0;
  6859. + priv->previous_id = 0;
  6860. + }
  6861. +
  6862. + return TRUE;
  6863. + }
  6864. + }
  6865. +
  6866. + return FALSE;
  6867. +}
  6868. +
  6869. +/**
  6870. + * ide_vim_state_machine_set_state:
  6871. + * @self: (in): #IdeVimStateMachine instance.
  6872. + * @id: (in): An existing IdeVimStateId.
  6873. + *
  6874. + * Return the succes status
  6875. + *
  6876. + * Returns: TRUE if succes, FALSE overwise.
  6877. + */
  6878. +gboolean
  6879. +ide_vim_state_machine_set_state (IdeVimStateMachine *self,
  6880. + IdeVimStateId id)
  6881. +{
  6882. + IdeVimStateMachinePrivate *priv = ide_vim_state_machine_get_instance_private (self);
  6883. +
  6884. + g_return_val_if_fail (IDE_IS_VIM_STATE_MACHINE (self), FALSE);
  6885. + g_return_val_if_fail (id != 0, FALSE);
  6886. +
  6887. + if (g_hash_table_lookup (priv->state_table, GINT_TO_POINTER (id)))
  6888. + {
  6889. + priv->previous_id = priv->state_id;
  6890. + priv->state_id = id;
  6891. + return TRUE;
  6892. + }
  6893. +
  6894. + g_warning (_("The state id:%i is not registered on this state machine\n"), id);
  6895. + return FALSE;
  6896. +}
  6897. +
  6898. +/**
  6899. + * ide_vim_state_machine_set_state_by_name:
  6900. + * @self: (in): #IdeVimStateMachine instance.
  6901. + * @name: (in): An existing #IdeVimState name
  6902. + *
  6903. + * Return the corresponding IdeVimStateId.
  6904. + *
  6905. + * Returns: the IdeVimStateId or 0 if the state doesn't exist.
  6906. + */
  6907. +IdeVimStateId
  6908. +ide_vim_state_machine_set_state_by_name (IdeVimStateMachine *self,
  6909. + const gchar *name)
  6910. +{
  6911. + IdeVimStateMachinePrivate *priv = ide_vim_state_machine_get_instance_private (self);
  6912. + IdeVimStateId id;
  6913. +
  6914. + g_return_val_if_fail (IDE_IS_VIM_STATE_MACHINE (self), 0);
  6915. + g_return_val_if_fail (!ide_str_empty0 (name), 0);
  6916. +
  6917. + id = try_state_id (name);
  6918. + if (!id)
  6919. + {
  6920. + g_warning (_("'%s' state doesn't exist\n"), name);
  6921. + return 0;
  6922. + }
  6923. +
  6924. + if (g_hash_table_lookup (priv->state_table, GINT_TO_POINTER (id)) == NULL)
  6925. + {
  6926. + g_warning (_("'%s' state is not registered on this state machine\n"), name);
  6927. + return 0;
  6928. + }
  6929. +
  6930. + priv->previous_id = priv->state_id;
  6931. + priv->state_id = id;
  6932. + return id;
  6933. +}
  6934. +
  6935. +/**
  6936. + * ide_vim_state_machine_get_state_name:
  6937. + * @self: (in): #IdeVimStateMachine instance.
  6938. + *
  6939. + * Return the current state's name.
  6940. + *
  6941. + * Returns: (tranfert none): the current state's name or an empty
  6942. + * string if the state is not set.
  6943. + */
  6944. +const gchar *
  6945. +ide_vim_state_machine_get_state_name (IdeVimStateMachine *self)
  6946. +{
  6947. + IdeVimStateMachinePrivate *priv = ide_vim_state_machine_get_instance_private (self);
  6948. + IdeVimStateId id;
  6949. +
  6950. + g_return_val_if_fail (IDE_IS_VIM_STATE_MACHINE (self), NULL);
  6951. +
  6952. + id = priv->state_id;
  6953. +
  6954. + return (id) ? g_quark_to_string (id) : NULL;
  6955. +}
  6956. +
  6957. +/**
  6958. + * ide_vim_state_machine_get_state_id:
  6959. + * @self: (in): #IdeVimStateMachine instance.
  6960. + *
  6961. + * Return the current state IdeVimStateId.
  6962. + *
  6963. + * Returns: a #IdeVimStateId.
  6964. + */
  6965. +IdeVimStateId
  6966. +ide_vim_state_machine_get_state_id (IdeVimStateMachine *self)
  6967. +{
  6968. + IdeVimStateMachinePrivate *priv = ide_vim_state_machine_get_instance_private (self);
  6969. +
  6970. + g_return_val_if_fail (IDE_IS_VIM_STATE_MACHINE (self), 0);
  6971. +
  6972. + return priv->state_id;
  6973. +}
  6974. +
  6975. +/**
  6976. + * ide_vim_state_machine_get_state_from_id:
  6977. + * @self: (in): #IdeVimStateMachine instance.
  6978. + * @id: (in): a registered #IdeVimStateId for this #IdeVimStateMachine.
  6979. + *
  6980. + * Return the corresponding #IdeVimState.
  6981. + *
  6982. + * Returns: a #IdeVimState.
  6983. + */
  6984. +IdeVimState *
  6985. +ide_vim_state_machine_get_state_from_id (IdeVimStateMachine *self,
  6986. + IdeVimStateId id)
  6987. +{
  6988. + IdeVimStateMachinePrivate *priv = ide_vim_state_machine_get_instance_private (self);
  6989. + const gchar *name;
  6990. + IdeVimState *state;
  6991. +
  6992. + g_return_val_if_fail (IDE_IS_VIM_STATE_MACHINE (self), NULL);
  6993. + g_return_val_if_fail (id > 0, NULL);
  6994. +
  6995. + state = (IdeVimState *)g_hash_table_lookup (priv->state_table, GINT_TO_POINTER (id));
  6996. + if (state == NULL)
  6997. + {
  6998. + name = g_quark_to_string ((GQuark)id);
  6999. + if (name == NULL)
  7000. + g_warning (_("State id:%i doesn't exist\n"), id);
  7001. + else if (!g_str_has_prefix (name, STATE_NAME_PREFIX))
  7002. + g_warning (_("'%s' state doesn't exist\n"), name);
  7003. + else
  7004. + g_warning (_("'%s' state is not registered on this state machine\n"), name);
  7005. +
  7006. + return NULL;
  7007. + }
  7008. +
  7009. + return state;
  7010. +}
  7011. +
  7012. +/**
  7013. + * ide_vim_state_machine_get_state_from_name:
  7014. + * @self: (in): #IdeVimStateMachine instance.
  7015. + * @name: (in): a registered state name for this #IdeVimStateMachine.
  7016. + *
  7017. + * Return the corresponding #IdeVimState.
  7018. + *
  7019. + * Returns: a #IdeVimState.
  7020. + */
  7021. +IdeVimState *
  7022. +ide_vim_state_machine_get_state_from_name (IdeVimStateMachine *self,
  7023. + const gchar *name)
  7024. +{
  7025. + IdeVimStateMachinePrivate *priv = ide_vim_state_machine_get_instance_private (self);
  7026. + IdeVimStateId id;
  7027. + IdeVimState *state = NULL;
  7028. +
  7029. + g_return_val_if_fail (IDE_IS_VIM_STATE_MACHINE (self), NULL);
  7030. + g_return_val_if_fail (!ide_str_empty0 (name), NULL);
  7031. +
  7032. + id = try_state_id (name);
  7033. + if (!id)
  7034. + {
  7035. + g_warning (_("'%s' state doesn't exist\n"), name);
  7036. + return NULL;
  7037. + }
  7038. +
  7039. + state = (IdeVimState *)g_hash_table_lookup (priv->state_table, GINT_TO_POINTER (id));
  7040. + if (state == NULL)
  7041. + g_warning (_("'%s' state is not registered on this state machine\n"), name);
  7042. +
  7043. + return state;
  7044. +}
  7045. +
  7046. +/**
  7047. + * ide_vim_state_machine_get_id_from_name:
  7048. + * @self: (in): #IdeVimStateMachine instance.
  7049. + * @name: (in): a registered state name for this #IdeVimStateMachine.
  7050. + *
  7051. + * Return the corresponding #IdeVimStateId.
  7052. + *
  7053. + * Returns: a #IdeVimStateId.
  7054. + */
  7055. +IdeVimStateId
  7056. +ide_vim_state_machine_get_id_from_name (IdeVimStateMachine *self,
  7057. + const gchar *name)
  7058. +{
  7059. + IdeVimStateMachinePrivate *priv = ide_vim_state_machine_get_instance_private (self);
  7060. + IdeVimStateId id = 0;
  7061. +
  7062. + g_return_val_if_fail (IDE_IS_VIM_STATE_MACHINE (self), 0);
  7063. + g_return_val_if_fail (!ide_str_empty0 (name), 0);
  7064. +
  7065. + id = try_state_id (name);
  7066. + if (!id)
  7067. + {
  7068. + g_warning (_("'%s' state doesn't exist\n"), name);
  7069. + return 0;
  7070. + }
  7071. +
  7072. + if (g_hash_table_lookup (priv->state_table, GINT_TO_POINTER (id)) == NULL)
  7073. + {
  7074. + g_warning (_("'%s' state is not registered on this state machine\n"), name);
  7075. + return 0;
  7076. + }
  7077. +
  7078. + return id;
  7079. +}
  7080. +
  7081. +gboolean
  7082. +ide_vim_state_machine_run_once (IdeVimStateMachine *self,
  7083. + gpointer data)
  7084. +{
  7085. + IdeVimStateMachinePrivate *priv = ide_vim_state_machine_get_instance_private (self);
  7086. + IdeVimState *state;
  7087. + IdeVimStateId current_id;
  7088. + IdeVimStateId transition_id = 0;
  7089. +
  7090. + g_return_val_if_fail (IDE_IS_VIM_STATE_MACHINE (self), FALSE);
  7091. +
  7092. + current_id = priv->state_id;
  7093. + if (!current_id)
  7094. + {
  7095. + g_warning (_("You need to start from an existing state"));
  7096. + return FALSE;
  7097. + }
  7098. +
  7099. + /* TODO: even when transitioning to the same state, do enter-leave */
  7100. + state = ide_vim_state_machine_get_state_from_id (self, current_id);
  7101. + if (priv->previous_id != current_id && state->enter_func)
  7102. + state->enter_func (self, data);
  7103. +
  7104. + if (state->transition_func)
  7105. + transition_id = state->transition_func (self, data);
  7106. +
  7107. + /* TODO: check transition_id validity */
  7108. + /* TODO: need return gboolean ? */
  7109. + if (state->action_func)
  7110. + state->action_func (self, data);
  7111. +
  7112. + if (current_id != transition_id && state->leave_func)
  7113. + state->leave_func (self, data);
  7114. +
  7115. + if (transition_id)
  7116. + ide_vim_state_machine_set_state (self, transition_id);
  7117. +
  7118. + return (transition_id != 0);
  7119. +}
  7120. +
  7121. +IdeVimStateMachine *
  7122. +ide_vim_state_machine_new (void)
  7123. +{
  7124. + return g_object_new (IDE_TYPE_VIM_STATE_MACHINE, NULL);
  7125. +}
  7126. +
  7127. +static void
  7128. +ide_vim_state_machine_finalize (GObject *object)
  7129. +{
  7130. + IdeVimStateMachine *self = IDE_VIM_STATE_MACHINE (object);
  7131. + IdeVimStateMachinePrivate *priv = ide_vim_state_machine_get_instance_private (self);
  7132. +
  7133. + g_clear_pointer (&priv->state_table, g_hash_table_unref);
  7134. +
  7135. + G_OBJECT_CLASS (ide_vim_state_machine_parent_class)->finalize (object);
  7136. +}
  7137. +
  7138. +static void
  7139. +ide_vim_state_machine_get_property (GObject *object,
  7140. + guint prop_id,
  7141. + GValue *value,
  7142. + GParamSpec *pspec)
  7143. +{
  7144. + IdeVimStateMachine *self = IDE_VIM_STATE_MACHINE (object);
  7145. +
  7146. + switch (prop_id)
  7147. + {
  7148. + case PROP_STATE_NAME:
  7149. + g_value_set_string (value, ide_vim_state_machine_get_state_name (self));
  7150. + break;
  7151. +
  7152. + case PROP_STATE_ID:
  7153. + g_value_set_uint (value, ide_vim_state_machine_get_state_id (self));
  7154. + break;
  7155. +
  7156. + default:
  7157. + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
  7158. + }
  7159. +}
  7160. +
  7161. +static void
  7162. +ide_vim_state_machine_set_property (GObject *object,
  7163. + guint prop_id,
  7164. + const GValue *value,
  7165. + GParamSpec *pspec)
  7166. +{
  7167. + IdeVimStateMachine *self = IDE_VIM_STATE_MACHINE (object);
  7168. +
  7169. + switch (prop_id)
  7170. + {
  7171. + case PROP_STATE_NAME:
  7172. + ide_vim_state_machine_set_state_by_name (self, g_value_get_string (value));
  7173. + break;
  7174. +
  7175. + default:
  7176. + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
  7177. + }
  7178. +}
  7179. +
  7180. +static void
  7181. +ide_vim_state_machine_init (IdeVimStateMachine *self)
  7182. +{
  7183. + IdeVimStateMachinePrivate *priv = ide_vim_state_machine_get_instance_private (self);
  7184. +
  7185. + priv->state_table = g_hash_table_new_full (g_direct_hash, g_direct_equal,
  7186. + NULL, (GDestroyNotify)ide_vim_state_unref);
  7187. +}
  7188. +
  7189. +static void
  7190. +ide_vim_state_machine_class_init (IdeVimStateMachineClass *klass)
  7191. +{
  7192. + GObjectClass *object_class = G_OBJECT_CLASS (klass);
  7193. +
  7194. + object_class->finalize = ide_vim_state_machine_finalize;
  7195. + object_class->get_property = ide_vim_state_machine_get_property;
  7196. + object_class->set_property = ide_vim_state_machine_set_property;
  7197. +
  7198. + /**
  7199. + * IdeVimStateMachine:state-name:
  7200. + *
  7201. + * A string holding the name of current state.
  7202. + */
  7203. + gParamSpecs[PROP_STATE_NAME] =
  7204. + g_param_spec_string ("state-name",
  7205. + _("current state name"),
  7206. + _("The name of the current state."),
  7207. + NULL,
  7208. + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
  7209. +
  7210. + /**
  7211. + * IdeVimStateMachine:state-id:
  7212. + *
  7213. + * the IdeVimStateId of current state.
  7214. + */
  7215. + gParamSpecs[PROP_STATE_ID] =
  7216. + g_param_spec_uint64 ("state-id",
  7217. + _("current state id"),
  7218. + _("The id of the current state."),
  7219. + 0,
  7220. + G_MAXUINT32,
  7221. + 0,
  7222. + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
  7223. +
  7224. + g_object_class_install_properties (object_class, LAST_PROP, gParamSpecs);
  7225. +}
  7226. diff --git a/libide/vim/ide-vim-state-machine.h b/libide/vim/ide-vim-state-machine.h
  7227. new file mode 100644
  7228. index 0000000..44b36b7
  7229. --- /dev/null
  7230. +++ b/libide/vim/ide-vim-state-machine.h
  7231. @@ -0,0 +1,63 @@
  7232. +/* ide-vim-state-machine.h
  7233. + *
  7234. + * Copyright (C) 2015 Sébastien Lafargue <slafargue@gnome.org>
  7235. + *
  7236. + * This program is free software: you can redistribute it and/or modify
  7237. + * it under the terms of the GNU General Public License as published by
  7238. + * the Free Software Foundation, either version 3 of the License, or
  7239. + * (at your option) any later version.
  7240. + *
  7241. + * This program is distributed in the hope that it will be useful,
  7242. + * but WITHOUT ANY WARRANTY; without even the implied warranty of
  7243. + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  7244. + * GNU General Public License for more details.
  7245. + *
  7246. + * You should have received a copy of the GNU General Public License
  7247. + * along with this program. If not, see <http://www.gnu.org/licenses/>.
  7248. + */
  7249. +
  7250. +#ifndef IDE_VIM_STATE_MACHINE_H
  7251. +#define IDE_VIM_STATE_MACHINE_H
  7252. +
  7253. +#include <glib.h>
  7254. +
  7255. +#include "ide-vim-state.h"
  7256. +
  7257. +G_BEGIN_DECLS
  7258. +
  7259. +#define IDE_TYPE_VIM_STATE_MACHINE (ide_vim_state_machine_get_type())
  7260. +
  7261. +G_DECLARE_FINAL_TYPE (IdeVimStateMachine, ide_vim_state_machine, IDE, VIM_STATE_MACHINE, GObject)
  7262. +
  7263. +struct _IdeVimStateMachine
  7264. +{
  7265. + GObject parent;
  7266. +};
  7267. +
  7268. +IdeVimStateId ide_vim_state_machine_add_state (IdeVimStateMachine *self,
  7269. + const gchar *name,
  7270. + StateEnterFunc enter_func,
  7271. + StateLeaveFunc leave_func,
  7272. + StateTransitionFunc transition_func,
  7273. + StateActionFunc action_func);
  7274. +IdeVimStateId ide_vim_state_machine_get_id_from_name (IdeVimStateMachine *self,
  7275. + const gchar *name);
  7276. +IdeVimState *ide_vim_state_machine_get_state_from_id (IdeVimStateMachine *self,
  7277. + IdeVimStateId id);
  7278. +IdeVimState *ide_vim_state_machine_get_state_from_name (IdeVimStateMachine *self,
  7279. + const gchar *name);
  7280. +const gchar *ide_vim_state_machine_get_state_name (IdeVimStateMachine *self);
  7281. +IdeVimStateId ide_vim_state_machine_get_state_id (IdeVimStateMachine *self);
  7282. +IdeVimStateMachine *ide_vim_state_machine_new (void);
  7283. +gboolean ide_vim_state_machine_remove_state_by_name (IdeVimStateMachine *self,
  7284. + const gchar *name);
  7285. +gboolean ide_vim_state_machine_run_once (IdeVimStateMachine *self,
  7286. + gpointer data);
  7287. +gboolean ide_vim_state_machine_set_state (IdeVimStateMachine *self,
  7288. + IdeVimStateId id);
  7289. +IdeVimStateId ide_vim_state_machine_set_state_by_name (IdeVimStateMachine *self,
  7290. + const gchar *name);
  7291. +
  7292. +G_END_DECLS
  7293. +
  7294. +#endif /* IDE_VIM_STATE_MACHINE_H */
  7295. diff --git a/libide/vim/ide-vim-state-private.h b/libide/vim/ide-vim-state-private.h
  7296. new file mode 100644
  7297. index 0000000..8f2512d
  7298. --- /dev/null
  7299. +++ b/libide/vim/ide-vim-state-private.h
  7300. @@ -0,0 +1,37 @@
  7301. +/* ide-vim-state-private.h
  7302. + *
  7303. + * Copyright (C) 2015 Sébastien Lafargue <slafargue@gnome.org>
  7304. + *
  7305. + * This program is free software: you can redistribute it and/or modify
  7306. + * it under the terms of the GNU General Public License as published by
  7307. + * the Free Software Foundation, either version 3 of the License, or
  7308. + * (at your option) any later version.
  7309. + *
  7310. + * This program is distributed in the hope that it will be useful,
  7311. + * but WITHOUT ANY WARRANTY; without even the implied warranty of
  7312. + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  7313. + * GNU General Public License for more details.
  7314. + *
  7315. + * You should have received a copy of the GNU General Public License
  7316. + * along with this program. If not, see <http://www.gnu.org/licenses/>.
  7317. + */
  7318. +
  7319. +#ifndef IDE_VIM_STATE_PRIVATE_H
  7320. +#define IDE_VIM_STATE_PRIVATE_H
  7321. +
  7322. +#include <glib-object.h>
  7323. +
  7324. +G_BEGIN_DECLS
  7325. +
  7326. +struct _IdeVimState
  7327. +{
  7328. + volatile gint ref_count;
  7329. +
  7330. + IdeVimStateId id;
  7331. + StateEnterFunc enter_func;
  7332. + StateLeaveFunc leave_func;
  7333. + StateTransitionFunc transition_func;
  7334. + StateActionFunc action_func;
  7335. +};
  7336. +
  7337. +endif /* IDE_VIM_STATE_PRIVATE_H */
  7338. diff --git a/libide/vim/ide-vim-state-result.c b/libide/vim/ide-vim-state-result.c
  7339. new file mode 100644
  7340. index 0000000..51ff639
  7341. --- /dev/null
  7342. +++ b/libide/vim/ide-vim-state-result.c
  7343. @@ -0,0 +1,73 @@
  7344. +/* ide-vim-state-result.c
  7345. + *
  7346. + * Copyright (C) 2015 Sébastien Lafargue <slafargue@gnome.org>
  7347. + *
  7348. + * This program is free software: you can redistribute it and/or modify
  7349. + * it under the terms of the GNU General Public License as published by
  7350. + * the Free Software Foundation, either version 3 of the License, or
  7351. + * (at your option) any later version.
  7352. + *
  7353. + * This program is distributed in the hope that it will be useful,
  7354. + * but WITHOUT ANY WARRANTY; without even the implied warranty of
  7355. + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  7356. + * GNU General Public License for more details.
  7357. + *
  7358. + * You should have received a copy of the GNU General Public License
  7359. + * along with this program. If not, see <http://www.gnu.org/licenses/>.
  7360. + */
  7361. +#include <string.h>
  7362. +#include <errno.h>
  7363. +#include <glib/gi18n.h>
  7364. +#include <glib/gprintf.h>
  7365. +
  7366. +#include "ide-debug.h"
  7367. +#include "ide-macros.h"
  7368. +
  7369. +#include <glib/gi18n.h>
  7370. +
  7371. +#include "ide-vim-state-result.h"
  7372. +
  7373. +G_DEFINE_BOXED_TYPE (IdeVimStateResult, ide_vim_state_result, ide_vim_state_result_ref, ide_vim_state_result_unref)
  7374. +
  7375. +struct _IdeVimStateResult
  7376. +{
  7377. + volatile gint ref_count;
  7378. +};
  7379. +
  7380. +static void
  7381. +ide_vim_state_result_finalize (IdeVimStateResult *self)
  7382. +{
  7383. + g_free (self);
  7384. +}
  7385. +
  7386. +IdeVimStateResult *
  7387. +ide_vim_state_result_ref (IdeVimStateResult *self)
  7388. +{
  7389. + g_return_val_if_fail (self, NULL);
  7390. + g_return_val_if_fail (self->ref_count > 0, NULL);
  7391. +
  7392. + g_atomic_int_inc (&self->ref_count);
  7393. +
  7394. + return self;
  7395. +}
  7396. +
  7397. +void
  7398. +ide_vim_state_result_unref (IdeVimStateResult *self)
  7399. +{
  7400. + g_return_if_fail (self);
  7401. + g_return_if_fail (self->ref_count > 0);
  7402. +
  7403. + if (g_atomic_int_dec_and_test (&self->ref_count))
  7404. + ide_vim_state_result_finalize (self);
  7405. +}
  7406. +
  7407. +IdeVimStateResult *
  7408. +ide_vim_state_result_new (void)
  7409. +{
  7410. + IdeVimStateResult *self;
  7411. +
  7412. + self = g_new0 (IdeVimStateResult, 1);
  7413. + self->ref_count = 1;
  7414. +
  7415. + return self;
  7416. +}
  7417. diff --git a/libide/vim/ide-vim-state-result.h b/libide/vim/ide-vim-state-result.h
  7418. new file mode 100644
  7419. index 0000000..3fbbb93
  7420. --- /dev/null
  7421. +++ b/libide/vim/ide-vim-state-result.h
  7422. @@ -0,0 +1,39 @@
  7423. +/* ide-vim-state-result.h
  7424. + *
  7425. + * Copyright (C) 2015 Sébastien Lafargue <slafargue@gnome.org>
  7426. + *
  7427. + * This program is free software: you can redistribute it and/or modify
  7428. + * it under the terms of the GNU General Public License as published by
  7429. + * the Free Software Foundation, either version 3 of the License, or
  7430. + * (at your option) any later version.
  7431. + *
  7432. + * This program is distributed in the hope that it will be useful,
  7433. + * but WITHOUT ANY WARRANTY; without even the implied warranty of
  7434. + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  7435. + * GNU General Public License for more details.
  7436. + *
  7437. + * You should have received a copy of the GNU General Public License
  7438. + * along with this program. If not, see <http://www.gnu.org/licenses/>.
  7439. + */
  7440. +
  7441. +#ifndef IDE_VIM_STATE_RESULT_H
  7442. +#define IDE_VIM_STATE_RESULT_H
  7443. +
  7444. +#include <glib-object.h>
  7445. +
  7446. +#include "ide-vim-state.h"
  7447. +
  7448. +G_BEGIN_DECLS
  7449. +
  7450. +#define IDE_TYPE_VIM_STATE_RESULT (ide_vim_state_result_get_type())
  7451. +
  7452. +typedef struct _IdeVimStateResult IdeVimStateResult;
  7453. +
  7454. +IdeVimStateResult *ide_vim_state_result_new (void);
  7455. +
  7456. +IdeVimStateResult *ide_vim_state_result_ref (IdeVimStateResult *self);
  7457. +void ide_vim_state_result_unref (IdeVimStateResult *self);
  7458. +
  7459. +G_END_DECLS
  7460. +
  7461. +#endif /* IDE_VIM_STATE_RESULT_H */
  7462. diff --git a/libide/vim/ide-vim-state.c b/libide/vim/ide-vim-state.c
  7463. new file mode 100644
  7464. index 0000000..caa3b5f
  7465. --- /dev/null
  7466. +++ b/libide/vim/ide-vim-state.c
  7467. @@ -0,0 +1,162 @@
  7468. +/* ide-vim-state.c
  7469. + *
  7470. + * Copyright (C) 2015 Sébastien Lafargue <slafargue@gnome.org>
  7471. + *
  7472. + * This program is free software: you can redistribute it and/or modify
  7473. + * it under the terms of the GNU General Public License as published by
  7474. + * the Free Software Foundation, either version 3 of the License, or
  7475. + * (at your option) any later version.
  7476. + *
  7477. + * This program is distributed in the hope that it will be useful,
  7478. + * but WITHOUT ANY WARRANTY; without even the implied warranty of
  7479. + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  7480. + * GNU General Public License for more details.
  7481. + *
  7482. + * You should have received a copy of the GNU General Public License
  7483. + * along with this program. If not, see <http://www.gnu.org/licenses/>.
  7484. + */
  7485. +#include <string.h>
  7486. +#include <errno.h>
  7487. +#include <glib/gi18n.h>
  7488. +#include <glib/gprintf.h>
  7489. +
  7490. +#include "ide-debug.h"
  7491. +#include "ide-macros.h"
  7492. +
  7493. +#include "ide-vim-state.h"
  7494. +
  7495. +G_DEFINE_BOXED_TYPE (IdeVimState, ide_vim_state, ide_vim_state_ref, ide_vim_state_unref)
  7496. +
  7497. +/**
  7498. + * ide_vim_state_change_funcs:
  7499. + * @self: (in): #IdeVimState instance.
  7500. + * @state_enter_func: a pointer to a StateEnterFunc for the enter phase.
  7501. + * @state_leave_func: a pointer to a StateLeaveFunc for the leave phase.
  7502. + * @state_transition_func: a pointer to a StateTransitionFunc for the transition phase.
  7503. + * @state_action_func: a pointer to a StateActionFunc for the action phase.
  7504. + *
  7505. + */
  7506. +void
  7507. +ide_vim_state_change_funcs (IdeVimState *self,
  7508. + StateEnterFunc enter_func,
  7509. + StateLeaveFunc leave_func,
  7510. + StateTransitionFunc transition_func,
  7511. + StateActionFunc action_func)
  7512. +{
  7513. + g_return_if_fail (self);
  7514. +
  7515. + self->enter_func = enter_func;
  7516. + self->leave_func = leave_func;
  7517. + self->transition_func = transition_func;
  7518. + self->action_func = action_func;
  7519. +}
  7520. +
  7521. +/**
  7522. + * ide_vim_state_get_name:
  7523. + * @self: (in): #IdeVimState instance.
  7524. + *
  7525. + * Return the state's name.
  7526. + *
  7527. + * Returns: (tranfert none): the state's name.
  7528. + */
  7529. +const gchar *
  7530. +ide_vim_state_get_name (IdeVimState *self)
  7531. +{
  7532. + g_return_val_if_fail (self, NULL);
  7533. +
  7534. + return self->name;
  7535. +}
  7536. +
  7537. +/**
  7538. + * ide_vim_state_get_id:
  7539. + * @self: (in): #IdeVimState instance.
  7540. + *
  7541. + * Return the #IdeVimStateId of the state.
  7542. + *
  7543. + * Returns: the #IdeVimStateId of the state.
  7544. + */
  7545. +IdeVimStateId
  7546. +ide_vim_state_get_id (IdeVimState *self)
  7547. +{
  7548. + g_return_val_if_fail (self, 0);
  7549. +
  7550. + return self->id;
  7551. +}
  7552. +
  7553. +static void
  7554. +ide_vim_state_finalize (IdeVimState *self)
  7555. +{
  7556. + g_clear_pointer (&self->name, g_free);
  7557. + g_free (self);
  7558. +}
  7559. +
  7560. +/**
  7561. + * ide_vim_state_ref:
  7562. + * @self: (in): #IdeVimState instance.
  7563. + *
  7564. + * Increment #IdeVimState ref count.
  7565. + *
  7566. + * Returns: the #IdeVimState itself.
  7567. + */
  7568. +IdeVimState *
  7569. +ide_vim_state_ref (IdeVimState *self)
  7570. +{
  7571. + g_return_val_if_fail (self, NULL);
  7572. + g_return_val_if_fail (self->ref_count > 0, NULL);
  7573. +
  7574. + g_atomic_int_inc (&self->ref_count);
  7575. +
  7576. + return self;
  7577. +}
  7578. +
  7579. +/**
  7580. + * ide_vim_state_unref:
  7581. + * @self: (in): #IdeVimState instance.
  7582. + *
  7583. + * Decrement #IdeVimState ref count.
  7584. + */
  7585. +void
  7586. +ide_vim_state_unref (IdeVimState *self)
  7587. +{
  7588. + g_return_if_fail (self);
  7589. + g_return_if_fail (self->ref_count > 0);
  7590. +
  7591. + if (g_atomic_int_dec_and_test (&self->ref_count))
  7592. + ide_vim_state_finalize (self);
  7593. +}
  7594. +
  7595. +/**
  7596. + * ide_vim_state_new:
  7597. + * @name: (in): the name of the state
  7598. + * @state_enter_func: a pointer to a StateEnterFunc for the enter phase.
  7599. + * @state_leave_func: a pointer to a StateLeaveFunc for the leave phase.
  7600. + * @state_transition_func: a pointer to a StateTransitionFunc for the transition phase.
  7601. + * @state_action_func: a pointer to a StateActionFunc for the action phase.
  7602. + *
  7603. + * Return an #IdeVimState instance.
  7604. + *
  7605. + * Returns: A new #IdeVimState or NULL if the state already exist.
  7606. + */
  7607. +IdeVimState *
  7608. +ide_vim_state_new (const gchar *name,
  7609. + StateEnterFunc enter_func,
  7610. + StateLeaveFunc leave_func,
  7611. + StateTransitionFunc transition_func,
  7612. + StateActionFunc action_func)
  7613. +{
  7614. + IdeVimState *self;
  7615. + g_autofree gchar *fullname;
  7616. +
  7617. + g_return_val_if_fail (!ide_str_empty0 (name), NULL);
  7618. +
  7619. + fullname = g_strconcat (STATE_NAME_PREFIX, name, NULL);
  7620. +
  7621. + self = g_new0 (IdeVimState, 1);
  7622. + self->ref_count = 1;
  7623. + self->id = g_quark_from_string (fullname);
  7624. + self->name = g_strdup (name);
  7625. +
  7626. + ide_vim_state_change_funcs (self, enter_func, leave_func, transition_func, action_func);
  7627. +
  7628. + return self;
  7629. +}
  7630. diff --git a/libide/vim/ide-vim-state.h b/libide/vim/ide-vim-state.h
  7631. new file mode 100644
  7632. index 0000000..83451d2
  7633. --- /dev/null
  7634. +++ b/libide/vim/ide-vim-state.h
  7635. @@ -0,0 +1,71 @@
  7636. +/* ide-vim-state.h
  7637. + *
  7638. + * Copyright (C) 2015 Sébastien Lafargue <slafargue@gnome.org>
  7639. + *
  7640. + * This program is free software: you can redistribute it and/or modify
  7641. + * it under the terms of the GNU General Public License as published by
  7642. + * the Free Software Foundation, either version 3 of the License, or
  7643. + * (at your option) any later version.
  7644. + *
  7645. + * This program is distributed in the hope that it will be useful,
  7646. + * but WITHOUT ANY WARRANTY; without even the implied warranty of
  7647. + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  7648. + * GNU General Public License for more details.
  7649. + *
  7650. + * You should have received a copy of the GNU General Public License
  7651. + * along with this program. If not, see <http://www.gnu.org/licenses/>.
  7652. + */
  7653. +
  7654. +#ifndef IDE_VIM_STATE_H
  7655. +#define IDE_VIM_STATE_H
  7656. +
  7657. +#include <glib-object.h>
  7658. +
  7659. +G_BEGIN_DECLS
  7660. +
  7661. +#define IDE_TYPE_VIM_STATE (ide_vim_state_get_type())
  7662. +
  7663. +/* Name prefix used to avoid confict with already existing quark string */
  7664. +#define STATE_NAME_PREFIX "ide-vim-state-"
  7665. +
  7666. +typedef struct _IdeVimStateMachine IdeVimStateMachine;
  7667. +typedef GQuark IdeVimStateId;
  7668. +
  7669. +typedef void (*StateEnterFunc) (IdeVimStateMachine *state_machine, gpointer data);
  7670. +typedef void (*StateLeaveFunc) (IdeVimStateMachine *state_machine, gpointer data);
  7671. +typedef IdeVimStateId (*StateTransitionFunc) (IdeVimStateMachine *state_machine, gpointer data);
  7672. +typedef gboolean (*StateActionFunc) (IdeVimStateMachine *state_machine, gpointer data);
  7673. +
  7674. +struct _IdeVimState
  7675. +{
  7676. + volatile gint ref_count;
  7677. +
  7678. + IdeVimStateId id;
  7679. + const gchar *name;
  7680. + StateEnterFunc enter_func;
  7681. + StateLeaveFunc leave_func;
  7682. + StateTransitionFunc transition_func;
  7683. + StateActionFunc action_func;
  7684. +};
  7685. +
  7686. +/* TODO: ide_vim_state_overwrite used ? */
  7687. +typedef struct _IdeVimState IdeVimState;
  7688. +
  7689. +IdeVimState *ide_vim_state_new (const gchar *name,
  7690. + StateEnterFunc enter_func,
  7691. + StateLeaveFunc leave_func,
  7692. + StateTransitionFunc transition_func,
  7693. + StateActionFunc action_func);
  7694. +void ide_vim_state_change_funcs (IdeVimState *self,
  7695. + StateEnterFunc enter_func,
  7696. + StateLeaveFunc leave_func,
  7697. + StateTransitionFunc transition_func,
  7698. + StateActionFunc action_func);
  7699. +const gchar *ide_vim_state_get_name (IdeVimState *self);
  7700. +IdeVimStateId ide_vim_state_get_id (IdeVimState *self);
  7701. +IdeVimState *ide_vim_state_ref (IdeVimState *self);
  7702. +void ide_vim_state_unref (IdeVimState *self);
  7703. +
  7704. +G_END_DECLS
  7705. +
  7706. +#endif /* IDE_VIM_STATE_H */
  7707. diff --git a/plugins/command-bar/Makefile.am b/plugins/command-bar/Makefile.am
  7708. index 4833eb1..1992509 100644
  7709. --- a/plugins/command-bar/Makefile.am
  7710. +++ b/plugins/command-bar/Makefile.am
  7711. @@ -12,6 +12,10 @@ libcommand_bar_la_SOURCES = \
  7712. gb-command-bar-resources.h \
  7713. gb-command-bar.c \
  7714. gb-command-bar.h \
  7715. + gb-command-bar-item.c \
  7716. + gb-command-bar-item.h \
  7717. + gb-command-complete-item.c \
  7718. + gb-command-complete-item.h \
  7719. gb-command-gaction-provider.c \
  7720. gb-command-gaction-provider.h \
  7721. gb-command-gaction.c \
  7722. diff --git a/plugins/command-bar/gb-command-bar-item.c b/plugins/command-bar/gb-command-bar-item.c
  7723. new file mode 100644
  7724. index 0000000..8700f0b
  7725. --- /dev/null
  7726. +++ b/plugins/command-bar/gb-command-bar-item.c
  7727. @@ -0,0 +1,257 @@
  7728. +/* gb-command-bar-item.c
  7729. + *
  7730. + * Copyright (C) 2014 Christian Hergert <christian@hergert.me>
  7731. + *
  7732. + * This program is free software: you can redistribute it and/or modify
  7733. + * it under the terms of the GNU General Public License as published by
  7734. + * the Free Software Foundation, either version 3 of the License, or
  7735. + * (at your option) any later version.
  7736. + *
  7737. + * This program is distributed in the hope that it will be useful,
  7738. + * but WITHOUT ANY WARRANTY; without even the implied warranty of
  7739. + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  7740. + * GNU General Public License for more details.
  7741. + *
  7742. + * You should have received a copy of the GNU General Public License
  7743. + * along with this program. If not, see <http://www.gnu.org/licenses/>.
  7744. + */
  7745. +
  7746. +#include <glib/gi18n.h>
  7747. +
  7748. +#include "gb-command-bar-item.h"
  7749. +#include "gb-widget.h"
  7750. +
  7751. +struct _GbCommandBarItem
  7752. +{
  7753. + GtkBin parent_instance;
  7754. + GbCommandResult *result;
  7755. +
  7756. + GtkWidget *command_text;
  7757. + GtkWidget *result_text;
  7758. + GtkWidget *equal_label;
  7759. + GtkWidget *status_image;
  7760. + GtkWidget *status_box;
  7761. +};
  7762. +
  7763. +G_DEFINE_TYPE (GbCommandBarItem, gb_command_bar_item, GTK_TYPE_BIN)
  7764. +
  7765. +enum {
  7766. + PROP_0,
  7767. + PROP_RESULT,
  7768. + LAST_PROP
  7769. +};
  7770. +
  7771. +static GParamSpec *gParamSpecs [LAST_PROP];
  7772. +
  7773. +GtkWidget *
  7774. +gb_command_bar_item_new (GbCommandResult *result)
  7775. +{
  7776. + return g_object_new (GB_TYPE_COMMAND_BAR_ITEM,
  7777. + "result", result,
  7778. + NULL);
  7779. +}
  7780. +
  7781. +/**
  7782. + * gb_command_bar_item_get_result:
  7783. + * @item: A #GbCommandBarItem.
  7784. + *
  7785. + * Retrieves the result text widget so it can be used in the command bar
  7786. + * sizing group.
  7787. + *
  7788. + * Returns: (transfer none): The result text widget.
  7789. + */
  7790. +GtkWidget *
  7791. +gb_command_bar_item_get_result (GbCommandBarItem *item)
  7792. +{
  7793. + g_return_val_if_fail (GB_IS_COMMAND_BAR_ITEM (item), NULL);
  7794. +
  7795. + return item->result_text;
  7796. +}
  7797. +
  7798. +static gboolean
  7799. +error_status_to_icon_name (GBinding *binding,
  7800. + const GValue *from_value,
  7801. + GValue *to_value,
  7802. + gpointer user_data)
  7803. +{
  7804. + GbCommandBarItem *item = GB_COMMAND_BAR_ITEM (user_data);
  7805. + gboolean error_status = g_value_get_boolean (from_value);
  7806. +
  7807. + if (error_status)
  7808. + g_value_set_string (to_value, "dialog-warning-symbolic");
  7809. + else if (!gb_command_result_get_is_running (item->result))
  7810. + g_value_set_string (to_value, "");
  7811. +
  7812. + return TRUE;
  7813. +}
  7814. +
  7815. +static gboolean
  7816. +running_status_to_icon_name (GBinding *binding,
  7817. + const GValue *from_value,
  7818. + GValue *to_value,
  7819. + gpointer user_data)
  7820. +{
  7821. + GbCommandBarItem *item = GB_COMMAND_BAR_ITEM (user_data);
  7822. + gboolean running_status = g_value_get_boolean (from_value);
  7823. +
  7824. + if (running_status && !gb_command_result_get_is_error (item->result))
  7825. + g_value_set_string (to_value, "system-run-symbolic");
  7826. + else if (gb_command_result_get_is_error (item->result))
  7827. + g_value_set_string (to_value, "dialog-warning-symbolic");
  7828. + else
  7829. + g_value_set_string (to_value, "");
  7830. +
  7831. + return TRUE;
  7832. +}
  7833. +
  7834. +static gboolean
  7835. +string_to_boolean (GBinding *binding,
  7836. + const GValue *from_value,
  7837. + GValue *to_value,
  7838. + gpointer user_data)
  7839. +{
  7840. + g_value_set_boolean (to_value, !!g_value_get_string (from_value));
  7841. + return TRUE;
  7842. +}
  7843. +
  7844. +static void
  7845. +gb_command_bar_item_set_result (GbCommandBarItem *item,
  7846. + GbCommandResult *result)
  7847. +{
  7848. + g_return_if_fail (GB_IS_COMMAND_BAR_ITEM (item));
  7849. + g_return_if_fail (GB_IS_COMMAND_RESULT (result));
  7850. +
  7851. + if (item->result != result)
  7852. + {
  7853. + g_clear_object (&item->result);
  7854. +
  7855. + if (result)
  7856. + {
  7857. + item->result = g_object_ref (result);
  7858. + g_object_bind_property (result, "command-text",
  7859. + item->command_text, "label",
  7860. + G_BINDING_SYNC_CREATE);
  7861. + g_object_bind_property (result, "result-text",
  7862. + item->result_text, "label",
  7863. + G_BINDING_SYNC_CREATE);
  7864. + g_object_bind_property_full (result, "result-text",
  7865. + item->equal_label, "visible",
  7866. + G_BINDING_SYNC_CREATE,
  7867. + string_to_boolean,
  7868. + NULL,
  7869. + item,
  7870. + NULL);
  7871. + g_object_bind_property_full (result, "result-text",
  7872. + item->result_text, "visible",
  7873. + G_BINDING_SYNC_CREATE,
  7874. + string_to_boolean,
  7875. + NULL,
  7876. + item,
  7877. + NULL);
  7878. + g_object_bind_property_full (result, "is-error",
  7879. + item->status_image, "icon-name",
  7880. + G_BINDING_SYNC_CREATE,
  7881. + error_status_to_icon_name,
  7882. + NULL, NULL, NULL);
  7883. + g_object_bind_property_full (result, "is-running",
  7884. + item->status_image, "icon-name",
  7885. + G_BINDING_SYNC_CREATE,
  7886. + running_status_to_icon_name,
  7887. + NULL,
  7888. + item,
  7889. + NULL);
  7890. + }
  7891. +
  7892. + g_object_notify_by_pspec (G_OBJECT (item), gParamSpecs [PROP_RESULT]);
  7893. + }
  7894. +}
  7895. +
  7896. +static void
  7897. +gb_command_bar_item_finalize (GObject *object)
  7898. +{
  7899. + GbCommandBarItem *self = GB_COMMAND_BAR_ITEM (object);
  7900. +
  7901. + g_clear_object (&self->result);
  7902. +
  7903. + G_OBJECT_CLASS (gb_command_bar_item_parent_class)->finalize (object);
  7904. +}
  7905. +
  7906. +static void
  7907. +gb_command_bar_item_get_property (GObject *object,
  7908. + guint prop_id,
  7909. + GValue *value,
  7910. + GParamSpec *pspec)
  7911. +{
  7912. + GbCommandBarItem *self = GB_COMMAND_BAR_ITEM (object);
  7913. +
  7914. + switch (prop_id)
  7915. + {
  7916. + case PROP_RESULT:
  7917. + g_value_set_object (value, gb_command_bar_item_get_result (self));
  7918. + break;
  7919. +
  7920. + default:
  7921. + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
  7922. + }
  7923. +}
  7924. +
  7925. +static void
  7926. +gb_command_bar_item_set_property (GObject *object,
  7927. + guint prop_id,
  7928. + const GValue *value,
  7929. + GParamSpec *pspec)
  7930. +{
  7931. + GbCommandBarItem *self = GB_COMMAND_BAR_ITEM (object);
  7932. +
  7933. + switch (prop_id)
  7934. + {
  7935. + case PROP_RESULT:
  7936. + gb_command_bar_item_set_result (self, g_value_get_object (value));
  7937. + break;
  7938. +
  7939. + default:
  7940. + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
  7941. + }
  7942. +}
  7943. +
  7944. +static void
  7945. +gb_command_bar_item_class_init (GbCommandBarItemClass *klass)
  7946. +{
  7947. + GObjectClass *object_class = G_OBJECT_CLASS (klass);
  7948. + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
  7949. +
  7950. + object_class->finalize = gb_command_bar_item_finalize;
  7951. + object_class->get_property = gb_command_bar_item_get_property;
  7952. + object_class->set_property = gb_command_bar_item_set_property;
  7953. +
  7954. + gtk_widget_class_set_template_from_resource (widget_class,
  7955. + "/org/gnome/builder/plugins/command-bar/gb-command-bar-item.ui");
  7956. +
  7957. + GB_WIDGET_CLASS_BIND (widget_class, GbCommandBarItem, command_text);
  7958. + GB_WIDGET_CLASS_BIND (widget_class, GbCommandBarItem, result_text);
  7959. + GB_WIDGET_CLASS_BIND (widget_class, GbCommandBarItem, equal_label);
  7960. + GB_WIDGET_CLASS_BIND (widget_class, GbCommandBarItem, status_image);
  7961. + GB_WIDGET_CLASS_BIND (widget_class, GbCommandBarItem, status_box);
  7962. +
  7963. + gParamSpecs [PROP_RESULT] =
  7964. + g_param_spec_object ("result",
  7965. + _("Result"),
  7966. + _("The result to be visualized in the item."),
  7967. + GB_TYPE_COMMAND_RESULT,
  7968. + (G_PARAM_READWRITE |
  7969. + G_PARAM_CONSTRUCT_ONLY |
  7970. + G_PARAM_STATIC_STRINGS));
  7971. +
  7972. + g_object_class_install_properties (object_class, LAST_PROP, gParamSpecs);
  7973. +}
  7974. +
  7975. +static void
  7976. +gb_command_bar_item_init (GbCommandBarItem *self)
  7977. +{
  7978. + gint width, height;
  7979. +
  7980. + gtk_widget_init_template (GTK_WIDGET (self));
  7981. +
  7982. + gtk_icon_size_lookup (GTK_ICON_SIZE_MENU, &width, &height);
  7983. + gtk_widget_set_size_request (self->status_box, width, height);
  7984. +}
  7985. diff --git a/plugins/command-bar/gb-command-bar-item.h b/plugins/command-bar/gb-command-bar-item.h
  7986. new file mode 100644
  7987. index 0000000..4db55da
  7988. --- /dev/null
  7989. +++ b/plugins/command-bar/gb-command-bar-item.h
  7990. @@ -0,0 +1,39 @@
  7991. +/* gb-command-bar-item.h
  7992. + *
  7993. + * Copyright (C) 2014 Christian Hergert <christian@hergert.me>
  7994. + *
  7995. + * This program is free software: you can redistribute it and/or modify
  7996. + * it under the terms of the GNU General Public License as published by
  7997. + * the Free Software Foundation, either version 3 of the License, or
  7998. + * (at your option) any later version.
  7999. + *
  8000. + * This program is distributed in the hope that it will be useful,
  8001. + * but WITHOUT ANY WARRANTY; without even the implied warranty of
  8002. + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  8003. + * GNU General Public License for more details.
  8004. + *
  8005. + * You should have received a copy of the GNU General Public License
  8006. + * along with this program. If not, see <http://www.gnu.org/licenses/>.
  8007. + */
  8008. +
  8009. +#ifndef GB_COMMAND_BAR_ITEM_H
  8010. +#define GB_COMMAND_BAR_ITEM_H
  8011. +
  8012. +#include <gtk/gtk.h>
  8013. +
  8014. +#include "gb-command-result.h"
  8015. +
  8016. +G_BEGIN_DECLS
  8017. +
  8018. +#define GB_TYPE_COMMAND_BAR_ITEM (gb_command_bar_item_get_type())
  8019. +
  8020. +G_DECLARE_FINAL_TYPE (GbCommandBarItem, gb_command_bar_item,
  8021. + GB, COMMAND_BAR_ITEM, GtkBin)
  8022. +
  8023. +GtkWidget *gb_command_bar_item_new (GbCommandResult *result);
  8024. +void gb_command_bar_item_connect_close (GbCommandBarItem *item);
  8025. +GtkWidget *gb_command_bar_item_get_result (GbCommandBarItem *item);
  8026. +
  8027. +G_END_DECLS
  8028. +
  8029. +#endif /* GB_COMMAND_BAR_ITEM_H */
  8030. diff --git a/plugins/command-bar/gb-command-bar-item.ui b/plugins/command-bar/gb-command-bar-item.ui
  8031. new file mode 100644
  8032. index 0000000..d5cb27b
  8033. --- /dev/null
  8034. +++ b/plugins/command-bar/gb-command-bar-item.ui
  8035. @@ -0,0 +1,83 @@
  8036. +<?xml version="1.0" encoding="UTF-8"?>
  8037. +<interface>
  8038. + <!-- interface-requires gtk+ 3.8 -->
  8039. + <template class="GbCommandBarItem" parent="GtkBin">
  8040. + <property name="visible">True</property>
  8041. + <child>
  8042. + <object class="GtkBox" id="hbox1">
  8043. + <property name="visible">True</property>
  8044. + <property name="orientation">horizontal</property>
  8045. + <property name="margin">3</property>
  8046. + <style>
  8047. + <class name="gb-command-bar-item"/>
  8048. + </style>
  8049. + <child>
  8050. + <object class="GtkBox" id="status_box">
  8051. + <property name="visible">True</property>
  8052. + <property name="margin-left">6</property>
  8053. + <child>
  8054. + <object class="GtkImage" id="status_image">
  8055. + <property name="visible">True</property>
  8056. + <property name="halign">center</property>
  8057. + <property name="valign">center</property>
  8058. + </object>
  8059. + </child>
  8060. + </object>
  8061. + </child>
  8062. + <child>
  8063. + <object class="GtkBox" id="vbox1">
  8064. + <property name="visible">True</property>
  8065. + <property name="margin-left">12</property>
  8066. + <property name="margin-right">3</property>
  8067. + <property name="margin-top">3</property>
  8068. + <property name="margin-bottom">3</property>
  8069. + <property name="orientation">vertical</property>
  8070. + <child>
  8071. + <object class="GtkBox" id="hbox2">
  8072. + <property name="visible">True</property>
  8073. + <property name="vexpand">False</property>
  8074. + <property name="spacing">6</property>
  8075. + <child>
  8076. + <object class="GtkLabel" id="command_text">
  8077. + <property name="visible">True</property>
  8078. + <property name="halign">start</property>
  8079. + </object>
  8080. + <packing>
  8081. + <property name="fill">True</property>
  8082. + </packing>
  8083. + </child>
  8084. + <child>
  8085. + <object class="GtkLabel" id="equal_label">
  8086. + <property name="visible">True</property>
  8087. + <property name="label">:</property>
  8088. + </object>
  8089. + <packing>
  8090. + <property name="fill">True</property>
  8091. + </packing>
  8092. + </child>
  8093. + </object>
  8094. + <packing>
  8095. + <property name="expand">True</property>
  8096. + <property name="fill">True</property>
  8097. + </packing>
  8098. + </child>
  8099. + <child>
  8100. + <object class="GtkLabel" id="result_text">
  8101. + <property name="visible">True</property>
  8102. + <property name="halign">start</property>
  8103. + <property name="vexpand">False</property>
  8104. + </object>
  8105. + <packing>
  8106. + <property name="fill">True</property>
  8107. + </packing>
  8108. + </child>
  8109. + </object>
  8110. + <packing>
  8111. + <property name="expand">True</property>
  8112. + <property name="fill">True</property>
  8113. + </packing>
  8114. + </child>
  8115. + </object>
  8116. + </child>
  8117. + </template>
  8118. +</interface>
  8119. diff --git a/plugins/command-bar/gb-command-bar.c b/plugins/command-bar/gb-command-bar.c
  8120. index 03e82b9..8416cb3 100644
  8121. --- a/plugins/command-bar/gb-command-bar.c
  8122. +++ b/plugins/command-bar/gb-command-bar.c
  8123. @@ -23,9 +23,13 @@
  8124. #include "gb-command.h"
  8125. #include "gb-command-bar-resources.h"
  8126. #include "gb-command-bar.h"
  8127. +#include "gb-command-bar-item.h"
  8128. +#include "gb-command-complete-item.h"
  8129. #include "gb-command-gaction-provider.h"
  8130. #include "gb-command-manager.h"
  8131. +#include "gb-command-vim.h"
  8132. #include "gb-command-vim-provider.h"
  8133. +#include "gb-command-result.h"
  8134. #include "gb-glib.h"
  8135. #include "gb-slider.h"
  8136. #include "gb-string.h"
  8137. @@ -34,6 +38,8 @@
  8138. #include "gb-workbench.h"
  8139. #include "gb-workbench-addin.h"
  8140.  
  8141. +#include <glib/gprintf.h>
  8142. +
  8143. struct _GbCommandBar
  8144. {
  8145. GtkBin parent_instance;
  8146. @@ -43,9 +49,15 @@ struct _GbCommandBar
  8147.  
  8148. GSimpleAction *show_action;
  8149.  
  8150. + gulong set_focus_handler;
  8151. +
  8152. GtkSizeGroup *result_size_group;
  8153. GtkEntry *entry;
  8154. + GtkMenuButton *results_button;
  8155. + GtkPopover *popover;
  8156. + GtkWidget *command_box;
  8157. GtkListBox *list_box;
  8158. + gint result_y_pos;
  8159. GtkScrolledWindow *scroller;
  8160. GtkScrolledWindow *completion_scroller;
  8161. GtkFlowBox *flow_box;
  8162. @@ -58,6 +70,7 @@ struct _GbCommandBar
  8163. gchar *saved_text;
  8164. int saved_position;
  8165. gboolean saved_position_valid;
  8166. + gboolean is_in_results_popup;
  8167. };
  8168.  
  8169. static void workbench_addin_init (GbWorkbenchAddinInterface *iface);
  8170. @@ -166,6 +179,58 @@ find_alternate_focus (GtkWidget *focus)
  8171. return focus;
  8172. }
  8173.  
  8174. +static void
  8175. +gb_command_bar_results_toggled_cb (GbCommandBar *self,
  8176. + GtkToggleButton *button)
  8177. +{
  8178. + g_return_if_fail (GB_IS_COMMAND_BAR (self));
  8179. + g_return_if_fail (GTK_IS_TOGGLE_BUTTON (button));
  8180. +
  8181. + if (gtk_toggle_button_get_active (button))
  8182. + {
  8183. + gtk_widget_grab_focus (GTK_WIDGET (self->list_box));
  8184. + gtk_widget_hide (GTK_WIDGET (self->completion_scroller));
  8185. + }
  8186. + else
  8187. + {
  8188. + gtk_widget_hide (GTK_WIDGET (self->popover));
  8189. + }
  8190. +}
  8191. +
  8192. +static gint
  8193. +gb_command_bar_results_length (GbCommandBar *self)
  8194. +{
  8195. + GList *children;
  8196. + gint len;
  8197. +
  8198. + g_return_val_if_fail (GB_IS_COMMAND_BAR (self), 0);
  8199. +
  8200. + children = gtk_container_get_children (GTK_CONTAINER (self->list_box));
  8201. + len = g_list_length (children);
  8202. +
  8203. + g_list_free (children);
  8204. + return len;
  8205. +}
  8206. +
  8207. +static void
  8208. +gb_command_bar_results_show (GbCommandBar *self)
  8209. +{
  8210. + g_return_if_fail (GB_IS_COMMAND_BAR (self));
  8211. + if (gb_command_bar_results_length (self) != 0)
  8212. + {
  8213. + gb_command_bar_show (self);
  8214. + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (self->results_button), TRUE);
  8215. + }
  8216. +}
  8217. +
  8218. +static void
  8219. +gb_command_bar_results_hide (GbCommandBar *self)
  8220. +{
  8221. + g_return_if_fail (GB_IS_COMMAND_BAR (self));
  8222. +
  8223. + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (self->results_button), FALSE);
  8224. +}
  8225. +
  8226. /**
  8227. * gb_command_bar_hide:
  8228. * @bar: A #GbCommandBar
  8229. @@ -185,6 +250,7 @@ gb_command_bar_hide (GbCommandBar *self)
  8230. return;
  8231.  
  8232. gb_slider_set_position (slider, GB_SLIDER_NONE);
  8233. + gb_command_bar_results_hide (self);
  8234.  
  8235. if (self->last_focus)
  8236. focus = find_alternate_focus (self->last_focus);
  8237. @@ -242,9 +308,148 @@ static void
  8238. gb_command_bar_push_result (GbCommandBar *self,
  8239. GbCommandResult *result)
  8240. {
  8241. - /*
  8242. - * TODO: if we decide to keep results visible, add them to list here.
  8243. - */
  8244. + GtkAdjustment *vadj;
  8245. + GdkFrameClock *frame_clock;
  8246. + GtkWidget *item;
  8247. + GtkWidget *result_widget;
  8248. + gdouble upper;
  8249. +
  8250. + g_return_if_fail (GB_IS_COMMAND_BAR (self));
  8251. + g_return_if_fail (GB_IS_COMMAND_RESULT (result));
  8252. +
  8253. + item = g_object_new (GB_TYPE_COMMAND_BAR_ITEM,
  8254. + "result", result,
  8255. + "visible", TRUE,
  8256. + NULL);
  8257. + gtk_container_add (GTK_CONTAINER (self->list_box), item);
  8258. +
  8259. + result_widget = gb_command_bar_item_get_result (GB_COMMAND_BAR_ITEM (item));
  8260. + gtk_size_group_add_widget (self->result_size_group, result_widget);
  8261. +
  8262. + vadj = gtk_list_box_get_adjustment (self->list_box);
  8263. + upper = gtk_adjustment_get_upper (vadj);
  8264. + frame_clock = gtk_widget_get_frame_clock (GTK_WIDGET (self->list_box));
  8265. +
  8266. + gb_command_bar_results_show (self);
  8267. + ide_object_animate (vadj,
  8268. + IDE_ANIMATION_EASE_IN_CUBIC,
  8269. + 250,
  8270. + frame_clock,
  8271. + "value", upper,
  8272. + NULL);
  8273. +}
  8274. +
  8275. +static void
  8276. +gb_command_bar_results_clear_all (GbCommandBar *self,
  8277. + GtkWidget *menuitem)
  8278. +{
  8279. + GList *children;
  8280. + GList *l;
  8281. +
  8282. + children = gtk_container_get_children (GTK_CONTAINER (self->list_box));
  8283. + for (l = children; l != NULL; l = l->next)
  8284. + gtk_widget_destroy (GTK_WIDGET (l->data));
  8285. + g_list_free (children);
  8286. +}
  8287. +
  8288. +static void
  8289. +gb_command_bar_results_clear (GbCommandBar *self,
  8290. + GtkWidget *menuitem)
  8291. +{
  8292. + GtkListBoxRow *row;
  8293. +
  8294. + if (self->result_y_pos == -1)
  8295. + row = gtk_list_box_get_selected_row (self->list_box);
  8296. + else
  8297. + row = gtk_list_box_get_row_at_y (self->list_box, self->result_y_pos);
  8298. +
  8299. + if (row != NULL)
  8300. + gtk_widget_destroy (GTK_WIDGET (row));
  8301. +}
  8302. +
  8303. +static void
  8304. +gb_command_bar_results_popup_desactivate_cb (GbCommandBar *self,
  8305. + GtkMenu *menu)
  8306. +{
  8307. + self->is_in_results_popup = FALSE;
  8308. + /* FIX: menu destroyed ? */
  8309. +}
  8310. +
  8311. +static void
  8312. +gb_command_bar_results_do_menu (GbCommandBar *self,
  8313. + GtkWidget *widget,
  8314. + GdkEventButton *event)
  8315. +{
  8316. + gint button;
  8317. + gint event_time;
  8318. + GtkWidget *menuitem;
  8319. + GtkWidget *menu;
  8320. +
  8321. + self->is_in_results_popup = TRUE;
  8322. + if (gb_command_bar_results_length (self) == 0)
  8323. + return;
  8324. +
  8325. + menu = gtk_menu_new ();
  8326. +
  8327. + menuitem = gtk_menu_item_new_with_label (_("Clear"));
  8328. + g_signal_connect_swapped (menuitem, "activate",
  8329. + G_CALLBACK (gb_command_bar_results_clear),
  8330. + self);
  8331. + gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
  8332. +
  8333. + menuitem = gtk_menu_item_new_with_label (_("Clear all"));
  8334. + g_signal_connect_swapped (menuitem, "activate",
  8335. + G_CALLBACK (gb_command_bar_results_clear_all),
  8336. + self);
  8337. + gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
  8338. +
  8339. + g_signal_connect_swapped (menu, "deactivate",
  8340. + G_CALLBACK (gb_command_bar_results_popup_desactivate_cb),
  8341. + self);
  8342. +
  8343. + self->result_y_pos = -1;
  8344. + if (event)
  8345. + {
  8346. + button = event->button;
  8347. + event_time = event->time;
  8348. + self->result_y_pos = ((GdkEventButton*)event)->y;
  8349. + }
  8350. + else
  8351. + {
  8352. + button = 0;
  8353. + event_time = gtk_get_current_event_time ();
  8354. + }
  8355. +
  8356. + gtk_menu_attach_to_widget (GTK_MENU (menu), GTK_WIDGET (widget), NULL);
  8357. + gtk_widget_show_all (menu);
  8358. + gtk_menu_popup_for_device (GTK_MENU (menu),
  8359. + gdk_event_get_device ((GdkEvent *)event),
  8360. + NULL, NULL,
  8361. + NULL, NULL, NULL,
  8362. + button,
  8363. + event_time);
  8364. +}
  8365. +
  8366. +static gboolean
  8367. +gb_command_bar_results_button_pressed_cb (GbCommandBar *self,
  8368. + GdkEventButton *event,
  8369. + GtkListBox *list_box)
  8370. +{
  8371. + if (gdk_event_triggers_context_menu ((GdkEvent *)event) && event->type == GDK_BUTTON_PRESS)
  8372. + {
  8373. + gb_command_bar_results_do_menu (self, GTK_WIDGET (list_box), event);
  8374. + return TRUE;
  8375. + }
  8376. +
  8377. + return FALSE;
  8378. +}
  8379. +
  8380. +static gboolean
  8381. +gb_command_bar_results_on_popup_menu_cb (GbCommandBar *self,
  8382. + GtkWidget *widget)
  8383. +{
  8384. + gb_command_bar_results_do_menu (self, widget, NULL);
  8385. + return TRUE;
  8386. }
  8387.  
  8388. static void
  8389. @@ -259,19 +464,26 @@ gb_command_bar_on_entry_activate (GbCommandBar *self,
  8390. text = gtk_entry_get_text (entry);
  8391.  
  8392. gtk_widget_hide (GTK_WIDGET (self->completion_scroller));
  8393. -
  8394. + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (self->results_button), FALSE);
  8395. +
  8396. if (!gb_str_empty0 (text))
  8397. {
  8398. GbCommandResult *result = NULL;
  8399. GbCommand *command = NULL;
  8400.  
  8401. - g_queue_push_head (self->history, g_strdup (text));
  8402. - g_free (g_queue_pop_nth (self->history, HISTORY_LENGTH));
  8403. -
  8404. - command = gb_command_manager_lookup (self->command_manager, text);
  8405. + command = gb_command_manager_lookup (self->command_manager, text, &result);
  8406. + if (result)
  8407. + {
  8408. + gb_command_bar_push_result (self, result);
  8409. + }
  8410.  
  8411. if (command)
  8412. {
  8413. + //printf ("command text=%s\n", gb_command_vim_get_command_text (GB_COMMAND_VIM (command)));
  8414. +
  8415. + g_queue_push_head (self->history, g_strdup (text));
  8416. + g_free (g_queue_pop_nth (self->history, HISTORY_LENGTH));
  8417. +
  8418. result = gb_command_execute (command);
  8419.  
  8420. /* if we got a result item, keep the bar open for observing it.
  8421. @@ -293,7 +505,6 @@ gb_command_bar_on_entry_activate (GbCommandBar *self,
  8422. "command-text", errmsg,
  8423. NULL);
  8424. gb_command_bar_push_result (self, result);
  8425. - g_object_unref (result);
  8426. g_free (errmsg);
  8427. }
  8428.  
  8429. @@ -307,20 +518,6 @@ gb_command_bar_on_entry_activate (GbCommandBar *self,
  8430. gtk_entry_set_text (self->entry, "");
  8431. }
  8432.  
  8433. -static gboolean
  8434. -gb_command_bar_on_entry_focus_out_event (GbCommandBar *self,
  8435. - GdkEventKey *event,
  8436. - GtkEntry *entry)
  8437. -{
  8438. - g_assert (GB_IS_COMMAND_BAR (self));
  8439. - g_assert (event != NULL);
  8440. - g_assert (GTK_IS_ENTRY (entry));
  8441. -
  8442. - gb_command_bar_hide (self);
  8443. -
  8444. - return GDK_EVENT_PROPAGATE;
  8445. -}
  8446. -
  8447. static void
  8448. gb_command_bar_grab_focus (GtkWidget *widget)
  8449. {
  8450. @@ -332,15 +529,19 @@ gb_command_bar_grab_focus (GtkWidget *widget)
  8451. }
  8452.  
  8453. static gchar *
  8454. -find_longest_common_prefix (gchar **strv)
  8455. +find_longest_common_prefix (GPtrArray *completions)
  8456. {
  8457. - gchar *lcp = NULL;
  8458. - gchar *lcp_end = NULL;
  8459. + const gchar *lcp = NULL;
  8460. + const gchar *lcp_end = NULL;
  8461. int i;
  8462. + const gchar *str;
  8463. + GbCommandCompleteItem *entry;
  8464. + guint len = completions->len;
  8465.  
  8466. - for (i = 0; strv[i] != NULL; i++)
  8467. + for (i = 0; i < len; i++)
  8468. {
  8469. - gchar *str = strv[i];
  8470. + entry = g_ptr_array_index (completions, i);
  8471. + str = gb_command_complete_item_get_name (entry);
  8472. if (lcp == NULL)
  8473. {
  8474. lcp = str;
  8475. @@ -348,7 +549,7 @@ find_longest_common_prefix (gchar **strv)
  8476. }
  8477. else
  8478. {
  8479. - gchar *tmp = lcp;
  8480. + const gchar *tmp = lcp;
  8481.  
  8482. while (tmp < lcp_end && *str != 0 && *tmp == *str)
  8483. {
  8484. @@ -369,12 +570,52 @@ find_longest_common_prefix (gchar **strv)
  8485. #define MIN_COMPLETION_COLUMS 3
  8486. #define N_UNSCROLLED_COMPLETION_ROWS 4
  8487.  
  8488. +static gchar *
  8489. +gb_command_bar_format_complete_string (GbCommandCompleteItem *item,
  8490. + gchar *prefix)
  8491. +{
  8492. + gchar *str;
  8493. + g_autofree gchar *decorated_remainder = NULL;
  8494. + g_autofree gchar *decorated_shortname = NULL;
  8495. + GbCommandCompleteItemKind kind;
  8496. + const gchar *name;
  8497. + const gchar *shortname;
  8498. + const gchar *remainder;
  8499. + gint prefix_len;
  8500. + gint shortname_len;
  8501. + gint len_s, len_n;
  8502. +
  8503. + kind = gb_command_complete_item_get_kind (item);
  8504. + name = gb_command_complete_item_get_name (item);
  8505. + shortname = gb_command_complete_item_get_shortname (item);
  8506. + prefix_len = strlen (prefix);
  8507. +
  8508. + if (kind == GB_COMMAND_COMPLETE_ITEM_KIND_COMMAND && !ide_str_empty0 (shortname))
  8509. + {
  8510. + shortname_len = strlen (shortname);
  8511. + len_s = MIN (shortname_len, prefix_len);
  8512. + len_n = MAX (0, prefix_len - shortname_len);
  8513. + remainder = name + shortname_len;
  8514. + decorated_shortname = g_strdup_printf ("<b>%.*s</b>%s", len_s, shortname, shortname + len_s);
  8515. + decorated_remainder = g_strdup_printf ("<b>%.*s</b>%s", len_n, remainder, remainder + len_n);
  8516. + str = g_strconcat ("[", decorated_shortname, "]", decorated_remainder, NULL);
  8517. + }
  8518. + else
  8519. + {
  8520. + str = g_strdup_printf ("<b>%s</b>%s", prefix, name + prefix_len);
  8521. + }
  8522. +
  8523. + return str;
  8524. +}
  8525. +
  8526. static void
  8527. gb_command_bar_complete (GbCommandBar *self)
  8528. {
  8529. + GPtrArray *completions;
  8530. + guint len;
  8531. GtkEditable *editable = GTK_EDITABLE (self->entry);
  8532. GtkWidget *viewport = gtk_bin_get_child (GTK_BIN (self->completion_scroller));
  8533. - gchar **completions;
  8534. +
  8535. int pos, i;
  8536. gchar *current_prefix, *expanded_prefix;
  8537.  
  8538. @@ -402,62 +643,72 @@ gb_command_bar_complete (GbCommandBar *self)
  8539. g_clear_pointer (&self->last_completion, g_free);
  8540.  
  8541. completions = gb_command_manager_complete (self->command_manager, current_prefix);
  8542. -
  8543. - expanded_prefix = find_longest_common_prefix (completions);
  8544. -
  8545. - if (strlen (expanded_prefix) > strlen (current_prefix))
  8546. - {
  8547. - gtk_widget_hide (GTK_WIDGET (self->completion_scroller));
  8548. - gtk_editable_insert_text (editable, expanded_prefix + strlen (current_prefix), -1, &pos);
  8549. - gtk_editable_set_position (editable, pos);
  8550. - }
  8551. - else if (g_strv_length (completions) > 1)
  8552. + if (completions != NULL)
  8553. {
  8554. - gint wrapped_height = 0;
  8555. - self->last_completion = g_strdup (current_prefix);
  8556. -
  8557. - gtk_widget_show (GTK_WIDGET (self->completion_scroller));
  8558. - gtk_container_foreach (GTK_CONTAINER (self->flow_box),
  8559. - (GtkCallback)gtk_widget_destroy, NULL);
  8560. + expanded_prefix = find_longest_common_prefix (completions);
  8561.  
  8562. - gtk_flow_box_set_min_children_per_line (self->flow_box, MIN_COMPLETION_COLUMS);
  8563. -
  8564. - for (i = 0; completions[i] != NULL; i++)
  8565. + if (strlen (expanded_prefix) > strlen (current_prefix))
  8566. {
  8567. - GtkWidget *label;
  8568. - char *s;
  8569. -
  8570. - label = gtk_label_new ("");
  8571. - s = g_strdup_printf ("<b>%s</b>%s", current_prefix, completions[i] + strlen (current_prefix));
  8572. - gtk_label_set_markup (GTK_LABEL (label), s);
  8573. - gtk_label_set_xalign (GTK_LABEL (label), 0.0f);
  8574. - g_free (s);
  8575. -
  8576. - gtk_container_add (GTK_CONTAINER (self->flow_box), label);
  8577. - gtk_widget_show (label);
  8578. -
  8579. - if (i == MIN_COMPLETION_COLUMS * N_UNSCROLLED_COMPLETION_ROWS - 1)
  8580. - gtk_widget_get_preferred_height (GTK_WIDGET (self->flow_box), &wrapped_height, NULL);
  8581. - }
  8582. -
  8583. - if (i < MIN_COMPLETION_COLUMS * N_UNSCROLLED_COMPLETION_ROWS)
  8584. - {
  8585. - gtk_widget_set_size_request (GTK_WIDGET (self->completion_scroller), -1, -1);
  8586. - gtk_scrolled_window_set_policy (self->completion_scroller,
  8587. - GTK_POLICY_NEVER, GTK_POLICY_NEVER);
  8588. + gtk_widget_hide (GTK_WIDGET (self->completion_scroller));
  8589. + gtk_editable_insert_text (editable, expanded_prefix + strlen (current_prefix), -1, &pos);
  8590. + gtk_editable_set_position (editable, pos);
  8591. }
  8592. else
  8593. {
  8594. - gtk_widget_set_size_request (GTK_WIDGET (self->completion_scroller), -1, wrapped_height);
  8595. - gtk_scrolled_window_set_policy (self->completion_scroller,
  8596. - GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
  8597. + len = completions->len;
  8598. + gint wrapped_height = 0;
  8599. + self->last_completion = g_strdup (current_prefix);
  8600. +
  8601. + gtk_widget_show (GTK_WIDGET (self->completion_scroller));
  8602. + gb_command_bar_results_hide (self);
  8603. +
  8604. + gtk_container_foreach (GTK_CONTAINER (self->flow_box),
  8605. + (GtkCallback)gtk_widget_destroy, NULL);
  8606. +
  8607. + gtk_flow_box_set_min_children_per_line (self->flow_box, MIN_COMPLETION_COLUMS);
  8608. +
  8609. + for (i = 0; i < len; i++)
  8610. + {
  8611. + GbCommandCompleteItem *entry = g_ptr_array_index (completions, i);
  8612. + GtkWidget *label;
  8613. + char *s;
  8614. +
  8615. + label = gtk_label_new ("");
  8616. + s = gb_command_bar_format_complete_string (entry, current_prefix);
  8617. + gtk_label_set_markup (GTK_LABEL (label), s);
  8618. + gtk_label_set_xalign (GTK_LABEL (label), 0.0f);
  8619. + g_free (s);
  8620. +
  8621. + gtk_container_add (GTK_CONTAINER (self->flow_box), label);
  8622. + gtk_widget_show (label);
  8623. +
  8624. + if (i == MIN_COMPLETION_COLUMS * N_UNSCROLLED_COMPLETION_ROWS - 1)
  8625. + gtk_widget_get_preferred_height (GTK_WIDGET (self->flow_box), &wrapped_height, NULL);
  8626. + }
  8627. +
  8628. + g_ptr_array_free (completions, TRUE);
  8629. +
  8630. + if (i < MIN_COMPLETION_COLUMS * N_UNSCROLLED_COMPLETION_ROWS)
  8631. + {
  8632. + gtk_widget_set_size_request (GTK_WIDGET (self->completion_scroller), -1, -1);
  8633. + gtk_scrolled_window_set_policy (self->completion_scroller,
  8634. + GTK_POLICY_NEVER, GTK_POLICY_NEVER);
  8635. + }
  8636. + else
  8637. + {
  8638. + gtk_widget_set_size_request (GTK_WIDGET (self->completion_scroller), -1, wrapped_height);
  8639. + gtk_scrolled_window_set_policy (self->completion_scroller,
  8640. + GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
  8641. + }
  8642. }
  8643. +
  8644. + g_free (expanded_prefix);
  8645. }
  8646. else
  8647. - gtk_widget_hide (GTK_WIDGET (self->completion_scroller));
  8648. -
  8649. - g_free (expanded_prefix);
  8650. - g_strfreev (completions);
  8651. + {
  8652. + gtk_widget_hide (GTK_WIDGET (self->completion_scroller));
  8653. + gb_command_bar_results_hide (self);
  8654. + }
  8655. }
  8656.  
  8657. g_free (current_prefix);
  8658. @@ -565,6 +816,63 @@ gb_command_bar_on_entry_key_press_event (GbCommandBar *bar,
  8659. }
  8660.  
  8661. static void
  8662. +gb_command_bar_set_focus (GbCommandBar *self,
  8663. + GtkWidget *focus,
  8664. + GbWorkbench *workbench)
  8665. +{
  8666. + g_return_if_fail (GB_IS_COMMAND_BAR (self));
  8667. + g_return_if_fail (!focus || GTK_IS_WIDGET (focus));
  8668. + g_return_if_fail (GB_IS_WORKBENCH (workbench));
  8669. +
  8670. + if (!focus ||
  8671. + (!gtk_widget_is_ancestor (focus, GTK_WIDGET (self)) &&
  8672. + !gtk_widget_is_ancestor (focus, GTK_WIDGET (self->popover))))
  8673. + {
  8674. + gb_command_bar_hide (self);
  8675. + }
  8676. +}
  8677. +
  8678. +static void
  8679. +gb_command_bar_map (GtkWidget *widget)
  8680. +{
  8681. + GbCommandBar *self = (GbCommandBar *)widget;
  8682. + GtkWidget *toplevel;
  8683. +
  8684. + g_return_if_fail (GB_IS_COMMAND_BAR (self));
  8685. +
  8686. + GTK_WIDGET_CLASS (gb_command_bar_parent_class)->map (widget);
  8687. +
  8688. + toplevel = gtk_widget_get_toplevel (widget);
  8689. +
  8690. + if (GB_IS_WORKBENCH (toplevel))
  8691. + {
  8692. + gb_set_weak_pointer (toplevel, &self->workbench);
  8693. + self->set_focus_handler =
  8694. + g_signal_connect_object (toplevel,
  8695. + "set-focus",
  8696. + G_CALLBACK (gb_command_bar_set_focus),
  8697. + self,
  8698. + G_CONNECT_SWAPPED | G_CONNECT_AFTER);
  8699. + }
  8700. +}
  8701. +
  8702. +static void
  8703. +gb_command_bar_unmap (GtkWidget *widget)
  8704. +{
  8705. + GbCommandBar *self = (GbCommandBar *)widget;
  8706. +
  8707. + g_return_if_fail (GB_IS_COMMAND_BAR (self));
  8708. +
  8709. + if (self->workbench)
  8710. + {
  8711. + ide_clear_signal_handler (self->workbench, &self->set_focus_handler);
  8712. + ide_clear_weak_pointer (&self->workbench);
  8713. + }
  8714. +
  8715. + GTK_WIDGET_CLASS (gb_command_bar_parent_class)->unmap (widget);
  8716. +}
  8717. +
  8718. +static void
  8719. update_header_func (GtkListBoxRow *row,
  8720. GtkListBoxRow *before,
  8721. gpointer user_data)
  8722. @@ -604,12 +912,6 @@ gb_command_bar_constructed (GObject *object)
  8723. G_CONNECT_SWAPPED);
  8724.  
  8725. g_signal_connect_object (self->entry,
  8726. - "focus-out-event",
  8727. - G_CALLBACK (gb_command_bar_on_entry_focus_out_event),
  8728. - self,
  8729. - G_CONNECT_SWAPPED);
  8730. -
  8731. - g_signal_connect_object (self->entry,
  8732. "key-press-event",
  8733. G_CALLBACK (gb_command_bar_on_entry_key_press_event),
  8734. self,
  8735. @@ -621,8 +923,28 @@ gb_command_bar_constructed (GObject *object)
  8736. self,
  8737. G_CONNECT_SWAPPED);
  8738.  
  8739. + g_signal_connect_object (self->list_box,
  8740. + "button-press-event",
  8741. + G_CALLBACK (gb_command_bar_results_button_pressed_cb),
  8742. + self,
  8743. + G_CONNECT_SWAPPED);
  8744. +
  8745. + g_signal_connect_object (self->list_box,
  8746. + "popup-menu",
  8747. + G_CALLBACK (gb_command_bar_results_on_popup_menu_cb),
  8748. + self,
  8749. + G_CONNECT_SWAPPED);
  8750. +
  8751. + g_signal_connect_object (self->results_button,
  8752. + "toggled",
  8753. + G_CALLBACK (gb_command_bar_results_toggled_cb),
  8754. + self,
  8755. + G_CONNECT_SWAPPED);
  8756. +
  8757. gtk_list_box_set_header_func (self->list_box, update_header_func,
  8758. NULL, NULL);
  8759. +
  8760. + gtk_popover_set_relative_to (self->popover, GTK_WIDGET (self->command_box));
  8761. }
  8762.  
  8763. static void
  8764. @@ -670,6 +992,8 @@ gb_command_bar_class_init (GbCommandBarClass *klass)
  8765. object_class->finalize = gb_command_bar_finalize;
  8766. object_class->set_property = gb_command_bar_set_property;
  8767.  
  8768. + widget_class->map = gb_command_bar_map;
  8769. + widget_class->unmap = gb_command_bar_unmap;
  8770. widget_class->grab_focus = gb_command_bar_grab_focus;
  8771.  
  8772. gParamSpecs [PROP_WORKBENCH] =
  8773. @@ -729,6 +1053,9 @@ gb_command_bar_class_init (GbCommandBarClass *klass)
  8774.  
  8775. gtk_widget_class_bind_template_child (widget_class, GbCommandBar, entry);
  8776. gtk_widget_class_bind_template_child (widget_class, GbCommandBar, list_box);
  8777. + gtk_widget_class_bind_template_child (widget_class, GbCommandBar, results_button);
  8778. + gtk_widget_class_bind_template_child (widget_class, GbCommandBar, command_box);
  8779. + gtk_widget_class_bind_template_child (widget_class, GbCommandBar, popover);
  8780. gtk_widget_class_bind_template_child (widget_class, GbCommandBar, scroller);
  8781. gtk_widget_class_bind_template_child (widget_class, GbCommandBar, result_size_group);
  8782. gtk_widget_class_bind_template_child (widget_class, GbCommandBar, completion_scroller);
  8783. diff --git a/plugins/command-bar/gb-command-bar.gresource.xml b/plugins/command-bar/gb-command-bar.gresource.xml
  8784. index 6197d47..61b8564 100644
  8785. --- a/plugins/command-bar/gb-command-bar.gresource.xml
  8786. +++ b/plugins/command-bar/gb-command-bar.gresource.xml
  8787. @@ -2,5 +2,6 @@
  8788. <gresources>
  8789. <gresource prefix="/org/gnome/builder/plugins/command-bar">
  8790. <file>gb-command-bar.ui</file>
  8791. + <file>gb-command-bar-item.ui</file>
  8792. </gresource>
  8793. </gresources>
  8794. diff --git a/plugins/command-bar/gb-command-bar.ui b/plugins/command-bar/gb-command-bar.ui
  8795. index f5c997e..33624b5 100644
  8796. --- a/plugins/command-bar/gb-command-bar.ui
  8797. +++ b/plugins/command-bar/gb-command-bar.ui
  8798. @@ -3,28 +3,10 @@
  8799. <!-- interface-requires gtk+ 3.8 -->
  8800. <template class="GbCommandBar" parent="GtkBin">
  8801. <child>
  8802. - <object class="GtkBox" id="vbox1">
  8803. + <object class="GtkBox">
  8804. <property name="visible">True</property>
  8805. <property name="orientation">vertical</property>
  8806. <child>
  8807. - <object class="GtkScrolledWindow" id="scroller">
  8808. - <property name="hscrollbar_policy">GTK_POLICY_NEVER</property>
  8809. - <property name="visible">False</property>
  8810. - <property name="expand">True</property>
  8811. - <property name="height_request">300</property>
  8812. - <child>
  8813. - <object class="GtkListBox" id="list_box">
  8814. - <property name="visible">True</property>
  8815. - <property name="expand">True</property>
  8816. - <property name="selection_mode">GTK_SELECTION_NONE</property>
  8817. - <style>
  8818. - <class name="view"/>
  8819. - </style>
  8820. - </object>
  8821. - </child>
  8822. - </object>
  8823. - </child>
  8824. - <child>
  8825. <object class="GtkScrolledWindow" id="completion_scroller">
  8826. <property name="hscrollbar_policy">GTK_POLICY_NEVER</property>
  8827. <property name="visible">False</property>
  8828. @@ -43,12 +25,13 @@
  8829. </object>
  8830. </child>
  8831. <child>
  8832. - <object class="GtkBox">
  8833. + <object class="GtkBox" id="command_box">
  8834. <property name="visible">True</property>
  8835. <property name="orientation">horizontal</property>
  8836. <property name="spacing">3</property>
  8837. <style>
  8838. <class name="gb-command-bar-box"/>
  8839. + <class name="linked"/>
  8840. </style>
  8841. <child>
  8842. <object class="GtkLabel">
  8843. @@ -68,6 +51,25 @@
  8844. </style>
  8845. </object>
  8846. </child>
  8847. + <child>
  8848. + <object class="GtkMenuButton" id="results_button">
  8849. + <property name="visible">true</property>
  8850. + <property name="can-focus">True</property>
  8851. + <property name="popover">popover</property>
  8852. + <style>
  8853. + <class name="button"/>
  8854. + <class name="flat"/>
  8855. + </style>
  8856. + <child>
  8857. + <object class="GtkImage">
  8858. + <!-- we use margin-top here because .image-button doesn't work with 10pt font -->
  8859. + <property name="margin-top">3</property>
  8860. + <property name="visible">True</property>
  8861. + <property name="icon_name">go-up-symbolic</property>
  8862. + </object>
  8863. + </child>
  8864. + </object>
  8865. + </child>
  8866. </object>
  8867. </child>
  8868. </object>
  8869. @@ -76,4 +78,29 @@
  8870. <object class="GtkSizeGroup" id="result_size_group">
  8871. <property name="mode">GTK_SIZE_GROUP_HORIZONTAL</property>
  8872. </object>
  8873. + <object class="GtkPopover" id="popover">
  8874. + <property name="visible">true</property>
  8875. + <property name="hexpand">True</property>
  8876. + <property name="modal">false</property>
  8877. + <style>
  8878. + <class name="gb-command-bar-results"/>
  8879. + </style>
  8880. + <child>
  8881. + <object class="GbScrolledWindow" id="scroller">
  8882. + <property name="hscrollbar_policy">GTK_POLICY_NEVER</property>
  8883. + <property name="max-content-height">300</property>
  8884. + <property name="min-content-height">150</property>
  8885. + <property name="min-content-width">775</property>
  8886. + <property name="visible">True</property>
  8887. + <property name="expand">True</property>
  8888. + <child>
  8889. + <object class="GtkListBox" id="list_box">
  8890. + <property name="visible">True</property>
  8891. + <property name="expand">True</property>
  8892. + <property name="selection_mode">GTK_SELECTION_SINGLE</property>
  8893. + </object>
  8894. + </child>
  8895. + </object>
  8896. + </child>
  8897. + </object>
  8898. </interface>
  8899. diff --git a/plugins/command-bar/gb-command-complete-item.c b/plugins/command-bar/gb-command-complete-item.c
  8900. new file mode 100644
  8901. index 0000000..7a127ae
  8902. --- /dev/null
  8903. +++ b/plugins/command-bar/gb-command-complete-item.c
  8904. @@ -0,0 +1,113 @@
  8905. +/* gb-command-complete-item.c
  8906. + *
  8907. + * Copyright (C) 2015 Sébastien Lafargue <slafargue@gnome.org>
  8908. + *
  8909. + * This program is free software: you can redistribute it and/or modify
  8910. + * it under the terms of the GNU General Public License as published by
  8911. + * the Free Software Foundation, either version 3 of the License, or
  8912. + * (at your option) any later version.
  8913. + *
  8914. + * This program is distributed in the hope that it will be useful,
  8915. + * but WITHOUT ANY WARRANTY; without even the implied warranty of
  8916. + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  8917. + * GNU General Public License for more details.
  8918. + *
  8919. + * You should have received a copy of the GNU General Public License
  8920. + * along with this program. If not, see <http://www.gnu.org/licenses/>.
  8921. + */
  8922. +
  8923. +#include <string.h>
  8924. +
  8925. +#include <glib/gi18n.h>
  8926. +#include <glib/gprintf.h>
  8927. +#include <ide.h>
  8928. +
  8929. +#include "gb-command-complete-item.h"
  8930. +
  8931. +G_DEFINE_BOXED_TYPE (GbCommandCompleteItem, gb_command_complete_item, gb_command_complete_item_ref, gb_command_complete_item_unref)
  8932. +
  8933. +struct _GbCommandCompleteItem
  8934. +{
  8935. + volatile gint ref_count;
  8936. +
  8937. + GbCommandCompleteItemKind kind;
  8938. + gchar *name;
  8939. + gchar *shortname;
  8940. +};
  8941. +
  8942. +
  8943. +const gchar *
  8944. +gb_command_complete_item_get_name (GbCommandCompleteItem *self)
  8945. +{
  8946. + g_return_val_if_fail (self, NULL);
  8947. +
  8948. + return self->name;
  8949. +}
  8950. +
  8951. +const gchar *
  8952. +gb_command_complete_item_get_shortname (GbCommandCompleteItem *self)
  8953. +{
  8954. + g_return_val_if_fail (self, NULL);
  8955. +
  8956. + return self->shortname;
  8957. +}
  8958. +
  8959. +GbCommandCompleteItemKind
  8960. +gb_command_complete_item_get_kind (GbCommandCompleteItem *self)
  8961. +{
  8962. + g_return_val_if_fail (self, GB_COMMAND_COMPLETE_ITEM_KIND_NONE);
  8963. +
  8964. + return self->kind;
  8965. +}
  8966. +
  8967. +static void
  8968. +gb_command_complete_item_finalize (GbCommandCompleteItem *self)
  8969. +{
  8970. + g_clear_pointer (&self->name, g_free);
  8971. + g_clear_pointer (&self->shortname, g_free);
  8972. +
  8973. + g_free (self);
  8974. +}
  8975. +
  8976. +GbCommandCompleteItem *
  8977. +gb_command_complete_item_ref (GbCommandCompleteItem *self)
  8978. +{
  8979. + g_return_val_if_fail (self, NULL);
  8980. + g_return_val_if_fail (self->ref_count > 0, NULL);
  8981. +
  8982. + g_atomic_int_inc (&self->ref_count);
  8983. +
  8984. + return self;
  8985. +}
  8986. +
  8987. +void
  8988. +gb_command_complete_item_unref (GbCommandCompleteItem *self)
  8989. +{
  8990. + g_return_if_fail (self);
  8991. + g_return_if_fail (self->ref_count > 0);
  8992. +
  8993. + if (g_atomic_int_dec_and_test (&self->ref_count))
  8994. + gb_command_complete_item_finalize (self);
  8995. +}
  8996. +
  8997. +GbCommandCompleteItem *
  8998. +gb_command_complete_item_new (GbCommandCompleteItemKind kind,
  8999. + const gchar *name,
  9000. + const gchar *shortname)
  9001. +{
  9002. +
  9003. + GbCommandCompleteItem *self;
  9004. +
  9005. + g_return_val_if_fail (!ide_str_empty0 (name), NULL);
  9006. + g_return_val_if_fail (kind < GB_COMMAND_COMPLETE_ITEM_KIND_LAST, NULL);
  9007. +
  9008. + self = g_new0 (GbCommandCompleteItem, 1);
  9009. + self->ref_count = 1;
  9010. +
  9011. + self->kind = kind;
  9012. + self->name = g_strdup (name);
  9013. + if (!ide_str_empty0 (shortname))
  9014. + self->shortname = g_strdup (shortname);
  9015. +
  9016. + return self;
  9017. +}
  9018. diff --git a/plugins/command-bar/gb-command-complete-item.h b/plugins/command-bar/gb-command-complete-item.h
  9019. new file mode 100644
  9020. index 0000000..8a7eaeb
  9021. --- /dev/null
  9022. +++ b/plugins/command-bar/gb-command-complete-item.h
  9023. @@ -0,0 +1,53 @@
  9024. +/* gb-command-complete-item.h
  9025. + *
  9026. + * Copyright (C) 2015 Sébastien Lafargue <slafargue@gnome.org>
  9027. + *
  9028. + * This program is free software: you can redistribute it and/or modify
  9029. + * it under the terms of the GNU General Public License as published by
  9030. + * the Free Software Foundation, either version 3 of the License, or
  9031. + * (at your option) any later version.
  9032. + *
  9033. + * This program is distributed in the hope that it will be useful,
  9034. + * but WITHOUT ANY WARRANTY; without even the implied warranty of
  9035. + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  9036. + * GNU General Public License for more details.
  9037. + *
  9038. + * You should have received a copy of the GNU General Public License
  9039. + * along with this program. If not, see <http://www.gnu.org/licenses/>.
  9040. + */
  9041. +
  9042. +#ifndef GB_COMMAND_COMPLETE_ITEM_H
  9043. +#define GB_COMMAND_COMPLETE_ITEM_H
  9044. +
  9045. +#include <glib-object.h>
  9046. +
  9047. +G_BEGIN_DECLS
  9048. +
  9049. +#define GB_TYPE_COMMAND_COMPLETE_ITEM (gb_command_complete_item_get_type())
  9050. +
  9051. +typedef struct _GbCommandCompleteItem GbCommandCompleteItem;
  9052. +
  9053. +typedef enum
  9054. +{
  9055. + GB_COMMAND_COMPLETE_ITEM_KIND_NONE,
  9056. + GB_COMMAND_COMPLETE_ITEM_KIND_SET,
  9057. + GB_COMMAND_COMPLETE_ITEM_KIND_COLORSCHEME,
  9058. + GB_COMMAND_COMPLETE_ITEM_KIND_COMMAND,
  9059. + GB_COMMAND_COMPLETE_ITEM_KIND_PATH,
  9060. + GB_COMMAND_COMPLETE_ITEM_KIND_ACTION,
  9061. +
  9062. + GB_COMMAND_COMPLETE_ITEM_KIND_LAST
  9063. +} GbCommandCompleteItemKind;
  9064. +
  9065. +GbCommandCompleteItem *gb_command_complete_item_new (GbCommandCompleteItemKind kind,
  9066. + const gchar *name,
  9067. + const gchar *shortname);
  9068. +GbCommandCompleteItem *gb_command_complete_item_ref (GbCommandCompleteItem *self);
  9069. +void gb_command_complete_item_unref (GbCommandCompleteItem *self);
  9070. +GbCommandCompleteItemKind gb_command_complete_item_get_kind (GbCommandCompleteItem *self);
  9071. +const gchar *gb_command_complete_item_get_name (GbCommandCompleteItem *self);
  9072. +const gchar *gb_command_complete_item_get_shortname (GbCommandCompleteItem *self);
  9073. +
  9074. +G_END_DECLS
  9075. +
  9076. +#endif /* GB_COMMAND_COMPLETE_ITEM_H */
  9077. diff --git a/plugins/command-bar/gb-command-gaction-provider.c b/plugins/command-bar/gb-command-gaction-provider.c
  9078. index b9c26cb..7d139d6 100644
  9079. --- a/plugins/command-bar/gb-command-gaction-provider.c
  9080. +++ b/plugins/command-bar/gb-command-gaction-provider.c
  9081. @@ -26,6 +26,7 @@
  9082.  
  9083. #include "gb-command-gaction-provider.h"
  9084. #include "gb-command-gaction.h"
  9085. +#include "gb-command-complete-item.h"
  9086. #include "gb-view.h"
  9087.  
  9088. struct _GbCommandGactionProvider
  9089. @@ -334,8 +335,10 @@ search_action_in_maps (const gchar *command_name,
  9090. }
  9091.  
  9092. static GbCommand *
  9093. -gb_command_gaction_provider_lookup (GbCommandProvider *provider,
  9094. - const gchar *command_text)
  9095. +gb_command_gaction_provider_lookup (GbCommandProvider *provider,
  9096. + const gchar *command_text,
  9097. + GbCommandResult **result)
  9098. +
  9099. {
  9100. GbCommandGactionProvider *self = (GbCommandGactionProvider *)provider;
  9101. GbCommand *command = NULL;
  9102. @@ -348,7 +351,7 @@ gb_command_gaction_provider_lookup (GbCommandProvider *provider,
  9103. const gchar *new_command_name = NULL;
  9104. const gchar *action_name = NULL;
  9105. const gchar *prefix = NULL;
  9106. - gboolean result = FALSE;
  9107. + gboolean ret = FALSE;
  9108.  
  9109. IDE_ENTRY;
  9110.  
  9111. @@ -370,13 +373,13 @@ gb_command_gaction_provider_lookup (GbCommandProvider *provider,
  9112.  
  9113. if (g_str_equal (prefix, gb_group->prefix) && g_action_group_has_action (group, action_name))
  9114. {
  9115. - result = TRUE;
  9116. + ret = TRUE;
  9117. break;
  9118. }
  9119. }
  9120. }
  9121.  
  9122. - if (!result)
  9123. + if (!ret)
  9124. {
  9125. for (iter = gb_groups; iter; iter = iter->next)
  9126. {
  9127. @@ -389,20 +392,22 @@ gb_command_gaction_provider_lookup (GbCommandProvider *provider,
  9128. prefix = gb_group->prefix;
  9129. if (search_command_in_maps (command_name, prefix, &new_command_name))
  9130. {
  9131. - result = FALSE;
  9132. + ret = FALSE;
  9133. break;
  9134. }
  9135.  
  9136. action_name = command_name;
  9137. - result = TRUE;
  9138. + ret = TRUE;
  9139. break;
  9140. }
  9141. }
  9142. }
  9143.  
  9144. - if (result)
  9145. + if (ret)
  9146. {
  9147. command = g_object_new (GB_TYPE_COMMAND_GACTION,
  9148. + "name", action_name,
  9149. + "shortname", NULL,
  9150. "action-group", group,
  9151. "action-name", action_name,
  9152. "parameters", params,
  9153. @@ -416,6 +421,16 @@ gb_command_gaction_provider_lookup (GbCommandProvider *provider,
  9154. IDE_RETURN (command);
  9155. }
  9156.  
  9157. +static inline GbCommandCompleteItem *
  9158. +create_completion_item (const gchar *name)
  9159. +{
  9160. + GbCommandCompleteItem *item;
  9161. +
  9162. + item = gb_command_complete_item_new (GB_COMMAND_COMPLETE_ITEM_KIND_ACTION, name, NULL);
  9163. +
  9164. + return item;
  9165. +}
  9166. +
  9167. static void
  9168. gb_command_gaction_provider_complete (GbCommandProvider *provider,
  9169. GPtrArray *completions,
  9170. @@ -450,13 +465,13 @@ gb_command_gaction_provider_complete (GbCommandProvider *provider,
  9171. if (search_command_in_maps (names [i], prefix, &command_name))
  9172. {
  9173. if (command_name != NULL && g_str_has_prefix (command_name, initial_command_text))
  9174. - g_ptr_array_add (completions, g_strdup (command_name));
  9175. + g_ptr_array_add (completions, create_completion_item (command_name));
  9176.  
  9177. continue;
  9178. }
  9179.  
  9180. if (g_str_has_prefix (names [i], initial_command_text))
  9181. - g_ptr_array_add (completions, g_strdup (names [i]));
  9182. + g_ptr_array_add (completions, create_completion_item (names [i]));
  9183. }
  9184.  
  9185. g_free (names);
  9186. diff --git a/plugins/command-bar/gb-command-gaction-provider.h b/plugins/command-bar/gb-command-gaction-provider.h
  9187. index aeb4e50..0d8db25 100644
  9188. --- a/plugins/command-bar/gb-command-gaction-provider.h
  9189. +++ b/plugins/command-bar/gb-command-gaction-provider.h
  9190. @@ -20,6 +20,7 @@
  9191. #define GB_COMMAND_GACTION_PROVIDER_H
  9192.  
  9193. #include "gb-command-provider.h"
  9194. +#include "gb-command-result.h"
  9195.  
  9196. G_BEGIN_DECLS
  9197.  
  9198. diff --git a/plugins/command-bar/gb-command-manager.c b/plugins/command-bar/gb-command-manager.c
  9199. index 896ae7a..4f81822 100644
  9200. --- a/plugins/command-bar/gb-command-manager.c
  9201. +++ b/plugins/command-bar/gb-command-manager.c
  9202. @@ -20,6 +20,7 @@
  9203.  
  9204. #include <string.h>
  9205.  
  9206. +#include "gb-command-complete-item.h"
  9207. #include "gb-command-manager.h"
  9208. #include "gb-workbench.h"
  9209.  
  9210. @@ -91,21 +92,23 @@ gb_command_manager_add_provider (GbCommandManager *manager,
  9211. }
  9212.  
  9213. GbCommand *
  9214. -gb_command_manager_lookup (GbCommandManager *manager,
  9215. - const gchar *command_text)
  9216. +gb_command_manager_lookup (GbCommandManager *manager,
  9217. + const gchar *command_text,
  9218. + GbCommandResult **result)
  9219. {
  9220. GbCommand *ret = NULL;
  9221. guint i;
  9222.  
  9223. g_return_val_if_fail (GB_IS_COMMAND_MANAGER (manager), NULL);
  9224. g_return_val_if_fail (command_text, NULL);
  9225. + g_return_val_if_fail (result, NULL);
  9226.  
  9227. for (i = 0; i < manager->providers->len; i++)
  9228. {
  9229. GbCommandProvider *provider;
  9230.  
  9231. provider = g_ptr_array_index (manager->providers, i);
  9232. - ret = gb_command_provider_lookup (provider, command_text);
  9233. + ret = gb_command_provider_lookup (provider, command_text, result);
  9234.  
  9235. if (ret)
  9236. return ret;
  9237. @@ -114,14 +117,45 @@ gb_command_manager_lookup (GbCommandManager *manager,
  9238. return NULL;
  9239. }
  9240.  
  9241. +gboolean
  9242. +gb_command_manager_validate (GbCommandManager *manager,
  9243. + const gchar *initial_command_text,
  9244. + GbCommandResult **result)
  9245. +{
  9246. + gboolean ret = FALSE;
  9247. + guint i;
  9248. +
  9249. + g_return_val_if_fail (GB_IS_COMMAND_MANAGER (manager), FALSE);
  9250. + g_return_val_if_fail (initial_command_text, FALSE);
  9251. + g_return_val_if_fail (result, FALSE);
  9252. +
  9253. + for (i = 0; i < manager->providers->len; i++)
  9254. + {
  9255. + GbCommandProvider *provider;
  9256. +
  9257. + provider = g_ptr_array_index (manager->providers, i);
  9258. + ret = gb_command_provider_validate (provider, initial_command_text, result);
  9259. + if (ret)
  9260. + break;
  9261. + }
  9262. +
  9263. + return ret;
  9264. +}
  9265. +
  9266. static gint
  9267. -sort_strings (const gchar * const * a,
  9268. - const gchar * const * b)
  9269. +sort_command_names (GbCommandCompleteItem **i1,
  9270. + GbCommandCompleteItem **i2)
  9271. {
  9272. - return strcmp (*a, *b);
  9273. + const gchar *name_i1;
  9274. + const gchar *name_i2;
  9275. +
  9276. + name_i1 = gb_command_complete_item_get_name (*i1);
  9277. + name_i2 = gb_command_complete_item_get_name (*i2);
  9278. +
  9279. + return strcmp (name_i1, name_i2);
  9280. }
  9281.  
  9282. -gchar **
  9283. +GPtrArray *
  9284. gb_command_manager_complete (GbCommandManager *manager,
  9285. const gchar *initial_command_text)
  9286. {
  9287. @@ -131,7 +165,7 @@ gb_command_manager_complete (GbCommandManager *manager,
  9288. g_return_val_if_fail (GB_IS_COMMAND_MANAGER (manager), NULL);
  9289. g_return_val_if_fail (initial_command_text, NULL);
  9290.  
  9291. - completions = g_ptr_array_new ();
  9292. + completions = g_ptr_array_new_full (32, (GDestroyNotify)gb_command_complete_item_unref);
  9293.  
  9294. for (i = 0; i < manager->providers->len; i++)
  9295. {
  9296. @@ -141,11 +175,14 @@ gb_command_manager_complete (GbCommandManager *manager,
  9297. gb_command_provider_complete (provider, completions, initial_command_text);
  9298. }
  9299.  
  9300. - g_ptr_array_sort (completions, (GCompareFunc)sort_strings);
  9301. -
  9302. - g_ptr_array_add (completions, NULL);
  9303. + if (completions->len == 0)
  9304. + {
  9305. + g_ptr_array_free (completions, TRUE);
  9306. + return NULL;
  9307. + }
  9308.  
  9309. - return (gchar **)g_ptr_array_free (completions, FALSE);
  9310. + g_ptr_array_sort (completions, (GCompareFunc)sort_command_names);
  9311. + return completions;
  9312. }
  9313.  
  9314.  
  9315. diff --git a/plugins/command-bar/gb-command-manager.h b/plugins/command-bar/gb-command-manager.h
  9316. index 2edb9d2..b1b551e 100644
  9317. --- a/plugins/command-bar/gb-command-manager.h
  9318. +++ b/plugins/command-bar/gb-command-manager.h
  9319. @@ -23,6 +23,7 @@
  9320.  
  9321. #include "gb-command.h"
  9322. #include "gb-command-provider.h"
  9323. +#include "gb-command-result.h"
  9324.  
  9325. G_BEGIN_DECLS
  9326.  
  9327. @@ -31,12 +32,16 @@ G_BEGIN_DECLS
  9328. G_DECLARE_FINAL_TYPE (GbCommandManager, gb_command_manager, GB, COMMAND_MANAGER, GObject)
  9329.  
  9330. GbCommandManager *gb_command_manager_new (void);
  9331. -GbCommand *gb_command_manager_lookup (GbCommandManager *manager,
  9332. - const gchar *command_text);
  9333. -gchar **gb_command_manager_complete (GbCommandManager *manager,
  9334. - const gchar *initial_command_text);
  9335. -void gb_command_manager_add_provider (GbCommandManager *manager,
  9336. - GbCommandProvider *provider);
  9337. +GbCommand *gb_command_manager_lookup (GbCommandManager *manager,
  9338. + const gchar *command_text,
  9339. + GbCommandResult **result);
  9340. +gboolean gb_command_manager_validate (GbCommandManager *manager,
  9341. + const gchar *initial_command_text,
  9342. + GbCommandResult **result);
  9343. +GPtrArray *gb_command_manager_complete (GbCommandManager *manager,
  9344. + const gchar *initial_command_text);
  9345. +void gb_command_manager_add_provider (GbCommandManager *manager,
  9346. + GbCommandProvider *provider);
  9347.  
  9348. G_END_DECLS
  9349.  
  9350. diff --git a/plugins/command-bar/gb-command-provider.c b/plugins/command-bar/gb-command-provider.c
  9351. index 15b4d92..1bb3427 100644
  9352. --- a/plugins/command-bar/gb-command-provider.c
  9353. +++ b/plugins/command-bar/gb-command-provider.c
  9354. @@ -41,6 +41,7 @@ enum {
  9355. enum {
  9356. LOOKUP,
  9357. COMPLETE,
  9358. + VALIDATE,
  9359. LAST_SIGNAL
  9360. };
  9361.  
  9362. @@ -218,20 +219,47 @@ gb_command_provider_set_priority (GbCommandProvider *provider,
  9363. * gb_command_provider_lookup:
  9364. * @provider: (in): The #GbCommandProvider
  9365. * @command_text: (in): Command text to be parsed
  9366. - *
  9367. + * @result: (out): #GbResult giving the result of the lookup.
  9368. *
  9369. * Returns: (transfer full): A #GbCommand if successful; otherwise %NULL.
  9370. */
  9371. GbCommand *
  9372. -gb_command_provider_lookup (GbCommandProvider *provider,
  9373. - const gchar *command_text)
  9374. +gb_command_provider_lookup (GbCommandProvider *provider,
  9375. + const gchar *command_text,
  9376. + GbCommandResult **result)
  9377. {
  9378. GbCommand *ret = NULL;
  9379.  
  9380. g_return_val_if_fail (GB_IS_COMMAND_PROVIDER (provider), NULL);
  9381. g_return_val_if_fail (command_text, NULL);
  9382. + g_return_val_if_fail (result, NULL);
  9383.  
  9384. - g_signal_emit (provider, gSignals [LOOKUP], 0, command_text, &ret);
  9385. + g_signal_emit (provider, gSignals [LOOKUP], 0, command_text, result, &ret);
  9386. +
  9387. + return ret;
  9388. +}
  9389. +
  9390. +/**
  9391. + * gb_command_provider_validate:
  9392. + * @provider: (in): The #GbCommandProvider
  9393. + * @initial_command_text: (in): Command text to be validated. Can be part of or
  9394. + * the whole command text.
  9395. + * @result: (out): #GbResult giving the result of the validation.
  9396. + *
  9397. + * Returns: TRUE if successful, FALSE overwise.
  9398. + */
  9399. +gboolean
  9400. +gb_command_provider_validate (GbCommandProvider *provider,
  9401. + const gchar *initial_command_text,
  9402. + GbCommandResult **result)
  9403. +{
  9404. + gboolean ret;
  9405. +
  9406. + g_return_val_if_fail (GB_IS_COMMAND_PROVIDER (provider), FALSE);
  9407. + g_return_val_if_fail (initial_command_text, FALSE);
  9408. + g_return_val_if_fail (result, FALSE);
  9409. +
  9410. + g_signal_emit (provider, gSignals [VALIDATE], 0, initial_command_text, result, &ret);
  9411.  
  9412. return ret;
  9413. }
  9414. @@ -363,14 +391,12 @@ gb_command_provider_class_init (GbCommandProviderClass *klass)
  9415. /**
  9416. * GbCommandProvider::lookup:
  9417. * @command_text: (in): the command line text to be processed.
  9418. - * @parameter: (out): a location to store any parsed parameters.
  9419. + * @result: (out): adress of a #GbResult pointer, giving the result of the lookup.
  9420. *
  9421. * This signal is emitted when a request to parse the command text is
  9422. * received. Only the first handler will respond to the action. The
  9423. - * callee should return a GAction if successful, otherwise %NULL.
  9424. + * callee should return a #GbCommand if successful, otherwise %NULL.
  9425. *
  9426. - * If successful, the callee can set @parameter, to specify the
  9427. - * parameters that should be passed to the resulting action.
  9428. */
  9429. gSignals [LOOKUP] =
  9430. g_signal_new ("lookup",
  9431. @@ -381,13 +407,39 @@ gb_command_provider_class_init (GbCommandProviderClass *klass)
  9432. NULL,
  9433. NULL,
  9434. GB_TYPE_COMMAND,
  9435. - 1,
  9436. - G_TYPE_STRING);
  9437. + 2,
  9438. + G_TYPE_STRING,
  9439. + G_TYPE_POINTER);
  9440. +
  9441. + /**
  9442. + * GbCommandProvider::validate:
  9443. + * @initial_command_text: (in): the command line text to be processed.
  9444. + * @result: (out): adress of a #GbResult pointer, giving the result of the validation.
  9445. + *
  9446. + * This signal is emitted when a request to validate the command text is
  9447. + * received. Only the first handler will respond to the action.
  9448. + *
  9449. + * If the callee can parsed the initial text, TRUE is returned, FALSE otherwise.
  9450. + */
  9451. + gSignals [VALIDATE] =
  9452. + g_signal_new ("validate",
  9453. + GB_TYPE_COMMAND_PROVIDER,
  9454. + G_SIGNAL_RUN_LAST,
  9455. + G_STRUCT_OFFSET (GbCommandProviderClass, validate),
  9456. + g_signal_accumulator_first_wins,
  9457. + NULL,
  9458. + NULL,
  9459. + G_TYPE_BOOLEAN,
  9460. + 2,
  9461. + G_TYPE_STRING,
  9462. + G_TYPE_POINTER);
  9463.  
  9464. /**
  9465. * GbCommandProvider::complete:
  9466. * @completions: (in): A #GPtrArray where completed strings can be added
  9467. * @initial_command_text: (in): the command line text to be processed.
  9468. + * @with_short_name: (in): indicate if you want the short name of the command and
  9469. + * the remainder separated with a '/'
  9470. *
  9471. * This signal is emitted when a request to complete a command text is
  9472. * received. All providers should all all possible completions, matching
  9473. @@ -402,9 +454,10 @@ gb_command_provider_class_init (GbCommandProviderClass *klass)
  9474. NULL,
  9475. NULL,
  9476. G_TYPE_NONE,
  9477. - 2,
  9478. + 3,
  9479. G_TYPE_PTR_ARRAY,
  9480. - G_TYPE_STRING);
  9481. + G_TYPE_STRING,
  9482. + G_TYPE_BOOLEAN);
  9483. }
  9484.  
  9485. static void
  9486. diff --git a/plugins/command-bar/gb-command-provider.h b/plugins/command-bar/gb-command-provider.h
  9487. index f1ee95b..80ff805 100644
  9488. --- a/plugins/command-bar/gb-command-provider.h
  9489. +++ b/plugins/command-bar/gb-command-provider.h
  9490. @@ -24,6 +24,7 @@
  9491. #include "gb-command.h"
  9492. #include "gb-view.h"
  9493. #include "gb-workbench-types.h"
  9494. +#include "gb-command-result.h"
  9495.  
  9496. G_BEGIN_DECLS
  9497.  
  9498. @@ -35,24 +36,32 @@ struct _GbCommandProviderClass
  9499. {
  9500. GObjectClass parent;
  9501.  
  9502. - GbCommand *(*lookup) (GbCommandProvider *provider,
  9503. - const gchar *command_text);
  9504. - void (*complete) (GbCommandProvider *provider,
  9505. - GPtrArray *completions,
  9506. - const gchar *command_text);
  9507. + GbCommand *(*lookup) (GbCommandProvider *provider,
  9508. + const gchar *command_text,
  9509. + GbCommandResult **result);
  9510. + gboolean (*validate) (GbCommandProvider *provider,
  9511. + const gchar *command_text,
  9512. + GbCommandResult **result);
  9513. + void (*complete) (GbCommandProvider *provider,
  9514. + GPtrArray *completions,
  9515. + const gchar *command_text);
  9516. };
  9517.  
  9518. -GbCommandProvider *gb_command_provider_new (GbWorkbench *workbench);
  9519. -GbWorkbench *gb_command_provider_get_workbench (GbCommandProvider *provider);
  9520. -GbView *gb_command_provider_get_active_view (GbCommandProvider *provider);
  9521. -gint gb_command_provider_get_priority (GbCommandProvider *provider);
  9522. -void gb_command_provider_set_priority (GbCommandProvider *provider,
  9523. - gint priority);
  9524. -GbCommand *gb_command_provider_lookup (GbCommandProvider *provider,
  9525. - const gchar *command_text);
  9526. -void gb_command_provider_complete (GbCommandProvider *provider,
  9527. - GPtrArray *completions,
  9528. - const gchar *initial_command_text);
  9529. +GbCommandProvider *gb_command_provider_new (GbWorkbench *workbench);
  9530. +GbWorkbench *gb_command_provider_get_workbench (GbCommandProvider *provider);
  9531. +GbView *gb_command_provider_get_active_view (GbCommandProvider *provider);
  9532. +gint gb_command_provider_get_priority (GbCommandProvider *provider);
  9533. +void gb_command_provider_set_priority (GbCommandProvider *provider,
  9534. + gint priority);
  9535. +GbCommand *gb_command_provider_lookup (GbCommandProvider *provider,
  9536. + const gchar *command_text,
  9537. + GbCommandResult **result);
  9538. +gboolean gb_command_provider_validate (GbCommandProvider *provider,
  9539. + const gchar *initial_command_text,
  9540. + GbCommandResult **result);
  9541. +void gb_command_provider_complete (GbCommandProvider *provider,
  9542. + GPtrArray *completions,
  9543. + const gchar *initial_command_text);
  9544.  
  9545. G_END_DECLS
  9546.  
  9547. diff --git a/plugins/command-bar/gb-command-vim-provider.c b/plugins/command-bar/gb-command-vim-provider.c
  9548. index e12484a..b45b159 100644
  9549. --- a/plugins/command-bar/gb-command-vim-provider.c
  9550. +++ b/plugins/command-bar/gb-command-vim-provider.c
  9551. @@ -31,12 +31,13 @@
  9552.  
  9553. struct _GbCommandVimProvider
  9554. {
  9555. - GbCommandProvider parent_instance;
  9556. + GbCommandProvider parent_instance;
  9557. + IdeVimParser *parser;
  9558. };
  9559.  
  9560. G_DEFINE_TYPE (GbCommandVimProvider, gb_command_vim_provider, GB_TYPE_COMMAND_PROVIDER)
  9561.  
  9562. -GtkWidget *
  9563. +static GtkWidget *
  9564. get_source_view (GbCommandProvider *provider)
  9565. {
  9566. GbWorkbench *workbench;
  9567. @@ -64,20 +65,61 @@ get_source_view (GbCommandProvider *provider)
  9568. }
  9569.  
  9570. static GbCommand *
  9571. -gb_command_vim_provider_lookup (GbCommandProvider *provider,
  9572. - const gchar *command_text)
  9573. +gb_command_vim_provider_lookup (GbCommandProvider *provider,
  9574. + const gchar *command_text,
  9575. + GbCommandResult **result)
  9576. {
  9577. GtkWidget *source_view;
  9578. + IdeVimParser *parser;
  9579. + IdeVimParserCommand *command;
  9580. + IdeVimParserError *error = NULL;
  9581. + g_autofree gchar *detail = NULL;
  9582. + const gchar *message;
  9583.  
  9584. g_return_val_if_fail (GB_IS_COMMAND_VIM_PROVIDER (provider), NULL);
  9585. g_return_val_if_fail (command_text, NULL);
  9586.  
  9587. source_view = get_source_view (provider);
  9588. + parser = GB_COMMAND_VIM_PROVIDER (provider)->parser;
  9589.  
  9590. - return g_object_new (GB_TYPE_COMMAND_VIM,
  9591. - "command-text", command_text,
  9592. - "source-view", source_view,
  9593. - NULL);
  9594. + command = gb_vim_lookup (GTK_SOURCE_VIEW (source_view), parser, command_text, &error);
  9595. + if (command != NULL)
  9596. + {
  9597. + return g_object_new (GB_TYPE_COMMAND_VIM,
  9598. + "command-text", command_text,
  9599. + "source-view", source_view,
  9600. + "vim-parser", parser,
  9601. + "command", command,
  9602. + NULL);
  9603. + }
  9604. +
  9605. + if (error != NULL)
  9606. + {
  9607. + message = ide_vim_parser_error_get_message (ide_vim_parser_error_get_error_code (error));
  9608. + detail = ide_vim_parser_error_get_detail (error);
  9609. + *result = g_object_new (GB_TYPE_COMMAND_RESULT,
  9610. + "is-error", TRUE,
  9611. + "command-text", message,
  9612. + "result-text", detail,
  9613. + NULL);
  9614. + }
  9615. +
  9616. + return NULL;
  9617. +}
  9618. +
  9619. +static gboolean
  9620. +gb_command_vim_provider_validate (GbCommandProvider *provider,
  9621. + const gchar *command_text,
  9622. + GbCommandResult **result)
  9623. +{
  9624. + GtkWidget *source_view;
  9625. +
  9626. + g_return_val_if_fail (GB_IS_COMMAND_VIM_PROVIDER (provider), FALSE);
  9627. + g_return_val_if_fail (command_text, FALSE);
  9628. +
  9629. + source_view = get_source_view (provider);
  9630. +
  9631. + return TRUE;
  9632. }
  9633.  
  9634. static void
  9635. @@ -85,32 +127,157 @@ gb_command_vim_provider_complete (GbCommandProvider *provider,
  9636. GPtrArray *completions,
  9637. const gchar *initial_command_text)
  9638. {
  9639. + GbCommandVimProvider *self = GB_COMMAND_VIM_PROVIDER (provider);
  9640. GtkWidget *source_view;
  9641. - gchar **results;
  9642. + GPtrArray *results;
  9643. gsize i;
  9644.  
  9645. - g_return_if_fail (GB_IS_COMMAND_VIM_PROVIDER (provider));
  9646. g_return_if_fail (completions);
  9647. g_return_if_fail (initial_command_text);
  9648.  
  9649. source_view = get_source_view (provider);
  9650.  
  9651. - results = gb_vim_complete (GTK_SOURCE_VIEW (source_view), initial_command_text);
  9652. - for (i = 0; results [i]; i++)
  9653. - g_ptr_array_add (completions, results [i]);
  9654. - g_free (results);
  9655. + results = gb_vim_complete (self->parser, GTK_SOURCE_VIEW (source_view), initial_command_text);
  9656. + if (results != NULL)
  9657. + {
  9658. + for (i = 0; i < results->len; i++)
  9659. + g_ptr_array_add (completions, g_ptr_array_index (results, i));
  9660. +
  9661. + /* We need to keep the elememts */
  9662. + g_ptr_array_set_free_func (results, NULL);
  9663. + g_ptr_array_free (results, TRUE);
  9664. + }
  9665. +}
  9666. +
  9667. +static void
  9668. +gb_command_vim_provider_fill_command_table (IdeVimParser *parser)
  9669. +{
  9670. + ide_vim_parser_add_command (parser, "bnext", "bn", gb_vim_command_bnext,
  9671. + IDE_VIM_PARSER_TOKEN_NUMBER, IDE_VIM_PARSER_TOKEN_FLAG_OPTIONNAL,
  9672. + IDE_VIM_PARSER_TOKEN_COMMAND_NAME, IDE_VIM_PARSER_TOKEN_FLAG_NONE,
  9673. + IDE_VIM_PARSER_TOKEN_NUMBER, IDE_VIM_PARSER_TOKEN_FLAG_OPTIONNAL,
  9674. + NULL);
  9675. +
  9676. + ide_vim_parser_add_command (parser, "bprevious", "bp", gb_vim_command_bprevious,
  9677. + IDE_VIM_PARSER_TOKEN_NUMBER, IDE_VIM_PARSER_TOKEN_FLAG_OPTIONNAL,
  9678. + IDE_VIM_PARSER_TOKEN_COMMAND_NAME, IDE_VIM_PARSER_TOKEN_FLAG_NONE,
  9679. + IDE_VIM_PARSER_TOKEN_NUMBER, IDE_VIM_PARSER_TOKEN_FLAG_OPTIONNAL,
  9680. + NULL);
  9681. +
  9682. + ide_vim_parser_add_command (parser, "cnext", "cn", gb_vim_command_cnext,
  9683. + IDE_VIM_PARSER_TOKEN_NUMBER, IDE_VIM_PARSER_TOKEN_FLAG_OPTIONNAL,
  9684. + IDE_VIM_PARSER_TOKEN_COMMAND_NAME, IDE_VIM_PARSER_TOKEN_FLAG_NONE,
  9685. + IDE_VIM_PARSER_TOKEN_NUMBER, IDE_VIM_PARSER_TOKEN_FLAG_OPTIONNAL,
  9686. + NULL);
  9687. +
  9688. + ide_vim_parser_add_command (parser, "cprevious", "cp", gb_vim_command_cprevious,
  9689. + IDE_VIM_PARSER_TOKEN_NUMBER, IDE_VIM_PARSER_TOKEN_FLAG_OPTIONNAL,
  9690. + IDE_VIM_PARSER_TOKEN_COMMAND_NAME, IDE_VIM_PARSER_TOKEN_FLAG_NONE,
  9691. + IDE_VIM_PARSER_TOKEN_NUMBER, IDE_VIM_PARSER_TOKEN_FLAG_OPTIONNAL,
  9692. + NULL);
  9693. +
  9694. + ide_vim_parser_add_command (parser, "buffers", NULL, gb_vim_command_buffers,
  9695. + IDE_VIM_PARSER_TOKEN_COMMAND_NAME, IDE_VIM_PARSER_TOKEN_FLAG_NONE,
  9696. + NULL);
  9697. +
  9698. + ide_vim_parser_add_command (parser, "ls", NULL, gb_vim_command_buffers,
  9699. + IDE_VIM_PARSER_TOKEN_COMMAND_NAME, IDE_VIM_PARSER_TOKEN_FLAG_NONE,
  9700. + NULL);
  9701. +
  9702. + ide_vim_parser_add_command (parser, "nohl", NULL, gb_vim_command_nohl,
  9703. + IDE_VIM_PARSER_TOKEN_COMMAND_NAME, IDE_VIM_PARSER_TOKEN_FLAG_NONE,
  9704. + NULL);
  9705. +
  9706. + ide_vim_parser_add_command (parser, "quit", NULL, gb_vim_command_quit,
  9707. + IDE_VIM_PARSER_TOKEN_COMMAND_NAME, IDE_VIM_PARSER_TOKEN_FLAG_NONE,
  9708. + NULL);
  9709. +
  9710. + ide_vim_parser_add_command (parser, "write", "w", gb_vim_command_write,
  9711. + IDE_VIM_PARSER_TOKEN_COMMAND_NAME, IDE_VIM_PARSER_TOKEN_FLAG_NONE,
  9712. + IDE_VIM_PARSER_TOKEN_STRING, IDE_VIM_PARSER_TOKEN_FLAG_OPTIONNAL,
  9713. + NULL);
  9714. +
  9715. + ide_vim_parser_add_command (parser, "wq", NULL, gb_vim_command_wq,
  9716. + IDE_VIM_PARSER_TOKEN_COMMAND_NAME, IDE_VIM_PARSER_TOKEN_FLAG_NONE,
  9717. + IDE_VIM_PARSER_TOKEN_STRING, IDE_VIM_PARSER_TOKEN_FLAG_OPTIONNAL,
  9718. + NULL);
  9719. +
  9720. + ide_vim_parser_add_command (parser, "split", "sp", gb_vim_command_split,
  9721. + IDE_VIM_PARSER_TOKEN_COMMAND_NAME, IDE_VIM_PARSER_TOKEN_FLAG_NONE,
  9722. + IDE_VIM_PARSER_TOKEN_STRING, IDE_VIM_PARSER_TOKEN_FLAG_OPTIONNAL,
  9723. + NULL);
  9724. +
  9725. + ide_vim_parser_add_command (parser, "vsplit", "vs", gb_vim_command_vsplit,
  9726. + IDE_VIM_PARSER_TOKEN_COMMAND_NAME, IDE_VIM_PARSER_TOKEN_FLAG_NONE,
  9727. + IDE_VIM_PARSER_TOKEN_STRING, IDE_VIM_PARSER_TOKEN_FLAG_OPTIONNAL,
  9728. + NULL);
  9729. +
  9730. + ide_vim_parser_add_command (parser, "colorscheme", NULL, gb_vim_command_colorscheme,
  9731. + IDE_VIM_PARSER_TOKEN_COMMAND_NAME, IDE_VIM_PARSER_TOKEN_FLAG_NONE,
  9732. + IDE_VIM_PARSER_TOKEN_STRING, IDE_VIM_PARSER_TOKEN_FLAG_NONE,
  9733. + NULL);
  9734. +
  9735. + ide_vim_parser_add_command (parser, "make", NULL, gb_vim_command_make,
  9736. + IDE_VIM_PARSER_TOKEN_COMMAND_NAME, IDE_VIM_PARSER_TOKEN_FLAG_NONE,
  9737. + NULL);
  9738. +
  9739. + ide_vim_parser_add_command (parser, "syntax", "sy", gb_vim_command_syntax,
  9740. + IDE_VIM_PARSER_TOKEN_COMMAND_NAME, IDE_VIM_PARSER_TOKEN_FLAG_NONE,
  9741. + IDE_VIM_PARSER_TOKEN_STRING, IDE_VIM_PARSER_TOKEN_FLAG_NONE,
  9742. + NULL);
  9743. +
  9744. + ide_vim_parser_add_command (parser, "help", NULL, gb_vim_command_help,
  9745. + IDE_VIM_PARSER_TOKEN_COMMAND_NAME, IDE_VIM_PARSER_TOKEN_FLAG_NONE,
  9746. + IDE_VIM_PARSER_TOKEN_STRING, IDE_VIM_PARSER_TOKEN_FLAG_NONE,
  9747. + NULL);
  9748. +
  9749. + ide_vim_parser_add_command (parser, "tabedit", "tabe", gb_vim_command_tabedit,
  9750. + IDE_VIM_PARSER_TOKEN_COMMAND_NAME, IDE_VIM_PARSER_TOKEN_FLAG_NONE,
  9751. + IDE_VIM_PARSER_TOKEN_STRING, IDE_VIM_PARSER_TOKEN_FLAG_OPTIONNAL,
  9752. + NULL);
  9753. +
  9754. + ide_vim_parser_add_command (parser, "edit", "e", gb_vim_command_edit,
  9755. + IDE_VIM_PARSER_TOKEN_COMMAND_NAME, IDE_VIM_PARSER_TOKEN_FLAG_NONE,
  9756. + IDE_VIM_PARSER_TOKEN_STRING, IDE_VIM_PARSER_TOKEN_FLAG_OPTIONNAL,
  9757. + NULL);
  9758. +
  9759. + ide_vim_parser_add_command (parser, "sort", "sor", gb_vim_command_sort,
  9760. + IDE_VIM_PARSER_TOKEN_COMMAND_NAME, IDE_VIM_PARSER_TOKEN_FLAG_NONE,
  9761. + NULL);
  9762. +
  9763. + ide_vim_parser_add_command (parser, "set", "se", gb_vim_command_set,
  9764. + IDE_VIM_PARSER_TOKEN_COMMAND_NAME, IDE_VIM_PARSER_TOKEN_FLAG_NONE,
  9765. + IDE_VIM_PARSER_TOKEN_STRING, IDE_VIM_PARSER_TOKEN_FLAG_NONE,
  9766. + NULL);
  9767. +}
  9768. +
  9769. +static void
  9770. +gb_command_vim_provider_finalize (GObject *object)
  9771. +{
  9772. + GbCommandVimProvider *self = GB_COMMAND_VIM_PROVIDER (object);
  9773. +
  9774. + g_clear_pointer (&self->parser, g_object_unref);
  9775. +
  9776. + G_OBJECT_CLASS (gb_command_vim_provider_parent_class)->finalize (object);
  9777. }
  9778.  
  9779. static void
  9780. gb_command_vim_provider_class_init (GbCommandVimProviderClass *klass)
  9781. {
  9782. + GObjectClass *object_class = G_OBJECT_CLASS (klass);
  9783. GbCommandProviderClass *provider_class = GB_COMMAND_PROVIDER_CLASS (klass);
  9784.  
  9785. + object_class->finalize = gb_command_vim_provider_finalize;
  9786. +
  9787. provider_class->lookup = gb_command_vim_provider_lookup;
  9788. provider_class->complete = gb_command_vim_provider_complete;
  9789. + provider_class->validate = gb_command_vim_provider_validate;
  9790. }
  9791.  
  9792. static void
  9793. gb_command_vim_provider_init (GbCommandVimProvider *self)
  9794. {
  9795. + self->parser = ide_vim_parser_new ();
  9796. +
  9797. + gb_command_vim_provider_fill_command_table (self->parser);
  9798. }
  9799. diff --git a/plugins/command-bar/gb-command-vim-provider.h b/plugins/command-bar/gb-command-vim-provider.h
  9800. index 5e4dffe..d9c6889 100644
  9801. --- a/plugins/command-bar/gb-command-vim-provider.h
  9802. +++ b/plugins/command-bar/gb-command-vim-provider.h
  9803. @@ -20,6 +20,7 @@
  9804. #define GB_COMMAND_VIM_PROVIDER_H
  9805.  
  9806. #include "gb-command-provider.h"
  9807. +#include "gb-command-result.h"
  9808.  
  9809. G_BEGIN_DECLS
  9810.  
  9811. diff --git a/plugins/command-bar/gb-command-vim.c b/plugins/command-bar/gb-command-vim.c
  9812. index bb0981a..74bdb5a 100644
  9813. --- a/plugins/command-bar/gb-command-vim.c
  9814. +++ b/plugins/command-bar/gb-command-vim.c
  9815. @@ -19,25 +19,30 @@
  9816. #define G_LOG_DOMAIN "gb-command-vim"
  9817.  
  9818. #include <glib/gi18n.h>
  9819. -#include <ide.h>
  9820. +#include <glib/gprintf.h>
  9821.  
  9822. +#include "gb-command-result.h"
  9823. #include "gb-command-vim.h"
  9824. #include "gb-vim.h"
  9825.  
  9826. struct _GbCommandVim
  9827. {
  9828. - GbCommand parent_instance;
  9829. + GbCommand parent_instance;
  9830.  
  9831. - IdeSourceView *source_view;
  9832. - gchar *command_text;
  9833. + IdeSourceView *source_view;
  9834. + IdeVimParser *vim_parser;
  9835. + IdeVimParserCommand *command;
  9836. + gchar *command_text;
  9837. };
  9838.  
  9839. G_DEFINE_TYPE (GbCommandVim, gb_command_vim, GB_TYPE_COMMAND)
  9840.  
  9841. enum {
  9842. PROP_0,
  9843. + PROP_COMMAND,
  9844. PROP_COMMAND_TEXT,
  9845. PROP_SOURCE_VIEW,
  9846. + PROP_VIM_PARSER,
  9847. LAST_PROP
  9848. };
  9849.  
  9850. @@ -62,6 +67,44 @@ gb_command_vim_set_source_view (GbCommandVim *vim,
  9851. g_object_notify_by_pspec (G_OBJECT (vim), gParamSpecs [PROP_SOURCE_VIEW]);
  9852. }
  9853.  
  9854. +IdeVimParserCommand *
  9855. +gb_command_vim_get_command (GbCommandVim *vim)
  9856. +{
  9857. + g_return_val_if_fail (GB_IS_COMMAND_VIM (vim), NULL);
  9858. +
  9859. + return vim->command;
  9860. +}
  9861. +
  9862. +static void
  9863. +gb_command_vim_set_command (GbCommandVim *vim,
  9864. + IdeVimParserCommand *command)
  9865. +{
  9866. + g_return_if_fail (GB_IS_COMMAND_VIM (vim));
  9867. + g_return_if_fail (IDE_IS_VIM_PARSER_COMMAND (command));
  9868. +
  9869. + if (ide_set_weak_pointer (&vim->command, command))
  9870. + g_object_notify_by_pspec (G_OBJECT (vim), gParamSpecs [PROP_COMMAND]);
  9871. +}
  9872. +
  9873. +IdeVimParser *
  9874. +gb_command_vim_get_vim_parser (GbCommandVim *vim)
  9875. +{
  9876. + g_return_val_if_fail (GB_IS_COMMAND_VIM (vim), NULL);
  9877. +
  9878. + return vim->vim_parser;
  9879. +}
  9880. +
  9881. +static void
  9882. +gb_command_vim_set_vim_parser (GbCommandVim *vim,
  9883. + IdeVimParser *vim_parser)
  9884. +{
  9885. + g_return_if_fail (GB_IS_COMMAND_VIM (vim));
  9886. + g_return_if_fail (IDE_IS_VIM_PARSER (vim_parser));
  9887. +
  9888. + if (ide_set_weak_pointer (&vim->vim_parser, vim_parser))
  9889. + g_object_notify_by_pspec (G_OBJECT (vim), gParamSpecs [PROP_VIM_PARSER]);
  9890. +}
  9891. +
  9892. const gchar *
  9893. gb_command_vim_get_command_text (GbCommandVim *vim)
  9894. {
  9895. @@ -70,16 +113,16 @@ gb_command_vim_get_command_text (GbCommandVim *vim)
  9896. return vim->command_text;
  9897. }
  9898.  
  9899. -void
  9900. +static void
  9901. gb_command_vim_set_command_text (GbCommandVim *vim,
  9902. const gchar *command_text)
  9903. {
  9904. g_return_if_fail (GB_IS_COMMAND_VIM (vim));
  9905. - g_return_if_fail (command_text);
  9906. + g_return_if_fail (command_text != NULL);
  9907.  
  9908. if (command_text != vim->command_text)
  9909. {
  9910. - g_free (vim->command_text);
  9911. + g_clear_pointer (&vim->command_text, g_free);
  9912. vim->command_text = g_strdup (command_text);
  9913. g_object_notify_by_pspec (G_OBJECT (vim), gParamSpecs [PROP_COMMAND_TEXT]);
  9914. }
  9915. @@ -89,24 +132,32 @@ static GbCommandResult *
  9916. gb_command_vim_execute (GbCommand *command)
  9917. {
  9918. GbCommandVim *self = (GbCommandVim *)command;
  9919. + IdeVimParserError *error = NULL;
  9920. + GbCommandResult *result = NULL;
  9921. + g_autofree gchar * detail = NULL;
  9922.  
  9923. g_return_val_if_fail (GB_IS_COMMAND_VIM (self), NULL);
  9924.  
  9925. - if (self->source_view)
  9926. + if (self->source_view != NULL && self->command != NULL)
  9927. {
  9928. GtkSourceView *source_view = (GtkSourceView *)self->source_view;
  9929. - GError *error = NULL;
  9930. + IdeVimParser *vim_parser = IDE_VIM_PARSER (self->vim_parser);
  9931.  
  9932. IDE_TRACE_MSG ("Executing Vim command: %s", self->command_text);
  9933.  
  9934. - if (!gb_vim_execute (source_view, self->command_text, &error))
  9935. + if (!gb_vim_execute (source_view, vim_parser, self->command, &error))
  9936. {
  9937. - g_warning ("%s", error->message);
  9938. - g_clear_error (&error);
  9939. + detail = ide_vim_parser_error_get_detail (error);
  9940. + g_printf ("execute, detail:%s\n", detail);
  9941. + result = g_object_new (GB_TYPE_COMMAND_RESULT,
  9942. + "is-error", TRUE,
  9943. + "command-text", error->code_text,
  9944. + "result-text", detail,
  9945. + NULL);
  9946. }
  9947. }
  9948.  
  9949. - return NULL;
  9950. + return result;
  9951. }
  9952.  
  9953. static void
  9954. @@ -115,6 +166,8 @@ gb_command_vim_finalize (GObject *object)
  9955. GbCommandVim *self = GB_COMMAND_VIM (object);
  9956.  
  9957. ide_clear_weak_pointer (&self->source_view);
  9958. + ide_clear_weak_pointer (&self->vim_parser);
  9959. + ide_clear_weak_pointer (&self->command);
  9960. g_clear_pointer (&self->command_text, g_free);
  9961.  
  9962. G_OBJECT_CLASS (gb_command_vim_parent_class)->finalize (object);
  9963. @@ -130,6 +183,10 @@ gb_command_vim_get_property (GObject *object,
  9964.  
  9965. switch (prop_id)
  9966. {
  9967. + case PROP_COMMAND:
  9968. + g_value_set_object (value, gb_command_vim_get_command (self));
  9969. + break;
  9970. +
  9971. case PROP_COMMAND_TEXT:
  9972. g_value_set_string (value, gb_command_vim_get_command_text (self));
  9973. break;
  9974. @@ -138,6 +195,10 @@ gb_command_vim_get_property (GObject *object,
  9975. g_value_set_object (value, gb_command_vim_get_source_view (self));
  9976. break;
  9977.  
  9978. + case PROP_VIM_PARSER:
  9979. + g_value_set_object (value, gb_command_vim_get_vim_parser (self));
  9980. + break;
  9981. +
  9982. default:
  9983. G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
  9984. }
  9985. @@ -153,6 +214,10 @@ gb_command_vim_set_property (GObject *object,
  9986.  
  9987. switch (prop_id)
  9988. {
  9989. + case PROP_COMMAND:
  9990. + gb_command_vim_set_command (self, g_value_get_object (value));
  9991. + break;
  9992. +
  9993. case PROP_COMMAND_TEXT:
  9994. gb_command_vim_set_command_text (self, g_value_get_string (value));
  9995. break;
  9996. @@ -161,6 +226,10 @@ gb_command_vim_set_property (GObject *object,
  9997. gb_command_vim_set_source_view (self, g_value_get_object (value));
  9998. break;
  9999.  
  10000. + case PROP_VIM_PARSER:
  10001. + gb_command_vim_set_vim_parser (self, g_value_get_object (value));
  10002. + break;
  10003. +
  10004. default:
  10005. G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
  10006. }
  10007. @@ -178,6 +247,14 @@ gb_command_vim_class_init (GbCommandVimClass *klass)
  10008.  
  10009. command_class->execute = gb_command_vim_execute;
  10010.  
  10011. + gParamSpecs [PROP_COMMAND] =
  10012. + g_param_spec_object ("command",
  10013. + _("IdeVimParserCommand command"),
  10014. + _("The IdeVimParserCommand parsed from the command text."),
  10015. + IDE_TYPE_VIM_PARSER_COMMAND,
  10016. + (G_PARAM_READWRITE |
  10017. + G_PARAM_STATIC_STRINGS));
  10018. +
  10019. gParamSpecs [PROP_COMMAND_TEXT] =
  10020. g_param_spec_string ("command-text",
  10021. _("Command Text"),
  10022. @@ -194,6 +271,14 @@ gb_command_vim_class_init (GbCommandVimClass *klass)
  10023. (G_PARAM_READWRITE |
  10024. G_PARAM_STATIC_STRINGS));
  10025.  
  10026. + gParamSpecs [PROP_VIM_PARSER] =
  10027. + g_param_spec_object ("vim-parser",
  10028. + _("Vim Parser"),
  10029. + _("The parser used with this command."),
  10030. + IDE_TYPE_VIM_PARSER,
  10031. + (G_PARAM_READWRITE |
  10032. + G_PARAM_STATIC_STRINGS));
  10033. +
  10034. g_object_class_install_properties (object_class, LAST_PROP, gParamSpecs);
  10035. }
  10036.  
  10037. diff --git a/plugins/command-bar/gb-command-vim.h b/plugins/command-bar/gb-command-vim.h
  10038. index 03a919c..8a232d7 100644
  10039. --- a/plugins/command-bar/gb-command-vim.h
  10040. +++ b/plugins/command-bar/gb-command-vim.h
  10041. @@ -19,6 +19,8 @@
  10042. #ifndef GB_COMMAND_VIM_H
  10043. #define GB_COMMAND_VIM_H
  10044.  
  10045. +#include <ide.h>
  10046. +
  10047. #include "gb-command.h"
  10048.  
  10049. G_BEGIN_DECLS
  10050. @@ -27,6 +29,10 @@ G_BEGIN_DECLS
  10051.  
  10052. G_DECLARE_FINAL_TYPE (GbCommandVim, gb_command_vim, GB, COMMAND_VIM, GbCommand)
  10053.  
  10054. +IdeVimParserCommand *gb_command_vim_get_command (GbCommandVim *vim);
  10055. +const gchar *gb_command_vim_get_command_text (GbCommandVim *command_vim);
  10056. +IdeSourceView *gb_command_vim_get_source_view (GbCommandVim *vim);
  10057. +IdeVimParser *gb_command_vim_get_vim_parser (GbCommandVim *vim);
  10058.  
  10059. G_END_DECLS
  10060.  
  10061. diff --git a/plugins/command-bar/gb-command.c b/plugins/command-bar/gb-command.c
  10062. index 191bc65..3db56bd 100644
  10063. --- a/plugins/command-bar/gb-command.c
  10064. +++ b/plugins/command-bar/gb-command.c
  10065. @@ -16,9 +16,24 @@
  10066. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  10067. */
  10068.  
  10069. +#include <string.h>
  10070. +
  10071. +#include <glib/gi18n.h>
  10072. +#include <glib/gprintf.h>
  10073. +
  10074. +#include <ide.h>
  10075. +
  10076. #include "gb-command.h"
  10077.  
  10078. -G_DEFINE_TYPE (GbCommand, gb_command, G_TYPE_OBJECT)
  10079. +typedef struct
  10080. +{
  10081. + gchar *name;
  10082. + gchar *shortname;
  10083. + gchar *syntax;
  10084. + gint id;
  10085. +} GbCommandPrivate;
  10086. +
  10087. +G_DEFINE_TYPE_WITH_PRIVATE (GbCommand, gb_command, G_TYPE_OBJECT)
  10088.  
  10089. enum {
  10090. EXECUTE,
  10091. @@ -27,6 +42,116 @@ enum {
  10092.  
  10093. static guint gSignals [LAST_SIGNAL];
  10094.  
  10095. +enum {
  10096. + PROP_0,
  10097. + PROP_NAME,
  10098. + PROP_SHORTNAME,
  10099. + PROP_SYNTAX,
  10100. + LAST_PROP
  10101. +};
  10102. +
  10103. +static GParamSpec *gParamSpecs [LAST_PROP];
  10104. +
  10105. +const gchar *
  10106. +gb_command_get_name (GbCommand *self)
  10107. +{
  10108. + GbCommandPrivate *priv = gb_command_get_instance_private (self);
  10109. +
  10110. + return priv->name;
  10111. +}
  10112. +
  10113. +const gchar *
  10114. +gb_command_get_shortname (GbCommand *self)
  10115. +{
  10116. + GbCommandPrivate *priv = gb_command_get_instance_private (self);
  10117. +
  10118. + return priv->shortname;
  10119. +}
  10120. +
  10121. +const gchar *
  10122. +gb_command_get_syntax (GbCommand *self)
  10123. +{
  10124. + const gchar *str = NULL;
  10125. +
  10126. + /* TODO: not yet implemented */
  10127. +
  10128. + return str;
  10129. +}
  10130. +
  10131. +static void
  10132. +gb_command_set_shortname (GbCommand *self,
  10133. + const gchar *shortname)
  10134. +{
  10135. + GbCommandPrivate *priv = gb_command_get_instance_private (self);
  10136. + guint shortname_len;
  10137. +
  10138. + if (!ide_str_empty0 (shortname))
  10139. + {
  10140. + shortname_len = strlen (shortname);
  10141. + if (shortname_len > 0 && shortname_len < strlen (priv->name) && g_str_has_prefix (priv->name, shortname))
  10142. + priv->shortname = g_strdup (shortname);
  10143. + else
  10144. + g_warning ("The shortname must be a prefix of the name and its length between 1 and name length - 1");
  10145. + }
  10146. +}
  10147. +
  10148. +static void
  10149. +gb_command_get_property (GObject *object,
  10150. + guint prop_id,
  10151. + GValue *value,
  10152. + GParamSpec *pspec)
  10153. +{
  10154. + GbCommand *self = GB_COMMAND (object);
  10155. + GbCommandPrivate *priv = gb_command_get_instance_private (self);
  10156. +
  10157. + switch (prop_id)
  10158. + {
  10159. + case PROP_NAME:
  10160. + g_value_set_string (value, priv->name);
  10161. + break;
  10162. +
  10163. + case PROP_SHORTNAME:
  10164. + g_value_set_string (value, priv->shortname);
  10165. + break;
  10166. +
  10167. + case PROP_SYNTAX:
  10168. + g_value_set_string (value, priv->syntax);
  10169. + break;
  10170. +
  10171. + default:
  10172. + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
  10173. + }
  10174. +}
  10175. +
  10176. +static void
  10177. +gb_command_set_property (GObject *object,
  10178. + guint prop_id,
  10179. + const GValue *value,
  10180. + GParamSpec *pspec)
  10181. +{
  10182. + GbCommand *self = GB_COMMAND (object);
  10183. + GbCommandPrivate *priv = gb_command_get_instance_private (self);
  10184. +
  10185. +
  10186. + switch (prop_id)
  10187. + {
  10188. + case PROP_NAME:
  10189. + priv->name = g_value_dup_string (value);
  10190. + break;
  10191. +
  10192. + case PROP_SHORTNAME:
  10193. + gb_command_set_shortname (self, g_value_get_string (value));
  10194. + break;
  10195. +
  10196. + case PROP_SYNTAX:
  10197. + priv->syntax = g_value_dup_string (value);
  10198. + break;
  10199. +
  10200. + default:
  10201. + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
  10202. + }
  10203. +}
  10204. +
  10205. GbCommand *
  10206. gb_command_new (void)
  10207. {
  10208. @@ -51,6 +176,11 @@ gb_command_execute (GbCommand *command)
  10209. static void
  10210. gb_command_class_init (GbCommandClass *klass)
  10211. {
  10212. + GObjectClass *object_class = G_OBJECT_CLASS (klass);
  10213. +
  10214. + object_class->get_property = gb_command_get_property;
  10215. + object_class->set_property = gb_command_set_property;
  10216. +
  10217. klass->execute = gb_command_real_execute;
  10218.  
  10219. gSignals [EXECUTE] =
  10220. @@ -62,6 +192,45 @@ gb_command_class_init (GbCommandClass *klass)
  10221. NULL, NULL,
  10222. GB_TYPE_COMMAND_RESULT,
  10223. 0);
  10224. +
  10225. + /**
  10226. + * GbCommand:name:
  10227. + *
  10228. + * A string holding the name of the command.
  10229. + */
  10230. + gParamSpecs[PROP_NAME] =
  10231. + g_param_spec_string ("name",
  10232. + _("Command name"),
  10233. + _("The name of the command."),
  10234. + NULL,
  10235. + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
  10236. +
  10237. + /**
  10238. + * GbCommand:shortname:
  10239. + *
  10240. + * A string holding the shortname of the command.
  10241. + */
  10242. + gParamSpecs[PROP_SHORTNAME] =
  10243. + g_param_spec_string ("shortname",
  10244. + _("Command shortname"),
  10245. + _("The shortname of the command."),
  10246. + NULL,
  10247. + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
  10248. +
  10249. + /**
  10250. + * GbCommand:syntax:
  10251. + *
  10252. + * A string holding the syntax of the command,
  10253. + * mostly used for helping purpose.
  10254. + */
  10255. + gParamSpecs[PROP_SYNTAX] =
  10256. + g_param_spec_string ("syntax",
  10257. + _("Command syntax"),
  10258. + _("The syntax of the command."),
  10259. + NULL,
  10260. + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |G_PARAM_STATIC_STRINGS);
  10261. +
  10262. + g_object_class_install_properties (object_class, LAST_PROP, gParamSpecs);
  10263. }
  10264.  
  10265. static void
  10266. diff --git a/plugins/command-bar/gb-command.h b/plugins/command-bar/gb-command.h
  10267. index 1d666ed..bb98d66 100644
  10268. --- a/plugins/command-bar/gb-command.h
  10269. +++ b/plugins/command-bar/gb-command.h
  10270. @@ -33,11 +33,14 @@ struct _GbCommandClass
  10271. {
  10272. GObjectClass parent;
  10273.  
  10274. - GbCommandResult *(*execute) (GbCommand *command);
  10275. + GbCommandResult *(*execute) (GbCommand *command);
  10276. };
  10277.  
  10278. -GbCommand *gb_command_new (void);
  10279. -GbCommandResult *gb_command_execute (GbCommand *command);
  10280. +GbCommand *gb_command_new (void);
  10281. +GbCommandResult *gb_command_execute (GbCommand *command);
  10282. +const gchar *gb_command_get_name (GbCommand *command);
  10283. +const gchar *gb_command_get_shortname (GbCommand *command);
  10284. +const gchar *gb_command_get_syntax (GbCommand *command);
  10285.  
  10286. G_END_DECLS
  10287.  
  10288. diff --git a/plugins/command-bar/gb-vim.c b/plugins/command-bar/gb-vim.c
  10289. index 5003fef..b0f3fff 100644
  10290. --- a/plugins/command-bar/gb-vim.c
  10291. +++ b/plugins/command-bar/gb-vim.c
  10292. @@ -20,12 +20,14 @@
  10293.  
  10294. #include <errno.h>
  10295. #include <glib/gi18n.h>
  10296. +#include <glib/gprintf.h>
  10297. #include <ide.h>
  10298.  
  10299. #include "gb-string.h"
  10300. #include "gb-vim.h"
  10301. #include "gb-widget.h"
  10302. #include "gb-workbench.h"
  10303. +#include "gb-command-complete-item.h"
  10304.  
  10305. G_DEFINE_QUARK (gb-vim-error-quark, gb_vim_error)
  10306.  
  10307. @@ -299,19 +301,21 @@ lookup_set (const gchar *key)
  10308. return NULL;
  10309. }
  10310.  
  10311. -static gboolean
  10312. -gb_vim_command_set (GtkSourceView *source_view,
  10313. - const gchar *command,
  10314. - const gchar *options,
  10315. - GError **error)
  10316. +gboolean
  10317. +gb_vim_command_set (GtkSourceView *source_view,
  10318. + IdeVimParser *parser,
  10319. + IdeVimParserCommand *command,
  10320. + IdeVimParserError **error)
  10321. {
  10322. gboolean ret = FALSE;
  10323. gchar **parts;
  10324. gsize i;
  10325.  
  10326. + gchar *options = NULL;
  10327. +
  10328. g_assert (GTK_SOURCE_IS_VIEW (source_view));
  10329. - g_assert (command);
  10330. - g_assert (options);
  10331. + g_assert (IDE_IS_VIM_PARSER (parser));
  10332. + g_assert (IDE_IS_VIM_PARSER_COMMAND (command));
  10333.  
  10334. parts = g_strsplit (options, " ", 0);
  10335.  
  10336. @@ -342,17 +346,20 @@ gb_vim_command_set (GtkSourceView *source_view,
  10337.  
  10338. if (set == NULL)
  10339. {
  10340. + /*
  10341. g_set_error (error,
  10342. GB_VIM_ERROR,
  10343. GB_VIM_ERROR_UNKNOWN_OPTION,
  10344. _("Unknown option: %s"),
  10345. key);
  10346. + */
  10347. goto cleanup;
  10348. }
  10349. -
  10350. +/*
  10351. if (!set->func (source_view, key, value, error))
  10352. goto cleanup;
  10353. - }
  10354. +*/
  10355. + }
  10356.  
  10357. ret = TRUE;
  10358.  
  10359. @@ -362,72 +369,93 @@ cleanup:
  10360. return ret;
  10361. }
  10362.  
  10363. -static gboolean
  10364. -gb_vim_command_colorscheme (GtkSourceView *source_view,
  10365. - const gchar *command,
  10366. - const gchar *options,
  10367. - GError **error)
  10368. +gboolean
  10369. +gb_vim_command_colorscheme (GtkSourceView *source_view,
  10370. + IdeVimParser *parser,
  10371. + IdeVimParserCommand *command,
  10372. + IdeVimParserError **error)
  10373. {
  10374. GtkSourceStyleSchemeManager *manager;
  10375. GtkSourceStyleScheme *style_scheme;
  10376. GtkTextBuffer *buffer;
  10377. - g_autofree gchar *trimmed = NULL;
  10378. + const gchar *colorscheme = NULL;
  10379.  
  10380. - trimmed = g_strstrip (g_strdup (options));
  10381. + if (ide_vim_parser_command_get_string_arg (command, 1, &colorscheme))
  10382. + {
  10383. + buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (source_view));
  10384. + manager = gtk_source_style_scheme_manager_get_default ();
  10385. + style_scheme = gtk_source_style_scheme_manager_get_scheme (manager, colorscheme);
  10386.  
  10387. - buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (source_view));
  10388. - manager = gtk_source_style_scheme_manager_get_default ();
  10389. - style_scheme = gtk_source_style_scheme_manager_get_scheme (manager, trimmed);
  10390. + if (style_scheme == NULL)
  10391. + {
  10392. + ide_vim_parser_error_literal_set (error, IDE_VIM_PARSER_ERROR_UNKNOWN_OPTION,
  10393. + _("Cannot find colorscheme '%s'"), colorscheme);
  10394. + return FALSE;
  10395. + }
  10396.  
  10397. - if (style_scheme == NULL)
  10398. - {
  10399. - g_set_error (error,
  10400. - GB_VIM_ERROR,
  10401. - GB_VIM_ERROR_UNKNOWN_OPTION,
  10402. - _("Cannot find colorscheme '%s'"),
  10403. - options);
  10404. - return FALSE;
  10405. - }
  10406. + g_object_set (buffer, "style-scheme", style_scheme, NULL);
  10407.  
  10408. - g_object_set (buffer, "style-scheme", style_scheme, NULL);
  10409. + return TRUE;
  10410. + }
  10411.  
  10412. + /* TODO: error handle: here, missuse of ide_vim_parser_command_get_string_arg
  10413. + * so a g_critical */
  10414. return TRUE;
  10415. }
  10416.  
  10417. -static gboolean
  10418. -gb_vim_command_edit (GtkSourceView *source_view,
  10419. - const gchar *command,
  10420. - const gchar *options,
  10421. - GError **error)
  10422. +static GFile *
  10423. +get_working_dir (GbWorkbench *workbench,
  10424. + IdeVimParserError **error)
  10425. {
  10426. - GbWorkbench *workbench;
  10427. IdeContext *context;
  10428. IdeVcs *vcs;
  10429. + GFile *workdir = NULL;
  10430. +
  10431. + if (!(context = gb_workbench_get_context (workbench)) ||
  10432. + !(vcs = ide_context_get_vcs (context)) ||
  10433. + !(workdir = ide_vcs_get_working_directory (vcs)))
  10434. + ide_vim_parser_error_set (error, IDE_VIM_PARSER_ERROR_NO_WORKING_DIR, NULL);
  10435. +
  10436. + return workdir;
  10437. +}
  10438. +
  10439. +gboolean
  10440. +gb_vim_command_edit (GtkSourceView *source_view,
  10441. + IdeVimParser *parser,
  10442. + IdeVimParserCommand *command,
  10443. + IdeVimParserError **error)
  10444. +{
  10445. + GbWorkbench *workbench;
  10446. GFile *workdir;
  10447. GFile *file = NULL;
  10448. + const gchar *file_name = NULL;
  10449. + IdeVimParserError *tmp_error = NULL;
  10450.  
  10451. - if (gb_str_empty0 (options))
  10452. + if (!ide_vim_parser_command_get_string_arg (command, 1, &file_name))
  10453. {
  10454. + g_printf ("edit open");
  10455. gb_widget_activate_action (GTK_WIDGET (source_view), "workbench", "open", NULL);
  10456. return TRUE;
  10457. }
  10458.  
  10459. - if (!(workbench = gb_widget_get_workbench (GTK_WIDGET (source_view))) ||
  10460. - !(context = gb_workbench_get_context (workbench)) ||
  10461. - !(vcs = ide_context_get_vcs (context)) ||
  10462. - !(workdir = ide_vcs_get_working_directory (vcs)))
  10463. + workbench = gb_widget_get_workbench (GTK_WIDGET (source_view));
  10464. + g_assert (workbench != NULL);
  10465. +
  10466. + if (g_path_is_absolute (file_name))
  10467. {
  10468. - g_set_error (error,
  10469. - GB_VIM_ERROR,
  10470. - GB_VIM_ERROR_NOT_SOURCE_VIEW,
  10471. - _("Failed to locate working directory"));
  10472. - return FALSE;
  10473. + file = g_file_new_for_path (file_name);
  10474. }
  10475. -
  10476. - if (g_path_is_absolute (options))
  10477. - file = g_file_new_for_path (options);
  10478. else
  10479. - file = g_file_get_child (workdir, options);
  10480. + {
  10481. + workdir = get_working_dir (workbench, &tmp_error);
  10482. + if (workdir == NULL)
  10483. + {
  10484. + ide_vim_parser_error_propagate (error, tmp_error);
  10485. + return FALSE;
  10486. + }
  10487. +
  10488. + file = g_file_get_child (workdir, file_name);
  10489. + }
  10490.  
  10491. gb_workbench_open (workbench, file);
  10492.  
  10493. @@ -436,76 +464,91 @@ gb_vim_command_edit (GtkSourceView *source_view,
  10494. return TRUE;
  10495. }
  10496.  
  10497. -static gboolean
  10498. -gb_vim_command_tabe (GtkSourceView *source_view,
  10499. - const gchar *command,
  10500. - const gchar *options,
  10501. - GError **error)
  10502. +gboolean
  10503. +gb_vim_command_tabedit (GtkSourceView *source_view,
  10504. + IdeVimParser *parser,
  10505. + IdeVimParserCommand *command,
  10506. + IdeVimParserError **error)
  10507. {
  10508. - if (!gb_str_empty0 (options))
  10509. - return gb_vim_command_edit (source_view, command, options, error);
  10510. + const gchar *file_name = NULL;
  10511. +
  10512. + if (ide_vim_parser_command_get_string_arg (command, 1, &file_name))
  10513. + return gb_vim_command_edit (source_view, parser, command, error);
  10514.  
  10515. gb_widget_activate_action (GTK_WIDGET (source_view), "workbench", "new-document", NULL);
  10516.  
  10517. return TRUE;
  10518. }
  10519.  
  10520. -static gboolean
  10521. -gb_vim_command_quit (GtkSourceView *source_view,
  10522. - const gchar *command,
  10523. - const gchar *options,
  10524. - GError **error)
  10525. +gboolean
  10526. +gb_vim_command_quit (GtkSourceView *source_view,
  10527. + IdeVimParser *parser,
  10528. + IdeVimParserCommand *command,
  10529. + IdeVimParserError **error)
  10530. {
  10531. + gboolean has_bang;
  10532. +
  10533. + /* TODO: handle bang */
  10534. + has_bang = ide_vim_parser_command_has_bang (command);
  10535. + if (has_bang)
  10536. + g_printf ("has bang\n");
  10537. +
  10538. gb_widget_activate_action (GTK_WIDGET (source_view), "view", "save", NULL);
  10539. gb_widget_activate_action (GTK_WIDGET (source_view), "view", "close", NULL);
  10540. return TRUE;
  10541. }
  10542.  
  10543. -static gboolean
  10544. -gb_vim_command_split (GtkSourceView *source_view,
  10545. - const gchar *command,
  10546. - const gchar *options,
  10547. - GError **error)
  10548. +gboolean
  10549. +gb_vim_command_split (GtkSourceView *source_view,
  10550. + IdeVimParser *parser,
  10551. + IdeVimParserCommand *command,
  10552. + IdeVimParserError **error)
  10553. {
  10554. + /* TODO: handle file argument */
  10555. +
  10556. gb_widget_activate_action (GTK_WIDGET (source_view), "view-stack", "split-down", NULL);
  10557. return TRUE;
  10558. }
  10559.  
  10560. -static gboolean
  10561. -gb_vim_command_vsplit (GtkSourceView *source_view,
  10562. - const gchar *command,
  10563. - const gchar *options,
  10564. - GError **error)
  10565. +gboolean
  10566. +gb_vim_command_vsplit (GtkSourceView *source_view,
  10567. + IdeVimParser *parser,
  10568. + IdeVimParserCommand *command,
  10569. + IdeVimParserError **error)
  10570. {
  10571. + /* TODO: handle file argument */
  10572. +
  10573. gb_widget_activate_action (GTK_WIDGET (source_view), "view-stack", "split-left", NULL);
  10574. return TRUE;
  10575. }
  10576.  
  10577. -static gboolean
  10578. -gb_vim_command_write (GtkSourceView *source_view,
  10579. - const gchar *command,
  10580. - const gchar *options,
  10581. - GError **error)
  10582. +gboolean
  10583. +gb_vim_command_write (GtkSourceView *source_view,
  10584. + IdeVimParser *parser,
  10585. + IdeVimParserCommand *command,
  10586. + IdeVimParserError **error)
  10587. {
  10588. + /* TODO: handle file argument */
  10589. gb_widget_activate_action (GTK_WIDGET (source_view), "view", "save", NULL);
  10590. return TRUE;
  10591. }
  10592.  
  10593. -static gboolean
  10594. -gb_vim_command_wq (GtkSourceView *source_view,
  10595. - const gchar *command,
  10596. - const gchar *options,
  10597. - GError **error)
  10598. +gboolean
  10599. +gb_vim_command_wq (GtkSourceView *source_view,
  10600. + IdeVimParser *parser,
  10601. + IdeVimParserCommand *command,
  10602. + IdeVimParserError **error)
  10603. {
  10604. - return (gb_vim_command_write (source_view, command, options, error) &&
  10605. - gb_vim_command_quit (source_view, command, options, error));
  10606. + /* TODO: check separate error return */
  10607. + return (gb_vim_command_write (source_view, parser, command, error) &&
  10608. + gb_vim_command_quit (source_view, parser, command, error));
  10609. }
  10610.  
  10611. -static gboolean
  10612. -gb_vim_command_nohl (GtkSourceView *source_view,
  10613. - const gchar *command,
  10614. - const gchar *options,
  10615. - GError **error)
  10616. +gboolean
  10617. +gb_vim_command_nohl (GtkSourceView *source_view,
  10618. + IdeVimParser *parser,
  10619. + IdeVimParserCommand *command,
  10620. + IdeVimParserError **error)
  10621. {
  10622. if (IDE_IS_SOURCE_VIEW (source_view))
  10623. {
  10624. @@ -519,44 +562,49 @@ gb_vim_command_nohl (GtkSourceView *source_view,
  10625. return TRUE;
  10626. }
  10627.  
  10628. -static gboolean
  10629. -gb_vim_command_make (GtkSourceView *source_view,
  10630. - const gchar *command,
  10631. - const gchar *options,
  10632. - GError **error)
  10633. +gboolean
  10634. +gb_vim_command_make (GtkSourceView *source_view,
  10635. + IdeVimParser *parser,
  10636. + IdeVimParserCommand *command,
  10637. + IdeVimParserError **error)
  10638. {
  10639. gb_widget_activate_action (GTK_WIDGET (source_view), "workbench", "build", NULL);
  10640. return TRUE;
  10641. }
  10642.  
  10643. -static gboolean
  10644. -gb_vim_command_syntax (GtkSourceView *source_view,
  10645. - const gchar *command,
  10646. - const gchar *options,
  10647. - GError **error)
  10648. +gboolean
  10649. +gb_vim_command_syntax (GtkSourceView *source_view,
  10650. + IdeVimParser *parser,
  10651. + IdeVimParserCommand *command,
  10652. + IdeVimParserError **error)
  10653. {
  10654. - if (g_str_equal (options, "enable") || g_str_equal (options, "on"))
  10655. - g_object_set (source_view, "highlight-syntax", TRUE, NULL);
  10656. - else if (g_str_equal (options, "off"))
  10657. - g_object_set (source_view, "highlight-syntax", FALSE, NULL);
  10658. - else
  10659. + GtkSourceBuffer *buffer;
  10660. + const gchar *option = NULL;
  10661. +
  10662. + if (ide_vim_parser_command_get_string_arg (command, 1, &option))
  10663. {
  10664. - g_set_error (error,
  10665. - GB_VIM_ERROR,
  10666. - GB_VIM_ERROR_UNKNOWN_OPTION,
  10667. - _("Invalid :syntax subcommand: %s"),
  10668. - options);
  10669. - return FALSE;
  10670. + buffer = GTK_SOURCE_BUFFER (gtk_text_view_get_buffer (GTK_TEXT_VIEW (source_view)));
  10671. +
  10672. + if (g_str_equal (option, "enable") || g_str_equal (option, "on"))
  10673. + g_object_set (buffer, "highlight-syntax", TRUE, NULL);
  10674. + else if (g_str_equal (option, "disable") || g_str_equal (option, "off"))
  10675. + g_object_set (buffer, "highlight-syntax", FALSE, NULL);
  10676. + else
  10677. + {
  10678. + ide_vim_parser_error_literal_set (error, IDE_VIM_PARSER_ERROR_UNKNOWN_OPTION,
  10679. + _("Invalid :syntax subcommand: '%s'"), option);
  10680. + return FALSE;
  10681. + }
  10682. }
  10683.  
  10684. return TRUE;
  10685. }
  10686.  
  10687. -static gboolean
  10688. -gb_vim_command_sort (GtkSourceView *source_view,
  10689. - const gchar *command,
  10690. - const gchar *options,
  10691. - GError **error)
  10692. +gboolean
  10693. +gb_vim_command_sort (GtkSourceView *source_view,
  10694. + IdeVimParser *parser,
  10695. + IdeVimParserCommand *command,
  10696. + IdeVimParserError **error)
  10697. {
  10698. if (IDE_IS_SOURCE_VIEW (source_view))
  10699. {
  10700. @@ -569,53 +617,77 @@ gb_vim_command_sort (GtkSourceView *source_view,
  10701. return TRUE;
  10702. }
  10703.  
  10704. -static gboolean
  10705. -gb_vim_command_bnext (GtkSourceView *source_view,
  10706. - const gchar *command,
  10707. - const gchar *options,
  10708. - GError **error)
  10709. +gboolean
  10710. +gb_vim_command_bnext (GtkSourceView *source_view,
  10711. + IdeVimParser *parser,
  10712. + IdeVimParserCommand *command,
  10713. + IdeVimParserError **error)
  10714. {
  10715. - gb_widget_activate_action (GTK_WIDGET (source_view), "view-stack", "next-view", NULL);
  10716. + gint count;
  10717. +
  10718. + if (!ide_vim_parser_command_get_number_arg (command, 2, &count) &&
  10719. + !ide_vim_parser_command_get_number_arg (command, 0, &count))
  10720. + count = 1;
  10721. +
  10722. + gb_widget_activate_action (GTK_WIDGET (source_view), "view-stack", "next-view", g_variant_new_int64 (count));
  10723. return TRUE;
  10724. }
  10725.  
  10726. -static gboolean
  10727. -gb_vim_command_bprevious (GtkSourceView *source_view,
  10728. - const gchar *command,
  10729. - const gchar *options,
  10730. - GError **error)
  10731. +gboolean
  10732. +gb_vim_command_bprevious (GtkSourceView *source_view,
  10733. + IdeVimParser *parser,
  10734. + IdeVimParserCommand *command,
  10735. + IdeVimParserError **error)
  10736. {
  10737. - gb_widget_activate_action (GTK_WIDGET (source_view), "view-stack", "previous-view", NULL);
  10738. + gint count;
  10739. +
  10740. + if (!ide_vim_parser_command_get_number_arg (command, 2, &count) &&
  10741. + !ide_vim_parser_command_get_number_arg (command, 0, &count))
  10742. + count = 1;
  10743. +
  10744. + gb_widget_activate_action (GTK_WIDGET (source_view), "view-stack", "previous-view", g_variant_new_int64 (count));
  10745. return TRUE;
  10746. }
  10747.  
  10748. -static gboolean
  10749. -gb_vim_command_cnext (GtkSourceView *source_view,
  10750. - const gchar *command,
  10751. - const gchar *options,
  10752. - GError **error)
  10753. +gboolean
  10754. +gb_vim_command_cnext (GtkSourceView *source_view,
  10755. + IdeVimParser *parser,
  10756. + IdeVimParserCommand *command,
  10757. + IdeVimParserError **error)
  10758. {
  10759. + gint count;
  10760. +
  10761. + if (!ide_vim_parser_command_get_number_arg (command, 2, &count) &&
  10762. + !ide_vim_parser_command_get_number_arg (command, 0, &count))
  10763. + count = 1;
  10764. +
  10765. if (IDE_IS_SOURCE_VIEW (source_view))
  10766. - g_signal_emit_by_name (source_view, "move-error", GTK_DIR_DOWN);
  10767. + g_signal_emit_by_name (source_view, "move-error", GTK_DIR_DOWN, count);
  10768. return TRUE;
  10769. }
  10770.  
  10771. -static gboolean
  10772. -gb_vim_command_cprevious (GtkSourceView *source_view,
  10773. - const gchar *command,
  10774. - const gchar *options,
  10775. - GError **error)
  10776. +gboolean
  10777. +gb_vim_command_cprevious (GtkSourceView *source_view,
  10778. + IdeVimParser *parser,
  10779. + IdeVimParserCommand *command,
  10780. + IdeVimParserError **error)
  10781. {
  10782. + gint count;
  10783. +
  10784. + if (!ide_vim_parser_command_get_number_arg (command, 2, &count) &&
  10785. + !ide_vim_parser_command_get_number_arg (command, 0, &count))
  10786. + count = 1;
  10787. +
  10788. if (IDE_IS_SOURCE_VIEW (source_view))
  10789. - g_signal_emit_by_name (source_view, "move-error", GTK_DIR_UP);
  10790. + g_signal_emit_by_name (source_view, "move-error", GTK_DIR_UP, count);
  10791. return TRUE;
  10792. }
  10793.  
  10794. -static gboolean
  10795. -gb_vim_command_buffers (GtkSourceView *source_view,
  10796. - const gchar *command,
  10797. - const gchar *options,
  10798. - GError **error)
  10799. +gboolean
  10800. +gb_vim_command_buffers (GtkSourceView *source_view,
  10801. + IdeVimParser *parser,
  10802. + IdeVimParserCommand *command,
  10803. + IdeVimParserError **error)
  10804. {
  10805. gb_widget_activate_action (GTK_WIDGET (source_view), "view-stack", "show-list", NULL);
  10806. return TRUE;
  10807. @@ -651,16 +723,21 @@ gb_vim_jump_to_line (GtkSourceView *source_view,
  10808. return TRUE;
  10809. }
  10810.  
  10811. -static gboolean
  10812. -gb_vim_command_help (GtkSourceView *source_view,
  10813. - const gchar *command,
  10814. - const gchar *options,
  10815. - GError **error)
  10816. +gboolean
  10817. +gb_vim_command_help (GtkSourceView *source_view,
  10818. + IdeVimParser *parser,
  10819. + IdeVimParserCommand *command,
  10820. + IdeVimParserError **error)
  10821. {
  10822. + const gchar *option = NULL;
  10823. GVariant *param;
  10824.  
  10825. - param = g_variant_new_string (options);
  10826. - gb_widget_activate_action (GTK_WIDGET (source_view), "workbench", "search-docs", param);
  10827. + if (ide_vim_parser_command_get_string_arg (command, 1, &option))
  10828. + {
  10829. + param = g_variant_new_string (option);
  10830. + gb_widget_activate_action (GTK_WIDGET (source_view), "workbench", "search-docs", param);
  10831. + }
  10832. +
  10833. return TRUE;
  10834. }
  10835.  
  10836. @@ -706,6 +783,8 @@ gb_vim_do_search_and_replace (GtkTextBuffer *buffer,
  10837. g_assert (replace_text);
  10838. g_assert ((!begin && !end) || (begin && end));
  10839.  
  10840. + printf ("substitute: form:%s, to:%s, global:%i\n", search_text, replace_text, is_global);
  10841. +
  10842. search_settings = gtk_source_search_settings_new ();
  10843. search_context = gtk_source_search_context_new (GTK_SOURCE_BUFFER (buffer), search_settings);
  10844.  
  10845. @@ -772,11 +851,16 @@ gb_vim_command_search (GtkSourceView *source_view,
  10846. gchar *search_text = NULL;
  10847. gchar *replace_text = NULL;
  10848. gunichar separator;
  10849. + gboolean range_is_file = FALSE;
  10850.  
  10851. g_assert (g_str_has_prefix (command, "%s") || g_str_has_prefix (command, "s"));
  10852.  
  10853. if (*command == '%')
  10854. - command++;
  10855. + {
  10856. + range_is_file = TRUE;
  10857. + command++;
  10858. + }
  10859. +
  10860. command++;
  10861.  
  10862. separator = g_utf8_get_char (command);
  10863. @@ -876,27 +960,27 @@ invalid_request:
  10864. }
  10865.  
  10866. static const GbVimCommand vim_commands[] = {
  10867. - { "bnext", gb_vim_command_bnext , NULL},
  10868. - { "bprevious", gb_vim_command_bprevious, NULL },
  10869. - { "buffers", gb_vim_command_buffers, NULL },
  10870. - { "ls", gb_vim_command_buffers, NULL },
  10871. - { "cnext", gb_vim_command_cnext, NULL },
  10872. - { "colorscheme", gb_vim_command_colorscheme, NULL },
  10873. - { "cprevious", gb_vim_command_cprevious, NULL },
  10874. - { "edit", gb_vim_command_edit, NULL },
  10875. - { "help", gb_vim_command_help, NULL },
  10876. - { "nohl", gb_vim_command_nohl, NULL },
  10877. - { "make", gb_vim_command_make, NULL },
  10878. - { "quit", gb_vim_command_quit, NULL },
  10879. - { "set", gb_vim_command_set, NULL },
  10880. - { "sort", gb_vim_command_sort, NULL },
  10881. - { "split", gb_vim_command_split, NULL },
  10882. - { "syntax", gb_vim_command_syntax, NULL },
  10883. - { "tabe", gb_vim_command_tabe, NULL },
  10884. - { "vsplit", gb_vim_command_vsplit, NULL },
  10885. - { "w", gb_vim_command_write, NULL },
  10886. - { "wq", gb_vim_command_wq, NULL },
  10887. - { "write", gb_vim_command_write, NULL },
  10888. + //{ "bnext", gb_vim_command_bnext , NULL},
  10889. + //{ "bprevious", gb_vim_command_bprevious, NULL },
  10890. + //{ "buffers", gb_vim_command_buffers, NULL },
  10891. + //{ "ls", gb_vim_command_buffers, NULL },
  10892. + //{ "cnext", gb_vim_command_cnext, NULL },
  10893. + //{ "colorscheme", gb_vim_command_colorscheme, NULL },
  10894. + //{ "cprevious", gb_vim_command_cprevious, NULL },
  10895. + //{ "edit", gb_vim_command_edit, NULL },
  10896. + //{ "help", gb_vim_command_help, NULL },
  10897. + //{ "nohl", gb_vim_command_nohl, NULL },
  10898. + //{ "make", gb_vim_command_make, NULL },
  10899. + //{ "quit", gb_vim_command_quit, NULL },
  10900. + //{ "set", gb_vim_command_set, NULL },
  10901. + //{ "sort", gb_vim_command_sort, NULL },
  10902. + //{ "split", gb_vim_command_split, NULL },
  10903. + //{ "syntax", gb_vim_command_syntax, NULL },
  10904. + //{ "tabe", gb_vim_command_tabe, NULL },
  10905. + //{ "vsplit", gb_vim_command_vsplit, NULL },
  10906. + //{ "w", gb_vim_command_write, NULL },
  10907. + //{ "wq", gb_vim_command_wq, NULL },
  10908. + //{ "write", gb_vim_command_write, NULL },
  10909. { NULL }
  10910. };
  10911.  
  10912. @@ -934,73 +1018,70 @@ lookup_command (const gchar *name)
  10913. return NULL;
  10914. }
  10915.  
  10916. -gboolean
  10917. -gb_vim_execute (GtkSourceView *source_view,
  10918. - const gchar *line,
  10919. - GError **error)
  10920. +IdeVimParserCommand *
  10921. +gb_vim_lookup (GtkSourceView *source_view,
  10922. + IdeVimParser *parser,
  10923. + const gchar *command_text,
  10924. + IdeVimParserError **error)
  10925. {
  10926. + IdeVimParserCommand *command = NULL;
  10927. + IdeVimParserError *tmp_error = NULL;
  10928. GtkTextBuffer *buffer;
  10929. - g_autofree gchar *name_slice = NULL;
  10930. - const GbVimCommand *command;
  10931. - const gchar *command_name = line;
  10932. - const gchar *options;
  10933. - g_autofree gchar *all_options = NULL;
  10934. - gboolean result;
  10935.  
  10936. - g_return_val_if_fail (GTK_SOURCE_IS_VIEW (source_view), FALSE);
  10937. - g_return_val_if_fail (line, FALSE);
  10938. + g_return_val_if_fail (GTK_SOURCE_IS_VIEW (source_view), NULL);
  10939. + g_return_val_if_fail (IDE_IS_VIM_PARSER (parser), NULL);
  10940. + g_return_val_if_fail (!gb_str_empty0 (command_text), NULL);
  10941.  
  10942. buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (source_view));
  10943. -
  10944. if (!GTK_SOURCE_IS_BUFFER (buffer))
  10945. {
  10946. - g_set_error (error,
  10947. - GB_VIM_ERROR,
  10948. - GB_VIM_ERROR_NOT_SOURCE_VIEW,
  10949. - _("vim mode requires GtkSourceView"));
  10950. - return FALSE;
  10951. + ide_vim_parser_error_set (error, IDE_VIM_PARSER_ERROR_NO_BUFFER, NULL);
  10952. + return NULL;
  10953. }
  10954.  
  10955. - for (options = line; *options; options = g_utf8_next_char (options))
  10956. + /* TODO: check for go-to-line command */
  10957. + command = ide_vim_parser_parse (parser, buffer, command_text, &tmp_error);
  10958. + if (tmp_error != NULL)
  10959. {
  10960. - gunichar ch;
  10961. -
  10962. - ch = g_utf8_get_char (options);
  10963. -
  10964. - if (g_unichar_isspace (ch))
  10965. - break;
  10966. + ide_vim_parser_error_propagate (error, tmp_error);
  10967. + return NULL;
  10968. }
  10969.  
  10970. - if (g_unichar_isspace (g_utf8_get_char (options)))
  10971. - {
  10972. - command_name = name_slice = g_strndup (line, options - line);
  10973. - options = g_utf8_next_char (options);
  10974. - }
  10975. + ide_vim_parser_debug_show_parsed (command);
  10976. + return command;
  10977. +}
  10978.  
  10979. - command = lookup_command (command_name);
  10980. +gboolean
  10981. +gb_vim_execute (GtkSourceView *source_view,
  10982. + IdeVimParser *parser,
  10983. + IdeVimParserCommand *command,
  10984. + IdeVimParserError **error)
  10985. +{
  10986. + GtkTextBuffer *buffer;
  10987. + IdeVimParserCommandFunc func;
  10988. + gboolean result;
  10989. + IdeVimParserError *tmp_error = NULL;
  10990.  
  10991. - if (command == NULL)
  10992. - {
  10993. - if (looks_like_search_and_replace (line))
  10994. - return gb_vim_command_search (source_view, line, "", error);
  10995. + g_return_val_if_fail (GTK_SOURCE_IS_VIEW (source_view), FALSE);
  10996. + g_return_val_if_fail (IDE_IS_VIM_PARSER (parser), FALSE);
  10997. + g_return_val_if_fail (IDE_IS_VIM_PARSER_COMMAND (command), FALSE);
  10998.  
  10999. - g_set_error (error,
  11000. - GB_VIM_ERROR,
  11001. - GB_VIM_ERROR_NOT_FOUND,
  11002. - _("Not an editor command: %s"),
  11003. - command_name);
  11004. + buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (source_view));
  11005. + if (!GTK_SOURCE_IS_BUFFER (buffer))
  11006. + {
  11007. + ide_vim_parser_error_set (error, IDE_VIM_PARSER_ERROR_NOT_SOURCE_VIEW, NULL);
  11008. return FALSE;
  11009. }
  11010.  
  11011. - if (command->options_sup)
  11012. - all_options = g_strconcat (options, " ", command->options_sup, NULL);
  11013. - else
  11014. - all_options = g_strdup (options);
  11015. -
  11016. - result = command->func (source_view, command_name, all_options, error);
  11017. - g_free (command->options_sup);
  11018. + func = ide_vim_parser_command_get_func (command);
  11019. + result = func (source_view, parser, command, &tmp_error);
  11020. + ide_vim_parser_error_propagate (error, tmp_error);
  11021.  
  11022. return result;
  11023. +/*
  11024. + if (looks_like_search_and_replace (line))
  11025. + return gb_vim_command_search (source_view, line, "", error);
  11026. +*/
  11027. }
  11028.  
  11029. static gchar *
  11030. @@ -1054,16 +1135,46 @@ gb_vim_complete_set (const gchar *line,
  11031. g_strfreev (parts);
  11032. }
  11033.  
  11034. +static inline GbCommandCompleteItem *
  11035. +convert_to_gb_command (IdeVimCompleteItem *ide_complete_item)
  11036. +{
  11037. + GbCommandCompleteItem *complete_item;
  11038. +
  11039. + complete_item = gb_command_complete_item_new (GB_COMMAND_COMPLETE_ITEM_KIND_COMMAND,
  11040. + g_strdup (ide_vim_complete_item_get_name (ide_complete_item)),
  11041. + g_strdup (ide_vim_complete_item_get_shortname (ide_complete_item)));
  11042. +
  11043. + return complete_item;
  11044. +}
  11045. +
  11046. static void
  11047. -gb_vim_complete_command (const gchar *line,
  11048. - GPtrArray *ar)
  11049. +gb_vim_complete_command (IdeVimParser *parser,
  11050. + GtkTextBuffer *buffer,
  11051. + const gchar *line,
  11052. + GPtrArray *ar_dst)
  11053. {
  11054. gsize i;
  11055. + GPtrArray *ar_src;
  11056. + GbCommandCompleteItem *complete_item;
  11057. +
  11058. +/* Here we get a g_ptr_array of IdeVimCompleteItem and
  11059. + * convert it to a g_prt_array of GbCommandCompleteItem.
  11060. + * We need to decouple things this way because the whole
  11061. + * IdeVimCmdlineParser has a vocation to land into GtkSourceView
  11062. + * which know nothing about GbCommandCompleteItem.
  11063. + */
  11064.  
  11065. - for (i = 0; vim_commands [i].name; i++)
  11066. + ar_src = ide_vim_parser_complete (parser, buffer, line);
  11067. + if (ar_src != NULL)
  11068. {
  11069. - if (g_str_has_prefix (vim_commands [i].name, line))
  11070. - g_ptr_array_add (ar, g_strdup (vim_commands [i].name));
  11071. + guint len = ar_src->len;
  11072. + for (i = 0; i < len; i++)
  11073. + {
  11074. + complete_item = convert_to_gb_command (g_ptr_array_index (ar_src, i));
  11075. + g_ptr_array_add (ar_dst, complete_item);
  11076. + }
  11077. +
  11078. + g_ptr_array_free (ar_src, TRUE);
  11079. }
  11080. }
  11081.  
  11082. @@ -1253,13 +1364,22 @@ gb_vim_complete_colorscheme (const gchar *line,
  11083. }
  11084. }
  11085.  
  11086. -gchar **
  11087. -gb_vim_complete (GtkSourceView *source_view,
  11088. - const gchar *line)
  11089. +GPtrArray *
  11090. +gb_vim_complete (IdeVimParser *parser,
  11091. + GtkSourceView *source_view,
  11092. + const gchar *line)
  11093. {
  11094. + GtkTextBuffer *buffer;
  11095. GPtrArray *ar;
  11096.  
  11097. - ar = g_ptr_array_new ();
  11098. + ar = g_ptr_array_new_full (32, g_object_unref);
  11099. +
  11100. + buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (source_view));
  11101. + if (!GTK_SOURCE_IS_BUFFER (buffer))
  11102. + {
  11103. + //ide_vim_parser_error_set (error, IDE_VIM_PARSER_ERROR_NO_BUFFER, NULL);
  11104. + return NULL;
  11105. + }
  11106.  
  11107. if (line != NULL)
  11108. {
  11109. @@ -1272,10 +1392,17 @@ gb_vim_complete (GtkSourceView *source_view,
  11110. else if (g_str_has_prefix (line, "colorscheme "))
  11111. gb_vim_complete_colorscheme (line, ar);
  11112. else
  11113. - gb_vim_complete_command (line, ar);
  11114. + gb_vim_complete_command (parser, buffer, line, ar);
  11115. }
  11116.  
  11117. - g_ptr_array_add (ar, NULL);
  11118. -
  11119. - return (gchar **)g_ptr_array_free (ar, FALSE);
  11120. + g_printf ("line:%s, ar len:%i\n", line, ar->len);
  11121. + if (ar->len > 0)
  11122. + {
  11123. + return ar;
  11124. + }
  11125. + else
  11126. + {
  11127. + g_ptr_array_free (ar, TRUE);
  11128. + return NULL;
  11129. + }
  11130. }
  11131. diff --git a/plugins/command-bar/gb-vim.h b/plugins/command-bar/gb-vim.h
  11132. index e28499d..8fc8134 100644
  11133. --- a/plugins/command-bar/gb-vim.h
  11134. +++ b/plugins/command-bar/gb-vim.h
  11135. @@ -21,6 +21,8 @@
  11136.  
  11137. #include <gtksourceview/gtksource.h>
  11138.  
  11139. +#include <ide.h>
  11140. +
  11141. G_BEGIN_DECLS
  11142.  
  11143. #define GB_VIM_ERROR (gb_vim_error_quark())
  11144. @@ -36,12 +38,96 @@ typedef enum
  11145. GB_VIM_ERROR_NOT_SOURCE_VIEW,
  11146. } IdeVimError;
  11147.  
  11148. -GQuark gb_vim_error_quark (void);
  11149. -gboolean gb_vim_execute (GtkSourceView *source_view,
  11150. - const gchar *line,
  11151. - GError **error);
  11152. -gchar **gb_vim_complete (GtkSourceView *source_view,
  11153. - const gchar *line);
  11154. +GQuark gb_vim_error_quark (void);
  11155. +gboolean gb_vim_execute (GtkSourceView *source_view,
  11156. + IdeVimParser *parser,
  11157. + IdeVimParserCommand *command,
  11158. + IdeVimParserError **error);
  11159. +GPtrArray *gb_vim_complete (IdeVimParser *parser,
  11160. + GtkSourceView *source_view,
  11161. + const gchar *line);
  11162. +IdeVimParserCommand *gb_vim_lookup (GtkSourceView *source_view,
  11163. + IdeVimParser *parser,
  11164. + const gchar *command_text,
  11165. + IdeVimParserError **error);
  11166. +
  11167. +/* Gb specific Vim Commands */
  11168. +gboolean gb_vim_command_bnext (GtkSourceView *source_view,
  11169. + IdeVimParser *parser,
  11170. + IdeVimParserCommand *command,
  11171. + IdeVimParserError **error);
  11172. +gboolean gb_vim_command_bprevious (GtkSourceView *source_view,
  11173. + IdeVimParser *parser,
  11174. + IdeVimParserCommand *command,
  11175. + IdeVimParserError **error);
  11176. +gboolean gb_vim_command_cnext (GtkSourceView *source_view,
  11177. + IdeVimParser *parser,
  11178. + IdeVimParserCommand *command,
  11179. + IdeVimParserError **error);
  11180. +gboolean gb_vim_command_cprevious (GtkSourceView *source_view,
  11181. + IdeVimParser *parser,
  11182. + IdeVimParserCommand *command,
  11183. + IdeVimParserError **error);
  11184. +gboolean gb_vim_command_buffers (GtkSourceView *source_view,
  11185. + IdeVimParser *parser,
  11186. + IdeVimParserCommand *command,
  11187. + IdeVimParserError **error);
  11188. +gboolean gb_vim_command_nohl (GtkSourceView *source_view,
  11189. + IdeVimParser *parser,
  11190. + IdeVimParserCommand *command,
  11191. + IdeVimParserError **error);
  11192. +gboolean gb_vim_command_quit (GtkSourceView *source_view,
  11193. + IdeVimParser *parser,
  11194. + IdeVimParserCommand *command,
  11195. + IdeVimParserError **error);
  11196. +gboolean gb_vim_command_write (GtkSourceView *source_view,
  11197. + IdeVimParser *parser,
  11198. + IdeVimParserCommand *command,
  11199. + IdeVimParserError **error);
  11200. +gboolean gb_vim_command_wq (GtkSourceView *source_view,
  11201. + IdeVimParser *parser,
  11202. + IdeVimParserCommand *command,
  11203. + IdeVimParserError **error);
  11204. +gboolean gb_vim_command_split (GtkSourceView *source_view,
  11205. + IdeVimParser *parser,
  11206. + IdeVimParserCommand *command,
  11207. + IdeVimParserError **error);
  11208. +gboolean gb_vim_command_vsplit (GtkSourceView *source_view,
  11209. + IdeVimParser *parser,
  11210. + IdeVimParserCommand *command,
  11211. + IdeVimParserError **error);
  11212. +gboolean gb_vim_command_colorscheme (GtkSourceView *source_view,
  11213. + IdeVimParser *parser,
  11214. + IdeVimParserCommand *command,
  11215. + IdeVimParserError **error);
  11216. +gboolean gb_vim_command_make (GtkSourceView *source_view,
  11217. + IdeVimParser *parser,
  11218. + IdeVimParserCommand *command,
  11219. + IdeVimParserError **error);
  11220. +gboolean gb_vim_command_syntax (GtkSourceView *source_view,
  11221. + IdeVimParser *parser,
  11222. + IdeVimParserCommand *command,
  11223. + IdeVimParserError **error);
  11224. +gboolean gb_vim_command_help (GtkSourceView *source_view,
  11225. + IdeVimParser *parser,
  11226. + IdeVimParserCommand *command,
  11227. + IdeVimParserError **error);
  11228. +gboolean gb_vim_command_tabedit (GtkSourceView *source_view,
  11229. + IdeVimParser *parser,
  11230. + IdeVimParserCommand *command,
  11231. + IdeVimParserError **error);
  11232. +gboolean gb_vim_command_edit (GtkSourceView *source_view,
  11233. + IdeVimParser *parser,
  11234. + IdeVimParserCommand *command,
  11235. + IdeVimParserError **error);
  11236. +gboolean gb_vim_command_sort (GtkSourceView *source_view,
  11237. + IdeVimParser *parser,
  11238. + IdeVimParserCommand *command,
  11239. + IdeVimParserError **error);
  11240. +gboolean gb_vim_command_set (GtkSourceView *source_view,
  11241. + IdeVimParser *parser,
  11242. + IdeVimParserCommand *command,
  11243. + IdeVimParserError **error);
  11244.  
  11245. G_END_DECLS
  11246.  
  11247. diff --git a/src/editor/gb-editor-frame.c b/src/editor/gb-editor-frame.c
  11248. index d32cf53..09e3684 100644
  11249. --- a/src/editor/gb-editor-frame.c
  11250. +++ b/src/editor/gb-editor-frame.c
  11251. @@ -547,8 +547,10 @@ gb_editor_frame_set_show_map (GbEditorFrame *self,
  11252. G_CALLBACK (gb_editor_frame_hide_map),
  11253. self,
  11254. G_CONNECT_SWAPPED);
  11255. +
  11256. gtk_container_add (GTK_CONTAINER (self->source_map_container),
  11257. GTK_WIDGET (self->source_map));
  11258. +
  11259. g_signal_emit_by_name (self->source_map, "show-map");
  11260. }
  11261.  
  11262. diff --git a/src/resources/gnome-builder.gresource.xml b/src/resources/gnome-builder.gresource.xml
  11263. index c2805df..6a41a99 100644
  11264. --- a/src/resources/gnome-builder.gresource.xml
  11265. +++ b/src/resources/gnome-builder.gresource.xml
  11266. @@ -16,7 +16,6 @@
  11267. <file alias="theme/Adwaita-dark.css">../../data/theme/Adwaita-dark.css</file>
  11268. <file alias="theme/Adwaita-shared.css">../../data/theme/Adwaita-shared.css</file>
  11269. <file alias="theme/shared.css">../../data/theme/shared.css</file>
  11270. -
  11271. <file alias="ui/gb-editor-frame.ui">../../data/ui/gb-editor-frame.ui</file>
  11272. <file alias="ui/gb-editor-settings-widget.ui">../../data/ui/gb-editor-settings-widget.ui</file>
  11273. <file alias="ui/gb-editor-tweak-widget.ui">../../data/ui/gb-editor-tweak-widget.ui</file>
  11274. diff --git a/src/views/gb-view-stack-actions.c b/src/views/gb-view-stack-actions.c
  11275. index c2d5781..431cfc7 100644
  11276. --- a/src/views/gb-view-stack-actions.c
  11277. +++ b/src/views/gb-view-stack-actions.c
  11278. @@ -26,6 +26,8 @@
  11279. #include "gb-view-stack-actions.h"
  11280. #include "gb-view-stack-private.h"
  11281.  
  11282. +#include <glib/gprintf.h>
  11283. +
  11284. static void
  11285. gb_view_stack_actions_close_cb (GObject *object,
  11286. GAsyncResult *result,
  11287. @@ -40,7 +42,6 @@ gb_view_stack_actions_close_cb (GObject *object,
  11288. gb_view_stack_remove (self, view);
  11289. }
  11290.  
  11291. -
  11292. static void
  11293. gb_view_stack_actions_close (GSimpleAction *action,
  11294. GVariant *param,
  11295. @@ -169,13 +170,17 @@ gb_view_stack_actions_split_right (GSimpleAction *action,
  11296. }
  11297.  
  11298. static void
  11299. -gb_view_stack_actions_next_view (GSimpleAction *action,
  11300. - GVariant *param,
  11301. - gpointer user_data)
  11302. +move_in__history (GbViewStack *self,
  11303. + gint count,
  11304. + gboolean up)
  11305. {
  11306. - GbViewStack *self = user_data;
  11307. GtkWidget *active_view;
  11308. GtkWidget *new_view;
  11309. + GList *children;
  11310. + GtkListBoxRow *row;
  11311. + gint selected_index;
  11312. + gint new_index;
  11313. + gint len;
  11314.  
  11315. IDE_ENTRY;
  11316.  
  11317. @@ -185,49 +190,59 @@ gb_view_stack_actions_next_view (GSimpleAction *action,
  11318. if (active_view == NULL || !GB_IS_VIEW (active_view))
  11319. return;
  11320.  
  11321. - if (g_list_length (self->focus_history) <= 1)
  11322. + children = gtk_container_get_children (GTK_CONTAINER (self->views_listbox));
  11323. + len = g_list_length (children);
  11324. + if (len <= 1)
  11325. return;
  11326.  
  11327. - new_view = g_list_last (self->focus_history)->data;
  11328. + selected_index = gtk_list_box_row_get_index (gtk_list_box_get_selected_row (self->views_listbox));
  11329. + if (up)
  11330. + {
  11331. + if (count > selected_index)
  11332. + new_index = 0;
  11333. + else
  11334. + new_index = selected_index - count;
  11335. + }
  11336. + else
  11337. + {
  11338. + if (selected_index + count > len - 1)
  11339. + new_index = len - 1;
  11340. + else
  11341. + new_index = selected_index + count;
  11342. + }
  11343. +
  11344. + row = gtk_list_box_get_row_at_index (self->views_listbox, new_index);
  11345. + new_view = GTK_WIDGET (g_object_get_data (G_OBJECT (row), "GB_VIEW"));
  11346. g_assert (GB_IS_VIEW (new_view));
  11347.  
  11348. gb_view_stack_set_active_view (self, new_view);
  11349. -
  11350. - IDE_EXIT;
  11351. + g_list_free (children);
  11352. }
  11353.  
  11354. static void
  11355. -gb_view_stack_actions_previous_view (GSimpleAction *action,
  11356. - GVariant *param,
  11357. - gpointer user_data)
  11358. +gb_view_stack_actions_next_view (GSimpleAction *action,
  11359. + GVariant *param,
  11360. + gpointer user_data)
  11361. {
  11362. GbViewStack *self = user_data;
  11363. - GtkWidget *active_view;
  11364. - GtkWidget *new_view;
  11365.  
  11366. IDE_ENTRY;
  11367.  
  11368. - g_assert (GB_IS_VIEW_STACK (self));
  11369. + move_in__history (self, g_variant_get_int64 (param), TRUE);
  11370.  
  11371. - active_view = gb_view_stack_get_active_view (self);
  11372. - if (active_view == NULL || !GB_IS_VIEW (active_view))
  11373. - return;
  11374. -
  11375. - if (g_list_length (self->focus_history) <= 1)
  11376. - return;
  11377. -
  11378. - g_assert (active_view);
  11379. - g_assert (self->focus_history);
  11380. - g_assert (self->focus_history->next);
  11381. - g_assert (active_view == self->focus_history->data);
  11382. + IDE_EXIT;
  11383. +}
  11384.  
  11385. - new_view = self->focus_history->next->data;
  11386. - g_assert (GB_IS_VIEW (new_view));
  11387. +static void
  11388. +gb_view_stack_actions_previous_view (GSimpleAction *action,
  11389. + GVariant *param,
  11390. + gpointer user_data)
  11391. +{
  11392. +GbViewStack *self = user_data;
  11393.  
  11394. - self->focus_history = g_list_remove_link (self->focus_history, self->focus_history);
  11395. - self->focus_history = g_list_append (self->focus_history, active_view);
  11396. + IDE_ENTRY;
  11397.  
  11398. - gb_view_stack_set_active_view (self, new_view);
  11399. + move_in__history (self, g_variant_get_int64 (param), FALSE);
  11400.  
  11401. IDE_EXIT;
  11402. }
  11403. @@ -276,8 +291,8 @@ static const GActionEntry gGbViewStackActions[] = {
  11404. { "go-backward", gb_view_stack_actions_go_backward },
  11405. { "move-left", gb_view_stack_actions_move_left },
  11406. { "move-right", gb_view_stack_actions_move_right },
  11407. - { "next-view", gb_view_stack_actions_next_view },
  11408. - { "previous-view", gb_view_stack_actions_previous_view },
  11409. + { "next-view", gb_view_stack_actions_next_view, "x" },
  11410. + { "previous-view", gb_view_stack_actions_previous_view, "x" },
  11411. { "show-list", gb_view_stack_actions_show_list },
  11412. { "split-down", NULL, NULL, "false", gb_view_stack_actions_split_down },
  11413. { "split-left", gb_view_stack_actions_split_left },
  11414. diff --git a/src/views/gb-view-stack-private.h b/src/views/gb-view-stack-private.h
  11415. index 349642a..8fe84dc 100644
  11416. --- a/src/views/gb-view-stack-private.h
  11417. +++ b/src/views/gb-view-stack-private.h
  11418. @@ -28,7 +28,6 @@ struct _GbViewStack
  11419. {
  11420. GtkBin parent_instance;
  11421.  
  11422. - GList *focus_history;
  11423. IdeBackForwardList *back_forward_list;
  11424.  
  11425. /* Weak references */
  11426. diff --git a/src/views/gb-view-stack.c b/src/views/gb-view-stack.c
  11427. index 3452c3f..5830469 100644
  11428. --- a/src/views/gb-view-stack.c
  11429. +++ b/src/views/gb-view-stack.c
  11430. @@ -17,6 +17,7 @@
  11431. */
  11432.  
  11433. #include <glib/gi18n.h>
  11434. +#include <glib/gprintf.h>
  11435. #include <ide.h>
  11436.  
  11437. #include "gb-document.h"
  11438. @@ -86,8 +87,8 @@ gb_view_stack_add_list_row (GbViewStack *self,
  11439. NULL);
  11440. g_object_bind_property (child, "modified", label, "visible", G_BINDING_SYNC_CREATE);
  11441. gtk_container_add (GTK_CONTAINER (box), label);
  11442. -
  11443. - gtk_container_add (GTK_CONTAINER (self->views_listbox), row);
  11444. + gtk_list_box_prepend (self->views_listbox, row);
  11445. + gtk_list_box_select_row (self->views_listbox, GTK_LIST_BOX_ROW (row));
  11446. }
  11447.  
  11448. static void
  11449. @@ -117,37 +118,6 @@ gb_view_stack_remove_list_row (GbViewStack *self,
  11450. }
  11451.  
  11452. static void
  11453. -gb_view_stack_move_top_list_row (GbViewStack *self,
  11454. - GbView *view)
  11455. -{
  11456. - GList *children;
  11457. - GList *iter;
  11458. -
  11459. - g_assert (GB_IS_VIEW_STACK (self));
  11460. - g_assert (GB_IS_VIEW (view));
  11461. -
  11462. - children = gtk_container_get_children (GTK_CONTAINER (self->views_listbox));
  11463. -
  11464. - for (iter = children; iter; iter = iter->next)
  11465. - {
  11466. - GtkWidget *row = iter->data;
  11467. - GbView *item = g_object_get_data (G_OBJECT (row), "GB_VIEW");
  11468. -
  11469. - if (item == view)
  11470. - {
  11471. - g_object_ref (row);
  11472. - gtk_container_remove (GTK_CONTAINER (self->views_listbox), row);
  11473. - gtk_list_box_prepend (self->views_listbox, row);
  11474. - gtk_list_box_select_row (self->views_listbox, GTK_LIST_BOX_ROW (row));
  11475. - g_object_unref (row);
  11476. - break;
  11477. - }
  11478. - }
  11479. -
  11480. - g_list_free (children);
  11481. -}
  11482. -
  11483. -static void
  11484. gb_view_stack_add (GtkContainer *container,
  11485. GtkWidget *child)
  11486. {
  11487. @@ -163,7 +133,6 @@ gb_view_stack_add (GtkContainer *container,
  11488. gtk_widget_set_sensitive (GTK_WIDGET (self->document_button), TRUE);
  11489. gtk_widget_set_visible (GTK_WIDGET (self->views_button), TRUE);
  11490.  
  11491. - self->focus_history = g_list_prepend (self->focus_history, child);
  11492. controls = gb_view_get_controls (GB_VIEW (child));
  11493. if (controls)
  11494. gtk_container_add (GTK_CONTAINER (self->controls_stack), controls);
  11495. @@ -182,19 +151,35 @@ void
  11496. gb_view_stack_remove (GbViewStack *self,
  11497. GbView *view)
  11498. {
  11499. + GbView *selected_view;
  11500. GtkWidget *controls;
  11501. GtkWidget *focus_after_close = NULL;
  11502. + GtkListBoxRow *selected_row;
  11503. + GtkListBoxRow *row;
  11504. + gint selected_index;
  11505.  
  11506. g_assert (GB_IS_VIEW_STACK (self));
  11507. g_assert (GB_IS_VIEW (view));
  11508.  
  11509. - focus_after_close = g_list_nth_data (self->focus_history, 1);
  11510. - if (focus_after_close != NULL)
  11511. - g_object_ref (focus_after_close);
  11512. + selected_row = gtk_list_box_get_selected_row (self->views_listbox);
  11513. + selected_view = GB_VIEW (g_object_get_data (G_OBJECT (selected_row), "GB_VIEW"));
  11514. + if (selected_view == view)
  11515. + {
  11516. + selected_index = gtk_list_box_row_get_index (selected_row);
  11517. + if (selected_index - 1 < 0)
  11518. + row = gtk_list_box_get_row_at_index (self->views_listbox, selected_index + 1);
  11519. + else
  11520. + row = gtk_list_box_get_row_at_index (self->views_listbox, selected_index - 1);
  11521. +
  11522. + if (row != NULL)
  11523. + {
  11524. + focus_after_close = GTK_WIDGET (g_object_get_data (G_OBJECT (row), "GB_VIEW"));
  11525. + g_object_ref (focus_after_close);
  11526. + }
  11527. + }
  11528.  
  11529. gb_view_stack_remove_list_row (self, view);
  11530.  
  11531. - self->focus_history = g_list_remove (self->focus_history, view);
  11532. controls = gb_view_get_controls (view);
  11533. if (controls)
  11534. gtk_container_remove (GTK_CONTAINER (self->controls_stack), controls);
  11535. @@ -204,6 +189,7 @@ gb_view_stack_remove (GbViewStack *self,
  11536. {
  11537. gtk_stack_set_visible_child (self->stack, focus_after_close);
  11538. gtk_widget_grab_focus (GTK_WIDGET (focus_after_close));
  11539. + gtk_list_box_select_row (self->views_listbox, GTK_LIST_BOX_ROW (row));
  11540. g_clear_object (&focus_after_close);
  11541. }
  11542. else
  11543. @@ -255,9 +241,16 @@ gb_view_stack_grab_focus (GtkWidget *widget)
  11544. static gboolean
  11545. gb_view_stack_is_empty (GbViewStack *self)
  11546. {
  11547. + GList *children;
  11548. + gint len;
  11549. +
  11550. g_return_val_if_fail (GB_IS_VIEW_STACK (self), FALSE);
  11551.  
  11552. - return (self->focus_history == NULL);
  11553. + children = gtk_container_get_children (GTK_CONTAINER (self->views_listbox));
  11554. + len = g_list_length (children);
  11555. + g_list_free (children);
  11556. +
  11557. + return (len == 0);
  11558. }
  11559.  
  11560. static void
  11561. @@ -392,8 +385,10 @@ gb_view_stack__views_listbox_row_activated_cb (GbViewStack *self,
  11562.  
  11563. if (GB_IS_VIEW (view))
  11564. {
  11565. - gtk_widget_hide (GTK_WIDGET (self->views_popover));
  11566. gb_view_stack_set_active_view (self, GTK_WIDGET (view));
  11567. + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (self->views_button), FALSE);
  11568. + /* FIX: why we need this now ? */
  11569. + gtk_widget_hide (GTK_WIDGET (self->views_popover));
  11570. gtk_widget_grab_focus (GTK_WIDGET (view));
  11571. }
  11572. }
  11573. @@ -405,6 +400,7 @@ gb_view_stack_destroy (GtkWidget *widget)
  11574.  
  11575. self->destroyed = TRUE;
  11576.  
  11577. +
  11578. GTK_WIDGET_CLASS (gb_view_stack_parent_class)->destroy (widget);
  11579. }
  11580.  
  11581. @@ -429,7 +425,6 @@ gb_view_stack_finalize (GObject *object)
  11582. {
  11583. GbViewStack *self = (GbViewStack *)object;
  11584.  
  11585. - g_clear_pointer (&self->focus_history, g_list_free);
  11586. ide_clear_weak_pointer (&self->context);
  11587. ide_clear_weak_pointer (&self->title_binding);
  11588. ide_clear_weak_pointer (&self->active_view);
  11589. @@ -611,14 +606,13 @@ gb_view_stack_set_active_view (GbViewStack *self,
  11590. GtkWidget *controls;
  11591. GBinding *binding;
  11592. GActionGroup *group;
  11593. + GList *children;
  11594. + GList *iter;
  11595.  
  11596. ide_set_weak_pointer (&self->active_view, active_view);
  11597. if (active_view != gtk_stack_get_visible_child (self->stack))
  11598. gtk_stack_set_visible_child (self->stack, active_view);
  11599.  
  11600. - self->focus_history = g_list_remove (self->focus_history, active_view);
  11601. - self->focus_history = g_list_prepend (self->focus_history, active_view);
  11602. -
  11603. binding = g_object_bind_property (active_view, "title",
  11604. self->title_label, "label",
  11605. G_BINDING_SYNC_CREATE);
  11606. @@ -640,7 +634,18 @@ gb_view_stack_set_active_view (GbViewStack *self,
  11607. if (group)
  11608. gtk_widget_insert_action_group (GTK_WIDGET (self), "view", group);
  11609.  
  11610. - gb_view_stack_move_top_list_row (self, GB_VIEW (active_view));
  11611. + children = gtk_container_get_children (GTK_CONTAINER (self->views_listbox));
  11612. + for (iter = children; iter; iter = iter->next)
  11613. + {
  11614. + GtkWidget *view = g_object_get_data (iter->data, "GB_VIEW");
  11615. + if (view == active_view)
  11616. + {
  11617. + gtk_list_box_select_row (self->views_listbox, GTK_LIST_BOX_ROW (iter->data));
  11618. + break;
  11619. + }
  11620. + }
  11621. +
  11622. + g_list_free (children);
  11623. }
  11624.  
  11625. g_object_notify_by_pspec (G_OBJECT (self), gParamSpecs [PROP_ACTIVE_VIEW]);
  11626. @@ -771,20 +776,24 @@ GbDocument *
  11627. gb_view_stack_find_document_typed (GbViewStack *self,
  11628. GType document_type)
  11629. {
  11630. + GList *children;
  11631. GList *iter;
  11632.  
  11633. g_return_val_if_fail (GB_IS_VIEW_STACK (self), NULL);
  11634. g_return_val_if_fail (g_type_is_a (document_type, GB_TYPE_DOCUMENT), NULL);
  11635.  
  11636. - for (iter = self->focus_history; iter; iter = iter->next)
  11637. + children = gtk_container_get_children (GTK_CONTAINER (self->views_listbox));
  11638. +
  11639. + for (iter = children; iter; iter = iter->next)
  11640. {
  11641. - GbDocument *document;
  11642. + GbView *view = g_object_get_data (iter->data, "GB_VIEW");
  11643. + GbDocument *document = gb_view_get_document (view);
  11644.  
  11645. - document = gb_view_get_document (iter->data);
  11646. if (g_type_is_a (G_TYPE_FROM_INSTANCE (document), document_type))
  11647. return document;
  11648. }
  11649.  
  11650. + g_list_free (children);
  11651. return NULL;
  11652. }
  11653.  
  11654. diff --git a/tests/Makefile.am b/tests/Makefile.am
  11655. index bdeff67..891c909 100644
  11656. --- a/tests/Makefile.am
  11657. +++ b/tests/Makefile.am
  11658. @@ -132,6 +132,15 @@ test_vim_LDADD = \
  11659. $(top_builddir)/src/libgnome-builder.la \
  11660. $(NULL)
  11661.  
  11662. +TESTS += test-ide-vim-parser
  11663. +test_ide_vim_parser_SOURCES = test-ide-vim-parser.c
  11664. +test_ide_vim_parser_CFLAGS = $(tests_cflags)
  11665. +test_ide_vim_parser_LDADD = $(tests_libs)
  11666. +
  11667. +TESTS += test-ide-vim-parser-settable
  11668. +test_ide_vim_parser_settable_SOURCES = test-ide-vim-parser-settable.c
  11669. +test_ide_vim_parser_settable_CFLAGS = $(tests_cflags)
  11670. +test_ide_vim_parser_settable_LDADD = $(tests_libs)
  11671.  
  11672. misc_programs += test-ide-source-view
  11673. test_ide_source_view_SOURCES = test-ide-source-view.c
  11674. diff --git a/tests/test-ide-vim-parser-settable.c b/tests/test-ide-vim-parser-settable.c
  11675. new file mode 100644
  11676. index 0000000..5f249fc
  11677. --- /dev/null
  11678. +++ b/tests/test-ide-vim-parser-settable.c
  11679. @@ -0,0 +1,280 @@
  11680. +/* test-ide-vim-parser-settable.c
  11681. + *
  11682. + * Copyright (C) 2015 Sébastien Lafargue <slafargue@gnome.org>
  11683. + *
  11684. + * This program is free software: you can redistribute it and/or modify
  11685. + * it under the terms of the GNU General Public License as published by
  11686. + * the Free Software Foundation, either version 3 of the License, or
  11687. + * (at your option) any later version.
  11688. + *
  11689. + * This program is distributed in the hope that it will be useful,
  11690. + * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11691. + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11692. + * GNU General Public License for more details.
  11693. + *
  11694. + * You should have received a copy of the GNU General Public License
  11695. + * along with this program. If not, see <http://www.gnu.org/licenses/>.
  11696. + */
  11697. +
  11698. +#include <string.h>
  11699. +
  11700. +#include <glib/gprintf.h>
  11701. +#include <gtk/gtk.h>
  11702. +
  11703. +#include <ide.h>
  11704. +
  11705. +static IdeVimParser *global_parser = NULL;
  11706. +
  11707. +static gboolean boolean_state = TRUE;
  11708. +static gint integer_state = 1;
  11709. +static gchar *string_state = NULL;
  11710. +
  11711. +static gboolean
  11712. +boolean_func (IdeVimParserSettable *settable,
  11713. + GValue *value,
  11714. + IdeVimParserSettableOp op,
  11715. + IdeVimParserError **error)
  11716. +{
  11717. + if (op == IDE_VIM_PARSER_SETTABLE_OP_SET || op == IDE_VIM_PARSER_SETTABLE_OP_RESET)
  11718. + boolean_state = g_value_get_boolean (value);
  11719. +
  11720. + if (op == IDE_VIM_PARSER_SETTABLE_OP_GET)
  11721. + g_value_set_boolean (value, boolean_state);
  11722. +
  11723. + return TRUE;
  11724. +}
  11725. +
  11726. +static gboolean
  11727. +integer_func (IdeVimParserSettable *settable,
  11728. + GValue *value,
  11729. + IdeVimParserSettableOp op,
  11730. + IdeVimParserError **error)
  11731. +{
  11732. + if (op == IDE_VIM_PARSER_SETTABLE_OP_SET || op == IDE_VIM_PARSER_SETTABLE_OP_RESET)
  11733. + integer_state = g_value_get_int64 (value);
  11734. +
  11735. + if (op == IDE_VIM_PARSER_SETTABLE_OP_GET)
  11736. + g_value_set_int64 (value, integer_state);
  11737. +
  11738. + return TRUE;
  11739. +}
  11740. +
  11741. +static gboolean
  11742. +string_func (IdeVimParserSettable *settable,
  11743. + GValue *value,
  11744. + IdeVimParserSettableOp op,
  11745. + IdeVimParserError **error)
  11746. +{
  11747. + if (op == IDE_VIM_PARSER_SETTABLE_OP_SET || op == IDE_VIM_PARSER_SETTABLE_OP_RESET)
  11748. + {
  11749. + g_clear_pointer (&string_state, g_free);
  11750. + string_state = g_strdup (g_value_get_string (value));
  11751. + }
  11752. +
  11753. + if (op == IDE_VIM_PARSER_SETTABLE_OP_GET)
  11754. + g_value_set_string (value, string_state);
  11755. +
  11756. + return TRUE;
  11757. +}
  11758. +
  11759. +static void
  11760. +test_boolean_settable (void)
  11761. +{
  11762. + IdeVimParserSettable *settable;
  11763. + GValue g_value = G_VALUE_INIT;
  11764. + IdeVimParserError *error = NULL;
  11765. + const gchar *name;
  11766. + const gchar *shortname;
  11767. + const gchar *help;
  11768. +
  11769. + name = g_strdup ("name");
  11770. + shortname = g_strdup ("shortname");
  11771. + help = g_strdup ("help");
  11772. +
  11773. + g_value_init (&g_value, G_TYPE_BOOLEAN);
  11774. + g_value_set_boolean (&g_value, FALSE);
  11775. +
  11776. + settable = ide_vim_parser_settable_boolean_new (name, shortname, help, TRUE, TRUE, boolean_func, NULL);
  11777. + g_assert (!g_strcmp0 (name, ide_vim_parser_settable_get_name (settable)));
  11778. + g_assert (!g_strcmp0 (shortname, ide_vim_parser_settable_get_shortname (settable)));
  11779. + g_assert (!g_strcmp0 (help, ide_vim_parser_settable_get_help (settable)));
  11780. + g_assert (g_value_get_boolean (ide_vim_parser_settable_get_default_value (settable)) == TRUE);
  11781. +
  11782. + g_assert (g_value_get_boolean (ide_vim_parser_settable_get_value (settable)) == TRUE);
  11783. + g_assert (ide_vim_parser_settable_set_value (settable, &g_value, &error));
  11784. + g_assert (error == NULL);
  11785. + g_assert (g_value_get_boolean (ide_vim_parser_settable_get_value (settable)) == FALSE);
  11786. + ide_vim_parser_settable_reset_value (settable);
  11787. + g_assert (g_value_get_boolean (ide_vim_parser_settable_get_value (settable)) == TRUE);
  11788. +}
  11789. +
  11790. +static void
  11791. +test_integer_settable (void)
  11792. +{
  11793. + IdeVimParserSettable *settable;
  11794. + GValue g_value = G_VALUE_INIT;
  11795. + IdeVimParserError *error = NULL;
  11796. + const gchar *name;
  11797. + const gchar *shortname;
  11798. + const gchar *help;
  11799. +
  11800. + name = g_strdup ("name");
  11801. + shortname = g_strdup ("shortname");
  11802. + help = g_strdup ("help");
  11803. +
  11804. + g_value_init (&g_value, G_TYPE_INT64);
  11805. + g_value_set_int64 (&g_value, 15);
  11806. +
  11807. + settable = ide_vim_parser_settable_integer_new (name, shortname, help, 5, 10, 0, 20, integer_func, NULL);
  11808. + g_assert (!g_strcmp0 (name, ide_vim_parser_settable_get_name (settable)));
  11809. + g_assert (!g_strcmp0 (shortname, ide_vim_parser_settable_get_shortname (settable)));
  11810. + g_assert (!g_strcmp0 (help, ide_vim_parser_settable_get_help (settable)));
  11811. + g_assert (g_value_get_int64 (ide_vim_parser_settable_get_default_value (settable)) == 10);
  11812. +
  11813. + g_assert (g_value_get_int64 (ide_vim_parser_settable_get_value (settable)) == 5);
  11814. + g_assert (ide_vim_parser_settable_set_value (settable, &g_value, &error));
  11815. + g_assert (error == NULL);
  11816. + g_assert (g_value_get_int64 (ide_vim_parser_settable_get_value (settable)) == 15);
  11817. + ide_vim_parser_settable_reset_value (settable);
  11818. + g_assert (g_value_get_int64 (ide_vim_parser_settable_get_value (settable)) == 10);
  11819. +
  11820. + g_value_set_int64 (&g_value, 30);
  11821. + g_assert (!ide_vim_parser_settable_set_value (settable, &g_value, &error));
  11822. + g_assert (error != NULL);
  11823. +}
  11824. +
  11825. +static void
  11826. +test_string_settable (void)
  11827. +{
  11828. + IdeVimParserSettable *settable;
  11829. + GValue g_value = G_VALUE_INIT;
  11830. + IdeVimParserError *error = NULL;
  11831. + const gchar *name;
  11832. + const gchar *shortname;
  11833. + const gchar *help;
  11834. +
  11835. + name = g_strdup ("name");
  11836. + shortname = g_strdup ("shortname");
  11837. + help = g_strdup ("help");
  11838. +
  11839. + g_value_init (&g_value, G_TYPE_STRING);
  11840. + g_value_set_string (&g_value, "value 1");
  11841. +
  11842. + settable = ide_vim_parser_settable_string_new (name, shortname, help, "initial value", "default value", string_func, NULL);
  11843. + g_assert (!g_strcmp0 (name, ide_vim_parser_settable_get_name (settable)));
  11844. + g_assert (!g_strcmp0 (shortname, ide_vim_parser_settable_get_shortname (settable)));
  11845. + g_assert (!g_strcmp0 (help, ide_vim_parser_settable_get_help (settable)));
  11846. + g_assert (!g_strcmp0 (g_value_get_string (ide_vim_parser_settable_get_default_value (settable)), "default value"));
  11847. +
  11848. + g_assert (!g_strcmp0 (g_value_get_string (ide_vim_parser_settable_get_value (settable)), "initial value"));
  11849. + g_assert (ide_vim_parser_settable_set_value (settable, &g_value, &error));
  11850. + g_assert (error == NULL);
  11851. + g_assert (!g_strcmp0 (g_value_get_string (ide_vim_parser_settable_get_value (settable)), "value 1"));
  11852. + ide_vim_parser_settable_reset_value (settable);
  11853. + g_assert (!g_strcmp0 (g_value_get_string (ide_vim_parser_settable_get_value (settable)), "default value"));
  11854. +}
  11855. +
  11856. +static void
  11857. +test_objects_pool (void)
  11858. +{
  11859. + IdeVimParserSettable *boolean_settable;
  11860. + IdeVimParserSettable *string_settable;
  11861. + IdeVimParserSettable *integer_settable;
  11862. + IdeVimParserObjectsPool *pool = ide_vim_parser_objects_pool_new ();
  11863. +
  11864. + boolean_settable = ide_vim_parser_settable_boolean_new ("auto-indent", "ai", "auto indent parameter",
  11865. + TRUE, FALSE, boolean_func, NULL);
  11866. + g_assert (boolean_settable != NULL);
  11867. + g_assert (ide_vim_parser_objects_pool_add_settable (pool, boolean_settable, TRUE));
  11868. +
  11869. + string_settable = ide_vim_parser_settable_string_new ("filetype", "ft", "language file type",
  11870. + "c", "none", string_func, NULL);
  11871. + g_assert (string_settable != NULL);
  11872. + g_assert (ide_vim_parser_objects_pool_add_settable (pool, string_settable, TRUE));
  11873. +
  11874. + integer_settable = ide_vim_parser_settable_integer_new ("scrolloff", "so", "scroll offset",
  11875. + 50, 0, 0, 1000, integer_func, NULL);
  11876. + g_assert (integer_settable != NULL);
  11877. + g_assert (ide_vim_parser_objects_pool_add_settable (pool, integer_settable, TRUE));
  11878. +
  11879. + g_assert (ide_vim_parser_objects_pool_lookup_settable (pool, "ai") == boolean_settable);
  11880. + g_assert (ide_vim_parser_objects_pool_lookup_settable (pool, "auto-indent") == boolean_settable);
  11881. +
  11882. + g_assert (ide_vim_parser_objects_pool_lookup_settable (pool, "filetype") == string_settable);
  11883. + g_assert (ide_vim_parser_objects_pool_lookup_settable (pool, "ft") == string_settable);
  11884. +
  11885. + g_assert (ide_vim_parser_objects_pool_lookup_settable (pool, "scrolloff") == integer_settable);
  11886. + g_assert (ide_vim_parser_objects_pool_lookup_settable (pool, "so") == integer_settable);
  11887. +
  11888. + g_assert (ide_vim_parser_objects_pool_remove_settable (pool, "ai"));
  11889. + g_assert (!ide_vim_parser_objects_pool_remove_settable (pool, "auto-indent"));
  11890. +
  11891. + g_assert (ide_vim_parser_objects_pool_remove_settable (pool, "filetype"));
  11892. + g_assert (!ide_vim_parser_objects_pool_remove_settable (pool, "ft"));
  11893. +
  11894. + g_assert (ide_vim_parser_objects_pool_remove_settable (pool, "scrolloff"));
  11895. + g_assert (!ide_vim_parser_objects_pool_remove_settable (pool, "so"));
  11896. +
  11897. + g_assert (ide_vim_parser_objects_pool_lookup_settable (pool, "auto-indent") == NULL);
  11898. + g_assert (ide_vim_parser_objects_pool_lookup_settable (pool, "ai") == NULL);
  11899. +
  11900. + g_assert (ide_vim_parser_objects_pool_lookup_settable (pool, "ft") == NULL);
  11901. + g_assert (ide_vim_parser_objects_pool_lookup_settable (pool, "filetype") == NULL);
  11902. +
  11903. + g_assert (ide_vim_parser_objects_pool_lookup_settable (pool, "so") == NULL);
  11904. + g_assert (ide_vim_parser_objects_pool_lookup_settable (pool, "scrolloff") == NULL);
  11905. +
  11906. + g_object_unref (pool);
  11907. +}
  11908. +
  11909. +static void
  11910. +test_objects_pool2 (void)
  11911. +{
  11912. + IdeVimParserSettable *boolean1_settable;
  11913. + IdeVimParserSettable *boolean2_settable;
  11914. + IdeVimParserObjectsPool *pool = ide_vim_parser_objects_pool_new ();
  11915. +
  11916. + boolean1_settable = ide_vim_parser_settable_boolean_new ("auto-indent", "ai", "auto indent parameter",
  11917. + TRUE, FALSE, boolean_func, NULL);
  11918. +
  11919. + boolean2_settable = ide_vim_parser_settable_boolean_new ("auto-indent", "xx", "auto indent parameter",
  11920. + TRUE, FALSE, boolean_func, NULL);
  11921. +
  11922. + ide_vim_parser_objects_pool_add_settable (pool, boolean1_settable, TRUE);
  11923. + g_assert (ide_vim_parser_objects_pool_add_settable (pool, boolean2_settable, TRUE));
  11924. +
  11925. + g_assert (ide_vim_parser_objects_pool_lookup_settable (pool, "ai") == NULL);
  11926. + g_assert (ide_vim_parser_objects_pool_lookup_settable (pool, "xx") == boolean2_settable);
  11927. +
  11928. + g_assert (!ide_vim_parser_objects_pool_add_settable (pool, boolean1_settable, FALSE));
  11929. +
  11930. + g_object_unref (pool);
  11931. +}
  11932. +
  11933. +static void
  11934. +test_parse_set (void)
  11935. +{
  11936. + global_parser = ide_vim_parser_new ();
  11937. +
  11938. + ide_vim_parser_parse_settable (global_parser, " ai filetype=html scrolloff:10 ", NULL);
  11939. +
  11940. + g_object_unref (global_parser);
  11941. +}
  11942. +
  11943. +gint
  11944. +main (gint argc, gchar *argv[])
  11945. +{
  11946. + gtk_init (&argc, &argv);
  11947. + g_test_init (&argc, &argv, NULL);
  11948. +
  11949. + g_test_add_func ("/Ide/VimParser/settable", test_boolean_settable);
  11950. + g_test_add_func ("/Ide/VimParser/settable", test_integer_settable);
  11951. + g_test_add_func ("/Ide/VimParser/settable", test_string_settable);
  11952. +
  11953. + g_test_add_func ("/Ide/VimParser/objects-pool", test_objects_pool);
  11954. + g_test_add_func ("/Ide/VimParser/objects-pool2", test_objects_pool2);
  11955. +
  11956. + g_test_add_func ("/Ide/VimParser/parse-set", test_parse_set);
  11957. +
  11958. + return g_test_run ();
  11959. +}
  11960. diff --git a/tests/test-ide-vim-parser.c b/tests/test-ide-vim-parser.c
  11961. new file mode 100644
  11962. index 0000000..e1b0fec
  11963. --- /dev/null
  11964. +++ b/tests/test-ide-vim-parser.c
  11965. @@ -0,0 +1,283 @@
  11966. +/* test-ide-vim-parser.c
  11967. + *
  11968. + * Copyright (C) 2015 Sébastien Lafargue <slafargue@gnome.org>
  11969. + *
  11970. + * This program is free software: you can redistribute it and/or modify
  11971. + * it under the terms of the GNU General Public License as published by
  11972. + * the Free Software Foundation, either version 3 of the License, or
  11973. + * (at your option) any later version.
  11974. + *
  11975. + * This program is distributed in the hope that it will be useful,
  11976. + * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11977. + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11978. + * GNU General Public License for more details.
  11979. + *
  11980. + * You should have received a copy of the GNU General Public License
  11981. + * along with this program. If not, see <http://www.gnu.org/licenses/>.
  11982. + */
  11983. +
  11984. +#include <string.h>
  11985. +
  11986. +#include <glib/gprintf.h>
  11987. +#include <gtk/gtk.h>
  11988. +
  11989. +#include <ide.h>
  11990. +
  11991. +static IdeVimParser *global_parser = NULL;
  11992. +static GtkTextBuffer *global_buffer = NULL;
  11993. +
  11994. +typedef struct
  11995. +{
  11996. + const gchar *comment;
  11997. + const gchar *line;
  11998. + IdeVimParserErrorCode error_code;
  11999. +} ParseTest;
  12000. +
  12001. +typedef struct
  12002. +{
  12003. + IdeVimCompleteItemKind kind;
  12004. + const gchar *name;
  12005. + const gchar *shortname;
  12006. +} CompleteResult;
  12007. +
  12008. +static gboolean
  12009. +test_cmd_a_func (GtkSourceView *source_view,
  12010. + IdeVimParser *parser,
  12011. + IdeVimParserCommand *command,
  12012. + IdeVimParserError **error)
  12013. +{
  12014. + return TRUE;
  12015. +}
  12016. +
  12017. +static gboolean
  12018. +test_generic_func (GtkSourceView *source_view,
  12019. + IdeVimParser *parser,
  12020. + IdeVimParserCommand *command,
  12021. + IdeVimParserError **error)
  12022. +{
  12023. + return TRUE;
  12024. +}
  12025. +
  12026. +static void
  12027. +context_test_complete_create (void)
  12028. +{
  12029. + global_buffer = gtk_text_buffer_new (NULL);
  12030. + global_parser = ide_vim_parser_new ();
  12031. +
  12032. + g_assert_true (ide_vim_parser_add_command (global_parser, "test_comp_a", "test_comp", test_generic_func,
  12033. + IDE_VIM_PARSER_TOKEN_COMMAND_NAME, IDE_VIM_PARSER_TOKEN_FLAG_NONE,
  12034. + NULL));
  12035. +
  12036. + g_assert_true (ide_vim_parser_add_command (global_parser, "test_comp_b", "test_co", test_generic_func,
  12037. + IDE_VIM_PARSER_TOKEN_COMMAND_NAME, IDE_VIM_PARSER_TOKEN_FLAG_NONE,
  12038. + NULL));
  12039. +
  12040. +}
  12041. +
  12042. +static void
  12043. +context_create (void)
  12044. +{
  12045. + global_buffer = gtk_text_buffer_new (NULL);
  12046. + global_parser = ide_vim_parser_new ();
  12047. +
  12048. + g_assert_true (ide_vim_parser_add_command (global_parser, "test_cmd_a", "test_cm", test_cmd_a_func,
  12049. + IDE_VIM_PARSER_TOKEN_NUMBER, IDE_VIM_PARSER_TOKEN_FLAG_OPTIONNAL,
  12050. + IDE_VIM_PARSER_TOKEN_COMMAND_NAME, IDE_VIM_PARSER_TOKEN_FLAG_NONE,
  12051. + IDE_VIM_PARSER_TOKEN_NUMBER, IDE_VIM_PARSER_TOKEN_FLAG_OPTIONNAL,
  12052. + NULL));
  12053. +
  12054. + g_assert_true (ide_vim_parser_add_command (global_parser, "test_cmd_b", "test_cmd", test_generic_func,
  12055. + IDE_VIM_PARSER_TOKEN_COMMAND_NAME, IDE_VIM_PARSER_TOKEN_FLAG_NONE,
  12056. + NULL));
  12057. +
  12058. + g_assert_true (ide_vim_parser_add_command (global_parser, "test_cmd_c", "", test_generic_func,
  12059. + IDE_VIM_PARSER_TOKEN_NUMBER, IDE_VIM_PARSER_TOKEN_FLAG_NONE,
  12060. + IDE_VIM_PARSER_TOKEN_COMMAND_NAME, IDE_VIM_PARSER_TOKEN_FLAG_NONE,
  12061. + IDE_VIM_PARSER_TOKEN_NUMBER, IDE_VIM_PARSER_TOKEN_FLAG_NONE,
  12062. + NULL));
  12063. +
  12064. + g_assert_true (ide_vim_parser_add_command (global_parser, "test_cmd_d", "", test_generic_func,
  12065. + IDE_VIM_PARSER_TOKEN_NUMBER, IDE_VIM_PARSER_TOKEN_FLAG_OPTIONNAL,
  12066. + IDE_VIM_PARSER_TOKEN_COMMAND_NAME, IDE_VIM_PARSER_TOKEN_FLAG_NONE,
  12067. + IDE_VIM_PARSER_TOKEN_NUMBER, IDE_VIM_PARSER_TOKEN_FLAG_NONE,
  12068. + NULL));
  12069. +
  12070. + g_assert_true (ide_vim_parser_add_command (global_parser, "test_cmd_e", "", test_generic_func,
  12071. + IDE_VIM_PARSER_TOKEN_NUMBER, IDE_VIM_PARSER_TOKEN_FLAG_NONE,
  12072. + IDE_VIM_PARSER_TOKEN_COMMAND_NAME, IDE_VIM_PARSER_TOKEN_FLAG_NONE,
  12073. + IDE_VIM_PARSER_TOKEN_NUMBER, IDE_VIM_PARSER_TOKEN_FLAG_OPTIONNAL,
  12074. + NULL));
  12075. +
  12076. + g_assert_true (ide_vim_parser_add_command (global_parser, "test_cmd_f", "", test_generic_func,
  12077. + IDE_VIM_PARSER_TOKEN_MARK, IDE_VIM_PARSER_TOKEN_FLAG_NONE,
  12078. + IDE_VIM_PARSER_TOKEN_COMMAND_NAME, IDE_VIM_PARSER_TOKEN_FLAG_NONE,
  12079. + NULL));
  12080. +
  12081. + g_assert_true (ide_vim_parser_add_command (global_parser, "test_cmd_g", "", test_generic_func,
  12082. + IDE_VIM_PARSER_TOKEN_REGISTER, IDE_VIM_PARSER_TOKEN_FLAG_NONE,
  12083. + IDE_VIM_PARSER_TOKEN_COMMAND_NAME, IDE_VIM_PARSER_TOKEN_FLAG_NONE,
  12084. + NULL));
  12085. +
  12086. + g_assert_true (ide_vim_parser_add_command (global_parser, "test_cmd_h", "", test_generic_func,
  12087. + IDE_VIM_PARSER_TOKEN_RANGE, IDE_VIM_PARSER_TOKEN_FLAG_NONE,
  12088. + IDE_VIM_PARSER_TOKEN_COMMAND_NAME, IDE_VIM_PARSER_TOKEN_FLAG_NONE,
  12089. + NULL));
  12090. +}
  12091. +
  12092. +static void
  12093. +context_destroy (void)
  12094. +{
  12095. + g_object_unref (global_parser);
  12096. + g_object_unref (global_buffer);
  12097. +}
  12098. +
  12099. +static gint
  12100. +sort_complete_item (IdeVimCompleteItem **i1,
  12101. + IdeVimCompleteItem **i2)
  12102. +{
  12103. + return strcmp (ide_vim_complete_item_get_name (*i1),
  12104. + ide_vim_complete_item_get_name (*i2));
  12105. +}
  12106. +
  12107. +static gboolean
  12108. +compare_complete_result (GPtrArray *array,
  12109. + const CompleteResult result_list [],
  12110. + gint size)
  12111. +{
  12112. + const CompleteResult *result;
  12113. + IdeVimCompleteItem *item;
  12114. + IdeVimCompleteItemKind kind;
  12115. + const gchar *name;
  12116. + const gchar *shortname;
  12117. + gint len;
  12118. + gboolean ret = TRUE;
  12119. + gint i;
  12120. +
  12121. + g_ptr_array_sort (array, (GCompareFunc)sort_complete_item);
  12122. + len =array->len;
  12123. + if (len != size)
  12124. + {
  12125. + ret = FALSE;
  12126. + goto out;
  12127. + }
  12128. +
  12129. + for (i=0; i < len; i++)
  12130. + {
  12131. + item = g_ptr_array_index (array, i);
  12132. + kind = ide_vim_complete_item_get_kind (item);
  12133. + name = ide_vim_complete_item_get_name (item);
  12134. + shortname = ide_vim_complete_item_get_shortname (item);
  12135. +
  12136. + result = &result_list [i];
  12137. + printf ("\tcomplete: kind:%i, name:%s, shortname:%s\t", kind, name, shortname);
  12138. +
  12139. + ret = ret && (kind == result->kind && ide_str_equal0 (name, result->name) && ide_str_equal0 (shortname, result->shortname));
  12140. + printf ("%s\n", ret ? "OK" : "NOK");
  12141. + }
  12142. +
  12143. +out:
  12144. + return ret;
  12145. +}
  12146. +
  12147. +static void
  12148. +test_complete (void)
  12149. +{
  12150. + static const CompleteResult comp_list1 [] = {
  12151. + { IDE_VIM_COMPLETE_ITEM_KIND_COMMAND, "test_comp_a", "test_comp"},
  12152. + { IDE_VIM_COMPLETE_ITEM_KIND_COMMAND, "test_comp_b", "test_co"}
  12153. + };
  12154. +
  12155. + GPtrArray *result;
  12156. +
  12157. + context_test_complete_create ();
  12158. +
  12159. + g_assert ((result = ide_vim_parser_complete (global_parser, global_buffer, "test_c")) != NULL);
  12160. + g_assert (compare_complete_result (result, comp_list1, G_N_ELEMENTS (comp_list1)) == TRUE);
  12161. +
  12162. + context_destroy ();
  12163. +}
  12164. +
  12165. +static void
  12166. +test_parse (void)
  12167. +{
  12168. + static const ParseTest parse_samples[] = {
  12169. + /* Command syntax tests */
  12170. + { "full syntax a1 spaces", " 2 test_cmd_a 4 ", IDE_VIM_PARSER_ERROR_NONE},
  12171. + { "full syntax a2 tabs ", "-4,2\ttest_cmd_a\t 4", IDE_VIM_PARSER_ERROR_NONE},
  12172. + { "minimum syntax a", "test_cmd_a", IDE_VIM_PARSER_ERROR_NONE},
  12173. + /* Sign tests */
  12174. + { "partial syntax a1, sign", "-4 test_cmd_a", IDE_VIM_PARSER_ERROR_NONE},
  12175. + { "partial syntax a2, sign", "+4 test_cmd_a", IDE_VIM_PARSER_ERROR_NONE},
  12176. + /* number or range tests */
  12177. + { "partial syntax a3, range or number", "-4,2 test_cmd_a", IDE_VIM_PARSER_ERROR_NONE},
  12178. + { "partial syntax a4, range or number", ".,$ test_cmd_a", IDE_VIM_PARSER_ERROR_NONE},
  12179. + /* Command syntax tests */
  12180. + { "partial syntax a7", "test_cmd_a 4", IDE_VIM_PARSER_ERROR_NONE},
  12181. + { "full syntax b1", "test_cmd_b", IDE_VIM_PARSER_ERROR_NONE},
  12182. + { "full syntax c1", "2 test_cmd_c 4", IDE_VIM_PARSER_ERROR_NONE},
  12183. + { "full syntax d1", "2 test_cmd_d 4", IDE_VIM_PARSER_ERROR_NONE},
  12184. + { "partial syntax d1", "test_cmd_d 4", IDE_VIM_PARSER_ERROR_NONE},
  12185. + { "full syntax e1", "2 test_cmd_e 4", IDE_VIM_PARSER_ERROR_NONE},
  12186. + { "partial syntax e1", "2 test_cmd_e", IDE_VIM_PARSER_ERROR_NONE},
  12187. + /* Mark tests */
  12188. + { "mark f1", "'a test_cmd_f", IDE_VIM_PARSER_ERROR_NONE},
  12189. + { "mark f2", "'^ test_cmd_f", IDE_VIM_PARSER_ERROR_NONE},
  12190. + /* Registers tests */
  12191. + { "register g1", "\"a test_cmd_g", IDE_VIM_PARSER_ERROR_NONE},
  12192. + { "register g2", "\"- test_cmd_g", IDE_VIM_PARSER_ERROR_NONE},
  12193. + /* Range test */
  12194. + { "full syntax h1, range", "%test_cmd_h", IDE_VIM_PARSER_ERROR_NONE},
  12195. + { "full syntax h2, range", "\\/,\\&,\\? test_cmd_h", IDE_VIM_PARSER_ERROR_NONE},
  12196. + /* Errors test */
  12197. + { "cmdline empty", "", IDE_VIM_PARSER_ERROR_CMDLINE_EMPTY},
  12198. + //{ "command not found", "xx", IDE_VIM_PARSER_ERROR_COMMAND_NOT_FOUND}
  12199. +
  12200. + };
  12201. +
  12202. + IdeVimParserError *parse_error = NULL;
  12203. + ParseTest sample;
  12204. + /* TODO: set autofree func in IdeVimParserCommand */
  12205. + IdeVimParserCommand *result = NULL;
  12206. + gint i;
  12207. +
  12208. + context_create ();
  12209. + g_printf ("\n");
  12210. +
  12211. + for (i = 0; i < G_N_ELEMENTS (parse_samples); i++)
  12212. + {
  12213. + sample = parse_samples [i];
  12214. + g_printf ("TEST: %s: %s\n", sample.comment, sample.line);
  12215. +
  12216. + result = ide_vim_parser_parse (global_parser, global_buffer, sample.line, &parse_error);
  12217. + g_assert ((result == NULL && parse_error != NULL) || (result != NULL && parse_error == NULL));
  12218. + if (parse_error != NULL)
  12219. + {
  12220. + ide_vim_parser_error_print (parse_error);
  12221. + g_assert (sample.error_code == parse_error->code);
  12222. + ide_vim_parser_error_free (parse_error);
  12223. + g_printf ("\n");
  12224. + }
  12225. + else
  12226. + {
  12227. + g_assert (sample.error_code == IDE_VIM_PARSER_ERROR_NONE);
  12228. + ide_vim_parser_debug_show_parsed (result);
  12229. + }
  12230. +
  12231. + g_printf ("\n");
  12232. + }
  12233. +
  12234. + context_destroy ();
  12235. +}
  12236. +
  12237. +gint
  12238. +main (gint argc,
  12239. + gchar *argv[])
  12240. +{
  12241. + gtk_init (&argc, &argv);
  12242. + g_test_init (&argc, &argv, NULL);
  12243. +
  12244. + g_test_add_func ("/Ide/VimParser/complete", test_complete);
  12245. + g_test_add_func ("/Ide/VimParser/parse", test_parse);
  12246. +
  12247. + return g_test_run ();
  12248. +}
  12249. --
  12250. 2.4.3
Add Comment
Please, Sign In to add comment