vimamsa 0.1.22 → 0.1.24

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. checksums.yaml +4 -4
  2. data/.dockerignore +32 -0
  3. data/Dockerfile +45 -0
  4. data/README.md +2 -2
  5. data/custom_example.rb +38 -9
  6. data/docker_cmd.sh +7 -0
  7. data/exe/run_tests.rb +23 -0
  8. data/img/screenshot1.png +0 -0
  9. data/img/screenshot2.png +0 -0
  10. data/lib/vimamsa/actions.rb +8 -0
  11. data/lib/vimamsa/buffer.rb +165 -53
  12. data/lib/vimamsa/buffer_changetext.rb +68 -14
  13. data/lib/vimamsa/buffer_cursor.rb +9 -3
  14. data/lib/vimamsa/buffer_list.rb +14 -28
  15. data/lib/vimamsa/buffer_manager.rb +1 -1
  16. data/lib/vimamsa/conf.rb +33 -1
  17. data/lib/vimamsa/diff_buffer.rb +185 -0
  18. data/lib/vimamsa/editor.rb +149 -80
  19. data/lib/vimamsa/file_finder.rb +6 -2
  20. data/lib/vimamsa/gui.rb +330 -135
  21. data/lib/vimamsa/gui_dialog.rb +2 -0
  22. data/lib/vimamsa/gui_file_panel.rb +94 -0
  23. data/lib/vimamsa/gui_form_generator.rb +4 -2
  24. data/lib/vimamsa/gui_func_panel.rb +127 -0
  25. data/lib/vimamsa/gui_image.rb +2 -4
  26. data/lib/vimamsa/gui_menu.rb +54 -1
  27. data/lib/vimamsa/gui_select_window.rb +18 -6
  28. data/lib/vimamsa/gui_settings.rb +486 -0
  29. data/lib/vimamsa/gui_sourceview.rb +196 -8
  30. data/lib/vimamsa/gui_text.rb +0 -22
  31. data/lib/vimamsa/hyper_plain_text.rb +1 -0
  32. data/lib/vimamsa/key_actions.rb +54 -31
  33. data/lib/vimamsa/key_binding_tree.rb +154 -8
  34. data/lib/vimamsa/key_bindings_vimlike.rb +48 -35
  35. data/lib/vimamsa/langservp.rb +161 -7
  36. data/lib/vimamsa/macro.rb +54 -7
  37. data/lib/vimamsa/main.rb +1 -0
  38. data/lib/vimamsa/rbvma.rb +5 -0
  39. data/lib/vimamsa/string_util.rb +56 -0
  40. data/lib/vimamsa/test_framework.rb +137 -0
  41. data/lib/vimamsa/util.rb +3 -36
  42. data/lib/vimamsa/version.rb +1 -1
  43. data/modules/calculator/calculator.rb +318 -0
  44. data/modules/calculator/calculator_info.rb +3 -0
  45. data/modules/terminal/terminal.rb +140 -0
  46. data/modules/terminal/terminal_info.rb +3 -0
  47. data/run_tests.rb +89 -0
  48. data/styles/dark.xml +1 -1
  49. data/styles/molokai_edit.xml +2 -2
  50. data/tests/key_bindings.rb +2 -0
  51. data/tests/test_basic_editing.rb +86 -0
  52. data/tests/test_copy_paste.rb +88 -0
  53. data/tests/test_key_bindings.rb +152 -0
  54. data/tests/test_module_interface.rb +98 -0
  55. data/tests/test_undo.rb +201 -0
  56. data/vimamsa.gemspec +6 -5
  57. metadata +52 -14
@@ -25,6 +25,8 @@ class PopupFormGenerator
25
25
 
26
26
  @callback = params[:callback]
27
27
  @window.title = ""
28
+ @window.set_transient_for($vmag.window) if $vmag&.window
29
+ @window.modal = true
28
30
 
29
31
  frame = Gtk::Frame.new()
30
32
  frame.margin_bottom = 8
@@ -56,7 +58,7 @@ class PopupFormGenerator
56
58
  @vals = {}
57
59
  @default_button = nil
58
60
 
59
- for id, elem in params["inputs"]
61
+ params["inputs"].each do |id, elem|
60
62
  if elem[:type] == :button
61
63
  button = Gtk::Button.new(:label => elem[:label])
62
64
  hbox.append(button)
@@ -92,7 +94,7 @@ class PopupFormGenerator
92
94
  end
93
95
  end
94
96
  end
95
- end
97
+ end # each
96
98
 
97
99
  vbox.append(hbox)
98
100
 
