Class: RuVim::StreamMixer

Inherits:
Object
  • Object
show all
Defined in:
lib/ruvim/stream_mixer.rb

Constant Summary collapse

LARGE_FILE_ASYNC_THRESHOLD_BYTES =
64 * 1024 * 1024
LARGE_FILE_STAGED_PREFIX_BYTES =
8 * 1024 * 1024

Instance Method Summary collapse

Constructor Details

#initialize(editor:, signal_w:) ⇒ StreamMixer

Returns a new instance of StreamMixer.



8
9
10
11
12
# File 'lib/ruvim/stream_mixer.rb', line 8

def initialize(editor:, signal_w:)
  @editor = editor
  @signal_w = signal_w
  @stream_event_queue = nil
end

Instance Method Details

#drain_events!Object



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
104
105
106
107
108
109
110
# File 'lib/ruvim/stream_mixer.rb', line 75

def drain_events!
  return false unless @stream_event_queue

  changed = false
  loop do
    event = @stream_event_queue.pop(true)
    case event[:type]
    when :stream_data
      changed = apply_stream_chunk!(event[:buffer_id], event[:data]) || changed
    when :stream_eof
      changed = finish_stream!(event[:buffer_id], status: event[:status]) || changed
    when :stream_error
      changed = fail_stream!(event[:buffer_id], event[:error]) || changed
    when :follow_data
      changed = apply_stream_chunk!(event[:buffer_id], event[:data]) || changed
    when :follow_truncated
      if (buf = @editor.buffers[event[:buffer_id]])
        @editor.echo("[follow] file truncated: #{buf.display_name}")
        changed = true
      end
    when :follow_deleted
      if (buf = @editor.buffers[event[:buffer_id]])
        @editor.echo("[follow] file deleted, waiting for re-creation: #{buf.display_name}")
        changed = true
      end
    when :file_lines
      changed = apply_async_file_lines!(event[:buffer_id], event[:head], event[:lines], loaded_bytes: event[:loaded_bytes], file_size: event[:file_size]) || changed
    when :file_eof
      changed = finish_async_file_load!(event[:buffer_id], ended_with_newline: event[:ended_with_newline]) || changed
    when :file_error
      changed = fail_async_file_load!(event[:buffer_id], event[:error]) || changed
    end
  end
rescue ThreadError
  changed
end

#ensure_event_queue!Object



181
182
183
# File 'lib/ruvim/stream_mixer.rb', line 181

def ensure_event_queue!
  @stream_event_queue ||= Queue.new
end

#ex_follow_toggleObject



112
113
114
115
116
117
118
119
120
121
122
# File 'lib/ruvim/stream_mixer.rb', line 112

def ex_follow_toggle
  buf = @editor.current_buffer
  raise RuVim::CommandError, "No file associated with buffer" unless buf.path

  if buf.stream.is_a?(Stream::Follow)
    stop_follow!(buf)
  else
    raise RuVim::CommandError, "Buffer has unsaved changes" if buf.modified?
    start_follow!(buf)
  end
end

#follow_active?(buf) ⇒ Boolean

Returns:

  • (Boolean)


162
163
164
# File 'lib/ruvim/stream_mixer.rb', line 162

def follow_active?(buf)
  buf.stream.is_a?(Stream::Follow)
end

#open_path_with_large_file_support(path) ⇒ Object



166
167
168
169
170
171
# File 'lib/ruvim/stream_mixer.rb', line 166

def open_path_with_large_file_support(path)
  return @editor.open_path_sync(path) unless should_open_path_async?(path)
  return @editor.open_path_sync(path) unless can_start_async_file_load?

  open_path_asynchronously!(path)
end

#prepare_stdin_stream_buffer!(io) ⇒ Object



14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# File 'lib/ruvim/stream_mixer.rb', line 14

def prepare_stdin_stream_buffer!(io)
  buf = @editor.current_buffer
  if buf.intro_buffer?
    @editor.materialize_intro_buffer!
    buf = @editor.current_buffer
  end

  buf.replace_all_lines!([""])
  buf.configure_special!(kind: :stream, name: "[stdin]", readonly: true, modifiable: false)
  buf.modified = false
  buf.options["filetype"] = "text"
  ensure_event_queue!
  move_window_to_stream_end!(@editor.current_window, buf)
  @editor.echo("[stdin] follow")
  @pending_stdin = { buf: buf, io: io }
  buf
