mbeditor 0.10.0 → 0.10.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1fe258515a6f0945da8bfcbfbe85197262af224c054924367597d97d370530e2
4
- data.tar.gz: d3f65b3c351fef56291170d9401103d9b73d4b3a7ebad24885713b8deef5df85
3
+ metadata.gz: 47a8199a082957d396dce3cfa9de010ece11a11b2b938cf0cf81adfa6dd171f1
4
+ data.tar.gz: 30dfee949bc6d06245c6bd1ba5b682431b36b064e0ad510b0dac44b320c5a287
5
5
  SHA512:
6
- metadata.gz: 4de0d8f9374a370f4df1339166ff648013dafb07b23eeba6936f3ebf1d492c831905d7143d5ece2ece4e397f4f4b314b7461e496cec00003c507db5701347604
7
- data.tar.gz: 17c4ef992175fc0a5fd8f18102214ac46cce75963ba7d06661f3ffac66eb7e7d55e69bef6c6f71e3ddb9999839c78fd9a2668abb99397442ac7bfb68da6ee561
6
+ metadata.gz: 81c342ce0dc3f80c44259ae3a130710013dcb81f45052d4cc4488d2a7dc112f7decc6b5d67f3f6aaa81cbcd7ef391d82136ee1adccdd369b369c7cfcd4d6afa7
7
+ data.tar.gz: b201dda9b9fe55e9f44981725788b1587bc052f940652e8eb7d86a8dcc3d17c45bb0984f71cd3bbbd9f3a001b3b0125ac09a9e53eedd3e580eab96a445be6a17
data/CHANGELOG.md CHANGED
@@ -5,6 +5,43 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.10.1] - 2026-07-27
9
+
10
+ ### Removed
11
+ - **The `listen`-based file watcher, and with it `config.watch_files`.**
12
+ 0.10.0 enabled a workspace watcher by default. On Linux each watched
13
+ directory costs an inotify watch from `fs.inotify.max_user_watches`, a
14
+ *per-user* budget shared with everything else watching files — including the
15
+ host app's own code reloader and any other gem using `listen`. Exhausting it
16
+ raises `iNotify max watches exceeded`, and because `listen` reports some of
17
+ those failures from its own background thread, mbeditor could not even
18
+ rescue them. Claiming a share of a scarce OS resource by default was the
19
+ wrong trade for a development tool, and raising the limit needs root, which
20
+ a developer may not have.
21
+
22
+ Nothing is lost: external changes are picked up by polling, which is how the
23
+ editor already tracked git state. If you set `config.watch_files`, remove it
24
+ — it is now ignored.
25
+
26
+ ### Fixed
27
+ - **The file tree never refreshed for changes made outside the editor.** Its
28
+ 10-second poll returned early whenever the Action Cable socket was connected,
29
+ on the reasoning that the push covered it — but the server only broadcasts
30
+ from mbeditor's own mutation endpoints. With a socket connected, which is the
31
+ normal case, an external `git checkout` or generator run was never picked up.
32
+ The poll now always runs; the push remains the instant path for our own
33
+ writes. This is the bug the 0.10.0 watcher was compensating for.
34
+ - **Git line-number tinting went stale for external changes**, for the same
35
+ reason, and its refresh timer was being cleared and recreated on every
36
+ re-render so it never survived long enough to fire. It now polls on its own
37
+ timer and on window focus, matching the file tree.
38
+ - **Watched paths were dropped when the workspace was reached through a
39
+ symlink** (macOS `/var` → `/private/var`, or a symlinked checkout), because
40
+ reported paths resolve to the real path and no longer matched the configured
41
+ root.
42
+
43
+ ---
44
+
8
45
  ## [0.10.0] - 2026-07-27
9
46
 
10
47
  ### Added
data/README.md CHANGED
@@ -16,7 +16,6 @@ Mbeditor (Mini Browser Editor) is a mountable Rails engine that adds a browser-b
16
16
  - Optional RuboCop lint and format endpoints (uses host app RuboCop)
17
17
  - Optional Ruby language-server integration (definitions, hover, completion, diagnostics)
18
18
  - Optional test runner with inline failure markers and a dedicated results panel (Minitest and RSpec)