@@ -0,0 +1,127 @@
1
+ # Left-side panel that displays LSP-provided functions/methods for the current buffer,
2
+ # grouped by the class or module they belong to.
3
+ class FuncPanel
4
+ COL_NAME = 0 # Tree column index: display name (class name or function name)
5
+ COL_LINE = 1 # Tree column index: 1-based line number to jump to; 0 = not a jump target
6
+
7
+ def initialize
8
+ # @store is the data model: a tree (not flat list) so we can nest functions
9
+ # under their parent class. Two columns: the display string and the line number.
10
+ @store = Gtk::TreeStore.new(String, Integer)
11
+
12
+ # @tree is the GTK widget that renders @store. It holds expand/collapse state
13
+ # and handles row selection. Backed by @store — clearing @store clears the view.
14
+ @tree = Gtk::TreeView.new(@store)
15
+ @tree.headers_visible = false
16
+ @tree.activate_on_single_click = true
17
+
18
+ # Single text column; ellipsize at end so long names don't overflow the panel width.
19
+ renderer = Gtk::CellRendererText.new
20
+ renderer.ellipsize = Pango::EllipsizeMode::END
21
+ col = Gtk::TreeViewColumn.new("", renderer, text: COL_NAME)
22
+ col.expand = true
23
+ @tree.append_column(col)
24
+
25
+ # Jump to the function's line when a row is clicked.
26
+ # Class header rows have COL_LINE = 0 and are skipped (no jump).
27
+ @tree.signal_connect("row-activated") do |_tv, path, _col|
28
+ iter = @store.get_iter(path)
29
+ next if iter.nil?
30
+ line = iter[COL_LINE]
31
+ next if line <= 0
32
+ vma.buf.jump_to_line(line)
33
+ end
34
+
35
+ sw = Gtk::ScrolledWindow.new
36
+ sw.set_policy(:never, :automatic)
37
+ sw.set_child(@tree)
38
+ sw.vexpand = true
39
+
40
+ header = Gtk::Label.new("<span weight='ultrabold'>Functions</span> (click to jump)")
41
+ header.use_markup = true
42
+
43
+ header.xalign = 0.0
44
+ header.margin_start = 6
45
+ header.margin_top = 4
46
+ header.margin_bottom = 2
47
+
48
+ # @box is the outermost widget: header label on top, scrollable tree below.
49
+ @box = Gtk::Box.new(:vertical, 0)
50
+ @box.set_size_request(160, -1)
51
+ @box.append(header)
52
+ @box.append(sw)
53
+ end
54
+
55
+ # Returns the outermost widget to embed in the paned layout.
56
+ def widget
57
+ @box
58
+ end
59
+
60
+ # Asynchronously fetch functions from the LSP server and repopulate @store.
61
+ # The LSP call runs in a background thread; the GTK update is marshalled back
62
+ # to the main thread via GLib::Idle.add to avoid threading issues.
63
+ def refresh
64
+ buf = vma.buf
65
+ unless buf&.fname
66
+ set_placeholder("(no file)")
67
+ return
68
+ end
69
+ lsp = LangSrv.get(buf.lang)
70
+ unless lsp
71
+ set_placeholder("(no LSP)")
72
+ return
73
+ end
74
+ fpath = buf.fname
75
+ Thread.new {
76
+ # groups: [{name:, line:, functions: [{name:, line:}, ...]}, ...]
77
+ # name: nil means top-level functions with no enclosing class.
78
+ groups = lsp.document_functions_grouped(fpath)
79
+ GLib::Idle.add {
80
+ @store.clear
81
+ if groups.nil? || groups.empty?
82
+ set_placeholder("(no functions)")
83
+ else
84
+ populate(groups)
85
+ end
86
+ false # returning false removes this idle callback after one run
87
+ }
88
+ }
89
+ end
90
+
91
+ private
92
+
93
+ # Fill @store from the grouped function list.
94
+ # Named groups become collapsible parent rows; top-level functions are root rows.
95
+ def populate(groups)
96
+ groups.each do |g|
97
+ if g[:name]
98
+ # Class/module header: parent row with the class name (and its own line number
99
+ # so clicking it jumps to the class definition when line > 0).
100
+ header = @store.append(nil)
101
+ header[COL_NAME] = g[:name]
102
+ header[COL_LINE] = g[:line] || 0
103
+ g[:functions].each do |f|
104
+ child = @store.append(header) # appended under the class header
105
+ child[COL_NAME] = f[:name]
106
+ child[COL_LINE] = f[:line]
107
+ end
108
+ else
109
+ # Top-level functions (not inside any class): added as root rows directly.
110
+ g[:functions].each do |f|
111
+ row = @store.append(nil)
112
+ row[COL_NAME] = f[:name]
113
+ row[COL_LINE] = f[:line]
114
+ end
115
+ end
116
+ end
117
+ @tree.expand_all # show all classes expanded by default
118
+ end
119
+
120
+ # Replace the entire store contents with a single non-clickable status message.
121
+ def set_placeholder(text)
122
+ @store.clear
123
+ iter = @store.append(nil)
124
+ iter[COL_NAME] = text
125
+ iter[COL_LINE] = 0
126
+ end
127
+ end
@@ -36,14 +36,12 @@ class ResizableImage < Gtk::DrawingArea
36
36
  @draw_image = pb
