lib/pwkeep/editor.rb in pwkeep-0.0.3 vs lib/pwkeep/editor.rb in pwkeep-0.0.4
- old
+ new
@@ -1,462 +1,13 @@
-require 'clipboard'
+require 'open3'
module PWKeep
- def self.run_editor(data, options)
- ret = [false, data]
-
- Dispel::Screen.open do |screen|
- $ruco_screen = screen
- app = PWKeep::EditorApplication.new(data.to_s,
- :lines => screen.lines, :columns => screen.columns
- )
-
- screen.draw *app.display_info
-
- Dispel::Keyboard.output do |key|
- if key == :resize
- app.resize(screen.lines, screen.columns)
- else
- result = app.key key
- if result == :quit
- ret = [app.editor.saved_content != data, app.editor.saved_content]
- break
- end
- end
-
- screen.draw *app.display_info
- end
- end
-
- return ret
- end
-
- class EditorApplication
- attr_reader :editor, :status, :command, :options
-
- def initialize(data, options)
- @options = Ruco::OptionAccessor.new(options)
- @data = data
-
- setup_actions
- setup_keys
- create_components
- end
-
- def display_info
- [view, style_map, cursor]
- end
-
- def view
- [status.view, editor.view, command.view].join("\n")
- end
-
- def style_map
- status.style_map + editor.style_map + command.style_map
- end
-
- def cursor
- Ruco::Position.new(@focused.cursor.line + @status_lines, @focused.cursor.column)
- end
-
- # user typed a key
- def key(key)
- # deactivate select_mode if its not re-enabled in this action
- @select_mode_was_on = @select_mode
- @select_mode = false
-
- if bound = @bindings[key]
- return execute_action(bound)
- end
-
- case key
-
- # move
- when :down then move_with_select_mode :relative, 1,0
- when :right then move_with_select_mode :relative, 0,1
- when :up then move_with_select_mode :relative, -1,0
- when :left then move_with_select_mode :relative, 0,-1
- when :page_up then move_with_select_mode :page_up
- when :page_down then move_with_select_mode :page_down
- when :"Ctrl+right", :"Alt+f" then move_with_select_mode :jump, :right
- when :"Ctrl+left", :"Alt+b" then move_with_select_mode :jump, :left
-
- # select
- when :"Shift+down" then @focused.selecting { move(:relative, 1, 0) }
- when :"Shift+right" then @focused.selecting { move(:relative, 0, 1) }
- when :"Shift+up" then @focused.selecting { move(:relative, -1, 0) }
- when :"Shift+left" then @focused.selecting { move(:relative, 0, -1) }
- when :"Ctrl+Shift+left", :"Alt+Shift+left" then @focused.selecting{ move(:jump, :left) }
- when :"Ctrl+Shift+right", :"Alt+Shift+right" then @focused.selecting{ move(:jump, :right) }
- when :"Shift+end" then @focused.selecting{ move(:to_eol) }
- when :"Shift+home" then @focused.selecting{ move(:to_bol) }
-
-
- # modify
- when :tab then
- if @editor.selection
- @editor.indent
- else
- @focused.insert("\t")
- end
- when :"Shift+tab" then @editor.unindent
- when :enter then @focused.insert("\n")
- when :backspace then @focused.delete(-1)
- when :delete then @focused.delete(1)
-
- when :escape then # escape from focused
- @focused.reset
- @focused = editor
- else
- @focused.insert(key) if key.is_a?(String)
- end
- end
-
- def bind(key, action=nil, &block)
- raise "Ctrl+m cannot be bound" if key == :"Ctrl+m" # would shadow enter -> bad
- raise "Cannot bind an action and a block" if action and block
- @bindings[key] = action || block
- end
-
- def action(name, &block)
- @actions[name] = block
- end
-
- def ask(question, options={}, &block)
- @focused = command
- command.ask(question, options) do |response|
- @focused = editor
- block.call(response)
- end
- end
-
- def loop_ask(question, options={}, &block)
- ask(question, options) do |result|
- finished = (block.call(result) == :finished)
- loop_ask(question, options, &block) unless finished
- end
- end
-
- def configure(&block)
- instance_exec(&block)
- end
-
- def resize(lines, columns)
- @options[:lines] = lines
- @options[:columns] = columns
- create_components
- @editor.resize(editor_lines, columns)
- end
-
- private
-
- def setup_actions
- @actions = {}
-
- action :paste do
- @focused.insert(Clipboard.paste)
- end
-
- action :copy do
- Clipboard.copy(@focused.text_in_selection)
- end
-
- action :cut do
- Clipboard.copy(@focused.text_in_selection)
- @focused.delete(0)
- end
-
- action :save do
- result = editor.save
- if result != true
- ask("#{result.slice(0,100)} -- Enter=Retry Esc=Cancel "){ @actions[:save].call }
- end
- end
-
- action :quit do
- if editor.modified?
- ask("Lose changes? Enter=Yes Esc=Cancel") do
- editor.store_session
- :quit
- end
- else
- editor.store_session
- :quit
- end
- end
-
- action :go_to_line do
- ask('Go to Line: ') do |result|
- editor.move(:to_line, result.to_i - 1)
- end
- end
-
- action :delete_line do
- editor.delete_line
- end
-
- action :select_mode do
- @select_mode = !@select_mode_was_on
- end
-
- action :select_all do
- @focused.move(:to, 0, 0)
- @focused.selecting do
- move(:to, 9999, 9999)
- end
- end
-
- action :find do
- ask("Find: ", :cache => true) do |result|
- next if editor.find(result)
-
- if editor.content.include?(result)
- ask("No matches found -- Enter=First match ESC=Stop") do
- editor.move(:to, 0,0)
- editor.find(result)
- end
- else
- ask("No matches found in entire file", :auto_enter => true){}
- end
- end
- end
-
- action :find_and_replace do
- ask("Find: ", :cache => true) do |term|
- if editor.find(term)
- ask("Replace with: ", :cache => true) do |replace|
- loop_ask("Replace=Enter Skip=s All=a Cancel=Esc", :auto_enter => true) do |ok|
- case ok
- when '' # enter
- editor.insert(replace)
- when 'a'
- stop = true
- editor.insert(replace)
- editor.insert(replace) while editor.find(term)
- when 's' # do nothing
- else
- stop = true
- end
-
- :finished if stop or not editor.find(term)
- end
- end
- end
- end
- end
-
- action(:undo){ @editor.undo if @focused == @editor }
- action(:redo){ @editor.redo if @focused == @editor }
- action(:move_line_up){ @editor.move_line(-1) if @focused == @editor }
- action(:move_line_down){ @editor.move_line(1) if @focused == @editor }
-
- action(:move_to_eol){ move_with_select_mode :to_eol }
- action(:move_to_bol){ move_with_select_mode :to_bol }
-
- action(:insert_hash_rocket){ @editor.insert(' => ') }
- end
-
- def setup_keys
- @bindings = {}
- bind :"Ctrl+s", :save
- bind :"Ctrl+w", :quit
- bind :"Ctrl+q", :quit
- bind :"Ctrl+g", :go_to_line
- bind :"Ctrl+f", :find
- bind :"Ctrl+r", :find_and_replace
- bind :"Ctrl+b", :select_mode
- bind :"Ctrl+a", :select_all
- bind :"Ctrl+d", :delete_line
- bind :"Ctrl+l", :insert_hash_rocket
- bind :"Ctrl+x", :cut
- bind :"Ctrl+c", :copy
- bind :"Ctrl+v", :paste
- bind :"Ctrl+z", :undo
- bind :"Ctrl+y", :redo
- bind :"Alt+Ctrl+down", :move_line_down
- bind :"Alt+Ctrl+up", :move_line_up
- bind :end, :move_to_eol
- bind :"Ctrl+e", :move_to_eol # for OsX
- bind :home, :move_to_bol
- end
-
- def load_user_config
- Ruco.application = self
- config = File.expand_path(@options[:rc] || "~/.ruco.rb")
- load config if File.exist?(config)
- end
-
- def create_components
- @status_lines = 1
-
- editor_options = @options.slice(
- :columns, :convert_tabs, :convert_newlines, :undo_stack_size, :color_theme
- ).merge(
- :window => @options.nested(:window),
- :history => @options.nested(:history),
- :lines => editor_lines
- ).merge(@options.nested(:editor))
-
- @editor ||= PWKeep::Editor.new(@data, editor_options)
- @status = PWKeep::StatusBar.new(@editor, @options.nested(:status_bar).merge(:columns => options[:columns]))
- @command = Ruco::CommandBar.new(@options.nested(:command_bar).merge(:columns => options[:columns]))
- command.cursor_line = editor_lines
- @focused = @editor
- end
-
- def editor_lines
- command_lines = 1
- @options[:lines] - @status_lines - command_lines
- end
-
- def parse_file_and_line(file)
- if file.to_s.include?(':') and not File.exist?(file)
- short_file, go_to_line = file.split(':',2)
- if File.exist?(short_file)
- file = short_file
- else
- go_to_line = nil
- end
- end
- [file, go_to_line]
- end
-
- def move_with_select_mode(*args)
- @select_mode = true if @select_mode_was_on
- if @select_mode
- @focused.selecting do
- move(*args)
- end
- else
- @focused.send(:move, *args)
- end
- end
-
- def execute_action(action)
- if action.is_a?(Symbol)
- @actions[action].call
- else
- action.call
- end
- end
- end
-
- class Editor
- attr_reader :file
- attr_reader :text_area
- attr_reader :history
- attr_reader :saved_content
-
- private :text_area
- delegate :view, :style_map, :cursor, :position,
- :insert, :indent, :unindent, :delete, :delete_line,
- :redo, :undo, :save_state,
- :selecting, :selection, :text_in_selection, :reset,
- :move, :resize, :move_line,
- :to => :text_area
-
- def initialize(data, options)
- @options = options
-
- content = data
- @options[:language] ||= LanguageSniffer.detect(@file, :content => content).language
- content.tabs_to_spaces! if @options[:convert_tabs]
-
- # cleanup newline formats
- @newline = content.match("\r\n|\r|\n")
- @newline = (@newline ? @newline[0] : "\n")
- content.gsub!(/\r\n?/,"\n")
-
- @saved_content = content
- @text_area = Ruco::EditorArea.new(content, @options)
- @history = @text_area.history
- restore_session
- end
-
- def find(text)
- move(:relative, 0,0) # reset selection
- start = text_area.content.index(text, text_area.index_for_position+1)
- return unless start
-
- # select the found word
- finish = start + text.size
- move(:to_index, finish)
- selecting{ move(:to_index, start) }
-
- true
- end
-
- def modified?
- @saved_content != text_area.content
- end
-
- def save
- lines = text_area.send(:lines)
- lines.each(&:rstrip!) if @options[:remove_trailing_whitespace_on_save]
- lines << '' if @options[:blank_line_before_eof_on_save] and lines.last.to_s !~ /^\s*$/
- content = lines * @newline
-
- @saved_content = content.gsub(/\r?\n/, "\n")
-
- true
- rescue Object => e
- e.message
- end
-
- def store_session
- end
-
- def content
- text_area.content.freeze # no modifications allowed
- end
-
- private
-
- def restore_session
- end
-
- def session_store
- end
- end
-
- class StatusBar
- def initialize(editor, options)
- @editor = editor
- @options = options
- end
-
- def view
- columns = @options[:columns]
-
- version = "Ruco #{Ruco::VERSION} -- "
- position = " #{@editor.position.line + 1}:#{@editor.position.column + 1}"
- indicators = "#{change_indicator}#{writable_indicator}"
- essential = version + position + indicators
- space_left = [columns - essential.size, 0].max
-
- # fit file name into remaining space
- "#{version}#{indicators}#{' ' * space_left}#{position}"[0, columns]
- end
-
- def style_map
- Dispel::StyleMap.single_line_reversed(@options[:columns])
- end
-
- def change_indicator
- @editor.modified? ? '*' : ' '
- end
-
- def writable_indicator
- true
- end
-
- private
-
- # fill the line with left column and then overwrite the right section
- def spread(left, right)
- empty = [@options[:columns] - left.size, 0].max
- line = left + (" " * empty)
- line[(@options[:columns] - right.size - 1)..-1] = ' ' + right
- line
+ def self.run_editor(data, options)
+ ret = [false, data]
+ Open3.pipeline_rw("vipe") do |din,dout,ts|
+ din.write data
+ din.close
+ data2 = dout.read
+ ret = [data != data2, data2]
end
end
end