Class: Zashoku::Viewer

Inherits:
Object
  • Object
show all
Includes:
Util
Defined in:
lib/viewer.rb

Overview

Main class for the zashoku application

Constant Summary collapse

HISTORY =
File.join(Zashoku::Config::CONF_DIR, 'cmd_history.yml')
COMMANDS =
{
  'quit'          => :cleanup,
  'enter_command' => :get_user_command,
  'change_view'   => :change_view,
  'map_key'       => :map_key
}.freeze

Instance Method Summary collapse

Methods included from Util

alert, decode_object, encode_object, get_yaml, put_yaml

Constructor Details

#initializeViewer

Returns a new instance of Viewer.



56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
# File 'lib/viewer.rb', line 56

def initialize
  @semaphore = Mutex.new

  Zashoku.logger = Logger.new(
    File.join(Zashoku::AppRoot, "#{Zashoku::AppName}.log")
  )

  Zashoku.conf = ViewConfig.new

  Zashoku.modules = Zashoku::Module.load(Zashoku::ViewerModules)
  Zashoku.modules.merge!('conf' => Conf)
  Zashoku.conf.reload

  # note: start daemon if not running
  Zashoku.client = Net::Client.new(Zashoku::Port)
  Zashoku.client.callbacks << method(:handle_event)

  @server_modules = Zashoku.client.command('modules')

  @views = Zashoku.modules.keys.zip(Zashoku.modules.map do |_k, m|
    m.view
  end).to_h
  @view = @views.keys.first

  Zashoku.logger.debug('initializing statusline')
  @statusline = Statusline.new
  Zashoku.logger.debug(@statusline)
  @statusline.add_observer(self, :draw_statusline)

  @cmdline = Util::Readline.new(
    ':',
    ac_tree:
        @views.map { |k, v| [k, v.methods - Object.methods] }.to_h
        .merge(COMMANDS.map { |k, _v| [k, nil] }.to_h)
        .merge({'conf' => Zashoku.conf.methods - Object.methods })
        .merge(@server_modules.map { |m| ['remote::' + m, nil] }.to_h),
    history: Util.get_yaml(HISTORY, [])
  )

  init_screen
  lay_traps!
  Thread.abort_on_exception = true
  Thread.new { scan_keyboard! }

  init_view(@view) if @view
  @statusline.draw
  sleep
end

Instance Method Details

#builtin_command(cmd, *args) ⇒ Object



177
178
179
180
# File 'lib/viewer.rb', line 177

def builtin_command(cmd, *args)
  Zashoku.logger.debug("executing builtin #{cmd}(#{args})")
  send(COMMANDS[cmd], *args)
end

#change_view(new_view) ⇒ Object



105
106
107
108
109
110
111
112
# File 'lib/viewer.rb', line 105

def change_view(new_view)
  Zashoku.logger.debug("Changing view to #{new_view}")
  @views[@view].delete_observers
  #@views[new_view].change_screen_size(redraw: false)
  #@views[new_view].expand(-1)
  @view = new_view
  init_view(@view)
end

#cleanupObject



134
135
136
137
138
139
140
141
# File 'lib/viewer.rb', line 134

def cleanup
  Zashoku.client.disconnect
  @key_thread&.exit
  Curses.close_screen
  Util.put_yaml(HISTORY, @cmdline.history)
  Term.show_cursor
  exit
end

#command(mod, meth, *args) ⇒ Object



150
151
152
153
154
155
156
# File 'lib/viewer.rb', line 150

def command(mod, meth, *args)
  if /^remote::.*/.match?(mod)
    Zashoku.command(mod: mod, meth: meth, args: args)
  elsif @views.key?(mod) && @views[mod].respond_to?(meth)
    @views[mod].send(meth, *args)
  end
end

#draw_statuslineObject



225
226
227
# File 'lib/viewer.rb', line 225

def draw_statusline
  @statusline.draw
end

#draw_viewObject



221
222
223
# File 'lib/viewer.rb', line 221

def draw_view
  @views[@view].draw
end

#get_user_commandObject



158
159
160
161
162
163
164
165
166
# File 'lib/viewer.rb', line 158

def get_user_command
  cmd = @cmdline.read
  return unless cmd
  return builtin_command(*cmd.split) if COMMANDS.key?(cmd.split.first)

  command(*cmd.split) unless cmd.empty?
rescue
  Util.alert('Error')
end

#handle_event(event) ⇒ Object



168
169
170
171
172
173
174
175
# File 'lib/viewer.rb', line 168

def handle_event(event)
  return @statusline.event(event) if event['statusline']

  case event['event']
  when 'items'
    command(event['sender'], :refresh)
  end
end

#init_screenObject



143
144
145
146
147
148
# File 'lib/viewer.rb', line 143

def init_screen
  Curses.noecho
  Curses.init_screen
  Term.hide_cursor
  Curses.stdscr.keypad(true)
end

#init_view(view) ⇒ Object



114
115
116
117
118
119
# File 'lib/viewer.rb', line 114

def init_view(view)
  @views[view].add_observer(self, :draw_view)
  @views[view].refresh
  @views[view].dirty_all_lines
  draw_view
end

#lay_traps!Object



121
122
123
124
125
126
127
128
129
130
131
132
# File 'lib/viewer.rb', line 121

def lay_traps!
  Signal.trap('WINCH') do
    Thread.new do
      @semaphore.synchronize { @views[@view].change_screen_size }
    end
  end
  Signal.trap('INT') do
      Thread.new do
        @semaphore.synchronize { cleanup }
    end
  end
end

#map_key(key, mod, command, *args) ⇒ Object



182
183
184
# File 'lib/viewer.rb', line 182

def map_key(key, mod, command, *args)
  Zashoku.conf.set(['keymaps', mod, key], "#{command} #{args.join(' ')}")
end

#scan_keyboard!Object



186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
# File 'lib/viewer.rb', line 186

def scan_keyboard!
  loop do
    key  = Curses.getch
    maps = Zashoku.conf.get(%w[keymaps])
    cmd =
      if maps['global'].key?(key)
        maps['global'][key]
      elsif maps[@view]&.key?(key)
        maps[@view][key]
      else
        ''
      end

    if COMMANDS.key?(cmd.split.first)
      builtin_command(*cmd.split)
      next
    end

    mod, meth, *args = cmd.split
    mod = mod == 'current_view' ? @view : mod
    args.map! do |arg|
      if arg.chr == '$'
        arg = arg[1..-1]
        chain = arg.split('.')
        chain[1..-1].reduce(@views[@view].send(chain.first)) do |m, a|
          m.send(a)
        end
      else
        arg
      end
    end
    command(mod, meth, *args)
  end
end