lutaml 0.10.4 → 0.10.6
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/.gitignore +8 -0
- data/.rubocop.yml +10 -0
- data/.rubocop_todo.yml +218 -94
- data/TODO.cleanups/01-resolve-production-todos.md +65 -0
- data/TODO.cleanups/02-reduce-metrics-offenses.md +37 -0
- data/TODO.cleanups/03-reduce-rspec-multiple-expectations.md +54 -0
- data/TODO.cleanups/04-reduce-rspec-example-length.md +45 -0
- data/TODO.cleanups/05-replace-marshal-load.md +37 -0
- data/TODO.cleanups/06-replace-eval-in-tests.md +41 -0
- data/TODO.cleanups/07-fix-lint-offenses.md +74 -0
- data/TODO.cleanups/08-reduce-memoized-helpers-and-nesting.md +43 -0
- data/TODO.cleanups/09-reduce-verified-doubles-and-rspec-style.md +57 -0
- data/TODO.cleanups/10-split-large-files.md +47 -0
- data/bin/console +0 -1
- data/exe/lutaml +1 -0
- data/lib/lutaml/cli/element_identifier.rb +3 -6
- data/lib/lutaml/cli/interactive_shell/bookmark_commands.rb +88 -0
- data/lib/lutaml/cli/interactive_shell/command_base.rb +32 -0
- data/lib/lutaml/cli/interactive_shell/export_handler.rb +67 -0
- data/lib/lutaml/cli/interactive_shell/help_display.rb +114 -0
- data/lib/lutaml/cli/interactive_shell/navigation_commands.rb +135 -0
- data/lib/lutaml/cli/interactive_shell/query_commands.rb +185 -0
- data/lib/lutaml/cli/interactive_shell.rb +116 -802
- data/lib/lutaml/cli/uml/build_command.rb +5 -5
- data/lib/lutaml/cli/uml/verify_command.rb +0 -1
- data/lib/lutaml/converter/xmi_to_uml.rb +3 -153
- data/lib/lutaml/converter/xmi_to_uml_generalization.rb +193 -0
- data/lib/lutaml/formatter/graphviz.rb +1 -2
- data/lib/lutaml/qea/database.rb +1 -47
- data/lib/lutaml/qea/factory/association_builder.rb +188 -0
- data/lib/lutaml/qea/factory/base_transformer.rb +0 -1
- data/lib/lutaml/qea/factory/class_transformer.rb +40 -590
- data/lib/lutaml/qea/factory/diagram_transformer.rb +0 -3
- data/lib/lutaml/qea/factory/generalization_builder.rb +211 -0
- data/lib/lutaml/qea/factory/package_transformer.rb +1 -2
- data/lib/lutaml/qea/factory/stereotype_loader.rb +34 -0
- data/lib/lutaml/qea/lookup_indexes.rb +54 -0
- data/lib/lutaml/qea/models/ea_datatype.rb +0 -2
- data/lib/lutaml/qea/validation/validation_engine.rb +0 -2
- data/lib/lutaml/uml/has_members.rb +0 -1
- data/lib/lutaml/uml/inheritance_walker.rb +92 -0
- data/lib/lutaml/uml/model_helpers.rb +129 -0
- data/lib/lutaml/uml/node/attribute.rb +3 -1
- data/lib/lutaml/uml/node/class_node.rb +3 -3
- data/lib/lutaml/uml/operation.rb +2 -0
- data/lib/lutaml/uml_repository/class_lookup_index.rb +40 -0
- data/lib/lutaml/uml_repository/exporters/markdown/class_page_builder.rb +179 -0
- data/lib/lutaml/uml_repository/exporters/markdown/formatting.rb +36 -0
- data/lib/lutaml/uml_repository/exporters/markdown/index_page_builder.rb +73 -0
- data/lib/lutaml/uml_repository/exporters/markdown/link_resolver.rb +40 -0
- data/lib/lutaml/uml_repository/exporters/markdown/package_page_builder.rb +107 -0
- data/lib/lutaml/uml_repository/exporters/markdown_exporter.rb +26 -538
- data/lib/lutaml/uml_repository/index_builder.rb +3 -271
- data/lib/lutaml/uml_repository/index_builders/association_index.rb +141 -0
- data/lib/lutaml/uml_repository/index_builders/class_index.rb +94 -0
- data/lib/lutaml/uml_repository/index_builders/package_index.rb +57 -0
- data/lib/lutaml/uml_repository/package_exporter.rb +10 -20
- data/lib/lutaml/uml_repository/package_loader.rb +37 -17
- data/lib/lutaml/uml_repository/repository/deprecated.rb +39 -0
- data/lib/lutaml/uml_repository/repository/loader.rb +112 -0
- data/lib/lutaml/uml_repository/repository.rb +7 -57
- data/lib/lutaml/uml_repository/static_site/association_serialization.rb +142 -0
- data/lib/lutaml/uml_repository/static_site/configuration.rb +0 -2
- data/lib/lutaml/uml_repository/static_site/data_transformer.rb +52 -873
- data/lib/lutaml/uml_repository/static_site/generator.rb +29 -8
- data/lib/lutaml/uml_repository/static_site/search_index_builder.rb +1 -4
- data/lib/lutaml/uml_repository/static_site/serializers/attribute_serializer.rb +78 -0
- data/lib/lutaml/uml_repository/static_site/serializers/class_serializer.rb +124 -0
- data/lib/lutaml/uml_repository/static_site/serializers/diagram_serializer.rb +60 -0
- data/lib/lutaml/uml_repository/static_site/serializers/inheritance_resolver.rb +258 -0
- data/lib/lutaml/uml_repository/static_site/serializers/metadata_builder.rb +48 -0
- data/lib/lutaml/uml_repository/static_site/serializers/operation_serializer.rb +57 -0
- data/lib/lutaml/uml_repository/static_site/serializers/package_serializer.rb +94 -0
- data/lib/lutaml/uml_repository/static_site/serializers/package_tree_builder.rb +93 -0
- data/lib/lutaml/version.rb +1 -1
- data/lib/lutaml/xmi/liquid_drops/association_drop.rb +13 -35
- data/lib/lutaml/xmi/liquid_drops/attribute_drop.rb +12 -18
- data/lib/lutaml/xmi/liquid_drops/cardinality_drop.rb +14 -6
- data/lib/lutaml/xmi/liquid_drops/connector_drop.rb +0 -3
- data/lib/lutaml/xmi/liquid_drops/constraint_drop.rb +1 -3
- data/lib/lutaml/xmi/liquid_drops/data_type_drop.rb +13 -70
- data/lib/lutaml/xmi/liquid_drops/dependency_drop.rb +2 -5
- data/lib/lutaml/xmi/liquid_drops/diagram_drop.rb +5 -11
- data/lib/lutaml/xmi/liquid_drops/enum_drop.rb +8 -16
- data/lib/lutaml/xmi/liquid_drops/enum_owned_literal_drop.rb +3 -9
- data/lib/lutaml/xmi/liquid_drops/generalization_attribute_drop.rb +11 -13
- data/lib/lutaml/xmi/liquid_drops/generalization_drop.rb +27 -85
- data/lib/lutaml/xmi/liquid_drops/klass_drop.rb +39 -91
- data/lib/lutaml/xmi/liquid_drops/operation_drop.rb +3 -9
- data/lib/lutaml/xmi/liquid_drops/package_drop.rb +16 -44
- data/lib/lutaml/xmi/liquid_drops/root_drop.rb +3 -11
- data/lib/lutaml/xmi/liquid_drops/source_target_drop.rb +2 -5
- data/lib/lutaml/xmi/parsers/xmi_base.rb +2 -749
- data/lib/lutaml/xmi/parsers/xmi_class_members.rb +45 -0
- data/lib/lutaml/xmi/parsers/xmi_connector.rb +251 -0
- data/lib/lutaml/xmi/parsers/xml.rb +7 -120
- data/lib/lutaml/xmi/xmi_lookup_service.rb +42 -0
- data/lib/lutaml.rb +0 -1
- metadata +48 -21
- data/lib/lutaml/cli/commands/base_command.rb +0 -118
- data/lib/lutaml/command_line.rb +0 -272
- data/lib/lutaml/sysml/allocate.rb +0 -9
- data/lib/lutaml/sysml/allocated.rb +0 -9
- data/lib/lutaml/sysml/binding_connector.rb +0 -9
- data/lib/lutaml/sysml/block.rb +0 -32
- data/lib/lutaml/sysml/constraint_block.rb +0 -14
- data/lib/lutaml/sysml/copy.rb +0 -8
- data/lib/lutaml/sysml/derive_requirement.rb +0 -9
- data/lib/lutaml/sysml/nested_connector_end.rb +0 -13
- data/lib/lutaml/sysml/refine.rb +0 -9
- data/lib/lutaml/sysml/requirement.rb +0 -44
- data/lib/lutaml/sysml/requirement_related.rb +0 -9
- data/lib/lutaml/sysml/satisfy.rb +0 -9
- data/lib/lutaml/sysml/test_case.rb +0 -25
- data/lib/lutaml/sysml/trace.rb +0 -9
- data/lib/lutaml/sysml/verify.rb +0 -8
- data/lib/lutaml/sysml/xmi_file.rb +0 -486
- data/lib/lutaml/sysml.rb +0 -11
|
@@ -4,21 +4,15 @@ require "readline"
|
|
|
4
4
|
require "pathname"
|
|
5
5
|
require_relative "enhanced_formatter"
|
|
6
6
|
require_relative "../uml_repository/repository"
|
|
7
|
+
require_relative "interactive_shell/command_base"
|
|
8
|
+
require_relative "interactive_shell/navigation_commands"
|
|
9
|
+
require_relative "interactive_shell/query_commands"
|
|
10
|
+
require_relative "interactive_shell/bookmark_commands"
|
|
11
|
+
require_relative "interactive_shell/export_handler"
|
|
12
|
+
require_relative "interactive_shell/help_display"
|
|
7
13
|
|
|
8
14
|
module Lutaml
|
|
9
15
|
module Cli
|
|
10
|
-
# InteractiveShell provides a full-featured REPL for
|
|
11
|
-
# exploring UML repositories
|
|
12
|
-
#
|
|
13
|
-
# Features:
|
|
14
|
-
# - Readline integration with history
|
|
15
|
-
# - Tab completion for commands and paths
|
|
16
|
-
# - Colorized prompts and output
|
|
17
|
-
# - Navigation commands (cd, pwd, ls, tree, up, root, back)
|
|
18
|
-
# - Query commands (find, show, search)
|
|
19
|
-
# - Bookmark management
|
|
20
|
-
# - Results management
|
|
21
|
-
# - Command history persistence
|
|
22
16
|
class InteractiveShell
|
|
23
17
|
HISTORY_FILE = File.expand_path("~/.lutaml-xmi-history")
|
|
24
18
|
MAX_HISTORY = 1000
|
|
@@ -26,14 +20,7 @@ module Lutaml
|
|
|
26
20
|
attr_reader :repository, :current_path, :config, :bookmarks,
|
|
27
21
|
:last_results, :path_history
|
|
28
22
|
|
|
29
|
-
|
|
30
|
-
#
|
|
31
|
-
# @param lur_path_or_repo [String, UmlRepository] Path to LUR file or
|
|
32
|
-
# repository
|
|
33
|
-
# @param config [Hash] Configuration options
|
|
34
|
-
# @option config [Boolean] :color Enable colored output
|
|
35
|
-
# @option config [Boolean] :icons Enable icons in output
|
|
36
|
-
def initialize(lur_path_or_repo, config: nil) # rubocop:disable Metrics/MethodLength
|
|
23
|
+
def initialize(lur_path_or_repo, config: nil)
|
|
37
24
|
@config = {
|
|
38
25
|
color: true,
|
|
39
26
|
icons: true,
|
|
@@ -41,7 +28,6 @@ module Lutaml
|
|
|
41
28
|
page_size: 50,
|
|
42
29
|
}.merge(config || {})
|
|
43
30
|
|
|
44
|
-
# Load repository
|
|
45
31
|
if lur_path_or_repo.is_a?(String)
|
|
46
32
|
OutputFormatter.progress("Loading repository")
|
|
47
33
|
@repository = Lutaml::UmlRepository::Repository.from_package(lur_path_or_repo)
|
|
@@ -50,37 +36,33 @@ module Lutaml
|
|
|
50
36
|
@repository = lur_path_or_repo
|
|
51
37
|
end
|
|
52
38
|
|
|
53
|
-
# Initialize state
|
|
54
39
|
@current_path = "ModelRoot"
|
|
55
40
|
@bookmarks = {}
|
|
56
41
|
@last_results = nil
|
|
57
42
|
@path_history = ["ModelRoot"]
|
|
58
43
|
@running = false
|
|
59
44
|
|
|
45
|
+
@navigation = NavigationCommands.new(self)
|
|
46
|
+
@query = QueryCommands.new(self)
|
|
47
|
+
@bookmarks_cmd = BookmarkCommands.new(self)
|
|
48
|
+
@export = ExportHandler.new(self)
|
|
49
|
+
@help = HelpDisplay.new(self)
|
|
50
|
+
|
|
60
51
|
setup_readline
|
|
61
52
|
load_history
|
|
62
53
|
end
|
|
63
54
|
|
|
64
|
-
|
|
65
|
-
#
|
|
66
|
-
# @return [void]
|
|
67
|
-
def start # rubocop:disable Metrics/AbcSize,Metrics/CyclomaticComplexity,Metrics/MethodLength,Metrics/PerceivedComplexity
|
|
55
|
+
def start
|
|
68
56
|
@running = true
|
|
69
|
-
display_welcome
|
|
57
|
+
@help.display_welcome
|
|
70
58
|
|
|
71
59
|
while @running
|
|
72
60
|
begin
|
|
73
61
|
input = Readline.readline(prompt, true)
|
|
74
|
-
|
|
75
|
-
# Exit on Ctrl+D or nil input
|
|
76
62
|
break if input.nil?
|
|
77
|
-
|
|
78
|
-
# Skip empty lines
|
|
79
63
|
next if input.strip.empty?
|
|
80
64
|
|
|
81
|
-
|
|
82
|
-
if Readline::HISTORY.length > 1 &&
|
|
83
|
-
Readline::HISTORY[-2] == input
|
|
65
|
+
if Readline::HISTORY.length > 1 && Readline::HISTORY[-2] == input
|
|
84
66
|
Readline::HISTORY.pop
|
|
85
67
|
end
|
|
86
68
|
|
|
@@ -99,9 +81,103 @@ module Lutaml
|
|
|
99
81
|
|
|
100
82
|
private
|
|
101
83
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
84
|
+
COMMAND_DISPATCH = {
|
|
85
|
+
# Navigation
|
|
86
|
+
"cd" => :@navigation, "pwd" => :@navigation,
|
|
87
|
+
"ls" => :@navigation, "list" => :@navigation,
|
|
88
|
+
"tree" => :@navigation, "up" => :@navigation,
|
|
89
|
+
"root" => :@navigation, "back" => :@navigation,
|
|
90
|
+
# Query
|
|
91
|
+
"find" => :@query, "f" => :@query,
|
|
92
|
+
"show" => :@query, "s" => :@query,
|
|
93
|
+
"search" => :@query, "?" => :@query,
|
|
94
|
+
"results" => :@query,
|
|
95
|
+
# Bookmarks
|
|
96
|
+
"bookmark" => :@bookmarks_cmd, "bm" => :@bookmarks_cmd,
|
|
97
|
+
# Export
|
|
98
|
+
"export" => :@export,
|
|
99
|
+
# Utilities
|
|
100
|
+
"help" => :@help, "h" => :@help,
|
|
101
|
+
"history" => :@help,
|
|
102
|
+
"clear" => :@help, "cls" => :@help,
|
|
103
|
+
"config" => :@help,
|
|
104
|
+
"stats" => :@help
|
|
105
|
+
}.freeze
|
|
106
|
+
|
|
107
|
+
METHOD_MAP = {
|
|
108
|
+
"cd" => :cmd_cd, "pwd" => :cmd_pwd,
|
|
109
|
+
"ls" => :cmd_ls, "list" => :cmd_ls,
|
|
110
|
+
"tree" => :cmd_tree, "up" => :cmd_up,
|
|
111
|
+
"root" => :cmd_root, "back" => :cmd_back,
|
|
112
|
+
"find" => :cmd_find, "f" => :cmd_find,
|
|
113
|
+
"show" => :cmd_show, "s" => :cmd_show,
|
|
114
|
+
"search" => :cmd_search, "?" => :cmd_search,
|
|
115
|
+
"results" => :cmd_results,
|
|
116
|
+
"bookmark" => :cmd_bookmark, "bm" => :cmd_bookmark,
|
|
117
|
+
"export" => :cmd_export,
|
|
118
|
+
"help" => :cmd_help, "h" => :cmd_help,
|
|
119
|
+
"history" => :cmd_history,
|
|
120
|
+
"clear" => :cmd_clear, "cls" => :cmd_clear,
|
|
121
|
+
"config" => :cmd_config,
|
|
122
|
+
"stats" => :cmd_stats
|
|
123
|
+
}.freeze
|
|
124
|
+
|
|
125
|
+
def execute_command(input)
|
|
126
|
+
parts = input.split(/\s+/)
|
|
127
|
+
command = parts[0].downcase
|
|
128
|
+
args = parts[1..]
|
|
129
|
+
|
|
130
|
+
if ["exit", "quit", "q"].include?(command)
|
|
131
|
+
@running = false
|
|
132
|
+
return
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
handler_var = COMMAND_DISPATCH[command]
|
|
136
|
+
method_name = METHOD_MAP[command]
|
|
137
|
+
|
|
138
|
+
if handler_var && method_name
|
|
139
|
+
instance_variable_get(handler_var).send(method_name, args)
|
|
140
|
+
else
|
|
141
|
+
puts OutputFormatter.warning("Unknown command: #{command}")
|
|
142
|
+
puts "Type 'help' for available commands"
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
# Delegate methods for backward compatibility with tests
|
|
147
|
+
def cmd_cd(args) = @navigation.cmd_cd(args)
|
|
148
|
+
def cmd_pwd(args) = @navigation.cmd_pwd(args)
|
|
149
|
+
def cmd_ls(args) = @navigation.cmd_ls(args)
|
|
150
|
+
def cmd_tree(args) = @navigation.cmd_tree(args)
|
|
151
|
+
def cmd_up(args) = @navigation.cmd_up(args)
|
|
152
|
+
def cmd_root(args) = @navigation.cmd_root(args)
|
|
153
|
+
def cmd_back(args) = @navigation.cmd_back(args)
|
|
154
|
+
def resolve_path(path) = @navigation.resolve_path(path)
|
|
155
|
+
def cmd_find(args) = @query.cmd_find(args)
|
|
156
|
+
def cmd_show(args) = @query.cmd_show(args)
|
|
157
|
+
def cmd_search(args) = @query.cmd_search(args)
|
|
158
|
+
def cmd_results(args) = @query.cmd_results(args)
|
|
159
|
+
def show_class(qname) = @query.show_class(qname)
|
|
160
|
+
def show_package(path) = @query.show_package(path)
|
|
161
|
+
def show_numbered_result(number) = @query.show_numbered_result(number)
|
|
162
|
+
def display_search_results(results) = @query.display_search_results(results)
|
|
163
|
+
def cmd_bookmark(args) = @bookmarks_cmd.cmd_bookmark(args)
|
|
164
|
+
def bookmark_add(name) = @bookmarks_cmd.bookmark_add(name)
|
|
165
|
+
def bookmark_list = @bookmarks_cmd.bookmark_list
|
|
166
|
+
def bookmark_go(name) = @bookmarks_cmd.bookmark_go(name)
|
|
167
|
+
def bookmark_remove(name) = @bookmarks_cmd.bookmark_remove(name)
|
|
168
|
+
def cmd_export(args) = @export.cmd_export(args)
|
|
169
|
+
def export_csv(path) = @export.export_csv(path)
|
|
170
|
+
def export_json(path) = @export.export_json(path)
|
|
171
|
+
def export_yaml(path) = @export.export_yaml(path)
|
|
172
|
+
def display_welcome = @help.display_welcome
|
|
173
|
+
def display_general_help = @help.display_general_help
|
|
174
|
+
def display_command_help(cmd) = @help.display_command_help(cmd)
|
|
175
|
+
def cmd_help(args) = @help.cmd_help(args)
|
|
176
|
+
def cmd_history(args) = @help.cmd_history(args)
|
|
177
|
+
def cmd_clear(args) = @help.cmd_clear(args)
|
|
178
|
+
def cmd_config(args) = @help.cmd_config(args)
|
|
179
|
+
def cmd_stats(args) = @help.cmd_stats(args)
|
|
180
|
+
|
|
105
181
|
def prompt
|
|
106
182
|
path_display = @current_path == "ModelRoot" ? "/" : "/#{@current_path}"
|
|
107
183
|
prompt_text = "lutaml[#{path_display}]> "
|
|
@@ -113,21 +189,11 @@ module Lutaml
|
|
|
113
189
|
end
|
|
114
190
|
end
|
|
115
191
|
|
|
116
|
-
# Setup readline with tab completion and history
|
|
117
|
-
#
|
|
118
|
-
# @return [void]
|
|
119
192
|
def setup_readline
|
|
120
|
-
|
|
121
|
-
Readline.completion_proc = proc do |word|
|
|
122
|
-
complete_command(word)
|
|
123
|
-
end
|
|
124
|
-
|
|
193
|
+
Readline.completion_proc = proc { |word| complete_command(word) }
|
|
125
194
|
Readline.completion_append_character = " "
|
|
126
195
|
end
|
|
127
196
|
|
|
128
|
-
# Load command history from file
|
|
129
|
-
#
|
|
130
|
-
# @return [void]
|
|
131
197
|
def load_history
|
|
132
198
|
return unless File.exist?(HISTORY_FILE)
|
|
133
199
|
|
|
@@ -135,13 +201,9 @@ module Lutaml
|
|
|
135
201
|
Readline::HISTORY.push(line.chomp)
|
|
136
202
|
end
|
|
137
203
|
rescue StandardError => e
|
|
138
|
-
# Silently ignore history load errors
|
|
139
204
|
warn "Warning: Could not load history: #{e.message}" if ENV["DEBUG"]
|
|
140
205
|
end
|
|
141
206
|
|
|
142
|
-
# Save command history to file
|
|
143
|
-
#
|
|
144
|
-
# @return [void]
|
|
145
207
|
def save_history
|
|
146
208
|
history_lines = Readline::HISTORY.to_a.last(MAX_HISTORY)
|
|
147
209
|
File.write(HISTORY_FILE, history_lines.join("\n"))
|
|
@@ -149,762 +211,14 @@ module Lutaml
|
|
|
149
211
|
warn "Warning: Could not save history: #{e.message}"
|
|
150
212
|
end
|
|
151
213
|
|
|
152
|
-
# Display welcome message
|
|
153
|
-
#
|
|
154
|
-
# @return [void]
|
|
155
|
-
def display_welcome # rubocop:disable Metrics/AbcSize,Metrics/MethodLength
|
|
156
|
-
puts OutputFormatter.colorize(
|
|
157
|
-
"╔═══════════════════════════════════════╗", :cyan
|
|
158
|
-
)
|
|
159
|
-
puts OutputFormatter.colorize(
|
|
160
|
-
"║ LutaML Interactive Shell (REPL) ║", :cyan
|
|
161
|
-
)
|
|
162
|
-
puts OutputFormatter.colorize(
|
|
163
|
-
"╚═══════════════════════════════════════╝", :cyan
|
|
164
|
-
)
|
|
165
|
-
puts ""
|
|
166
|
-
puts "Type 'help' for available commands, 'exit' to quit"
|
|
167
|
-
puts ""
|
|
168
|
-
|
|
169
|
-
# Show quick stats
|
|
170
|
-
stats = @repository.statistics
|
|
171
|
-
puts "Repository loaded:"
|
|
172
|
-
puts " #{stats[:total_packages]} packages, " \
|
|
173
|
-
"#{stats[:total_classes]} classes"
|
|
174
|
-
puts ""
|
|
175
|
-
end
|
|
176
|
-
|
|
177
|
-
# Execute a command
|
|
178
|
-
#
|
|
179
|
-
# @param input [String] User input
|
|
180
|
-
# @return [void]
|
|
181
|
-
def execute_command(input) # rubocop:disable Metrics/AbcSize,Metrics/CyclomaticComplexity,Metrics/MethodLength
|
|
182
|
-
parts = input.split(/\s+/)
|
|
183
|
-
command = parts[0].downcase
|
|
184
|
-
args = parts[1..]
|
|
185
|
-
|
|
186
|
-
case command
|
|
187
|
-
# Navigation
|
|
188
|
-
when "cd"
|
|
189
|
-
cmd_cd(args)
|
|
190
|
-
when "pwd"
|
|
191
|
-
cmd_pwd(args)
|
|
192
|
-
when "ls", "list"
|
|
193
|
-
cmd_ls(args)
|
|
194
|
-
when "tree"
|
|
195
|
-
cmd_tree(args)
|
|
196
|
-
when "up"
|
|
197
|
-
cmd_up(args)
|
|
198
|
-
when "root"
|
|
199
|
-
cmd_root(args)
|
|
200
|
-
when "back"
|
|
201
|
-
cmd_back(args)
|
|
202
|
-
|
|
203
|
-
# Query
|
|
204
|
-
when "find", "f"
|
|
205
|
-
cmd_find(args)
|
|
206
|
-
when "show", "s"
|
|
207
|
-
cmd_show(args)
|
|
208
|
-
when "search", "?"
|
|
209
|
-
cmd_search(args)
|
|
210
|
-
|
|
211
|
-
# Bookmarks
|
|
212
|
-
when "bookmark", "bm"
|
|
213
|
-
cmd_bookmark(args)
|
|
214
|
-
|
|
215
|
-
# Results
|
|
216
|
-
when "results"
|
|
217
|
-
cmd_results(args)
|
|
218
|
-
when "export"
|
|
219
|
-
cmd_export(args)
|
|
220
|
-
|
|
221
|
-
# Utilities
|
|
222
|
-
when "help", "h"
|
|
223
|
-
cmd_help(args)
|
|
224
|
-
when "history"
|
|
225
|
-
cmd_history(args)
|
|
226
|
-
when "clear", "cls"
|
|
227
|
-
cmd_clear(args)
|
|
228
|
-
when "config"
|
|
229
|
-
cmd_config(args)
|
|
230
|
-
when "stats"
|
|
231
|
-
cmd_stats(args)
|
|
232
|
-
when "exit", "quit", "q"
|
|
233
|
-
@running = false
|
|
234
|
-
|
|
235
|
-
else
|
|
236
|
-
puts OutputFormatter.warning("Unknown command: #{command}")
|
|
237
|
-
puts "Type 'help' for available commands"
|
|
238
|
-
end
|
|
239
|
-
end
|
|
240
|
-
|
|
241
|
-
# Tab completion for commands
|
|
242
|
-
#
|
|
243
|
-
# @param word [String] Word to complete
|
|
244
|
-
# @return [Array<String>] Completion options
|
|
245
214
|
def complete_command(word)
|
|
246
|
-
|
|
215
|
+
%w[
|
|
247
216
|
cd pwd ls list tree up root back
|
|
248
217
|
find show search
|
|
249
218
|
bookmark bm
|
|
250
219
|
results export
|
|
251
220
|
help history clear config stats exit quit
|
|
252
|
-
]
|
|
253
|
-
|
|
254
|
-
commands.grep(/^#{Regexp.escape(word)}/)
|
|
255
|
-
end
|
|
256
|
-
|
|
257
|
-
# Navigation: Change directory
|
|
258
|
-
#
|
|
259
|
-
# @param args [Array<String>] Command arguments
|
|
260
|
-
# @return [void]
|
|
261
|
-
def cmd_cd(args) # rubocop:disable Metrics/MethodLength
|
|
262
|
-
if args.empty?
|
|
263
|
-
puts OutputFormatter.warning("Usage: cd PATH")
|
|
264
|
-
return
|
|
265
|
-
end
|
|
266
|
-
|
|
267
|
-
path = resolve_path(args[0])
|
|
268
|
-
pkg = @repository.find_package(path)
|
|
269
|
-
|
|
270
|
-
if pkg
|
|
271
|
-
unless @path_history.last == @current_path
|
|
272
|
-
@path_history << @current_path
|
|
273
|
-
end
|
|
274
|
-
@current_path = path
|
|
275
|
-
puts "Changed to: #{path}"
|
|
276
|
-
else
|
|
277
|
-
puts OutputFormatter.error("Package not found: #{path}")
|
|
278
|
-
end
|
|
279
|
-
end
|
|
280
|
-
|
|
281
|
-
# Navigation: Print working directory
|
|
282
|
-
#
|
|
283
|
-
# @param _args [Array<String>] Command arguments (unused)
|
|
284
|
-
# @return [void]
|
|
285
|
-
def cmd_pwd(_args)
|
|
286
|
-
puts @current_path
|
|
287
|
-
end
|
|
288
|
-
|
|
289
|
-
# Navigation: List packages
|
|
290
|
-
#
|
|
291
|
-
# @param args [Array<String>] Command arguments
|
|
292
|
-
# @return [void]
|
|
293
|
-
def cmd_ls(args) # rubocop:disable Metrics/AbcSize,Metrics/MethodLength,Metrics/PerceivedComplexity
|
|
294
|
-
path = args.empty? ? @current_path : resolve_path(args[0])
|
|
295
|
-
recursive = args.include?("-r") || args.include?("--recursive")
|
|
296
|
-
|
|
297
|
-
packages = @repository.list_packages(path, recursive: recursive)
|
|
298
|
-
|
|
299
|
-
if packages.empty?
|
|
300
|
-
puts OutputFormatter.warning("No packages found at #{path}")
|
|
301
|
-
else
|
|
302
|
-
packages.each do |pkg|
|
|
303
|
-
icon = if @config[:icons]
|
|
304
|
-
"#{EnhancedFormatter::ICONS[:package]} "
|
|
305
|
-
else
|
|
306
|
-
""
|
|
307
|
-
end
|
|
308
|
-
puts "#{icon}#{pkg.name}"
|
|
309
|
-
end
|
|
310
|
-
puts ""
|
|
311
|
-
puts "Total: #{packages.size} package(s)"
|
|
312
|
-
end
|
|
313
|
-
end
|
|
314
|
-
|
|
315
|
-
# Navigation: Show tree
|
|
316
|
-
#
|
|
317
|
-
# @param args [Array<String>] Command arguments
|
|
318
|
-
# @return [void]
|
|
319
|
-
def cmd_tree(args) # rubocop:disable Metrics/AbcSize,Metrics/CyclomaticComplexity,Metrics/MethodLength,Metrics/PerceivedComplexity
|
|
320
|
-
path = args.empty? ? @current_path : resolve_path(args[0])
|
|
321
|
-
|
|
322
|
-
# Parse depth option
|
|
323
|
-
max_depth = nil
|
|
324
|
-
args.each_with_index do |arg, i|
|
|
325
|
-
if arg == "-d" && args[i + 1]
|
|
326
|
-
max_depth = args[i + 1].to_i
|
|
327
|
-
end
|
|
328
|
-
end
|
|
329
|
-
|
|
330
|
-
tree_data = @repository.package_tree(path, max_depth: max_depth)
|
|
331
|
-
|
|
332
|
-
unless tree_data
|
|
333
|
-
puts OutputFormatter.error("Package not found: #{path}")
|
|
334
|
-
return
|
|
335
|
-
end
|
|
336
|
-
|
|
337
|
-
if @config[:icons]
|
|
338
|
-
puts EnhancedFormatter.format_tree_with_icons(tree_data, @config)
|
|
339
|
-
else
|
|
340
|
-
puts OutputFormatter.format_tree(tree_data)
|
|
341
|
-
end
|
|
342
|
-
end
|
|
343
|
-
|
|
344
|
-
# Navigation: Go up one level
|
|
345
|
-
#
|
|
346
|
-
# @param _args [Array<String>] Command arguments (unused)
|
|
347
|
-
# @return [void]
|
|
348
|
-
def cmd_up(_args) # rubocop:disable Metrics/MethodLength
|
|
349
|
-
if @current_path == "ModelRoot"
|
|
350
|
-
puts OutputFormatter.warning("Already at root")
|
|
351
|
-
return
|
|
352
|
-
end
|
|
353
|
-
|
|
354
|
-
parts = @current_path.split("::")
|
|
355
|
-
parts.pop
|
|
356
|
-
new_path = parts.empty? ? "ModelRoot" : parts.join("::")
|
|
357
|
-
|
|
358
|
-
unless @path_history.last == @current_path
|
|
359
|
-
@path_history << @current_path
|
|
360
|
-
end
|
|
361
|
-
@current_path = new_path
|
|
362
|
-
puts "Changed to: #{@current_path}"
|
|
363
|
-
end
|
|
364
|
-
|
|
365
|
-
# Navigation: Go to root
|
|
366
|
-
#
|
|
367
|
-
# @param _args [Array<String>] Command arguments (unused)
|
|
368
|
-
# @return [void]
|
|
369
|
-
def cmd_root(_args)
|
|
370
|
-
if @current_path == "ModelRoot"
|
|
371
|
-
puts "Already at root"
|
|
372
|
-
else
|
|
373
|
-
unless @path_history.last == @current_path
|
|
374
|
-
@path_history << @current_path
|
|
375
|
-
end
|
|
376
|
-
@current_path = "ModelRoot"
|
|
377
|
-
puts "Changed to: ModelRoot"
|
|
378
|
-
end
|
|
379
|
-
end
|
|
380
|
-
|
|
381
|
-
# Navigation: Go back to previous location
|
|
382
|
-
#
|
|
383
|
-
# @param _args [Array<String>] Command arguments (unused)
|
|
384
|
-
# @return [void]
|
|
385
|
-
def cmd_back(_args)
|
|
386
|
-
if @path_history.size > 1
|
|
387
|
-
@path_history.pop
|
|
388
|
-
@current_path = @path_history.last
|
|
389
|
-
puts "Changed to: #{@current_path}"
|
|
390
|
-
else
|
|
391
|
-
puts OutputFormatter.warning("No previous location")
|
|
392
|
-
end
|
|
393
|
-
end
|
|
394
|
-
|
|
395
|
-
# Query: Find class by name
|
|
396
|
-
#
|
|
397
|
-
# @param args [Array<String>] Command arguments
|
|
398
|
-
# @return [void]
|
|
399
|
-
def cmd_find(args) # rubocop:disable Metrics/AbcSize,Metrics/MethodLength
|
|
400
|
-
if args.empty?
|
|
401
|
-
puts OutputFormatter.warning("Usage: find CLASS_NAME")
|
|
402
|
-
return
|
|
403
|
-
end
|
|
404
|
-
|
|
405
|
-
query = args.join(" ")
|
|
406
|
-
results = @repository.search(query, types: [:class])
|
|
407
|
-
|
|
408
|
-
if results[:class].empty?
|
|
409
|
-
puts OutputFormatter.warning("No classes found matching '#{query}'")
|
|
410
|
-
else
|
|
411
|
-
@last_results = results[:class]
|
|
412
|
-
|
|
413
|
-
puts OutputFormatter.colorize(
|
|
414
|
-
"Found #{@last_results.size} class(es):", :cyan
|
|
415
|
-
)
|
|
416
|
-
@last_results.each_with_index do |qname, i|
|
|
417
|
-
puts " #{i + 1}. #{qname}"
|
|
418
|
-
end
|
|
419
|
-
puts ""
|
|
420
|
-
puts "Use 'show NUMBER' to view details"
|
|
421
|
-
end
|
|
422
|
-
end
|
|
423
|
-
|
|
424
|
-
# Query: Show details
|
|
425
|
-
#
|
|
426
|
-
# @param args [Array<String>] Command arguments
|
|
427
|
-
# @return [void]
|
|
428
|
-
def cmd_show(args) # rubocop:disable Metrics/AbcSize,Metrics/MethodLength
|
|
429
|
-
if args.empty?
|
|
430
|
-
puts OutputFormatter.warning(
|
|
431
|
-
"Usage: show class QNAME | show package PATH | show NUMBER",
|
|
432
|
-
)
|
|
433
|
-
return
|
|
434
|
-
end
|
|
435
|
-
|
|
436
|
-
subcommand = args[0].downcase
|
|
437
|
-
|
|
438
|
-
case subcommand
|
|
439
|
-
when "class"
|
|
440
|
-
show_class(args[1..].join(" "))
|
|
441
|
-
when "package"
|
|
442
|
-
show_package(args[1..].join(" "))
|
|
443
|
-
when /^\d+$/
|
|
444
|
-
show_numbered_result(subcommand.to_i)
|
|
445
|
-
else
|
|
446
|
-
# Try as class name
|
|
447
|
-
show_class(args.join(" "))
|
|
448
|
-
end
|
|
449
|
-
end
|
|
450
|
-
|
|
451
|
-
# Query: Full-text search
|
|
452
|
-
#
|
|
453
|
-
# @param args [Array<String>] Command arguments
|
|
454
|
-
# @return [void]
|
|
455
|
-
def cmd_search(args) # rubocop:disable Metrics/MethodLength
|
|
456
|
-
if args.empty?
|
|
457
|
-
puts OutputFormatter.warning("Usage: search QUERY")
|
|
458
|
-
return
|
|
459
|
-
end
|
|
460
|
-
|
|
461
|
-
query = args.join(" ")
|
|
462
|
-
results = @repository.search(query)
|
|
463
|
-
|
|
464
|
-
if results.values.all?(&:empty?)
|
|
465
|
-
puts OutputFormatter.warning("No results found for '#{query}'")
|
|
466
|
-
else
|
|
467
|
-
display_search_results(results)
|
|
468
|
-
end
|
|
469
|
-
end
|
|
470
|
-
|
|
471
|
-
# Bookmarks: Manage bookmarks
|
|
472
|
-
#
|
|
473
|
-
# @param args [Array<String>] Command arguments
|
|
474
|
-
# @return [void]
|
|
475
|
-
def cmd_bookmark(args) # rubocop:disable Metrics/MethodLength
|
|
476
|
-
return bookmark_list if args.empty?
|
|
477
|
-
|
|
478
|
-
subcommand = args[0].downcase
|
|
479
|
-
|
|
480
|
-
case subcommand
|
|
481
|
-
when "add"
|
|
482
|
-
bookmark_add(args[1])
|
|
483
|
-
when "list"
|
|
484
|
-
bookmark_list
|
|
485
|
-
when "go"
|
|
486
|
-
bookmark_go(args[1])
|
|
487
|
-
when "rm", "remove"
|
|
488
|
-
bookmark_remove(args[1])
|
|
489
|
-
else
|
|
490
|
-
# Quick jump
|
|
491
|
-
bookmark_go(subcommand)
|
|
492
|
-
end
|
|
493
|
-
end
|
|
494
|
-
|
|
495
|
-
# Results: Show last search results
|
|
496
|
-
#
|
|
497
|
-
# @param _args [Array<String>] Command arguments (unused)
|
|
498
|
-
# @return [void]
|
|
499
|
-
def cmd_results(_args)
|
|
500
|
-
if @last_results.nil? || @last_results.empty?
|
|
501
|
-
puts OutputFormatter.warning("No previous results")
|
|
502
|
-
else
|
|
503
|
-
puts OutputFormatter.colorize(
|
|
504
|
-
"Last results (#{@last_results.size}):", :cyan
|
|
505
|
-
)
|
|
506
|
-
@last_results.each_with_index do |item, i|
|
|
507
|
-
puts " #{i + 1}. #{item}"
|
|
508
|
-
end
|
|
509
|
-
end
|
|
510
|
-
end
|
|
511
|
-
|
|
512
|
-
# Export: Export results
|
|
513
|
-
#
|
|
514
|
-
# @param args [Array<String>] Command arguments
|
|
515
|
-
# @return [void]
|
|
516
|
-
def cmd_export(args) # rubocop:disable Metrics/AbcSize,Metrics/CyclomaticComplexity,Metrics/MethodLength
|
|
517
|
-
if @last_results.nil? || @last_results.empty?
|
|
518
|
-
puts OutputFormatter.warning("No results to export")
|
|
519
|
-
return
|
|
520
|
-
end
|
|
521
|
-
|
|
522
|
-
if args.size < 3 || args[0] != "last"
|
|
523
|
-
puts OutputFormatter.warning("Usage: export last FORMAT FILE")
|
|
524
|
-
return
|
|
525
|
-
end
|
|
526
|
-
|
|
527
|
-
format = args[1].downcase
|
|
528
|
-
file_path = args[2]
|
|
529
|
-
|
|
530
|
-
case format
|
|
531
|
-
when "csv"
|
|
532
|
-
export_csv(file_path)
|
|
533
|
-
when "json"
|
|
534
|
-
export_json(file_path)
|
|
535
|
-
when "yaml"
|
|
536
|
-
export_yaml(file_path)
|
|
537
|
-
else
|
|
538
|
-
puts OutputFormatter.error("Unsupported format: #{format}")
|
|
539
|
-
end
|
|
540
|
-
end
|
|
541
|
-
|
|
542
|
-
# Utilities: Show help
|
|
543
|
-
#
|
|
544
|
-
# @param args [Array<String>] Command arguments
|
|
545
|
-
# @return [void]
|
|
546
|
-
def cmd_help(args)
|
|
547
|
-
if args.empty?
|
|
548
|
-
display_general_help
|
|
549
|
-
else
|
|
550
|
-
display_command_help(args[0])
|
|
551
|
-
end
|
|
552
|
-
end
|
|
553
|
-
|
|
554
|
-
# Utilities: Show command history
|
|
555
|
-
#
|
|
556
|
-
# @param _args [Array<String>] Command arguments (unused)
|
|
557
|
-
# @return [void]
|
|
558
|
-
def cmd_history(_args)
|
|
559
|
-
history = Readline::HISTORY.to_a.last(20)
|
|
560
|
-
history.each_with_index do |line, i|
|
|
561
|
-
puts "#{i + 1}. #{line}"
|
|
562
|
-
end
|
|
563
|
-
end
|
|
564
|
-
|
|
565
|
-
# Utilities: Clear screen
|
|
566
|
-
#
|
|
567
|
-
# @param _args [Array<String>] Command arguments (unused)
|
|
568
|
-
# @return [void]
|
|
569
|
-
def cmd_clear(_args)
|
|
570
|
-
print "\e[2J\e[H"
|
|
571
|
-
end
|
|
572
|
-
|
|
573
|
-
# Utilities: Show configuration
|
|
574
|
-
#
|
|
575
|
-
# @param _args [Array<String>] Command arguments (unused)
|
|
576
|
-
# @return [void]
|
|
577
|
-
def cmd_config(_args)
|
|
578
|
-
puts OutputFormatter.colorize("Current Configuration:", :cyan)
|
|
579
|
-
@config.each do |key, value|
|
|
580
|
-
puts " #{key}: #{value}"
|
|
581
|
-
end
|
|
582
|
-
end
|
|
583
|
-
|
|
584
|
-
# Utilities: Show quick statistics
|
|
585
|
-
#
|
|
586
|
-
# @param _args [Array<String>] Command arguments (unused)
|
|
587
|
-
# @return [void]
|
|
588
|
-
def cmd_stats(_args)
|
|
589
|
-
stats = @repository.statistics
|
|
590
|
-
|
|
591
|
-
if @config[:icons]
|
|
592
|
-
puts EnhancedFormatter.format_stats_enhanced(stats)
|
|
593
|
-
else
|
|
594
|
-
puts OutputFormatter.format_stats(stats, detailed: false)
|
|
595
|
-
end
|
|
596
|
-
end
|
|
597
|
-
|
|
598
|
-
# Show class details
|
|
599
|
-
#
|
|
600
|
-
# @param qname [String] Qualified class name
|
|
601
|
-
# @return [void]
|
|
602
|
-
def show_class(qname) # rubocop:disable Metrics/AbcSize,Metrics/CyclomaticComplexity,Metrics/MethodLength,Metrics/PerceivedComplexity
|
|
603
|
-
cls = @repository.find_class(qname)
|
|
604
|
-
|
|
605
|
-
unless cls
|
|
606
|
-
puts OutputFormatter.error("Class not found: #{qname}")
|
|
607
|
-
return
|
|
608
|
-
end
|
|
609
|
-
|
|
610
|
-
if @config[:icons]
|
|
611
|
-
puts EnhancedFormatter.format_class_details_enhanced(cls)
|
|
612
|
-
else
|
|
613
|
-
puts OutputFormatter.colorize("Class: #{qname}", :cyan)
|
|
614
|
-
puts "=" * 50
|
|
615
|
-
puts ""
|
|
616
|
-
puts "Name: #{cls.name}"
|
|
617
|
-
|
|
618
|
-
if cls.respond_to?(:attributes) && cls.attributes &&
|
|
619
|
-
!cls.attributes.empty?
|
|
620
|
-
puts ""
|
|
621
|
-
puts OutputFormatter.colorize("Attributes:", :yellow)
|
|
622
|
-
cls.attributes.each do |attr|
|
|
623
|
-
puts " - #{attr.name}: #{attr.type}"
|
|
624
|
-
end
|
|
625
|
-
end
|
|
626
|
-
end
|
|
627
|
-
end
|
|
628
|
-
|
|
629
|
-
# Show package details
|
|
630
|
-
#
|
|
631
|
-
# @param path [String] Package path
|
|
632
|
-
# @return [void]
|
|
633
|
-
def show_package(path) # rubocop:disable Metrics/AbcSize,Metrics/MethodLength
|
|
634
|
-
path = resolve_path(path)
|
|
635
|
-
pkg = @repository.find_package(path)
|
|
636
|
-
|
|
637
|
-
unless pkg
|
|
638
|
-
puts OutputFormatter.error("Package not found: #{path}")
|
|
639
|
-
return
|
|
640
|
-
end
|
|
641
|
-
|
|
642
|
-
puts OutputFormatter.colorize("Package: #{path}", :cyan)
|
|
643
|
-
puts "=" * 50
|
|
644
|
-
puts ""
|
|
645
|
-
puts "Name: #{pkg.name}"
|
|
646
|
-
puts ""
|
|
647
|
-
|
|
648
|
-
classes = @repository.classes_in_package(path)
|
|
649
|
-
puts OutputFormatter.colorize("Classes (#{classes.size}):", :yellow)
|
|
650
|
-
classes.each do |cls|
|
|
651
|
-
icon = @config[:icons] ? "#{EnhancedFormatter::ICONS[:class]} " : ""
|
|
652
|
-
puts " #{icon}#{cls.name}"
|
|
653
|
-
end
|
|
654
|
-
end
|
|
655
|
-
|
|
656
|
-
# Show numbered result from last search
|
|
657
|
-
#
|
|
658
|
-
# @param number [Integer] Result number (1-indexed)
|
|
659
|
-
# @return [void]
|
|
660
|
-
def show_numbered_result(number) # rubocop:disable Metrics/MethodLength
|
|
661
|
-
if @last_results.nil? || @last_results.empty?
|
|
662
|
-
puts OutputFormatter.warning("No previous results")
|
|
663
|
-
return
|
|
664
|
-
end
|
|
665
|
-
|
|
666
|
-
index = number - 1
|
|
667
|
-
if index.negative? || index >= @last_results.size
|
|
668
|
-
puts OutputFormatter.error("Invalid result number: #{number}")
|
|
669
|
-
return
|
|
670
|
-
end
|
|
671
|
-
|
|
672
|
-
item = @last_results[index]
|
|
673
|
-
show_class(item)
|
|
674
|
-
end
|
|
675
|
-
|
|
676
|
-
# Display search results
|
|
677
|
-
#
|
|
678
|
-
# @param results [Hash] Search results by type
|
|
679
|
-
# @return [void]
|
|
680
|
-
def display_search_results(results) # rubocop:disable Metrics/AbcSize,Metrics/CyclomaticComplexity,Metrics/MethodLength,Metrics/PerceivedComplexity
|
|
681
|
-
results.each do |type, items| # rubocop:disable Metrics/BlockLength
|
|
682
|
-
next if items.empty?
|
|
683
|
-
|
|
684
|
-
puts ""
|
|
685
|
-
puts OutputFormatter.colorize(
|
|
686
|
-
"#{type.to_s.capitalize} Results (#{items.size}):", :cyan
|
|
687
|
-
)
|
|
688
|
-
|
|
689
|
-
case type
|
|
690
|
-
when :class
|
|
691
|
-
@last_results = items
|
|
692
|
-
items.each_with_index do |qname, i|
|
|
693
|
-
icon = if @config[:icons]
|
|
694
|
-
"#{EnhancedFormatter::ICONS[:class]} "
|
|
695
|
-
else
|
|
696
|
-
""
|
|
697
|
-
end
|
|
698
|
-
puts " #{i + 1}. #{icon}#{qname}"
|
|
699
|
-
end
|
|
700
|
-
puts ""
|
|
701
|
-
puts "Use 'show NUMBER' to view details"
|
|
702
|
-
when :attribute
|
|
703
|
-
items.each do |item|
|
|
704
|
-
puts " - #{item[:class_name]}::#{item[:attribute_name]} : " \
|
|
705
|
-
"#{item[:type]}"
|
|
706
|
-
end
|
|
707
|
-
when :association
|
|
708
|
-
items.each do |item|
|
|
709
|
-
icon = if @config[:icons]
|
|
710
|
-
"#{EnhancedFormatter::ICONS[:association]} "
|
|
711
|
-
else
|
|
712
|
-
""
|
|
713
|
-
end
|
|
714
|
-
puts " #{icon}#{item[:source]} → #{item[:target]}"
|
|
715
|
-
end
|
|
716
|
-
end
|
|
717
|
-
end
|
|
718
|
-
end
|
|
719
|
-
|
|
720
|
-
# Add bookmark
|
|
721
|
-
#
|
|
722
|
-
# @param name [String] Bookmark name
|
|
723
|
-
# @return [void]
|
|
724
|
-
def bookmark_add(name)
|
|
725
|
-
if name.nil? || name.empty?
|
|
726
|
-
puts OutputFormatter.warning("Usage: bookmark add NAME")
|
|
727
|
-
return
|
|
728
|
-
end
|
|
729
|
-
|
|
730
|
-
target = @last_results&.first || @current_path
|
|
731
|
-
@bookmarks[name] = target
|
|
732
|
-
puts OutputFormatter.success("Bookmark '#{name}' added: #{target}")
|
|
733
|
-
end
|
|
734
|
-
|
|
735
|
-
# List bookmarks
|
|
736
|
-
#
|
|
737
|
-
# @return [void]
|
|
738
|
-
def bookmark_list # rubocop:disable Metrics/MethodLength
|
|
739
|
-
if @bookmarks.empty?
|
|
740
|
-
puts "No bookmarks"
|
|
741
|
-
else
|
|
742
|
-
puts OutputFormatter.colorize("Bookmarks:", :cyan)
|
|
743
|
-
@bookmarks.each do |name, target|
|
|
744
|
-
icon = if @config[:icons]
|
|
745
|
-
"#{EnhancedFormatter::ICONS[:favorite]} "
|
|
746
|
-
else
|
|
747
|
-
""
|
|
748
|
-
end
|
|
749
|
-
puts " #{icon}#{name} → #{target}"
|
|
750
|
-
end
|
|
751
|
-
end
|
|
752
|
-
end
|
|
753
|
-
|
|
754
|
-
# Jump to bookmark
|
|
755
|
-
#
|
|
756
|
-
# @param name [String] Bookmark name
|
|
757
|
-
# @return [void]
|
|
758
|
-
def bookmark_go(name) # rubocop:disable Metrics/MethodLength
|
|
759
|
-
unless @bookmarks.key?(name)
|
|
760
|
-
puts OutputFormatter.error("Bookmark not found: #{name}")
|
|
761
|
-
return
|
|
762
|
-
end
|
|
763
|
-
|
|
764
|
-
target = @bookmarks[name]
|
|
765
|
-
if @repository.find_package(target)
|
|
766
|
-
unless @path_history.last == @current_path
|
|
767
|
-
@path_history << @current_path
|
|
768
|
-
end
|
|
769
|
-
@current_path = target
|
|
770
|
-
puts "Changed to: #{target}"
|
|
771
|
-
else
|
|
772
|
-
puts OutputFormatter.warning(
|
|
773
|
-
"Bookmark target no longer exists: #{target}",
|
|
774
|
-
)
|
|
775
|
-
end
|
|
776
|
-
end
|
|
777
|
-
|
|
778
|
-
# Remove bookmark
|
|
779
|
-
#
|
|
780
|
-
# @param name [String] Bookmark name
|
|
781
|
-
# @return [void]
|
|
782
|
-
def bookmark_remove(name)
|
|
783
|
-
if @bookmarks.delete(name)
|
|
784
|
-
puts OutputFormatter.success("Bookmark '#{name}' removed")
|
|
785
|
-
else
|
|
786
|
-
puts OutputFormatter.error("Bookmark not found: #{name}")
|
|
787
|
-
end
|
|
788
|
-
end
|
|
789
|
-
|
|
790
|
-
# Export results to CSV
|
|
791
|
-
#
|
|
792
|
-
# @param file_path [String] Output file path
|
|
793
|
-
# @return [void]
|
|
794
|
-
def export_csv(file_path)
|
|
795
|
-
require "csv"
|
|
796
|
-
|
|
797
|
-
CSV.open(file_path, "w") do |csv|
|
|
798
|
-
csv << ["Qualified Name"]
|
|
799
|
-
@last_results.each do |qname|
|
|
800
|
-
csv << [qname]
|
|
801
|
-
end
|
|
802
|
-
end
|
|
803
|
-
|
|
804
|
-
puts OutputFormatter.success("Exported #{@last_results.size} " \
|
|
805
|
-
"results to #{file_path}")
|
|
806
|
-
end
|
|
807
|
-
|
|
808
|
-
# Export results to JSON
|
|
809
|
-
#
|
|
810
|
-
# @param file_path [String] Output file path
|
|
811
|
-
# @return [void]
|
|
812
|
-
def export_json(file_path)
|
|
813
|
-
require "json"
|
|
814
|
-
|
|
815
|
-
File.write(file_path, JSON.pretty_generate(@last_results))
|
|
816
|
-
puts OutputFormatter.success("Exported #{@last_results.size} " \
|
|
817
|
-
"results to #{file_path}")
|
|
818
|
-
end
|
|
819
|
-
|
|
820
|
-
# Export results to YAML
|
|
821
|
-
#
|
|
822
|
-
# @param file_path [String] Output file path
|
|
823
|
-
# @return [void]
|
|
824
|
-
def export_yaml(file_path)
|
|
825
|
-
require "yaml"
|
|
826
|
-
|
|
827
|
-
File.write(file_path, @last_results.to_yaml)
|
|
828
|
-
puts OutputFormatter.success("Exported #{@last_results.size} " \
|
|
829
|
-
"results to #{file_path}")
|
|
830
|
-
end
|
|
831
|
-
|
|
832
|
-
# Display general help
|
|
833
|
-
#
|
|
834
|
-
# @return [void]
|
|
835
|
-
def display_general_help # rubocop:disable Metrics/AbcSize,Metrics/MethodLength
|
|
836
|
-
puts OutputFormatter.colorize("Available Commands:", :cyan)
|
|
837
|
-
puts ""
|
|
838
|
-
|
|
839
|
-
puts OutputFormatter.colorize("Navigation:", :yellow)
|
|
840
|
-
puts " cd PATH Change to package path"
|
|
841
|
-
puts " pwd Print current path"
|
|
842
|
-
puts " ls [PATH] List packages"
|
|
843
|
-
puts " tree [PATH] Show package tree"
|
|
844
|
-
puts " up Go to parent package"
|
|
845
|
-
puts " root Go to ModelRoot"
|
|
846
|
-
puts " back Go to previous location"
|
|
847
|
-
puts ""
|
|
848
|
-
|
|
849
|
-
puts OutputFormatter.colorize("Query:", :yellow)
|
|
850
|
-
puts " find CLASS Find class (fuzzy search)"
|
|
851
|
-
puts " show class QNAME Show class details"
|
|
852
|
-
puts " show package PATH Show package details"
|
|
853
|
-
puts " show NUMBER Show numbered result"
|
|
854
|
-
puts " search QUERY Full-text search"
|
|
855
|
-
puts " ? QUERY Alias for search"
|
|
856
|
-
puts ""
|
|
857
|
-
|
|
858
|
-
puts OutputFormatter.colorize("Bookmarks:", :yellow)
|
|
859
|
-
puts " bookmark add NAME Bookmark current location"
|
|
860
|
-
puts " bookmark list List bookmarks"
|
|
861
|
-
puts " bookmark go NAME Jump to bookmark"
|
|
862
|
-
puts " bookmark rm NAME Remove bookmark"
|
|
863
|
-
puts " bm NAME Quick jump"
|
|
864
|
-
puts ""
|
|
865
|
-
|
|
866
|
-
puts OutputFormatter.colorize("Utilities:", :yellow)
|
|
867
|
-
puts " help [COMMAND] Show help"
|
|
868
|
-
puts " history Show command history"
|
|
869
|
-
puts " clear Clear screen"
|
|
870
|
-
puts " config Show configuration"
|
|
871
|
-
puts " stats Show statistics"
|
|
872
|
-
puts " exit, quit, q Exit shell"
|
|
873
|
-
end
|
|
874
|
-
|
|
875
|
-
# Display command-specific help
|
|
876
|
-
#
|
|
877
|
-
# @param command [String] Command name
|
|
878
|
-
# @return [void]
|
|
879
|
-
def display_command_help(command)
|
|
880
|
-
# Command-specific help would go here
|
|
881
|
-
puts "Help for '#{command}' not yet implemented"
|
|
882
|
-
puts "Use 'help' for general help"
|
|
883
|
-
end
|
|
884
|
-
|
|
885
|
-
# Resolve path relative to current location
|
|
886
|
-
#
|
|
887
|
-
# @param path [String] Path to resolve
|
|
888
|
-
# @return [String] Resolved path
|
|
889
|
-
def resolve_path(path) # rubocop:disable Metrics/AbcSize,Metrics/CyclomaticComplexity,Metrics/MethodLength,Metrics/PerceivedComplexity
|
|
890
|
-
return path if path.start_with?("ModelRoot")
|
|
891
|
-
return @current_path if path == "."
|
|
892
|
-
return "ModelRoot" if path == "/"
|
|
893
|
-
|
|
894
|
-
if path.start_with?("../")
|
|
895
|
-
# Go up and then navigate
|
|
896
|
-
parts = @current_path.split("::")
|
|
897
|
-
path.scan("../").each { parts.pop }
|
|
898
|
-
remaining = path.gsub(/^(\.\.\/)+/, "")
|
|
899
|
-
new_path = parts + remaining.split("/")
|
|
900
|
-
new_path.join("::")
|
|
901
|
-
elsif path.start_with?("./")
|
|
902
|
-
# Relative to current
|
|
903
|
-
"#{@current_path}::#{path[2..]}"
|
|
904
|
-
else
|
|
905
|
-
# Append to current
|
|
906
|
-
@current_path == "ModelRoot" ? path : "#{@current_path}::#{path}"
|
|
907
|
-
end
|
|
221
|
+
].grep(/^#{Regexp.escape(word)}/)
|
|
908
222
|
end
|
|
909
223
|
end
|
|
910
224
|
end
|