vimamsa 0.1.17 → 0.1.18
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +1 -1
- data/lib/vimamsa/buffer.rb +16 -38
- data/lib/vimamsa/buffer_changetext.rb +20 -5
- data/lib/vimamsa/buffer_list.rb +6 -0
- data/lib/vimamsa/editor.rb +13 -5
- data/lib/vimamsa/encrypt.rb +38 -1
- data/lib/vimamsa/file_manager.rb +1 -1
- data/lib/vimamsa/gui.rb +33 -24
- data/lib/vimamsa/gui_sourceview.rb +75 -55
- data/lib/vimamsa/gui_sourceview_autocomplete.rb +3 -1
- data/lib/vimamsa/hyper_plain_text.rb +4 -3
- data/lib/vimamsa/key_bindings_vimlike.rb +2 -0
- data/lib/vimamsa/rbvma.rb +2 -0
- data/lib/vimamsa/util.rb +48 -14
- data/lib/vimamsa/version.rb +1 -1
- data/vimamsa.gemspec +4 -4
- metadata +28 -28
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4d45a54c1e25d490c27465b9649d09fa3b0e5db85e91324fb8c70201de9ff5a1
|
4
|
+
data.tar.gz: f7393daa458da9e7c3b1e769049f7205e96e4ba157c162f9dacbdae700b6731d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e4d8be7daf217add0cef74e246bebf114d886f60739bc700d532035067f9a3c26dabde5ffbec7ef662278a6f7829f0513210a961e4b9c661b0f9c52ea839971a
|
7
|
+
data.tar.gz: 33315be557aaf4f294005e80a1c8b9be61da65b9d0d21cc90b8e03bddb0c4cf34df332217e11787d5e2dd3da1a0de459065c342b44f8a37f1aec5cd34f7ed4c1
|
data/README.md
CHANGED
data/lib/vimamsa/buffer.rb
CHANGED
@@ -24,6 +24,7 @@ class Buffer < String
|
|
24
24
|
|
25
25
|
update_access_time
|
26
26
|
@images = []
|
27
|
+
@prefix = prefix
|
27
28
|
@audiofiles = []
|
28
29
|
@lang = nil
|
29
30
|
@id = @@num_buffers
|
@@ -47,27 +48,10 @@ class Buffer < String
|
|
47
48
|
@is_parsing_syntax = false
|
48
49
|
@last_update = Time.now - 100
|
49
50
|
@highlights = {}
|
50
|
-
|
51
|
-
|
52
|
-
detect_file_language()
|
53
|
-
else
|
54
|
-
@fname = fname
|
55
|
-
end
|
51
|
+
|
52
|
+
set_filename(fname)
|
56
53
|
@hl_queue = []
|
57
54
|
@line_action_handler = nil
|
58
|
-
|
59
|
-
@dirname = nil
|
60
|
-
@title = "*#{prefix}-#{@id}*"
|
61
|
-
@subtitle = ""
|
62
|
-
|
63
|
-
if @fname
|
64
|
-
@title = File.basename(@fname)
|
65
|
-
@dirname = File.dirname(@fname)
|
66
|
-
userhome = File.expand_path("~")
|
67
|
-
# @subtitle = @dirname.gsub(/^#{userhome}/, "~")
|
68
|
-
@subtitle = @fname.gsub(/^#{userhome}/, "~")
|
69
|
-
end
|
70
|
-
|
71
55
|
t1 = Time.now
|
72
56
|
|
73
57
|
set_content(str)
|
@@ -304,7 +288,7 @@ class Buffer < String
|
|
304
288
|
if is_image_file(fname)
|
305
289
|
debug "Dropped image file"
|
306
290
|
insert_image_after_current_line(fname)
|
307
|
-
elsif
|
291
|
+
elsif file_is_text_file(fname)
|
308
292
|
debug "Dropped text file"
|
309
293
|
open_new_file(fname)
|
310
294
|
else
|
@@ -325,15 +309,10 @@ class Buffer < String
|
|
325
309
|
self.set_content(str)
|
326
310
|
end
|
327
311
|
|
328
|
-
def
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
str = @crypt.decrypt(@encrypted_str)
|
333
|
-
rescue OpenSSL::Cipher::CipherError => e
|
334
|
-
str = "incorrect password"
|
335
|
-
end
|
336
|
-
self.set_content(str)
|
312
|
+
def init_encrypted(crypt:, filename:, encrypted:)
|
313
|
+
@crypt = crypt
|
314
|
+
@encrypted_str = encrypted
|
315
|
+
set_filename(filename)
|
337
316
|
end
|
338
317
|
|
339
318
|
def sanitycheck_btree()
|
@@ -365,12 +344,6 @@ class Buffer < String
|
|
365
344
|
@encrypted_str = nil
|
366
345
|
@gui_update_highlight = true
|
367
346
|
@ftype = nil
|
368
|
-
if str[0..10] == "VMACRYPT001"
|
369
|
-
@encrypted_str = str[11..-1]
|
370
|
-
callback = proc { |x| self.decrypt(x) }
|
371
|
-
gui_one_input_action("Decrypt file \n #{@fname}", "Password:", "decrypt", callback, { :hide => true })
|
372
|
-
str = "ENCRYPTED"
|
373
|
-
end
|
374
347
|
|
375
348
|
if (str[-1] != "\n")
|
376
349
|
str << "\n"
|
@@ -424,7 +397,10 @@ class Buffer < String
|
|
424
397
|
end
|
425
398
|
|
426
399
|
def set_filename(filename)
|
400
|
+
debug "set_filename(filename) #{filename}"
|
427
401
|
@fname = filename
|
402
|
+
@title = "*#{@prefix}-#{@id}*"
|
403
|
+
return if @fname.nil?
|
428
404
|
@pathname = Pathname.new(fname) if @fname
|
429
405
|
@basename = @pathname.basename if @fname
|
430
406
|
|
@@ -435,6 +411,7 @@ class Buffer < String
|
|
435
411
|
vma.buffers.last_dir = @dirname
|
436
412
|
|
437
413
|
detect_file_language
|
414
|
+
gui_set_window_title(@title, @subtitle)
|
438
415
|
end
|
439
416
|
|
440
417
|
def get_short_path()
|
@@ -1098,6 +1075,7 @@ class Buffer < String
|
|
1098
1075
|
else
|
1099
1076
|
wtype = :file
|
1100
1077
|
end
|
1078
|
+
word = path
|
1101
1079
|
# elsif hpt_check_cur_word(word) #TODO: check only
|
1102
1080
|
# debug word
|
1103
1081
|
elsif linep != nil
|
@@ -1111,6 +1089,7 @@ class Buffer < String
|
|
1111
1089
|
return [fn, :hpt_link]
|
1112
1090
|
end
|
1113
1091
|
end
|
1092
|
+
|
1114
1093
|
return [word, wtype]
|
1115
1094
|
end
|
1116
1095
|
|
@@ -1268,18 +1247,17 @@ class Buffer < String
|
|
1268
1247
|
@visual_mode = false
|
1269
1248
|
#TODO: remove @visual_mode
|
1270
1249
|
end
|
1271
|
-
|
1250
|
+
|
1272
1251
|
def start_selection()
|
1273
1252
|
@selection_start = @pos
|
1274
1253
|
@selection_active = true
|
1275
1254
|
@visual_mode = true
|
1276
1255
|
end
|
1277
|
-
|
1256
|
+
|
1278
1257
|
# Start selection if not already started
|
1279
1258
|
def continue_selection()
|
1280
1259
|
start_selection if !@selection_active
|
1281
1260
|
end
|
1282
|
-
|
1283
1261
|
|
1284
1262
|
def copy_active_selection(x = nil)
|
1285
1263
|
debug "!COPY SELECTION"
|
@@ -74,20 +74,35 @@ class Buffer < String
|
|
74
74
|
set_pos(l.end + 1)
|
75
75
|
end
|
76
76
|
|
77
|
+
# Workaround for https://github.com/ruby-gnome/ruby-gnome/issues/1609
|
78
|
+
def paste_start_xclip(at, register)
|
79
|
+
@clipboard_paste_running = true
|
80
|
+
Thread.new {
|
81
|
+
text = `xclip -selection c -o`
|
82
|
+
paste_finish(text, at, register)
|
83
|
+
}
|
84
|
+
return nil
|
85
|
+
end
|
86
|
+
|
77
87
|
# Start asynchronous read of system clipboard
|
78
88
|
def paste_start(at, register)
|
79
89
|
@clipboard_paste_running = true
|
90
|
+
|
91
|
+
if true or running_wayland? and !GLib::Version::or_later?(2, 79, 0)
|
92
|
+
return paste_start_xclip(at, register)
|
93
|
+
end
|
94
|
+
|
80
95
|
clipboard = vma.gui.window.display.clipboard
|
81
96
|
clipboard.read_text_async do |_clipboard, result|
|
82
97
|
begin
|
83
98
|
text = clipboard.read_text_finish(result)
|
84
99
|
rescue Gio::IOError::NotSupported
|
85
|
-
# Happens when pasting from KeePassX and clipboard cleared
|
86
100
|
debug Gio::IOError::NotSupported
|
87
101
|
else
|
88
102
|
paste_finish(text, at, register)
|
89
103
|
end
|
90
104
|
end
|
105
|
+
return nil
|
91
106
|
end
|
92
107
|
|
93
108
|
def paste_finish(text, at, register)
|
@@ -141,7 +156,7 @@ class Buffer < String
|
|
141
156
|
return if !is_legal_pos(p)
|
142
157
|
(word, range) = get_word_in_pos(p, boundary: :word)
|
143
158
|
debug [word, range].to_s, 2
|
144
|
-
endpos = range.begin+rep.size
|
159
|
+
endpos = range.begin + rep.size
|
145
160
|
replace_range(range, rep)
|
146
161
|
set_pos(endpos)
|
147
162
|
end
|
@@ -246,9 +261,9 @@ class Buffer < String
|
|
246
261
|
end
|
247
262
|
|
248
263
|
def insert_tab
|
249
|
-
convert =
|
250
|
-
convert = true if
|
251
|
-
convert = false if
|
264
|
+
convert = cnf.tab.to_spaces_default?
|
265
|
+
convert = true if cnf.tab.to_spaces_languages?.include?(@lang)
|
266
|
+
convert = false if cnf.tab.to_spaces_not_languages?.include?(@lang)
|
252
267
|
tw = conf(:tab_width)
|
253
268
|
if convert
|
254
269
|
indent_to = (@cpos / tw) * tw + tw
|
data/lib/vimamsa/buffer_list.rb
CHANGED
data/lib/vimamsa/editor.rb
CHANGED
@@ -1,9 +1,9 @@
|
|
1
1
|
require "pty"
|
2
2
|
|
3
|
-
def handle_drag_and_drop(fname)
|
4
|
-
|
5
|
-
|
6
|
-
end
|
3
|
+
# def handle_drag_and_drop(fname)
|
4
|
+
# debug "EDITOR:handle_drag_and_drop"
|
5
|
+
# buf.handle_drag_and_drop(fname)
|
6
|
+
# end
|
7
7
|
|
8
8
|
class Editor
|
9
9
|
attr_reader :file_content_search_paths, :file_name_search_paths, :gui, :hook, :macro, :actions
|
@@ -129,9 +129,12 @@ class Editor
|
|
129
129
|
FileManager.init
|
130
130
|
Autocomplete.init
|
131
131
|
|
132
|
+
if cnf.audio.enabled?
|
133
|
+
require "vimamsa/audio"
|
134
|
+
end
|
135
|
+
|
132
136
|
if cnf.lsp.enabled?
|
133
137
|
require "vimamsa/langservp"
|
134
|
-
require "vimamsa/audio" # TODO:config
|
135
138
|
@langsrv["ruby"] = LangSrv.new("ruby")
|
136
139
|
@langsrv["cpp"] = LangSrv.new("cpp")
|
137
140
|
end
|
@@ -552,6 +555,7 @@ end
|
|
552
555
|
|
553
556
|
def jump_to_file(filename, tnum = nil, charn = nil)
|
554
557
|
b = open_new_file(filename)
|
558
|
+
return if b.nil?
|
555
559
|
# debug "open_new_file #{filename}, #{tnum} = nil, #{charn}",2
|
556
560
|
|
557
561
|
# Link to character position
|
@@ -595,6 +599,10 @@ def open_new_file(filename, file_contents = "")
|
|
595
599
|
message("File #{filename} does not contain text")
|
596
600
|
return false
|
597
601
|
end
|
602
|
+
if Encrypt.is_encrypted?(filename)
|
603
|
+
decrypt_dialog(filename: filename)
|
604
|
+
return nil
|
605
|
+
end
|
598
606
|
message "New file opened: #{filename}"
|
599
607
|
fname = filename
|
600
608
|
bu = load_buffer(fname)
|
data/lib/vimamsa/encrypt.rb
CHANGED
@@ -1,6 +1,44 @@
|
|
1
1
|
require "openssl"
|
2
2
|
|
3
|
+
def decrypt_dialog(filename:, wrong_pass: false)
|
4
|
+
callback = proc { |x| Encrypt.open(filename, x) }
|
5
|
+
msg = ""
|
6
|
+
msg = "\nWRONG PASSWORD!\n" if wrong_pass
|
7
|
+
gui_one_input_action("Decrypt file \n #{filename}\n#{msg}", "Password:", "Decrypt", callback, { :hide => true })
|
8
|
+
end
|
9
|
+
|
3
10
|
class Encrypt
|
11
|
+
def self.is_encrypted?(fn)
|
12
|
+
debug "self.is_encrypted?(fn)", 2
|
13
|
+
begin
|
14
|
+
file = File.open(fn, "r")
|
15
|
+
first_11_characters = file.read(11)
|
16
|
+
return true if first_11_characters == "VMACRYPT001"
|
17
|
+
rescue Errno::ENOENT
|
18
|
+
puts "File not found: #{file_path}"
|
19
|
+
rescue => e
|
20
|
+
puts "An error occurred: #{e.message}"
|
21
|
+
ensure
|
22
|
+
file&.close
|
23
|
+
end
|
24
|
+
return false
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.open(fn, password)
|
28
|
+
debug "open_encrypted(filename,password)", 2
|
29
|
+
encrypted = read_file("", fn)[11..-1]
|
30
|
+
begin
|
31
|
+
crypt = Encrypt.new(password)
|
32
|
+
str = crypt.decrypt(encrypted)
|
33
|
+
# debug "PASS OK!", 2
|
34
|
+
bu = create_new_buffer(str)
|
35
|
+
bu.init_encrypted(crypt: crypt, filename: fn, encrypted: encrypted)
|
36
|
+
rescue OpenSSL::Cipher::CipherError => e
|
37
|
+
# Wrong password
|
38
|
+
decrypt_dialog(filename: fn, wrong_pass: true)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
4
42
|
def initialize(pass_phrase)
|
5
43
|
salt = "uvgixEtU"
|
6
44
|
@enc = OpenSSL::Cipher.new "AES-128-CBC"
|
@@ -31,7 +69,6 @@ class Encrypt
|
|
31
69
|
end
|
32
70
|
end
|
33
71
|
|
34
|
-
|
35
72
|
def encrypt_cur_buffer()
|
36
73
|
callback = proc { |x| encrypt_cur_buffer_callback(x) }
|
37
74
|
gui_one_input_action("Encrypt", "Password:", "Encrypt", callback, { :hide => true })
|
data/lib/vimamsa/file_manager.rb
CHANGED
data/lib/vimamsa/gui.rb
CHANGED
@@ -8,6 +8,17 @@ def iterate_gui_main_loop
|
|
8
8
|
GLib::MainContext.default.iteration(true)
|
9
9
|
end
|
10
10
|
|
11
|
+
def start_profiler
|
12
|
+
require "ruby-prof"
|
13
|
+
RubyProf.start
|
14
|
+
end
|
15
|
+
|
16
|
+
def end_profiler
|
17
|
+
result = RubyProf.stop
|
18
|
+
printer = RubyProf::FlatPrinter.new(result)
|
19
|
+
printer.print(STDOUT)
|
20
|
+
end
|
21
|
+
|
11
22
|
# Wait for window resize to take effect
|
12
23
|
# GTk3 had a resize notify event which got removed in gtk4
|
13
24
|
# https://discourse.gnome.org/t/gtk4-any-way-to-connect-to-a-window-resize-signal/14869/3
|
@@ -193,21 +204,6 @@ class VMAgui
|
|
193
204
|
@img_resizer_active = false
|
194
205
|
@windows = {}
|
195
206
|
@app = nil
|
196
|
-
# imgproc = proc {
|
197
|
-
# GLib::Idle.add(proc {
|
198
|
-
# if !buf.images.empty?
|
199
|
-
# vma.gui.scale_all_images
|
200
|
-
|
201
|
-
# w = Gtk::Window.new(:toplevel)
|
202
|
-
# w.set_default_size(1, 1)
|
203
|
-
# w.show_all
|
204
|
-
# Thread.new { sleep 0.1; w.destroy }
|
205
|
-
# end
|
206
|
-
|
207
|
-
# false
|
208
|
-
# })
|
209
|
-
# }
|
210
|
-
# @delex = DelayExecutioner.new(1, imgproc)
|
211
207
|
end
|
212
208
|
|
213
209
|
def run
|
@@ -225,16 +221,13 @@ class VMAgui
|
|
225
221
|
end
|
226
222
|
end
|
227
223
|
|
228
|
-
def delay_scale()
|
229
|
-
if Time.now - @dtime > 2.0
|
230
|
-
end
|
231
|
-
end
|
232
|
-
|
233
224
|
def scale_all_images
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
img[:obj].
|
225
|
+
for k, window in @windows
|
226
|
+
bu = window[:sw].child.bufo
|
227
|
+
for img in bu.images
|
228
|
+
if !img[:obj].destroyed?
|
229
|
+
img[:obj].scale_image
|
230
|
+
end
|
238
231
|
end
|
239
232
|
end
|
240
233
|
false
|
@@ -735,12 +728,28 @@ class VMAgui
|
|
735
728
|
end
|
736
729
|
|
737
730
|
# Vimamsa::Menu.new(@menubar) #TODO:gtk4
|
731
|
+
GLib::Idle.add(proc { self.monitor })
|
732
|
+
|
738
733
|
app.run
|
739
734
|
|
740
735
|
# @window.show_all
|
741
736
|
# @window.show
|
742
737
|
end
|
743
738
|
|
739
|
+
def monitor
|
740
|
+
@monitor_time ||= Time.now
|
741
|
+
@sw_width ||= @sw.width
|
742
|
+
return true if Time.now - @monitor_time < 0.2
|
743
|
+
# Detect element resize
|
744
|
+
if @sw.width != @sw_width
|
745
|
+
# puts "@sw.width=#{@sw.width}"
|
746
|
+
@sw_width = @sw.width
|
747
|
+
DelayExecutioner.exec(id: :scale_images, wait: 0.7, callable: proc { vma.gui.scale_all_images })
|
748
|
+
end
|
749
|
+
@monitor_time = Time.now
|
750
|
+
return true
|
751
|
+
end
|
752
|
+
|
744
753
|
def init_menu
|
745
754
|
Vimamsa::Menu.new(@menubar, @app)
|
746
755
|
end
|
@@ -79,7 +79,6 @@ class VSourceView < GtkSource::View
|
|
79
79
|
end
|
80
80
|
|
81
81
|
def set_content(str)
|
82
|
-
delete_cursorchar
|
83
82
|
self.buffer.set_text(str)
|
84
83
|
end
|
85
84
|
|
@@ -123,6 +122,52 @@ class VSourceView < GtkSource::View
|
|
123
122
|
def register_signals()
|
124
123
|
check_controllers
|
125
124
|
|
125
|
+
# TODO: accept GLib::Type::STRING also?
|
126
|
+
@dt = Gtk::DropTarget.new(GLib::Type["GFile"], [Gdk::DragAction::COPY, Gdk::DragAction::MOVE])
|
127
|
+
# GLib::Type::INVALID
|
128
|
+
|
129
|
+
self.add_controller(@dt)
|
130
|
+
@dt.signal_connect "drop" do |obj, v, x, y|
|
131
|
+
if v.value.gtype == GLib::Type["GLocalFile"]
|
132
|
+
uri = v.value.uri
|
133
|
+
elsif v.value.class == String
|
134
|
+
uri = v.value.gsub(/\r\n$/, "")
|
135
|
+
end
|
136
|
+
debug "dt,drop #{v.value},#{x},#{y}", 2
|
137
|
+
begin
|
138
|
+
fp = URI(uri).path
|
139
|
+
buf.handle_drag_and_drop(fp)
|
140
|
+
rescue URI::InvalidURIError
|
141
|
+
end
|
142
|
+
true
|
143
|
+
end
|
144
|
+
|
145
|
+
@dt.signal_connect "enter" do |gesture, x, y, z, m|
|
146
|
+
debug "dt,enter", 2
|
147
|
+
Gdk::DragAction::COPY
|
148
|
+
end
|
149
|
+
|
150
|
+
@dt.signal_connect "motion" do |obj, x, y|
|
151
|
+
debug "dt,move", 2
|
152
|
+
|
153
|
+
Gdk::DragAction::COPY
|
154
|
+
end
|
155
|
+
|
156
|
+
# dc = Gtk::DropControllerMotion.new
|
157
|
+
# self.add_controller(dc)
|
158
|
+
# dc.signal_connect "enter" do |gesture, x, y|
|
159
|
+
# debug "enter", 2
|
160
|
+
# debug [x, y]
|
161
|
+
# # Ripl.start :binding => binding
|
162
|
+
# true
|
163
|
+
# end
|
164
|
+
|
165
|
+
# dc.signal_connect "motion" do |gesture, x, y|
|
166
|
+
# debug "move", 2
|
167
|
+
# debug [x, y]
|
168
|
+
# true
|
169
|
+
# end
|
170
|
+
|
126
171
|
# Implement mouse selections using @cnt_mo and @cnt_drag
|
127
172
|
@cnt_mo = Gtk::EventControllerMotion.new
|
128
173
|
self.add_controller(@cnt_mo)
|
@@ -151,7 +196,7 @@ class VSourceView < GtkSource::View
|
|
151
196
|
debug "Not enough drag", 2
|
152
197
|
buf.end_selection
|
153
198
|
# elsif !buf.visual_mode? and vma.kbd.get_scope != :editor
|
154
|
-
elsif vma.kbd.get_scope != :editor
|
199
|
+
elsif vma.kbd.get_scope != :editor
|
155
200
|
# Can't transition from editor wide mode to buffer specific mode
|
156
201
|
vma.kbd.set_mode(:visual)
|
157
202
|
else
|
@@ -236,14 +281,12 @@ class VSourceView < GtkSource::View
|
|
236
281
|
|
237
282
|
def handle_scrolling()
|
238
283
|
return # TODO
|
239
|
-
delete_cursorchar
|
240
284
|
# curpos = buffer.cursor_position
|
241
285
|
# debug "MOVE CURSOR: #{curpos}"
|
242
286
|
return nil if vma.gui.nil?
|
243
287
|
return nil if @bufo.nil?
|
244
288
|
vma.gui.run_after_scrolling proc {
|
245
289
|
debug "START UPDATE POS AFTER SCROLLING", 2
|
246
|
-
delete_cursorchar
|
247
290
|
bc = window_to_buffer_coords(Gtk::TextWindowType::WIDGET, gutter_width + 2, 60)
|
248
291
|
if !bc.nil?
|
249
292
|
i = coord_to_iter(bc[0], bc[1])
|
@@ -257,7 +300,6 @@ class VSourceView < GtkSource::View
|
|
257
300
|
|
258
301
|
def set_cursor_to_top
|
259
302
|
debug "set_cursor_to_top", 2
|
260
|
-
delete_cursorchar
|
261
303
|
bc = window_to_buffer_coords(Gtk::TextWindowType::WIDGET, gutter_width + 2, 60)
|
262
304
|
if !bc.nil?
|
263
305
|
i = coord_to_iter(bc[0], bc[1])
|
@@ -270,7 +312,6 @@ class VSourceView < GtkSource::View
|
|
270
312
|
|
271
313
|
# def handle_key_event(event, sig)
|
272
314
|
def handle_key_event(keyval, keyname, sig)
|
273
|
-
delete_cursorchar
|
274
315
|
if $update_cursor
|
275
316
|
handle_scrolling
|
276
317
|
end
|
@@ -394,7 +435,6 @@ class VSourceView < GtkSource::View
|
|
394
435
|
end
|
395
436
|
|
396
437
|
def handle_deltas()
|
397
|
-
delete_cursorchar
|
398
438
|
any_change = false
|
399
439
|
while d = @bufo.deltas.shift
|
400
440
|
any_change = true
|
@@ -435,7 +475,6 @@ class VSourceView < GtkSource::View
|
|
435
475
|
end
|
436
476
|
|
437
477
|
def set_cursor_pos(pos)
|
438
|
-
delete_cursorchar
|
439
478
|
itr = buffer.get_iter_at(:offset => pos)
|
440
479
|
itr2 = buffer.get_iter_at(:offset => pos + 1)
|
441
480
|
buffer.place_cursor(itr)
|
@@ -454,7 +493,7 @@ class VSourceView < GtkSource::View
|
|
454
493
|
$idle_scroll_to_mark = true
|
455
494
|
ensure_cursor_visible
|
456
495
|
|
457
|
-
draw_cursor
|
496
|
+
# draw_cursor
|
458
497
|
|
459
498
|
return true
|
460
499
|
end
|
@@ -516,18 +555,7 @@ class VSourceView < GtkSource::View
|
|
516
555
|
end
|
517
556
|
end
|
518
557
|
|
519
|
-
# Delete the extra char added to buffer to represent the cursor
|
520
|
-
def delete_cursorchar
|
521
|
-
if !@cursorchar.nil?
|
522
|
-
itr = buffer.get_iter_at(:offset => @cursorchar)
|
523
|
-
itr2 = buffer.get_iter_at(:offset => @cursorchar + 1)
|
524
|
-
buffer.delete(itr, itr2)
|
525
|
-
@cursorchar = nil
|
526
|
-
end
|
527
|
-
end
|
528
|
-
|
529
558
|
def after_action
|
530
|
-
delete_cursorchar
|
531
559
|
iterate_gui_main_loop
|
532
560
|
handle_deltas
|
533
561
|
iterate_gui_main_loop
|
@@ -543,7 +571,8 @@ class VSourceView < GtkSource::View
|
|
543
571
|
self.style_context.remove_provider(@cursor_prov)
|
544
572
|
end
|
545
573
|
prov = Gtk::CssProvider.new
|
546
|
-
prov.load(data: ".view text selection { background-color: #{bg}; color: #ffffff; }")
|
574
|
+
# prov.load(data: ".view text selection { background-color: #{bg}; color: #ffffff; }")
|
575
|
+
prov.load(data: ".view text selection { background-color: #{bg}; color: #ffffff; } .view { caret-color: #{bg}; }")
|
547
576
|
self.style_context.add_provider(prov)
|
548
577
|
@cursor_prov = prov
|
549
578
|
end
|
@@ -557,40 +586,28 @@ class VSourceView < GtkSource::View
|
|
557
586
|
# @tt.font = "Arial"
|
558
587
|
# end
|
559
588
|
|
589
|
+
sv = vma.gui.sw.child
|
560
590
|
mode = vma.kbd.get_mode
|
561
591
|
ctype = vma.kbd.get_cursor_type
|
562
592
|
ctype = :visual if vma.buf.selection_active?
|
563
593
|
|
564
|
-
delete_cursorchar
|
565
594
|
vma.gui.remove_overlay_cursor
|
566
595
|
if [:command, :replace, :browse].include?(ctype)
|
567
596
|
set_cursor_color(ctype)
|
568
|
-
if @bufo[@bufo.pos] == "\n"
|
569
|
-
# If we are at end of line, it's not possible to draw the cursor by making a selection. I tried to do this by drawing an overlay, but that generates issues. If moving the cursor causes the ScrolledWindow to be scrolled, these errors randomly appear and the whole view shows blank:
|
570
|
-
# (ruby:21016): Gtk-WARNING **: 19:52:23.181: Trying to snapshot GtkSourceView 0x55a97524c8c0 without a current allocation
|
571
|
-
# (ruby:21016): Gtk-WARNING **: 19:52:23.181: Trying to snapshot GtkGizmo 0x55a9727d2580 without a current allocation
|
572
|
-
# (ruby:21016): Gtk-WARNING **: 19:52:23.243: Trying to snapshot GtkSourceView 0x55a97524c8c0 without a current allocation
|
573
|
-
# vma.gui.overlay_draw_cursor(@bufo.pos)
|
574
|
-
|
575
|
-
# Current workaround is to add an empty space to the place where the cursor is and then remove this whenever we get any kind of event that might cause this class to be accessed.
|
576
|
-
itr = buffer.get_iter_at(:offset => @bufo.pos)
|
577
|
-
buffer.insert(itr, " ") # normal space
|
578
|
-
# buffer.insert(itr, " ") # thin space (U+2009)
|
579
|
-
# buffer.insert(itr, "l")
|
580
|
-
@cursorchar = @bufo.pos
|
581
|
-
|
582
|
-
# Apparently we need to redo this after buffer.insert:
|
583
|
-
itr = buffer.get_iter_at(:offset => @bufo.pos)
|
584
|
-
itr2 = buffer.get_iter_at(:offset => @bufo.pos + 1)
|
585
|
-
# buffer.apply_tag(@tt, itr, itr2)
|
586
|
-
buffer.select_range(itr, itr2)
|
587
|
-
else
|
588
|
-
itr = buffer.get_iter_at(:offset => @bufo.pos)
|
589
|
-
itr2 = buffer.get_iter_at(:offset => @bufo.pos + 1)
|
590
|
-
buffer.select_range(itr, itr2)
|
591
|
-
end
|
592
|
-
# elsif @bufo.visual_mode?
|
593
597
|
|
598
|
+
sv.overwrite = true
|
599
|
+
# sv.cursor_visible = true
|
600
|
+
# sv.reset_cursor_blink
|
601
|
+
|
602
|
+
# (Via trial and error) This combination is needed to make cursor visible:
|
603
|
+
sv.cursor_visible = false
|
604
|
+
sv.cursor_visible = true
|
605
|
+
|
606
|
+
# sv.reset_cursor_blink
|
607
|
+
Gtk::Settings.default.gtk_cursor_blink = false
|
608
|
+
# Gtk::Settings.default.gtk_cursor_blink_time = 8000
|
609
|
+
# vma.gui.sw.child.toggle_cursor_visible
|
610
|
+
# vma.gui.sw.child.cursor_visible = true
|
594
611
|
elsif ctype == :visual
|
595
612
|
set_cursor_color(ctype)
|
596
613
|
# debug "VISUAL MODE"
|
@@ -601,16 +618,19 @@ class VSourceView < GtkSource::View
|
|
601
618
|
# Pango-CRITICAL **: pango_layout_get_cursor_pos: assertion 'index >= 0 && index <= layout->length' failed
|
602
619
|
buffer.select_range(itr, itr2)
|
603
620
|
elsif ctype == :insert
|
604
|
-
|
605
|
-
|
606
|
-
|
607
|
-
|
608
|
-
# Via trial and error, this combination is only thing that seems to work:
|
609
|
-
vma.gui.sw.child.toggle_cursor_visible
|
610
|
-
vma.gui.sw.child.cursor_visible = true
|
611
|
-
|
621
|
+
set_cursor_color(ctype)
|
622
|
+
sv.overwrite = false
|
623
|
+
# sv.cursor_visible = false
|
624
|
+
# sv.cursor_visible = true
|
612
625
|
debug "INSERT MODE"
|
613
626
|
else # TODO
|
614
627
|
end
|
628
|
+
if [:insert, :command, :replace, :browse].include?(ctype)
|
629
|
+
# Place cursor where it already is
|
630
|
+
# Without this hack, the cursor doesn't always get drawn
|
631
|
+
pos = @bufo.pos
|
632
|
+
itr = buffer.get_iter_at(:offset => pos)
|
633
|
+
buffer.place_cursor(itr)
|
634
|
+
end
|
615
635
|
end
|
616
636
|
end
|
@@ -7,7 +7,7 @@ def hpt_check_cur_word(w)
|
|
7
7
|
dn = File.dirname(vma.buf.fname)
|
8
8
|
|
9
9
|
fcands = []
|
10
|
-
if fpfx[0] != "/"
|
10
|
+
if fpfx[0] != "/" and fpfx[0] != "~"
|
11
11
|
fcands << "#{dn}/#{fpfx}"
|
12
12
|
fcands << "#{dn}/#{fpfx}.txt"
|
13
13
|
end
|
@@ -26,7 +26,7 @@ def hpt_check_cur_word(w)
|
|
26
26
|
if fn
|
27
27
|
if m[2] == "audio"
|
28
28
|
# Thread.new { Audio.play(fn) }
|
29
|
-
Audio.play(fn)
|
29
|
+
Audio.play(fn) if cnf.audio.enabled?
|
30
30
|
else
|
31
31
|
if !file_is_text_file(fn)
|
32
32
|
message "Not text file #{fn}"
|
@@ -52,6 +52,7 @@ def hpt_check_cur_word(w)
|
|
52
52
|
return nil
|
53
53
|
end
|
54
54
|
|
55
|
+
|
55
56
|
def hpt_create_new_file(fn)
|
56
57
|
create_new_file(fn)
|
57
58
|
end
|
@@ -128,7 +129,7 @@ def hpt_scan_images(bf = nil)
|
|
128
129
|
}
|
129
130
|
|
130
131
|
# Need to scale after buffer loaded
|
131
|
-
|
132
|
+
run_as_idle proc { vma.gui.scale_all_images }
|
132
133
|
|
133
134
|
# vma.gui.delex.run #TODO:gtk4
|
134
135
|
end
|
@@ -17,6 +17,7 @@ cnf.mode.default.cursor.background = "#03fcca"
|
|
17
17
|
cnf.mode.visual.cursor.background = "#bc6040"
|
18
18
|
cnf.mode.replace.cursor.background = "#fc0331"
|
19
19
|
cnf.mode.browse.cursor.background = "#f803fc"
|
20
|
+
cnf.mode.insert.cursor.background = "#ffffff"
|
20
21
|
|
21
22
|
def _insert_move(op)
|
22
23
|
if op == :pagedown
|
@@ -332,6 +333,7 @@ default_keys = {
|
|
332
333
|
"CV , R" => "restart_application",
|
333
334
|
# "I ctrl!" => "vma.kbd.to_previous_mode",
|
334
335
|
"C shift!" => "buf.save",
|
336
|
+
"I ctrl-s" => "buf.save",
|
335
337
|
"I <char>" => "buf.insert_txt(<char>)",
|
336
338
|
"I esc || I ctrl!" => "vma.kbd.to_previous_mode",
|
337
339
|
|
data/lib/vimamsa/rbvma.rb
CHANGED
data/lib/vimamsa/util.rb
CHANGED
@@ -3,6 +3,18 @@ require "open3"
|
|
3
3
|
VOWELS = %w(a e i o u)
|
4
4
|
CONSONANTS = %w(b c d f g h j k l m n p q r s t v w x y z)
|
5
5
|
|
6
|
+
def running_wayland?
|
7
|
+
sess = ENV["DESKTOP_SESSION"]
|
8
|
+
sess ||= ENV["XDG_SESSION_DESKTOP"]
|
9
|
+
sess ||= ENV["GDMSESSION"]
|
10
|
+
sess ||= ""
|
11
|
+
if sess.match(/wayland/)
|
12
|
+
return true
|
13
|
+
else
|
14
|
+
return false
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
6
18
|
def tilde_path(abspath)
|
7
19
|
userhome = File.expand_path("~/")
|
8
20
|
abspath.sub(/^#{Regexp.escape(userhome)}\//, "~/")
|
@@ -46,13 +58,16 @@ end
|
|
46
58
|
|
47
59
|
def file_mime_type(fpath)
|
48
60
|
fpath = File.expand_path(fpath)
|
49
|
-
return
|
61
|
+
return nil if !File.readable?(fpath)
|
50
62
|
r = exec_cmd("file", "--mime-type", "--mime-encoding", fpath)
|
51
|
-
return
|
52
|
-
return
|
63
|
+
return nil if r.class != String
|
64
|
+
return nil if r.size < 2
|
53
65
|
m = r.match(".*:\s*(.*)")
|
54
|
-
|
55
|
-
|
66
|
+
b = m[1].match(/(.*);/)
|
67
|
+
c = m[1].match(/charset=(.*)/)
|
68
|
+
return nil if b.nil? or c.nil?
|
69
|
+
mimetype = b[1]
|
70
|
+
charset = c[1]
|
56
71
|
return [mimetype, charset]
|
57
72
|
end
|
58
73
|
|
@@ -185,6 +200,17 @@ end
|
|
185
200
|
# Used for image scaling after window resize
|
186
201
|
|
187
202
|
class DelayExecutioner
|
203
|
+
|
204
|
+
# Run 'callable.call' if 'wait' time elapsed from last exec call for this id
|
205
|
+
def self.exec(id:, wait:, callable:)
|
206
|
+
@@h ||= {}
|
207
|
+
h = @@h
|
208
|
+
if h[id].nil?
|
209
|
+
h[id] = DelayExecutioner.new(wait, callable)
|
210
|
+
end
|
211
|
+
h[id].run
|
212
|
+
end
|
213
|
+
|
188
214
|
def initialize(wait_time, _proc)
|
189
215
|
@wait_time = wait_time
|
190
216
|
@proc = _proc
|
@@ -206,7 +232,10 @@ class DelayExecutioner
|
|
206
232
|
end
|
207
233
|
|
208
234
|
def run()
|
235
|
+
# Reset @lastt to further delay execution until @wait_time from now
|
209
236
|
@lastt = Time.now
|
237
|
+
|
238
|
+
# If already executed after last call to run()
|
210
239
|
if @thread_running == false
|
211
240
|
@thread_running = true
|
212
241
|
start_thread
|
@@ -299,17 +328,22 @@ end
|
|
299
328
|
|
300
329
|
def is_image_file(fpath)
|
301
330
|
return false if !File.exist?(fpath)
|
302
|
-
return false if !fpath.match(/.(jpg|jpeg|png)$/i)
|
303
|
-
|
304
|
-
|
331
|
+
# return false if !fpath.match(/.(jpg|jpeg|png)$/i)
|
332
|
+
mime = file_mime_type(fpath)
|
333
|
+
if !mime.nil?
|
334
|
+
if mime[0].match(/image\//)
|
335
|
+
return true
|
336
|
+
end
|
337
|
+
end
|
338
|
+
return false
|
305
339
|
end
|
306
340
|
|
307
|
-
def is_text_file(fpath)
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
end
|
341
|
+
# def is_text_file(fpath)
|
342
|
+
# return false if !File.exist?(fpath)
|
343
|
+
# return false if !fpath.match(/.(txt|cpp|h|rb|c|php|java|py)$/i)
|
344
|
+
# #TODO: check contents of file
|
345
|
+
# return true
|
346
|
+
# end
|
313
347
|
|
314
348
|
def is_path(s)
|
315
349
|
m = s.match(/(~[a-z]*)?\/.*\//)
|
data/lib/vimamsa/version.rb
CHANGED
data/vimamsa.gemspec
CHANGED
@@ -27,16 +27,16 @@ Gem::Specification.new do |spec|
|
|
27
27
|
|
28
28
|
spec.add_runtime_dependency 'ripl', '~> 0.7.1'
|
29
29
|
spec.add_runtime_dependency 'ripl-multi_line', '~> 0.3.1'
|
30
|
-
spec.add_runtime_dependency 'gdk4', '~> 4.2.
|
31
|
-
spec.add_runtime_dependency 'gtk4', '~> 4.2.
|
30
|
+
spec.add_runtime_dependency 'gdk4', '~> 4.2.1'
|
31
|
+
spec.add_runtime_dependency 'gtk4', '~> 4.2.1'
|
32
|
+
spec.add_runtime_dependency 'gtksourceview5', '~> 4.2.1'
|
33
|
+
spec.add_runtime_dependency 'gstreamer', '~> 4.2.1'
|
32
34
|
spec.add_runtime_dependency 'rambling-trie', '~> 2.3.1'
|
33
35
|
|
34
36
|
spec.add_runtime_dependency 'differ', '~> 0.1.2'
|
35
|
-
spec.add_runtime_dependency 'gtksourceview5', '~> 4.2.0'
|
36
37
|
spec.add_runtime_dependency 'parallel', '~> 1.14' #TODO: update?
|
37
38
|
spec.add_runtime_dependency 'listen', '~> 3.4' #TODO: update?
|
38
39
|
spec.add_runtime_dependency 'language_server-protocol', '~> 3.17.0.3'
|
39
|
-
spec.add_runtime_dependency 'gstreamer', '~> 4.2.0'
|
40
40
|
|
41
41
|
spec.extensions = ["ext/vmaext/extconf.rb"]
|
42
42
|
spec.licenses = ['GPL-3.0+']
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: vimamsa
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.18
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sami Sieranoja
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2024-03-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -86,126 +86,126 @@ dependencies:
|
|
86
86
|
requirements:
|
87
87
|
- - "~>"
|
88
88
|
- !ruby/object:Gem::Version
|
89
|
-
version: 4.2.
|
89
|
+
version: 4.2.1
|
90
90
|
type: :runtime
|
91
91
|
prerelease: false
|
92
92
|
version_requirements: !ruby/object:Gem::Requirement
|
93
93
|
requirements:
|
94
94
|
- - "~>"
|
95
95
|
- !ruby/object:Gem::Version
|
96
|
-
version: 4.2.
|
96
|
+
version: 4.2.1
|
97
97
|
- !ruby/object:Gem::Dependency
|
98
98
|
name: gtk4
|
99
99
|
requirement: !ruby/object:Gem::Requirement
|
100
100
|
requirements:
|
101
101
|
- - "~>"
|
102
102
|
- !ruby/object:Gem::Version
|
103
|
-
version: 4.2.
|
103
|
+
version: 4.2.1
|
104
104
|
type: :runtime
|
105
105
|
prerelease: false
|
106
106
|
version_requirements: !ruby/object:Gem::Requirement
|
107
107
|
requirements:
|
108
108
|
- - "~>"
|
109
109
|
- !ruby/object:Gem::Version
|
110
|
-
version: 4.2.
|
110
|
+
version: 4.2.1
|
111
111
|
- !ruby/object:Gem::Dependency
|
112
|
-
name:
|
112
|
+
name: gtksourceview5
|
113
113
|
requirement: !ruby/object:Gem::Requirement
|
114
114
|
requirements:
|
115
115
|
- - "~>"
|
116
116
|
- !ruby/object:Gem::Version
|
117
|
-
version: 2.
|
117
|
+
version: 4.2.1
|
118
118
|
type: :runtime
|
119
119
|
prerelease: false
|
120
120
|
version_requirements: !ruby/object:Gem::Requirement
|
121
121
|
requirements:
|
122
122
|
- - "~>"
|
123
123
|
- !ruby/object:Gem::Version
|
124
|
-
version: 2.
|
124
|
+
version: 4.2.1
|
125
125
|
- !ruby/object:Gem::Dependency
|
126
|
-
name:
|
126
|
+
name: gstreamer
|
127
127
|
requirement: !ruby/object:Gem::Requirement
|
128
128
|
requirements:
|
129
129
|
- - "~>"
|
130
130
|
- !ruby/object:Gem::Version
|
131
|
-
version:
|
131
|
+
version: 4.2.1
|
132
132
|
type: :runtime
|
133
133
|
prerelease: false
|
134
134
|
version_requirements: !ruby/object:Gem::Requirement
|
135
135
|
requirements:
|
136
136
|
- - "~>"
|
137
137
|
- !ruby/object:Gem::Version
|
138
|
-
version:
|
138
|
+
version: 4.2.1
|
139
139
|
- !ruby/object:Gem::Dependency
|
140
|
-
name:
|
140
|
+
name: rambling-trie
|
141
141
|
requirement: !ruby/object:Gem::Requirement
|
142
142
|
requirements:
|
143
143
|
- - "~>"
|
144
144
|
- !ruby/object:Gem::Version
|
145
|
-
version:
|
145
|
+
version: 2.3.1
|
146
146
|
type: :runtime
|
147
147
|
prerelease: false
|
148
148
|
version_requirements: !ruby/object:Gem::Requirement
|
149
149
|
requirements:
|
150
150
|
- - "~>"
|
151
151
|
- !ruby/object:Gem::Version
|
152
|
-
version:
|
152
|
+
version: 2.3.1
|
153
153
|
- !ruby/object:Gem::Dependency
|
154
|
-
name:
|
154
|
+
name: differ
|
155
155
|
requirement: !ruby/object:Gem::Requirement
|
156
156
|
requirements:
|
157
157
|
- - "~>"
|
158
158
|
- !ruby/object:Gem::Version
|
159
|
-
version:
|
159
|
+
version: 0.1.2
|
160
160
|
type: :runtime
|
161
161
|
prerelease: false
|
162
162
|
version_requirements: !ruby/object:Gem::Requirement
|
163
163
|
requirements:
|
164
164
|
- - "~>"
|
165
165
|
- !ruby/object:Gem::Version
|
166
|
-
version:
|
166
|
+
version: 0.1.2
|
167
167
|
- !ruby/object:Gem::Dependency
|
168
|
-
name:
|
168
|
+
name: parallel
|
169
169
|
requirement: !ruby/object:Gem::Requirement
|
170
170
|
requirements:
|
171
171
|
- - "~>"
|
172
172
|
- !ruby/object:Gem::Version
|
173
|
-
version: '
|
173
|
+
version: '1.14'
|
174
174
|
type: :runtime
|
175
175
|
prerelease: false
|
176
176
|
version_requirements: !ruby/object:Gem::Requirement
|
177
177
|
requirements:
|
178
178
|
- - "~>"
|
179
179
|
- !ruby/object:Gem::Version
|
180
|
-
version: '
|
180
|
+
version: '1.14'
|
181
181
|
- !ruby/object:Gem::Dependency
|
182
|
-
name:
|
182
|
+
name: listen
|
183
183
|
requirement: !ruby/object:Gem::Requirement
|
184
184
|
requirements:
|
185
185
|
- - "~>"
|
186
186
|
- !ruby/object:Gem::Version
|
187
|
-
version: 3.
|
187
|
+
version: '3.4'
|
188
188
|
type: :runtime
|
189
189
|
prerelease: false
|
190
190
|
version_requirements: !ruby/object:Gem::Requirement
|
191
191
|
requirements:
|
192
192
|
- - "~>"
|
193
193
|
- !ruby/object:Gem::Version
|
194
|
-
version: 3.
|
194
|
+
version: '3.4'
|
195
195
|
- !ruby/object:Gem::Dependency
|
196
|
-
name:
|
196
|
+
name: language_server-protocol
|
197
197
|
requirement: !ruby/object:Gem::Requirement
|
198
198
|
requirements:
|
199
199
|
- - "~>"
|
200
200
|
- !ruby/object:Gem::Version
|
201
|
-
version:
|
201
|
+
version: 3.17.0.3
|
202
202
|
type: :runtime
|
203
203
|
prerelease: false
|
204
204
|
version_requirements: !ruby/object:Gem::Requirement
|
205
205
|
requirements:
|
206
206
|
- - "~>"
|
207
207
|
- !ruby/object:Gem::Version
|
208
|
-
version:
|
208
|
+
version: 3.17.0.3
|
209
209
|
description: Vi/Vim -inspired experimental GUI-oriented text editor written with Ruby
|
210
210
|
and GTK.
|
211
211
|
email:
|
@@ -298,7 +298,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
298
298
|
- !ruby/object:Gem::Version
|
299
299
|
version: '0'
|
300
300
|
requirements: []
|
301
|
-
rubygems_version: 3.
|
301
|
+
rubygems_version: 3.3.26
|
302
302
|
signing_key:
|
303
303
|
specification_version: 4
|
304
304
|
summary: Vimamsa
|