Module: Mbeditor::FileWatcher

Defined in:
lib/mbeditor/file_watcher.rb

Overview

Watches the workspace for changes made outside the editor — a terminal git checkout, a rebase, another editor, a generator — and broadcasts the same files_changed payload the mutation endpoints send. Clients refresh the file tree, git line-number tinting and cached globals from it.

The listen gem is an optional host dependency. Without it the editor behaves exactly as before: only changes made through mbeditor announce themselves. Nothing warns loudly about the missing gem — it is opt-in.

Only one watcher runs per process. It is deliberately not started in test or in non-server processes (rake, console, the Rails runner), where a background listener thread is pure overhead.

Constant Summary collapse

DEBOUNCE_SECONDS =

Coalesce bursts: a branch switch touches hundreds of files, and each one would otherwise be its own broadcast.

0.3

Class Method Summary collapse

Class Method Details

.available?Boolean

Returns:

  • (Boolean)


22
23
24
25
26
27
28
29
30
31
# File 'lib/mbeditor/file_watcher.rb', line 22

def available?
  return @available if defined?(@available)

  @available = begin
    require "listen"
    true
  rescue LoadError
    false
  end
end

.running?Boolean

Returns:

  • (Boolean)


33
34
35
# File 'lib/mbeditor/file_watcher.rb', line 33

def running?
  !@listener.nil?
end

.start(root) ⇒ Object

Returns true when a watcher was started, false for every reason not to (gem absent, already running, no workspace, disabled by config).



53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
# File 'lib/mbeditor/file_watcher.rb', line 53

def start(root)
  return false unless available?
  return false if running?

  root = root.to_s
  return false if root.empty? || !File.directory?(root)

  ignores = ignore_patterns(root)
  @listener = ::Listen.to(root, ignore: ignores, latency: DEBOUNCE_SECONDS) do |modified, added, removed|
    broadcast(root, modified + added + removed)
  end
  @listener.start
  Rails.logger.info("[mbeditor] watching #{root} for external changes")
  true
rescue StandardError => e
  # A watcher that cannot start must never take the host app down with it:
  # inotify limits on Linux, permission issues, an unreadable root.
  Rails.logger.warn("[mbeditor] file watcher failed to start: #{e.class}: #{e.message}")
  @listener = nil
  false
end

.start_if_enabledObject

Boot entry point. Confined to the environments the editor is allowed in and to processes that actually serve requests — a rake task or console has no client to broadcast to, and a listener thread there would only burn file handles. MBEDITOR_FORCE_WATCH overrides the process check for unusual servers and for tests.



42
43
44
45
46
47
48
49
# File 'lib/mbeditor/file_watcher.rb', line 42

def start_if_enabled
  cfg = Mbeditor.configuration
  return false if cfg.watch_files == false
  return false unless cfg.allowed_environments.map(&:to_s).include?(Rails.env.to_s)
  return false unless serving_requests?

  start(cfg.workspace_root.presence || Rails.root.to_s)
end

.stopObject



75
76
77
78
79
80
81
# File 'lib/mbeditor/file_watcher.rb', line 75

def stop
  @listener&.stop
rescue StandardError
  nil
ensure
  @listener = nil
end