19
- - Optional workspace file watching, so changes made outside the editor refresh the tree and git decorations
20
19
 
21
20
  ## Security Warning
22
21
  Mbeditor exposes read and write access to your Rails application directory over HTTP. It is intended only for local development.
@@ -101,7 +100,6 @@ end
101
100
  | `ruby_lsp` | `:auto` | Use the host's [ruby-lsp](https://github.com/Shopify/ruby-lsp) for Ruby go-to-definition, hover, completion, and diagnostics when it's installed (a persistent process is managed per workspace). `false` disables. Without ruby-lsp everything degrades to the built-in grep/Ripper services — no behavior change. |
102
101
  | `ruby_lsp_command` | `nil` | Override the ruby-lsp launch command (String or Array). `nil` auto-resolves `bin/ruby-lsp` → installed gem → `bundle exec ruby-lsp`. |
103
102
  | `ruby_lsp_timeout` | `3` | Seconds per LSP request; on timeout (e.g. during initial indexing) the editor falls back to the built-in services for that request. |
104
- | `watch_files` | `:auto` | Watch the workspace for changes made outside the editor (a terminal `git checkout`, a generator, another editor) and push a refresh to open clients. Requires the host's [`listen`](https://github.com/guard/listen) gem; without it the editor behaves as before and only announces its own writes. `false` disables. |
105
103
 
106
104
  ### Authentication
107
105
 
@@ -205,113 +203,9 @@ The gem keeps host/tooling responsibilities in the host app:
205
203
  - `minitest` or `rspec` in the host app's bundle (required for the test runner)
206
204
  - `actioncable` framework/gem (optional, required only for realtime file-change push + websocket state saves)
207
205
  - `ruby-lsp` gem (optional — see below)
208
- - `listen` gem (optional — see below)
209
206
 
210
207
  All lint and test tools are auto-detected at runtime. The engine gracefully disables features if the tools are not available. Neither `rubocop`, `haml_lint`, nor any test framework are runtime dependencies of the gem itself — they are discovered from the host app's environment.
211
208
 