37
37
  @oldimg = pb
38
38
  #TODO: Should be better way to compensate for the gutter
39
- self.set_size_request(pb.width+@view.gutter_width, pb.height)
39
+ self.set_size_request(pb.width, pb.height) # +@view.gutter_width?
40
40
  end
41
41
 
42
42
  def do_draw(da, cr)
43
43
  # puts @fpath
44
- # Ripl.start :binding => binding
45
-
46
- cr.set_source_pixbuf(@draw_image, @view.gutter_width, 0)
44
+ cr.set_source_pixbuf(@draw_image, 0, 0) # @view.gutter_width
47
45
  cr.paint
48
46
  end
49
47
  end
@@ -29,6 +29,7 @@ module Vimamsa
29
29
  add_to_menu "File.New", { :label => "New file", :action => :buf_new }
30
30
  add_to_menu "File.Revert", { :label => "Reload file from disk", :action => :buf_revert }
31
31
  add_to_menu "File.List", { :label => "List open files", :action => :start_buf_manager }
32
+ add_to_menu "File.Close", { :label => "Close file", :action => :close_current_buffer }
32
33
 
33
34
  add_to_menu "File.Quit", { :label => "Quit", :action => :quit }
34
35
 
@@ -39,7 +40,10 @@ module Vimamsa
39
40
 
40
41
  # add_to_menu "Edit.StartCompletion", { :label => "StartCompletion", :action => :start_autocomplete }
41
42
  # add_to_menu "Edit.ShowCompletion", { :label => "ShowCompletion", :action => :show_autocomplete }
42
- add_to_menu "Edit.CustomRb", { :action => :edit_customrb }
43
+
44
+ add_to_menu "Settings.Preferences", { :label => "Preferences...", :action => :show_settings }
45
+ add_to_menu "Settings.Customize", { :action => :edit_customrb }
46
+ add_to_menu "Settings.ReloadCustom", { :action => :reload_customrb }
43
47
 
44
48
  add_to_menu "Actions.SearchForActions", { :label => "Search for Actions", :action => :search_actions }
45
49
 
@@ -48,18 +52,29 @@ module Vimamsa
48
52
  add_to_menu "Actions.FileHistoryFinder", { :label => "Search files in history", :action => :gui_file_history_finder }
49
53
 
50
54
  add_to_menu "Actions.experimental.Diff", { :label => "Show Diff of\nunsaved changes", :action => :diff_buffer }
55
+ add_to_menu "Actions.experimental.GitDiff", { :label => "Show git diff", :action => :git_diff_buffer }
56
+ add_to_menu "Actions.experimental.GitDiffW", { :label => "Show git diff -w (repo)", :action => :git_diff_w }
51
57
 
58
+ add_to_menu "Actions.experimental.PrintBufferAccessList", { :label => "Print buffers by access time", :action => :print_buffer_access_list }
52
59
  add_to_menu "Actions.experimental.EnableDebug", { :label => "Enable debug", :action => :enable_debug }
53
60
  add_to_menu "Actions.experimental.DisableDebug", { :label => "Disable debug", :action => :disable_debug }
54
61
  add_to_menu "Actions.experimental.ShowImages", { :label => "Show images ⟦img:path⟧", :action => :show_images }
62
+
63
+ add_to_menu "Actions.experimental.ShowImages", { :action => :experimental_eval }
64
+
55
65
 
56
66
  add_to_menu "Actions.debug.dumpkbd", { :label => "Dump kbd state", :action => :kbd_dump_state }
67
+ add_to_menu "Actions.debug.ToggleKbdPassthrough", { :label => "Toggle kbd event passthrough", :action => :toggle_kbd_passthrough }
57
68
 
58
69
  add_to_menu "View.BufferManager", { :label => "Show open files", :action => :start_buf_manager }
59
70
  add_to_menu "View.TwoColumn", { :label => "Toggle two column mode", :action => :toggle_two_column }
