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.
- checksums.yaml +4 -4
- data/.dockerignore +32 -0
- data/Dockerfile +45 -0
- data/README.md +2 -2
- data/custom_example.rb +38 -9
- data/docker_cmd.sh +7 -0
- data/exe/run_tests.rb +23 -0
- data/img/screenshot1.png +0 -0
- data/img/screenshot2.png +0 -0
- data/lib/vimamsa/actions.rb +8 -0
- data/lib/vimamsa/buffer.rb +165 -53
- data/lib/vimamsa/buffer_changetext.rb +68 -14
- data/lib/vimamsa/buffer_cursor.rb +9 -3
- data/lib/vimamsa/buffer_list.rb +14 -28
- data/lib/vimamsa/buffer_manager.rb +1 -1
- data/lib/vimamsa/conf.rb +33 -1
- data/lib/vimamsa/diff_buffer.rb +185 -0
- data/lib/vimamsa/editor.rb +149 -80
- data/lib/vimamsa/file_finder.rb +6 -2
- data/lib/vimamsa/gui.rb +330 -135
- data/lib/vimamsa/gui_dialog.rb +2 -0
- data/lib/vimamsa/gui_file_panel.rb +94 -0
- data/lib/vimamsa/gui_form_generator.rb +4 -2
- data/lib/vimamsa/gui_func_panel.rb +127 -0
- data/lib/vimamsa/gui_image.rb +2 -4
- data/lib/vimamsa/gui_menu.rb +54 -1
- data/lib/vimamsa/gui_select_window.rb +18 -6
- data/lib/vimamsa/gui_settings.rb +486 -0
- data/lib/vimamsa/gui_sourceview.rb +196 -8
- data/lib/vimamsa/gui_text.rb +0 -22
- data/lib/vimamsa/hyper_plain_text.rb +1 -0
- data/lib/vimamsa/key_actions.rb +54 -31
- data/lib/vimamsa/key_binding_tree.rb +154 -8
- data/lib/vimamsa/key_bindings_vimlike.rb +48 -35
- data/lib/vimamsa/langservp.rb +161 -7
- data/lib/vimamsa/macro.rb +54 -7
- data/lib/vimamsa/main.rb +1 -0
- data/lib/vimamsa/rbvma.rb +5 -0
- data/lib/vimamsa/string_util.rb +56 -0
- data/lib/vimamsa/test_framework.rb +137 -0
- data/lib/vimamsa/util.rb +3 -36
- data/lib/vimamsa/version.rb +1 -1
- data/modules/calculator/calculator.rb +318 -0
- data/modules/calculator/calculator_info.rb +3 -0
- data/modules/terminal/terminal.rb +140 -0
- data/modules/terminal/terminal_info.rb +3 -0
- data/run_tests.rb +89 -0
- data/styles/dark.xml +1 -1
- data/styles/molokai_edit.xml +2 -2
- data/tests/key_bindings.rb +2 -0
- data/tests/test_basic_editing.rb +86 -0
- data/tests/test_copy_paste.rb +88 -0
- data/tests/test_key_bindings.rb +152 -0
- data/tests/test_module_interface.rb +98 -0
- data/tests/test_undo.rb +201 -0
- data/vimamsa.gemspec +6 -5
- 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
|
-
|
|
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
|
data/lib/vimamsa/gui_image.rb
CHANGED
|
@@ -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
|
|
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
|
-
|
|
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
|
data/lib/vimamsa/gui_menu.rb
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
37
|
-
|
|
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
|