end

#shutdown!Object



173
174
175
176
177
178
179
# File 'lib/ruvim/stream_mixer.rb', line 173

def shutdown!
  @editor.buffers.each_value do |buf|
    buf.stream&.stop!
  rescue StandardError
    nil
  end
end

#start_command_stream!(buf, command) ⇒ Object



45
46
47
48
49
50
51
# File 'lib/ruvim/stream_mixer.rb', line 45

def start_command_stream!(buf, command)
  ensure_event_queue!
  buf.stream = Stream::Run.new(
    command: command, buffer_id: buf.id, queue: @stream_event_queue,
    stop_handler: -> { stop_buffer_stream!(buf) }, &method(:notify_signal_wakeup)
  )
end

#start_follow!(buf) ⇒ Object



124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
# File 'lib/ruvim/stream_mixer.rb', line 124

def start_follow!(buf)
  ensure_event_queue!
  Buffer.ensure_regular_file!(buf.path) if buf.path

  if buf.path && File.file?(buf.path)
    data = File.binread(buf.path)
    if data.end_with?("\n") && buf.lines.last.to_s != ""
      following_wins = @editor.windows.values.select do |w|
        w.buffer_id == buf.id && stream_window_following_end?(w, buf)
      end
      buf.append_stream_text!("\n")
      following_wins.each { |w| move_window_to_stream_end!(w, buf) }
    end
  end

  buf.stream = Stream::Follow.new(
    path: buf.path, buffer_id: buf.id, queue: @stream_event_queue,
    stop_handler: -> { stop_follow!(buf) }, &method(:notify_signal_wakeup)
  )
  @editor.echo("[follow] #{buf.display_name}")
end

#start_git_stream_command(buffer_id, cmd, root) ⇒ Object



53
54
55
56
57
58
59
# File 'lib/ruvim/stream_mixer.rb', line 53

def start_git_stream_command(buffer_id, cmd, root)
  ensure_event_queue!
  buf = @editor.buffers[buffer_id]
  return unless buf

  buf.stream = Stream::Git.new(cmd: cmd, root: root, buffer_id: buffer_id, queue: @stream_event_queue, &method(:notify_signal_wakeup))
end

#start_pending_stdin!Object



32
33
34
35
36
37
38
39
40
41
42
43
# File 'lib/ruvim/stream_mixer.rb', line 32

def start_pending_stdin!
  return unless @pending_stdin

  ps = @pending_stdin
  @pending_stdin = nil
  buf = ps[:buf]
  ensure_event_queue!
  buf.stream = Stream::Stdin.new(
    io: ps[:io], buffer_id: buf.id, queue: @stream_event_queue,
    stop_handler: -> { stop_buffer_stream!(buf) }, &method(:notify_signal_wakeup)
  )
end

#stop_buffer_stream!(buf) ⇒ Object



61
62
63
64
65
66
67
68
# File 'lib/ruvim/stream_mixer.rb', line 61

def stop_buffer_stream!(buf)
  return false unless buf&.stream&.live?

  buf.stream.stop!
  @editor.echo("#{buf.display_name} stopped")
  notify_signal_wakeup
  true
end

#stop_follow!(buf) ⇒ Object



146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
# File 'lib/ruvim/stream_mixer.rb', line 146

def stop_follow!(buf)
  buf.stream&.stop!
  # Remove trailing empty line added as sentinel by start_follow!
  if buf.line_count > 1 && buf.lines.last.to_s == ""
    buf.lines.pop
    last = buf.line_count - 1
    @editor.windows.each_value do |win|
      next unless win.buffer_id == buf.id
      win.cursor_y = last if win.cursor_y > last
    end
  end
  buf.stream = nil
  @editor.echo("[follow] stopped")
  true
end

#stop_git_stream!(buffer_id) ⇒ Object



70
71
72
73
# File 'lib/ruvim/stream_mixer.rb', line 70

def stop_git_stream!(buffer_id)
  buf = @editor.buffers[buffer_id]
  buf&.stream&.stop!
end