212
- ### Workspace file watching (Optional)
213
-
214
- Add [`listen`](https://github.com/guard/listen) to the host app's development
215
- group and mbeditor watches the workspace for changes it did not make itself:
216
-
217
- ```ruby
218
- group :development do
219
- gem 'listen'
220
- end
221
- ```
222
-
223
- Without it, only writes made *through* the editor announce themselves, so a
224
- `git checkout` or a generator run in a terminal leaves the file tree and the
225
- git line-number colours stale until you reopen the file. With it, those refresh
226
- on their own.
227
-
228
- The watcher runs only in `allowed_environments` and only in processes that
229
- serve requests, so rake tasks and consoles never start one. It respects
230
- `excluded_paths`, coalesces bursts (a branch switch is one refresh, not
231
- hundreds), and requires Action Cable to actually deliver the push. If it cannot
232
- start — inotify limits, permissions — it logs a warning and the editor carries
233
- on exactly as it would without the gem. Set `config.watch_files = false` to
234
- disable it even when `listen` is present.
235
-
236
- ### Ruby language server (Optional)
237
-
238
- Add [ruby-lsp](https://github.com/Shopify/ruby-lsp) to the host app's
239
- development group and mbeditor uses it automatically for Ruby
240
- go-to-definition, hover, completion, and diagnostics:
241
-
242
- ```ruby
243
- gem "ruby-lsp", require: false, group: :development
244
- gem "ruby-lsp-rails", require: false, group: :development # Rails-aware results
245
- ```
246
-
247
- `ruby-lsp-rails` needs no mbeditor configuration — ruby-lsp loads it as an
248
- addon, so associations, model attributes, and route helpers start resolving on
249
- their own.
250
-
251
- What changes when it's present:
252
-
253
- - **Diagnostics.** Ruby files are checked by ruby-lsp instead of booting
254
- RuboCop over HTTP on every debounce, so you also get Prism syntax errors and
255
- warnings alongside RuboCop offenses. Quick-fix lightbulbs still work for
256
- correctable cops. Very large files fall back to syntax-only diagnostics.
257
- - **Definitions, hover, completion.** Answered from the language server's index
258
- rather than a workspace grep, including your unsaved buffer contents.
259
-
260
- Everything degrades on its own: if ruby-lsp is missing, times out (its first
261
- index of a large app takes a while), or crashes, that request falls back to the
262
- built-in grep/Ripper services. ERB templates always use the built-in services —
263
- ruby-lsp cannot parse ERB.
264
-
265
- ### Realtime via Action Cable (Optional)
266
-
267
- Mbeditor works without Action Cable. If Action Cable is unavailable, unreachable, or returns transient errors, the editor automatically falls back to polling.
268
-
269
- To enable realtime features in a host app:
270
-
271
- 1. Ensure Action Cable is enabled in the host app (for apps that do not load it by default, add the framework/gem explicitly).
272
- 2. Mount cable in host routes:
273
-
274
- ```ruby
275
- mount ActionCable.server => '/cable'
276
- ```
277
-
278
- 3. Make Action Cable JavaScript available to the page (for asset-pipeline apps, `actioncable.js` is typically sufficient).
279
-
280
- If any of these are missing, mbeditor still runs in polling mode.
281
-
282
- ### Syntax Highlighting Support
283
- Monaco runtime assets are served from the engine route namespace (`/mbeditor/monaco-editor/*` and `/mbeditor/monaco_worker.js`).
284
- The gem includes syntax highlighting for common Rails and React development file types:
285
-
286
- **Web & Template Languages:**
287
- - **Ruby** (.rb, Gemfile, gemspec, Rakefile)
288
- - **HTML**
289
- - **ERB** (.html.erb, .erb) — dedicated ERB grammar, plus Ruby intellisense
290
- inside `<% %>` tags: hover, completion, go-to-definition (Ctrl/Cmd+click or
291
- F12) and auto-`end`, all inert in the surrounding HTML. ERB uses the built-in
292
- workspace services rather than ruby-lsp, which cannot parse ERB.
293
- - **HAML** (.haml) — plaintext syntax highlighting (no dedicated HAML grammar in Monaco; haml-lint provides inline error markers when available)
294
- - **CSS** and **SCSS** stylesheets
295
-
296
- **JavaScript & React:**
297
- - **JavaScript / JSX** (.js, .jsx, .js.jsx) — Monaco's TypeScript worker runs in
298
- checked-JS mode with JSX enabled. Built for the Sprockets world where every
299
- top-level `var`/`function`/`class` (and `window.X =` assignment) is a global:
300
- the editor scans the workspace once at boot (`GET /js_globals`) and declares
301
- all of them as ambient globals, so cross-file component references need no
302
- `import` and produce no "Cannot find name" diagnostics. The list refreshes
303
- automatically when files change. Runtime-only globals the static scan can't
304
- see (e.g. `Routes`, `I18n`) can be declared via
305
- `config.js_global_identifiers = %w[Routes I18n]`.
306
- Known limits: everything is typed `any` (no cross-file type inference), and
307
- genuinely undefined names are shown as warnings, not errors.
308
- - **TypeScript** (.ts, .tsx)
309
-
310
- **Configuration & Documentation:**
311
- - **YAML** (.yml, .yaml)
312
- - **Markdown** (.md)
313
-
314
- These language modules are packaged locally with the gem for true offline operation. No network fallback is needed—all highlighting works without internet connectivity.
315
209
 
316
210
  ## Asset Pipeline
317
211
 
@@ -70,6 +70,9 @@ var EditorPanel = function EditorPanel(_ref) {
70
70
 
71
71
  var blameDecorationsRef = useRef([]);
72
72
  var gitLineDecorationsRef = useRef([]);
73
+ // Latest git line-diff refresh, read by the poll effect so its interval does
74
+ // not have to be torn down whenever the active tab changes.
75
+ var gitLineRefreshRef = useRef(null);
73
76
  var blameZoneIdsRef = useRef([]);
74
77
  var testDecorationIdsRef = useRef([]);
75
78
  var testZoneIdsRef = useRef([]);
@@ -1290,7 +1293,14 @@ var EditorPanel = function EditorPanel(_ref) {
1290
1293
  // The ranges come from `git diff -U0` against HEAD, so they describe the file
1291
1294
  // as last written to disk. Monaco anchors decorations to the model and shifts
1292
1295
  // them as you type, which keeps them roughly right mid-edit; the authoritative
1293
- // refresh happens when the file lands on disk and the server broadcasts it.
1296
+ // refresh happens on the signals wired up at the end of this effect.
1297
+
1298
+ // Poll cadence for the tint, matching the file tree's. Slower to react than a
1299
+ // filesystem watcher would be, at the cost of one `git diff -U0` on one file
1300
+ // per visible pane — against a resource nothing else is competing for, rather
1301
+ // than a share of the kernel's inotify budget.
1302
+ var GIT_LINE_POLL_MS = 10000;
1303
+
1294
1304
  useEffect(function () {
1295
1305
  if (!gitAvailable || !tab.path || tab.isDiff || tab.isCombinedDiff) return;
1296
1306
 
@@ -1347,17 +1357,45 @@ var EditorPanel = function EditorPanel(_ref) {
1347
1357
 
1348
1358
  refresh();
1349
1359
 
1360
+ // Publish the current refresh for the poll below, which lives in its own
1361
+ // effect so a re-render of this one cannot restart its clock.
1362
+ gitLineRefreshRef.current = refresh;
1363
+
1364
+ // Immediate path for writes mbeditor made itself.
1350
1365
  var hasSocket = typeof WebSocketService !== 'undefined' && WebSocketService.onFilesChanged;
1351
1366
  var onChanged = hasSocket ? function () { refresh(); } : null;
1352
1367
  if (onChanged) WebSocketService.onFilesChanged(onChanged);
1353
1368
 
1354
1369
  return function () {
1355
1370
  cancelled = true;
1371
+ gitLineRefreshRef.current = null;
1356
1372
  if (onChanged && WebSocketService.offFilesChanged) WebSocketService.offFilesChanged(onChanged);
1357
1373
  clear();
1358
1374
  };
1359
1375
  }, [tab.path, tab.externalContentVersion, tab.isDiff, tab.isCombinedDiff, gitAvailable]);
1360
1376
 
1377
+ // The poll for changes made outside the editor — a terminal commit or branch
1378
+ // switch alters the diff without touching this buffer.
1379
+ //
1380
+ // Deliberately its own effect with no dependencies. Held inside the effect
1381
+ // above, the interval was cleared and recreated on every re-render of that
1382
+ // one and never survived long enough to fire, so the tint only ever updated
1383
+ // via the WebSocket — i.e. never for external changes, which is the whole
1384
+ // point of it. Reading the refresh through a ref keeps this clock running
1385
+ // across tab switches.
1386
+ useEffect(function () {
1387
+ var tick = function () {
1388
+ if (document.hidden) return;
1389
+ if (gitLineRefreshRef.current) gitLineRefreshRef.current();
1390
+ };
1391
+ var intervalId = setInterval(tick, GIT_LINE_POLL_MS);
1392
+ window.addEventListener('focus', tick);
1393
+ return function () {
1394
+ clearInterval(intervalId);
1395
+ window.removeEventListener('focus', tick);
1396
+ };
1397
+ }, []);
1398
+
1361
1399
  // Handle Blame data fetching
1362
1400
  useEffect(function () {
1363
1401
  if (!isBlameVisible) {
@@ -1498,14 +1498,18 @@ var MbeditorApp = function MbeditorApp() {
1498
1498
  });
1499
1499
  }
1500
1500
 
1501
- // Auto-refresh the file tree every 10s to pick up external changes (new files, deletions, etc.)
1502
- // When an ActionCable WebSocket is connected this acts only as a safety-net fallback —
1503
- // the WebSocket push above handles immediate invalidation after mbeditor mutations.
1501
+ // Auto-refresh the file tree every 10s to pick up external changes (new files,
1502
+ // deletions, a branch switch in a terminal).
1503
+ //
1504
+ // This runs whether or not the WebSocket is connected. It used to skip when
1505
+ // connected, on the reasoning that the push covered it — but the server only
1506
+ // broadcasts from mbeditor's own mutation endpoints, so a connected socket
1507
+ // meant external changes were never picked up at all. The push remains the
1508
+ // instant path for our own writes; this is what catches everything else.
1504
1509
  // Uses functional setTreeData to skip the re-render when nothing has changed.
1505
1510
  useEffect(function () {
1506
1511
  var intervalId = setInterval(function () {
1507
1512
  if (document.hidden) return;
1508
- if (WebSocketService.isConnected()) return; // WebSocket is handling refreshes
1509
1513
  FileService.getTree().then(function (data) {
1510
1514
  var newData = data || [];
1511
1515
  setTreeData(function (prevData) {
@@ -11,7 +11,7 @@ module Mbeditor
11
11
  :mount_path, :resilient_routing, :js_global_identifiers,
12
12
  :js_syntax_check, :babel_standalone_path,
13
13
  :ruby_lsp, :ruby_lsp_command, :ruby_lsp_timeout,
14
- :search_respect_gitignore, :watch_files
14
+ :search_respect_gitignore
15
15
 
16
16
  def initialize
17
17
  @allowed_environments = [:development]
@@ -42,7 +42,6 @@ module Mbeditor
42
42
  @ruby_lsp_timeout = 3 # seconds per LSP request before falling back to the built-in services
43
43
  @mount_path = nil # explicit URL prefix override; nil falls through to detection/"/mbeditor"
44
44
  @resilient_routing = true # serve /mbeditor from middleware so the editor survives a broken host routes.rb; false is the escape hatch
45
- @watch_files = :auto # watch the workspace for changes made outside the editor when the host has the `listen` gem; false disables
46
45
  end
47
46
  end
48
47
  end
@@ -4,7 +4,6 @@ require "mbeditor/rack/silence_ping_request"
4
4
  require "mbeditor/rack/handle_pending_migrations"
5
5
  require "mbeditor/rack/resilient_router"
6
6
  require "mbeditor/cable_log_filter"
7
- require "mbeditor/file_watcher"
8
7
 
9
8
  module Mbeditor
10
9
  class Engine < ::Rails::Engine
@@ -82,8 +81,6 @@ module Mbeditor
82
81
  raise ArgumentError, "[mbeditor] config.workspace_root is set to '#{cfg.workspace_root}' but that path is not a directory"
83
82
  end
84
83
 
85
- Mbeditor::FileWatcher.start_if_enabled
86
-
87
84
  if cfg.redmine_enabled
88
85
  require "uri"
89
86
  if cfg.redmine_url.blank?
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Mbeditor
4
- VERSION = "0.10.0"
4
+ VERSION = "0.10.1"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mbeditor
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.10.0
4
+ version: 0.10.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Oliver Noonan
@@ -136,7 +136,6 @@ files:
136
136
  - lib/mbeditor/configuration.rb
137
137
  - lib/mbeditor/editor_bootstrap.rb
138
138
  - lib/mbeditor/engine.rb
139
- - lib/mbeditor/file_watcher.rb
140
139
  - lib/mbeditor/mount_path.rb
141
140
  - lib/mbeditor/private_routes.rb
142
141
  - lib/mbeditor/rack/handle_pending_migrations.rb
@@ -1,136 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Mbeditor
4
- # Watches the workspace for changes made outside the editor — a terminal
5
- # `git checkout`, a rebase, another editor, a generator — and broadcasts the
6
- # same `files_changed` payload the mutation endpoints send. Clients refresh
7
- # the file tree, git line-number tinting and cached globals from it.
8
- #
9
- # The `listen` gem is an optional host dependency. Without it the editor
10
- # behaves exactly as before: only changes made *through* mbeditor announce
11
- # themselves. Nothing warns loudly about the missing gem — it is opt-in.
12
- #
13
- # Only one watcher runs per process. It is deliberately not started in test
14
- # or in non-server processes (rake, console, the Rails runner), where a
15
- # background listener thread is pure overhead.
16
- module FileWatcher
17
- # Coalesce bursts: a branch switch touches hundreds of files, and each one
18
- # would otherwise be its own broadcast.
19
- DEBOUNCE_SECONDS = 0.3
20
-
21
- class << self
22
- def available?
23
- return @available if defined?(@available)
24
-
25
- @available = begin
26
- require "listen"
27
- true
28
- rescue LoadError
29
- false
30
- end
31
- end
32
-
33
- def running?
34
- !@listener.nil?
35
- end
36
-
37
- # Boot entry point. Confined to the environments the editor is allowed in
38
- # and to processes that actually serve requests — a rake task or console
39
- # has no client to broadcast to, and a listener thread there would only
40
- # burn file handles. MBEDITOR_FORCE_WATCH overrides the process check for
41
- # unusual servers and for tests.
42
- def start_if_enabled
43
- cfg = Mbeditor.configuration
44
- return false if cfg.watch_files == false
45
- return false unless cfg.allowed_environments.map(&:to_s).include?(Rails.env.to_s)
46
- return false unless serving_requests?
47
-
48
- start(cfg.workspace_root.presence || Rails.root.to_s)
49
- end
50
-
51
- # Returns true when a watcher was started, false for every reason not to
52
- # (gem absent, already running, no workspace, disabled by config).
53
- def start(root)
54
- return false unless available?
55
- return false if running?
56
-
57
- root = root.to_s
58
- return false if root.empty? || !File.directory?(root)
59
-
60
- ignores = ignore_patterns(root)
61
- @listener = ::Listen.to(root, ignore: ignores, latency: DEBOUNCE_SECONDS) do |modified, added, removed|
62
- broadcast(root, modified + added + removed)
63
- end
64
- @listener.start
65
- Rails.logger.info("[mbeditor] watching #{root} for external changes")
66
- true
67
- rescue StandardError => e
68
- # A watcher that cannot start must never take the host app down with it:
69
- # inotify limits on Linux, permission issues, an unreadable root.
70
- Rails.logger.warn("[mbeditor] file watcher failed to start: #{e.class}: #{e.message}")
71
- @listener = nil
72
- false
73
- end
74
-
75
- def stop
76
- @listener&.stop
77
- rescue StandardError
78
- nil
79
- ensure
80
- @listener = nil
81
- end
82
-
83
- private
84
-
85
- def serving_requests?
86
- return true if ENV["MBEDITOR_FORCE_WATCH"]
87
-
88
- defined?(Rails::Server) || defined?(Puma::Server) || defined?(Unicorn) || defined?(Passenger)
89
- end
90
-
91
- # `listen` matches ignores against paths relative to the watched root, so
92
- # the configured exclusions become anchored regexps. Escaping matters:
93
- # entries like "vendor/bundle" and "public/assets" contain separators, and
94
- # a stray metacharacter in host config should not build a bogus pattern.
95
- def ignore_patterns(root)
96
- Array(Mbeditor.configuration.excluded_paths).map(&:to_s).reject(&:empty?).map do |path|
97
- %r{\A#{Regexp.escape(path.delete_prefix("/").delete_suffix("/"))}(/|\z)}
98
- end
99
- end
100
-
101
- # Paths arrive absolute. Anything that does not sit under the workspace
102
- # is dropped rather than sent raw: the client keys everything by
103
- # workspace-relative path, and an absolute one would leak host layout.
104
- def relative_paths(root, paths)
105
- paths.filter_map do |path|
106
- rel = path.to_s.delete_prefix("#{root}/")
107
- rel unless rel.empty? || rel == path.to_s
108
- end
109
- end
110
-
111
- def broadcast(root, paths)
112
- relative = relative_paths(root, paths)
113
-
114
- invalidate_caches(root)
115
- return unless defined?(ActionCable.server)
116
-
117
- payload = { type: "files_changed" }
118
- payload[:paths] = relative.first(200) if relative.any?
119
- ActionCable.server.broadcast("mbeditor_editor", payload)
120
- rescue StandardError => e
121
- Rails.logger.warn("[mbeditor] file watcher broadcast failed: #{e.class}: #{e.message}")
122
- end
123
-
124
- # Mirrors EditorsController#broadcast_files_changed: a change the editor
125
- # did not make invalidates exactly the same caches as one it did.
126
- def invalidate_caches(root)
127
- FileTreeService.invalidate(root)
128
- SearchReplaceService.invalidate_cache(root)
129
- JsGlobalsService.invalidate(root)
130
- GitInfoService.invalidate(root)
131
- rescue StandardError => e
132
- Rails.logger.warn("[mbeditor] file watcher cache invalidation failed: #{e.class}: #{e.message}")
133
- end
134
- end
135
- end
136
- end