71
+ add_to_menu "View.FilePanel", { :label => "Toggle file panel", :action => :toggle_file_panel }
72
+ add_to_menu "View.FuncPanel", { :label => "Toggle function panel (LSP)", :action => :toggle_func_panel }
73
+ add_to_menu "View.MsgHistory", { :label => "Message history", :action => :show_message_history }
60
74
 
61
75
  add_to_menu "Actions.EncryptFile", { :label => "Encrypt file", :action => :encrypt_file }
62
76
  add_to_menu "Help.KeyBindings", { :label => "Show key bindings", :action => :show_key_bindings }
77
+ add_to_menu "Help.InstallDemo", { :action => :install_demo_files }
63
78
 
64
79
  #TODO: :auto_indent_buffer
65
80
 
@@ -70,6 +85,11 @@ module Vimamsa
70
85
  def initialize(menubar, _app)
71
86
  @app = _app
72
87
  @nfo = {}
88
+ @menubar = menubar
89
+ # Gio::Menu for the "Modules" top-level menu, created on first use.
90
+ @module_menu = nil
91
+ # Ordered list of action symbols in @module_menu, used to find removal positions.
92
+ @module_actions = []
73
93
 
74
94
  add_menu_items
75
95
 
@@ -78,6 +98,39 @@ module Vimamsa
78
98
  end
79
99
  end
80
100
 
101
+ # Add a menu item under the top-level "Modules" menu.
102
+ # Creates the Modules menu the first time it is called.
103
+ def add_module_action(action, label)
104
+ if @module_menu.nil?
105
+ @module_menu = Gio::Menu.new
106
+ modules_item = Gio::MenuItem.new("Modules", nil)
107
+ modules_item.submenu = @module_menu
108
+ @menubar.append_item(modules_item)
109
+ end
110
+
111
+ act = Gio::SimpleAction.new(action.to_s)
112
+ @app.add_action(act)
113
+ act.signal_connect("activate") { call_action(action) }
114
+
115
+ item = Gio::MenuItem.new(label, "app.#{action}")
116
+ @module_menu.append_item(item)
117
+ @module_actions << action
118
+ end
119
+
120
+ # Remove a previously added module menu item.
121
+ def remove_module_action(action)
122
+ idx = @module_actions.index(action)
123
+ return if idx.nil?
124
+ @module_menu.remove(idx)
125
+ @module_actions.delete_at(idx)
126
+ @app.remove_action(action.to_s)
127
+ end
128
+
129
+ # Return true if action was added via add_module_action and not yet removed.
130
+ def module_action?(action)
131
+ @module_actions.include?(action)
132
+ end
133
+
81
134
  def build_menu(nfo, parent)
82
135
  menu = Gio::Menu.new
83
136
  if nfo[:action]
@@ -30,18 +30,19 @@ class SelectUpdateWindow
30
30
  end
31
31
 
32
32
  def set_selected_row(rownum)
33
+ count = @model.count
34
+ return if count == 0
33
35
  rownum = 0 if rownum < 0
36
+ rownum = count - 1 if rownum >= count
34
37
  @selected_row = rownum
35
-
36
- if @model.count > 0
37
- path = Gtk::TreePath.new(@selected_row.to_s)
38
- iter = @model.get_iter(path)
39
- @tv.selection.select_iter(iter)
40
- end
38
+ path = Gtk::TreePath.new(@selected_row.to_s)
39
+ iter = @model.get_iter(path)
40
+ @tv.selection.select_iter(iter)
41
41
  end
42
42
 
43
43
  def initialize(main_window, item_list, jump_keys, select_callback, update_callback, opt = {})
44
44
  @window = Gtk::Window.new()
45
+ @window.set_transient_for($vmag.window) if $vmag&.window
45
46
  # @window.screen = main_window.screen
46
47
  @window.title = ""
47
48
  if !opt[:title].nil?
@@ -125,6 +126,17 @@ class SelectUpdateWindow
125
126
  @window.destroy
126
127
  # debug iter[1].inspect
127
128
  true
129
+ elsif keyval == Gdk::Keyval::KEY_Delete && @opt[:delete_callback]
130
+ path = Gtk::TreePath.new(@selected_row.to_s)
131
+ iter = @model.get_iter(path)
132
+ next false unless iter
133
+ name = iter[COLUMN_DESCRIPTION]
134
+ refresh = proc {
135
+ update_item_list(@update_callback.call(@entry.text))
136
+ @window.present
137
+ }
138
+ @opt[:delete_callback].call(name, refresh)
139
+ true
128
140
  elsif keyval == Gdk::Keyval::KEY_Escape
129
141
  @window.destroy
130
142
  true