mbeditor 0.5.1 → 0.5.2
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 +4 -4
- data/app/assets/javascripts/mbeditor/components/FileTree.js +8 -1
- data/app/assets/javascripts/mbeditor/editor_plugins.js +72 -32
- data/app/controllers/mbeditor/editors_controller.rb +14 -5
- data/app/services/mbeditor/ruby_definition_service.rb +84 -64
- data/lib/mbeditor/configuration.rb +4 -2
- data/lib/mbeditor/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: cd0837fbeccc3634d7804e6f9957bbbaca06bb4c02942eb4decc5e8b174499ab
|
|
4
|
+
data.tar.gz: 9f97239369b2968c17cd3909abdced4917329a349564d02ebde6beb89cb8c36f
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 6310dc010236970bf25c0945a465122e1f8a081c399696d8870dc852d85454153863b01b1f1e9a8871abc3fbe07b1489724a065835c2909daa15426899701833
|
|
7
|
+
data.tar.gz: f8ed953601769d6785a4fecd4c3b16a6ce820e6e7f5c88a7cb12750d8f32b5122dc6bc25fd67be5e86bd7ef02ae1beba9ece2dbc5fc5bc0324df47785a03c9ba
|
|
@@ -12,6 +12,13 @@ var useRef = _React.useRef;
|
|
|
12
12
|
var useEffect = _React.useEffect;
|
|
13
13
|
var useMemo = _React.useMemo;
|
|
14
14
|
|
|
15
|
+
function formatSize(bytes) {
|
|
16
|
+
if (typeof bytes !== 'number' || bytes < 0) return '';
|
|
17
|
+
if (bytes < 1024) return bytes + ' B';
|
|
18
|
+
if (bytes < 1024 * 1024) return (bytes / 1024).toFixed(1) + ' KB';
|
|
19
|
+
return (bytes / (1024 * 1024)).toFixed(1) + ' MB';
|
|
20
|
+
}
|
|
21
|
+
|
|
15
22
|
var FileTree = function FileTree(_ref) {
|
|
16
23
|
var items = _ref.items;
|
|
17
24
|
var onSelect = _ref.onSelect;
|
|
@@ -477,7 +484,7 @@ var FileTree = function FileTree(_ref) {
|
|
|
477
484
|
),
|
|
478
485
|
React.createElement(
|
|
479
486
|
'div',
|
|
480
|
-
{ className: 'tree-item-name', title: node.path },
|
|
487
|
+
{ className: 'tree-item-name', title: node.type === 'file' && node.size != null ? node.path + ' — ' + formatSize(node.size) : node.path },
|
|
481
488
|
node.name
|
|
482
489
|
),
|
|
483
490
|
statusMeta && React.createElement(
|
|
@@ -51,12 +51,16 @@
|
|
|
51
51
|
|
|
52
52
|
var globalsRegistered = false;
|
|
53
53
|
|
|
54
|
-
// Enumerate window for user-defined
|
|
55
|
-
//
|
|
56
|
-
//
|
|
57
|
-
//
|
|
54
|
+
// Enumerate window for user-defined globals and return a TypeScript declaration string.
|
|
55
|
+
// Sprockets exposes every top-level var/function as a window property before Monaco
|
|
56
|
+
// initialises, so scanning at registration time captures all components and helpers.
|
|
57
|
+
//
|
|
58
|
+
// Filter: keep only plain writable data properties (configurable, writable, no getter).
|
|
59
|
+
// Browser built-ins are either non-configurable or accessor properties (hasGet), so
|
|
60
|
+
// this reliably separates them from user-assigned globals without a native-code test
|
|
61
|
+
// (which only works for functions, not objects like `document` or `location`).
|
|
58
62
|
function buildWindowGlobalsShim() {
|
|
59
|
-
var alreadyDeclared = { React: 1, ReactDOM: 1, PropTypes: 1, MaterialUI: 1 };
|
|
63
|
+
var alreadyDeclared = { React: 1, ReactDOM: 1, PropTypes: 1, MaterialUI: 1, $: 1, jQuery: 1 };
|
|
60
64
|
var lines = [];
|
|
61
65
|
try {
|
|
62
66
|
var keys = Object.keys(window);
|
|
@@ -64,14 +68,12 @@
|
|
|
64
68
|
var key = keys[i];
|
|
65
69
|
if (alreadyDeclared[key]) continue;
|
|
66
70
|
if (!/^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(key)) continue;
|
|
71
|
+
var desc;
|
|
72
|
+
try { desc = Object.getOwnPropertyDescriptor(window, key); } catch (e) { continue; }
|
|
73
|
+
if (!desc || !desc.configurable || !desc.writable || desc.get) continue;
|
|
67
74
|
var value;
|
|
68
75
|
try { value = window[key]; } catch (e) { continue; }
|
|
69
76
|
if (value === null || value === undefined) continue;
|
|
70
|
-
if (typeof value === 'function') {
|
|
71
|
-
try {
|
|
72
|
-
if (/\[native code\]/.test(Function.prototype.toString.call(value))) continue;
|
|
73
|
-
} catch (e) { continue; }
|
|
74
|
-
}
|
|
75
77
|
lines.push('declare var ' + key + ': any;');
|
|
76
78
|
}
|
|
77
79
|
} catch (e) {}
|
|
@@ -322,6 +324,30 @@
|
|
|
322
324
|
event.stopPropagation();
|
|
323
325
|
});
|
|
324
326
|
|
|
327
|
+
// Navigate to a Ruby symbol: modules/classes go to their definition file,
|
|
328
|
+
// lowercase symbols go to their def line.
|
|
329
|
+
function navigateToWord(word) {
|
|
330
|
+
if (/^[A-Z]/.test(word) && typeof FileService !== 'undefined' && FileService.getModuleMembers) {
|
|
331
|
+
FileService.getModuleMembers(word).then(function(data) {
|
|
332
|
+
if (!data || !data.file) return;
|
|
333
|
+
var filename = data.file.split('/').pop();
|
|
334
|
+
if (typeof TabManager !== 'undefined' && TabManager.openTab) {
|
|
335
|
+
TabManager.openTab(data.file, filename, 1);
|
|
336
|
+
}
|
|
337
|
+
}).catch(function() {});
|
|
338
|
+
return;
|
|
339
|
+
}
|
|
340
|
+
if (typeof FileService === 'undefined' || !FileService.getDefinition) return;
|
|
341
|
+
FileService.getDefinition(word, 'ruby').then(function(data) {
|
|
342
|
+
var results = data && Array.isArray(data.results) ? data.results : [];
|
|
343
|
+
if (results.length === 0) return;
|
|
344
|
+
var r = results[0];
|
|
345
|
+
if (typeof TabManager !== 'undefined' && TabManager.openTab) {
|
|
346
|
+
TabManager.openTab(r.file, r.file.split('/').pop(), r.line);
|
|
347
|
+
}
|
|
348
|
+
}).catch(function() {});
|
|
349
|
+
}
|
|
350
|
+
|
|
325
351
|
// Ctrl/Cmd+click — navigate to definition
|
|
326
352
|
gotoMouseDisposable = editor.onMouseDown(function(event) {
|
|
327
353
|
var ctrlOrCmd = event.event.ctrlKey || event.event.metaKey;
|
|
@@ -336,19 +362,9 @@
|
|
|
336
362
|
if (!wordInfo || !wordInfo.word || wordInfo.word.length < 2) return;
|
|
337
363
|
if (RUBY_KEYWORDS[wordInfo.word]) return;
|
|
338
364
|
if (RUBY_CORE_METHODS[wordInfo.word]) return;
|
|
339
|
-
if (typeof FileService === 'undefined' || !FileService.getDefinition) return;
|
|
340
365
|
|
|
341
366
|
event.event.preventDefault();
|
|
342
|
-
|
|
343
|
-
FileService.getDefinition(wordInfo.word, 'ruby').then(function(data) {
|
|
344
|
-
var results = data && Array.isArray(data.results) ? data.results : [];
|
|
345
|
-
if (results.length === 0) return;
|
|
346
|
-
var r = results[0];
|
|
347
|
-
var filename = r.file.split('/').pop();
|
|
348
|
-
if (typeof TabManager !== 'undefined' && TabManager.openTab) {
|
|
349
|
-
TabManager.openTab(r.file, filename, r.line);
|
|
350
|
-
}
|
|
351
|
-
}).catch(function() {});
|
|
367
|
+
navigateToWord(wordInfo.word);
|
|
352
368
|
});
|
|
353
369
|
|
|
354
370
|
// F12 — go to definition from keyboard
|
|
@@ -365,17 +381,7 @@
|
|
|
365
381
|
if (!wordInfo || !wordInfo.word || wordInfo.word.length < 2) return;
|
|
366
382
|
if (RUBY_KEYWORDS[wordInfo.word]) return;
|
|
367
383
|
if (RUBY_CORE_METHODS[wordInfo.word]) return;
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
FileService.getDefinition(wordInfo.word, 'ruby').then(function(data) {
|
|
371
|
-
var results = data && Array.isArray(data.results) ? data.results : [];
|
|
372
|
-
if (results.length === 0) return;
|
|
373
|
-
var r = results[0];
|
|
374
|
-
var filename = r.file.split('/').pop();
|
|
375
|
-
if (typeof TabManager !== 'undefined' && TabManager.openTab) {
|
|
376
|
-
TabManager.openTab(r.file, filename, r.line);
|
|
377
|
-
}
|
|
378
|
-
}).catch(function() {});
|
|
384
|
+
navigateToWord(wordInfo.word);
|
|
379
385
|
}
|
|
380
386
|
});
|
|
381
387
|
}
|
|
@@ -494,6 +500,8 @@
|
|
|
494
500
|
'declare var ReactDOM: any;',
|
|
495
501
|
'declare var PropTypes: any;',
|
|
496
502
|
'declare var MaterialUI: any;',
|
|
503
|
+
'declare var $: any;',
|
|
504
|
+
'declare var jQuery: any;',
|
|
497
505
|
'interface Window { [key: string]: any; }'
|
|
498
506
|
].join('\n'),
|
|
499
507
|
'inmemory://mbeditor/sprockets-globals.d.ts'
|
|
@@ -506,6 +514,38 @@
|
|
|
506
514
|
'inmemory://mbeditor/window-globals.d.ts'
|
|
507
515
|
);
|
|
508
516
|
}
|
|
517
|
+
|
|
518
|
+
// Downgrade "declared but never read" (TS6133) from Error to Warning.
|
|
519
|
+
// TypeScript has no built-in way to emit this as a warning, so we intercept
|
|
520
|
+
// the marker set after the worker fires and re-apply with lower severity.
|
|
521
|
+
// JS files use owner 'javascript', TS files use 'typescript'.
|
|
522
|
+
var WARN_CODES = { '6133': true };
|
|
523
|
+
var TS_OWNERS = ['javascript', 'typescript'];
|
|
524
|
+
var _severityPatchActive = false;
|
|
525
|
+
monaco.editor.onDidChangeMarkers(function(uris) {
|
|
526
|
+
if (_severityPatchActive) return;
|
|
527
|
+
_severityPatchActive = true;
|
|
528
|
+
try {
|
|
529
|
+
uris.forEach(function(uri) {
|
|
530
|
+
var model = monaco.editor.getModel(uri);
|
|
531
|
+
if (!model) return;
|
|
532
|
+
TS_OWNERS.forEach(function(owner) {
|
|
533
|
+
var markers = monaco.editor.getModelMarkers({ resource: uri, owner: owner });
|
|
534
|
+
var needsPatch = markers.some(function(m) {
|
|
535
|
+
return m.severity === monaco.MarkerSeverity.Error && WARN_CODES[String(m.code)];
|
|
536
|
+
});
|
|
537
|
+
if (!needsPatch) return;
|
|
538
|
+
monaco.editor.setModelMarkers(model, owner, markers.map(function(m) {
|
|
539
|
+
return (m.severity === monaco.MarkerSeverity.Error && WARN_CODES[String(m.code)])
|
|
540
|
+
? Object.assign({}, m, { severity: monaco.MarkerSeverity.Warning })
|
|
541
|
+
: m;
|
|
542
|
+
}));
|
|
543
|
+
});
|
|
544
|
+
});
|
|
545
|
+
} finally {
|
|
546
|
+
_severityPatchActive = false;
|
|
547
|
+
}
|
|
548
|
+
});
|
|
509
549
|
}
|
|
510
550
|
|
|
511
551
|
// TypeScript: enable JSX for .tsx files and catch unused locals.
|
|
@@ -337,7 +337,8 @@ module Mbeditor
|
|
|
337
337
|
workspace_root,
|
|
338
338
|
symbol,
|
|
339
339
|
excluded_dirnames: excluded_dirnames,
|
|
340
|
-
excluded_paths:
|
|
340
|
+
excluded_paths: excluded_paths,
|
|
341
|
+
included_dirs: ruby_def_include_dirs
|
|
341
342
|
)
|
|
342
343
|
ri = RiDefinitionService.call(symbol)
|
|
343
344
|
workspace + ri
|
|
@@ -360,7 +361,8 @@ module Mbeditor
|
|
|
360
361
|
file = RubyDefinitionService.module_defined_in(
|
|
361
362
|
workspace_root, name,
|
|
362
363
|
excluded_dirnames: excluded_dirnames,
|
|
363
|
-
excluded_paths: excluded_paths
|
|
364
|
+
excluded_paths: excluded_paths,
|
|
365
|
+
included_dirs: ruby_def_include_dirs
|
|
364
366
|
)
|
|
365
367
|
return render json: { name: name, methods: [] } unless file
|
|
366
368
|
|
|
@@ -383,14 +385,16 @@ module Mbeditor
|
|
|
383
385
|
# Fast no-op on subsequent calls (mtime checks only).
|
|
384
386
|
RubyDefinitionService.scan(workspace_root,
|
|
385
387
|
excluded_dirnames: excluded_dirnames,
|
|
386
|
-
excluded_paths: excluded_paths
|
|
388
|
+
excluded_paths: excluded_paths,
|
|
389
|
+
included_dirs: ruby_def_include_dirs)
|
|
387
390
|
|
|
388
391
|
module_names = RubyDefinitionService.includes_in_file(path)
|
|
389
392
|
includes = module_names.filter_map do |mod_name|
|
|
390
393
|
mod_file = RubyDefinitionService.module_defined_in(
|
|
391
394
|
workspace_root, mod_name,
|
|
392
395
|
excluded_dirnames: excluded_dirnames,
|
|
393
|
-
excluded_paths: excluded_paths
|
|
396
|
+
excluded_paths: excluded_paths,
|
|
397
|
+
included_dirs: ruby_def_include_dirs
|
|
394
398
|
)
|
|
395
399
|
next unless mod_file
|
|
396
400
|
|
|
@@ -1068,7 +1072,8 @@ module Mbeditor
|
|
|
1068
1072
|
if File.directory?(full)
|
|
1069
1073
|
{ name: name, type: "folder", path: rel, children: build_tree(full, depth: depth + 1) }
|
|
1070
1074
|
else
|
|
1071
|
-
|
|
1075
|
+
size = File.size(full) rescue nil
|
|
1076
|
+
{ name: name, type: "file", path: rel, size: size }
|
|
1072
1077
|
end
|
|
1073
1078
|
end
|
|
1074
1079
|
rescue Errno::EACCES
|
|
@@ -1083,6 +1088,10 @@ module Mbeditor
|
|
|
1083
1088
|
excluded_paths.filter { |path| !path.include?("/") }
|
|
1084
1089
|
end
|
|
1085
1090
|
|
|
1091
|
+
def ruby_def_include_dirs
|
|
1092
|
+
Array(Mbeditor.configuration.ruby_def_include_dirs).map(&:to_s).reject(&:blank?)
|
|
1093
|
+
end
|
|
1094
|
+
|
|
1086
1095
|
def excluded_path?(relative_path, name)
|
|
1087
1096
|
excluded_paths.any? do |pattern|
|
|
1088
1097
|
if pattern.include?("/")
|
|
@@ -51,10 +51,11 @@ module Mbeditor
|
|
|
51
51
|
attr_reader :file_cache, :mutex
|
|
52
52
|
attr_accessor :cache_path
|
|
53
53
|
|
|
54
|
-
def call(workspace_root, symbol, excluded_dirnames: [], excluded_paths: [])
|
|
54
|
+
def call(workspace_root, symbol, excluded_dirnames: [], excluded_paths: [], included_dirs: [])
|
|
55
55
|
new(workspace_root, symbol,
|
|
56
56
|
excluded_dirnames: excluded_dirnames,
|
|
57
|
-
excluded_paths:
|
|
57
|
+
excluded_paths: excluded_paths,
|
|
58
|
+
included_dirs: included_dirs).call
|
|
58
59
|
end
|
|
59
60
|
|
|
60
61
|
# Load the JSON cache from disk exactly once per process (double-checked
|
|
@@ -122,20 +123,26 @@ module Mbeditor
|
|
|
122
123
|
# Searches the cache (and triggers a workspace scan if needed) to find
|
|
123
124
|
# which file in +workspace_root+ defines the given module or class name.
|
|
124
125
|
# Returns the absolute file path string or nil.
|
|
125
|
-
def module_defined_in(workspace_root, module_name, excluded_dirnames: [], excluded_paths: [])
|
|
126
|
+
def module_defined_in(workspace_root, module_name, excluded_dirnames: [], excluded_paths: [], included_dirs: [])
|
|
126
127
|
load_disk_cache_once
|
|
128
|
+
root_prefix = workspace_root.to_s.chomp("/")
|
|
129
|
+
within_dirs = ->(path) {
|
|
130
|
+
return true if included_dirs.empty?
|
|
131
|
+
included_dirs.any? { |d| path.start_with?(File.join(root_prefix, d) + "/") }
|
|
132
|
+
}
|
|
127
133
|
result = @mutex.synchronize do
|
|
128
|
-
@file_cache.find { |
|
|
134
|
+
@file_cache.find { |path, entry| within_dirs.call(path) && entry[:module_names]&.include?(module_name) }
|
|
129
135
|
end
|
|
130
136
|
return result[0] if result
|
|
131
137
|
|
|
132
138
|
# Cache miss: scan workspace to populate cache entries with module_names.
|
|
133
139
|
new(workspace_root, nil,
|
|
134
140
|
excluded_dirnames: excluded_dirnames,
|
|
135
|
-
excluded_paths: excluded_paths
|
|
141
|
+
excluded_paths: excluded_paths,
|
|
142
|
+
included_dirs: included_dirs).scan_workspace
|
|
136
143
|
|
|
137
144
|
result = @mutex.synchronize do
|
|
138
|
-
@file_cache.find { |
|
|
145
|
+
@file_cache.find { |path, entry| within_dirs.call(path) && entry[:module_names]&.include?(module_name) }
|
|
139
146
|
end
|
|
140
147
|
result ? result[0] : nil
|
|
141
148
|
end
|
|
@@ -153,18 +160,20 @@ module Mbeditor
|
|
|
153
160
|
|
|
154
161
|
# Convenience wrapper: scan the whole workspace to warm the cache.
|
|
155
162
|
# Fast on subsequent calls (only re-parses files whose mtime changed).
|
|
156
|
-
def scan(workspace_root, excluded_dirnames: [], excluded_paths: [])
|
|
163
|
+
def scan(workspace_root, excluded_dirnames: [], excluded_paths: [], included_dirs: [])
|
|
157
164
|
new(workspace_root, nil,
|
|
158
165
|
excluded_dirnames: excluded_dirnames,
|
|
159
|
-
excluded_paths: excluded_paths
|
|
166
|
+
excluded_paths: excluded_paths,
|
|
167
|
+
included_dirs: included_dirs).scan_workspace
|
|
160
168
|
end
|
|
161
169
|
end
|
|
162
170
|
|
|
163
|
-
def initialize(workspace_root, symbol, excluded_dirnames: [], excluded_paths: [])
|
|
164
|
-
@workspace_root
|
|
165
|
-
@symbol
|
|
171
|
+
def initialize(workspace_root, symbol, excluded_dirnames: [], excluded_paths: [], included_dirs: [])
|
|
172
|
+
@workspace_root = workspace_root.to_s.chomp("/")
|
|
173
|
+
@symbol = symbol
|
|
166
174
|
@excluded_dirnames = Array(excluded_dirnames)
|
|
167
175
|
@excluded_paths = Array(excluded_paths)
|
|
176
|
+
@included_dirs = Array(included_dirs)
|
|
168
177
|
end
|
|
169
178
|
|
|
170
179
|
# Walks the entire workspace and populates the per-file cache (including the
|
|
@@ -176,27 +185,29 @@ module Mbeditor
|
|
|
176
185
|
files_scanned = 0
|
|
177
186
|
evict_deleted_cache_entries
|
|
178
187
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
188
|
+
search_roots.each do |root|
|
|
189
|
+
Find.find(root) do |path|
|
|
190
|
+
if File.directory?(path)
|
|
191
|
+
dirname = File.basename(path)
|
|
192
|
+
rel_dir = relative_path(path)
|
|
193
|
+
Find.prune if path != root && excluded_dir?(dirname, rel_dir)
|
|
194
|
+
next
|
|
195
|
+
end
|
|
196
|
+
next unless path.end_with?(".rb")
|
|
187
197
|
|
|
188
|
-
|
|
189
|
-
|
|
198
|
+
rel = relative_path(path)
|
|
199
|
+
next if excluded_rel_path?(rel, File.basename(path))
|
|
190
200
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
201
|
+
files_scanned += 1
|
|
202
|
+
if files_scanned > MAX_FILES_SCANNED
|
|
203
|
+
Rails.logger.warn("[mbeditor] RubyDefinitionService: workspace exceeds #{MAX_FILES_SCANNED} .rb files; stopping scan early")
|
|
204
|
+
break
|
|
205
|
+
end
|
|
206
|
+
begin
|
|
207
|
+
cache_entry_for(path)
|
|
208
|
+
rescue StandardError
|
|
209
|
+
nil
|
|
210
|
+
end
|
|
200
211
|
end
|
|
201
212
|
end
|
|
202
213
|
|
|
@@ -212,46 +223,46 @@ module Mbeditor
|
|
|
212
223
|
|
|
213
224
|
evict_deleted_cache_entries
|
|
214
225
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
Find.prune
|
|
226
|
+
search_roots.each do |root|
|
|
227
|
+
Find.find(root) do |path|
|
|
228
|
+
# Prune excluded directories
|
|
229
|
+
if File.directory?(path)
|
|
230
|
+
dirname = File.basename(path)
|
|
231
|
+
rel_dir = relative_path(path)
|
|
232
|
+
Find.prune if path != root && excluded_dir?(dirname, rel_dir)
|
|
233
|
+
next
|
|
222
234
|
end
|
|
223
|
-
next
|
|
224
|
-
end
|
|
225
235
|
|
|
226
|
-
|
|
236
|
+
next unless path.end_with?(".rb")
|
|
227
237
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
files_scanned += 1
|
|
232
|
-
if files_scanned > MAX_FILES_SCANNED
|
|
233
|
-
Rails.logger.warn("[mbeditor] RubyDefinitionService: workspace exceeds #{MAX_FILES_SCANNED} .rb files; stopping scan early")
|
|
234
|
-
break
|
|
235
|
-
end
|
|
238
|
+
rel = relative_path(path)
|
|
239
|
+
next if excluded_rel_path?(rel, File.basename(path))
|
|
236
240
|
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
next unless hit_lines && hit_lines.any?
|
|
241
|
+
files_scanned += 1
|
|
242
|
+
if files_scanned > MAX_FILES_SCANNED
|
|
243
|
+
Rails.logger.warn("[mbeditor] RubyDefinitionService: workspace exceeds #{MAX_FILES_SCANNED} .rb files; stopping scan early")
|
|
244
|
+
break
|
|
245
|
+
end
|
|
243
246
|
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
247
|
+
begin
|
|
248
|
+
cached = cache_entry_for(path)
|
|
249
|
+
next unless cached
|
|
250
|
+
|
|
251
|
+
hit_lines = @symbol ? cached[:all_defs].fetch(@symbol, nil) : nil
|
|
252
|
+
next unless hit_lines && hit_lines.any?
|
|
253
|
+
|
|
254
|
+
hit_lines.each do |def_line|
|
|
255
|
+
results << {
|
|
256
|
+
file: rel,
|
|
257
|
+
line: def_line,
|
|
258
|
+
signature: (cached[:lines][def_line - 1] || "").strip,
|
|
259
|
+
comments: extract_comments(cached[:lines], def_line)
|
|
260
|
+
}
|
|
261
|
+
return results if results.length >= MAX_RESULTS
|
|
262
|
+
end
|
|
263
|
+
rescue StandardError
|
|
264
|
+
# Malformed file or unreadable; skip silently
|
|
252
265
|
end
|
|
253
|
-
rescue StandardError
|
|
254
|
-
# Malformed file or unreadable; skip silently
|
|
255
266
|
end
|
|
256
267
|
end
|
|
257
268
|
|
|
@@ -386,6 +397,15 @@ module Mbeditor
|
|
|
386
397
|
full_path.to_s.delete_prefix(@workspace_root).delete_prefix("/")
|
|
387
398
|
end
|
|
388
399
|
|
|
400
|
+
def search_roots
|
|
401
|
+
return [@workspace_root] if @included_dirs.empty?
|
|
402
|
+
|
|
403
|
+
dirs = @included_dirs
|
|
404
|
+
.map { |d| File.join(@workspace_root, d) }
|
|
405
|
+
.select { |d| File.directory?(d) }
|
|
406
|
+
dirs.empty? ? [@workspace_root] : dirs
|
|
407
|
+
end
|
|
408
|
+
|
|
389
409
|
def excluded_dir?(dirname, rel_dir)
|
|
390
410
|
@excluded_dirnames.include?(dirname) ||
|
|
391
411
|
@excluded_paths.any? do |pattern|
|
|
@@ -6,7 +6,8 @@ module Mbeditor
|
|
|
6
6
|
:redmine_enabled, :redmine_url, :redmine_api_key, :redmine_ticket_source,
|
|
7
7
|
:test_framework, :test_command, :test_timeout,
|
|
8
8
|
:authenticate_with,
|
|
9
|
-
:lint_timeout, :base_branch_candidates, :git_timeout
|
|
9
|
+
:lint_timeout, :base_branch_candidates, :git_timeout,
|
|
10
|
+
:ruby_def_include_dirs
|
|
10
11
|
|
|
11
12
|
def initialize
|
|
12
13
|
@allowed_environments = [:development]
|
|
@@ -22,7 +23,8 @@ module Mbeditor
|
|
|
22
23
|
@test_timeout = 60 # seconds
|
|
23
24
|
@lint_timeout = 15 # seconds for RuboCop/haml-lint subprocesses
|
|
24
25
|
@base_branch_candidates = %w[origin/develop origin/main origin/master develop main master]
|
|
25
|
-
@git_timeout
|
|
26
|
+
@git_timeout = nil # seconds; nil disables (no timeout on git subprocesses)
|
|
27
|
+
@ruby_def_include_dirs = %w[app/models app/controllers app/helpers app/concerns]
|
|
26
28
|
end
|
|
27
29
|
end
|
|
28
30
|
end
|
data/lib/mbeditor/version.rb
CHANGED