Created
January 1, 2014 12:29
-
-
Save penguin2716/8207698 to your computer and use it in GitHub Desktop.
vimlike_textview for mikutter (development preview)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env ruby | |
#-*- coding: utf-8 -*- | |
require 'gtk2' | |
require 'gtksourceview2' | |
class VimlikeTextView < Gtk::SourceView | |
attr_accessor :current_mode, :default_bgcolor, :insert_bgcolor, :visual_bgcolor, :select | |
@@hook_keys_normal = ['a', 'A', 'i', 'I', 'o', 'O', 'v', 'h', 'j', 'k', 'l', 'u', 'p', 'P', 'x', 'X', 'BackSpace'] | |
@@hook_keys_insert = ['Escape'] | |
@@hook_keys_visual = ['Escape', 'h', 'j', 'k', 'l', 'y', 'x'] | |
@@hist_limit = 8000 | |
@@post_history = [] | |
@@post_history_ptr = 0 | |
module Mode | |
NORMAL = 0 | |
INSERT = 1 | |
VISUAL = 2 | |
end | |
def initialize | |
super | |
self.show_line_numbers = true | |
@select = false | |
@current_mode = Mode::NORMAL | |
update_bgcolor | |
@default_bgcolor = Gdk::Color.new(0xffff, 0xffff, 0xffff) | |
@insert_bgcolor = Gdk::Color.new(0xffff, 0xffff, 0xaaaa) | |
@visual_bgcolor = Gdk::Color.new(0xffff, 0xaaaa, 0xffff) | |
@history_stack = [] | |
@history_stack.push([self.buffer.text, self.buffer.cursor_position]) | |
@stack_ptr = 0 | |
@isundo = false | |
add_signal(self.buffer) | |
signal_connect('key_press_event') { |w, e| | |
key = Gdk::Keyval.to_name(e.keyval) | |
@isundo = (current_mode == Mode::NORMAL) and (key == 'u') | |
case current_mode | |
when Mode::NORMAL | |
case key | |
when 'a' | |
self.current_mode = Mode::INSERT | |
update_bgcolor | |
self.move_cursor(Gtk::MOVEMENT_VISUAL_POSITIONS, 1, @select) | |
when 'A' | |
self.current_mode = Mode::INSERT | |
update_bgcolor | |
self.move_cursor(Gtk::MOVEMENT_PARAGRAPH_ENDS, 1, @select) | |
when 'i' | |
self.current_mode = Mode::INSERT | |
update_bgcolor | |
when 'I' | |
self.current_mode = Mode::INSERT | |
update_bgcolor | |
self.move_cursor(Gtk::MOVEMENT_PARAGRAPH_ENDS, -1, @select) | |
when 'o' | |
self.current_mode = Mode::INSERT | |
update_bgcolor | |
self.move_cursor(Gtk::MOVEMENT_PARAGRAPH_ENDS, 1, @select) | |
self.insert_at_cursor("\n") | |
when 'O' | |
self.current_mode = Mode::INSERT | |
update_bgcolor | |
self.move_cursor(Gtk::MOVEMENT_PARAGRAPH_ENDS, -1, @select) | |
self.insert_at_cursor("\n") | |
self.move_cursor(Gtk::MOVEMENT_DISPLAY_LINES, -1, @select) | |
when 'v' | |
self.current_mode = Mode::VISUAL | |
update_bgcolor | |
self.select = true | |
when 'h' | |
self.move_cursor(Gtk::MOVEMENT_VISUAL_POSITIONS, -1, @select) | |
when 'j' | |
self.move_cursor(Gtk::MOVEMENT_DISPLAY_LINES, 1, @select) | |
when 'k' | |
self.move_cursor(Gtk::MOVEMENT_DISPLAY_LINES, -1, @select) | |
when 'l' | |
self.move_cursor(Gtk::MOVEMENT_VISUAL_POSITIONS, 1, @select) | |
when 'u' | |
self.undo | |
when 'p' | |
self.move_cursor(Gtk::MOVEMENT_VISUAL_POSITIONS, 1, @select) | |
self.paste_clipboard | |
when 'P' | |
self.paste_clipboard | |
when 'x' | |
self.delete_from_cursor(Gtk::DELETE_CHARS, 1) | |
when 'X' | |
self.delete_from_cursor(Gtk::DELETE_CHARS, -1) | |
when 'BackSpace' | |
self.move_cursor(Gtk::MOVEMENT_VISUAL_POSITIONS, -1, @select) | |
end | |
true | |
when Mode::INSERT | |
case key | |
when 'Escape' | |
self.current_mode = Mode::NORMAL | |
update_bgcolor | |
end | |
@@hook_keys_insert.include? key | |
when Mode::VISUAL | |
case key | |
when 'Escape' | |
self.current_mode = Mode::NORMAL | |
update_bgcolor | |
self.select = false | |
self.select_all(false) | |
when 'h' | |
self.move_cursor(Gtk::MOVEMENT_VISUAL_POSITIONS, -1, @select) | |
when 'j' | |
self.move_cursor(Gtk::MOVEMENT_DISPLAY_LINES, 1, @select) | |
when 'k' | |
self.move_cursor(Gtk::MOVEMENT_DISPLAY_LINES, -1, @select) | |
when 'l' | |
self.move_cursor(Gtk::MOVEMENT_VISUAL_POSITIONS, 1, @select) | |
when 'y' | |
self.copy_clipboard | |
self.select = false | |
self.select_all(false) | |
self.current_mode = Mode::NORMAL | |
update_bgcolor | |
when 'x' | |
self.delete_from_cursor(Gtk::DELETE_CHARS, 1) | |
end | |
true | |
end | |
} | |
end | |
def update_bgcolor | |
case current_mode | |
when Mode::NORMAL | |
modify_base(Gtk::STATE_NORMAL, default_bgcolor) | |
when Mode::INSERT | |
modify_base(Gtk::STATE_NORMAL, insert_bgcolor) | |
when Mode::VISUAL | |
modify_base(Gtk::STATE_NORMAL, visual_bgcolor) | |
end | |
end | |
def self.pushGlobalStack(text) | |
@@post_history_ptr = @@post_history.length | |
@@post_history.push(text) end | |
def add_signal(buffer) | |
buffer.signal_connect('changed') { | |
if not @isundo then | |
@history_stack += @history_stack[@stack_ptr..-2].reverse | |
self.push_buffer | |
@stack_ptr = @history_stack.length - 1 | |
end | |
} | |
buffer | |
end | |
# 現在のバッファと最新の履歴が異なっていればスタックに現在の状態を追加 | |
def push_buffer | |
if @history_stack == nil then | |
@history_stack = [['', 0]] | |
end | |
if self.buffer.text != '' then | |
if @history_stack[-1][0] != self.buffer.text then | |
@history_stack.push([self.buffer.text, self.buffer.cursor_position]) | |
end | |
if @history_stack.length > @@hist_limit then | |
@history_stack = @history_stack[(@history_stack.length - @@hist_limit)..-1] | |
end | |
end | |
end | |
# undoの実装.バッファの内容を変更すると自動的に履歴スタックに追加されるので, | |
# 履歴スタックに追加したら最新の履歴を捨てる | |
def undo | |
top = @history_stack[@stack_ptr] | |
if top != nil then | |
if top[0] == self.buffer.text then | |
# 最新履歴が現在の状態と同じなら,2番目の履歴を参照 | |
decStackPtr | |
second = @history_stack[@stack_ptr] | |
if second != nil then | |
self.buffer.set_text(second[0]) | |
self.buffer.place_cursor(self.buffer.get_iter_at_offset(second[1])) | |
else # 上から2番目が空 | |
self.buffer.set_text('') | |
end | |
else | |
self.buffer.set_text(top[0]) | |
self.buffer.place_cursor(self.buffer.get_iter_at_offset(top[1])) | |
end | |
else # 履歴スタックが空 | |
self.buffer.set_text('') | |
end | |
end | |
def incStackPtr | |
if @history_stack.length > @stack_ptr + 1 then | |
@stack_ptr += 1 | |
end | |
end | |
def decStackPtr | |
if @stack_ptr > 0 then | |
@stack_ptr -= 1 | |
end | |
end | |
def undoGlobalStack | |
if @@post_history != [] | |
if not defined? @is_global_undo | |
@is_global_undo = true | |
end | |
if @is_global_undo == false | |
@@post_history_ptr = (@@post_history_ptr - 1) % @@post_history.length | |
@@post_history_ptr = (@@post_history_ptr - 1) % @@post_history.length | |
end | |
self.buffer.set_text(@@post_history[@@post_history_ptr]) | |
@@post_history_ptr = (@@post_history_ptr - 1) % @@post_history.length | |
@is_global_undo = true | |
end | |
end | |
def redoGlobalStack | |
if @@post_history != [] | |
if not defined? @is_global_undo | |
@is_global_undo = false | |
end | |
if @is_global_undo == true | |
@@post_history_ptr = (@@post_history_ptr + 1) % @@post_history.length | |
@@post_history_ptr = (@@post_history_ptr + 1) % @@post_history.length | |
end | |
self.buffer.set_text(@@post_history[@@post_history_ptr]) | |
@@post_history_ptr = (@@post_history_ptr + 1) % @@post_history.length | |
@is_global_undo = false | |
end | |
end | |
end | |
vtv = VimlikeTextView.new | |
w = Gtk::Window.new | |
w.add(vtv) | |
w.set_size_request(300,200) | |
w.signal_connect('key_press_event') { |w, e| | |
if Gdk::Keyval.to_name(e.keyval) == 'q' then | |
Gtk.main_quit | |
end | |
} | |
w.signal_connect('destroy') { | |
Gtk.main_quit | |
} | |
w.show_all | |
Gtk.main |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment