Difference between revisions of "CA UI Implementation"
(→Graphical User Interface: + class diagram) |
m (Manus moved page User:Stefan/Code Analysis/UI Implementation to CA UI Implementation without leaving a redirect) |
||
(6 intermediate revisions by 2 users not shown) | |||
Line 1: | Line 1: | ||
− | + | [[Category:Code Analysis]] | |
− | + | ||
− | + | ||
== Graphical User Interface == | == Graphical User Interface == | ||
− | The classes of the graphical user interface of | + | The classes of the graphical user interface of Inspector Eiffel are all located in the ''interface'' cluster of EiffelStudio, in the subfolder ''graphical > tools > code_analysis''. Here is a short overview of what the single classes do: |
; <e>{ES_CODE_ANALYSIS_TOOL}</e> : Represents the code analysis GUI tool. Contains the tool title and icon, otherwise not much interesting stuff. | ; <e>{ES_CODE_ANALYSIS_TOOL}</e> : Represents the code analysis GUI tool. Contains the tool title and icon, otherwise not much interesting stuff. | ||
Line 14: | Line 12: | ||
; <e>{ES_CA_FIX_EXECUTOR}</e> : This class [[User:Stefan/Code Analysis/Using Analysis Results#Fixing Rule Violations|fixes]] a rule violation that has been found by the code analysis tool. | ; <e>{ES_CA_FIX_EXECUTOR}</e> : This class [[User:Stefan/Code Analysis/Using Analysis Results#Fixing Rule Violations|fixes]] a rule violation that has been found by the code analysis tool. | ||
− | These are roughly the class relations for the | + | These are roughly the class relations for the Inspector Eiffel GUI: |
− | [[File:CA GUI Diagram.png|thumb|center|650px|The most interesting class relations of the | + | [[File:CA GUI Diagram.png|thumb|center|650px|The most interesting class relations of the Inspector Eiffel GUI.]] |
=== Caching === | === Caching === | ||
− | + | It is a common case that GUI users run the code analyzer again after having made some changes to the code. We do not need to analyze the same unchanged code again and again. Therefore code analysis caches the results in memory. This only applies to the GUI mode. | |
− | The whole command-line functionality of the code analyzer is located in the class <e>{EWB_CODE_ANALYSIS}</e>. It is located in the ''tty'' cluster of | + | Code analysis uses cached results exactly in one case: when the whole system is analyzed and the previous analysis was on the whole system, too. |
+ | |||
+ | The caching functionality is implemented in <e>{ES_CODE_ANALYSIS_COMMAND}</e>. When the command for analyzing the system is executed, the timestamps of the last modification of the classes are stored in <e>analysis_timestamp : HASH_TABLE [INTEGER, CLASS_I]</e> before the analysis. Note that the cached results (the rule violations) themselves are managed by <e>{CA_CODE_ANALYZER}</e>. The only difference to a non-cached analysis is that the rule violations are not deleted by <e>{ES_CODE_ANALYSIS_COMMAND}</e> before the next analysis. Then, in case the next command is also for analyzing the whole system, the current timestamps are compared to the stored timestamps. Any class that has been changed in the meantime will be analyzed again; for any unchanged class the rule violations are taken from the cache. | ||
+ | |||
+ | === Example Command: Analyze One Class === | ||
+ | |||
+ | We will now roughly go through the code that is executed on the GUI part when the user wants to analyze a single class. As mentioned in Chapter [[User:Stefan/Code Analysis/Running the Analyzer|Running the Analyzer]], this can be done using the class context menu or by dropping the class stone on the button ''Analyze Item''. | ||
+ | |||
+ | In any case <e>{ES_CODE_ANALYSIS_COMMAND}.execute_with_stone</e> is called, which delegates to <e>execute_with_stone_content</e>: | ||
+ | |||
+ | <e> | ||
+ | execute_with_stone (a_stone: STONE) | ||
+ | -- Execute with `a_stone'. | ||
+ | do | ||
+ | execute_with_stone_content (a_stone, Void) | ||
+ | end | ||
+ | |||
+ | execute_with_stone_content (a_stone: STONE; a_content: SD_CONTENT) | ||
+ | -- Execute with `a_stone'. | ||
+ | local | ||
+ | l_save_confirm: ES_DISCARDABLE_COMPILE_SAVE_FILES_PROMPT | ||
+ | l_classes: DS_ARRAYED_LIST [CLASS_I] | ||
+ | do | ||
+ | -- Show the tool right from the start. | ||
+ | show_ca_tool | ||
+ | |||
+ | if not eiffel_project.is_compiling then | ||
+ | if window_manager.has_modified_windows then | ||
+ | create l_classes.make_default | ||
+ | window_manager.all_modified_classes.do_all (agent l_classes.force_last) | ||
+ | create l_save_confirm.make (l_classes) | ||
+ | l_save_confirm.set_button_action (l_save_confirm.dialog_buttons.yes_button, agent save_compile_and_analyze (a_stone)) | ||
+ | l_save_confirm.set_button_action (l_save_confirm.dialog_buttons.no_button, agent compile_and_analyze (a_stone)) | ||
+ | l_save_confirm.show_on_active_window | ||
+ | else | ||
+ | compile_and_analyze (a_stone) | ||
+ | end | ||
+ | end | ||
+ | end | ||
+ | </e> | ||
+ | |||
+ | If there are modified, unsaved windows a save confirmation dialog is displayed. Eventually program flow passes on to <e>compile_and_analyze</e>: | ||
+ | |||
+ | <e> | ||
+ | compile_and_analyze (a_stone: STONE) | ||
+ | -- Compile project and perform analysis of stone `a_stone'. | ||
+ | local | ||
+ | l_helper: ES_CODE_ANALYSIS_BENCH_HELPER | ||
+ | l_dialog: ES_INFORMATION_PROMPT | ||
+ | do | ||
+ | -- Compile the project and only analyze if the compilation was successful. | ||
+ | eiffel_project.quick_melt (True, True, True) | ||
+ | if eiffel_project.successful then | ||
+ | create l_helper | ||
+ | if l_helper.code_analyzer.is_running then | ||
+ | create l_dialog.make_standard (ca_messages.already_running_long) | ||
+ | l_dialog.show_on_active_window | ||
+ | else | ||
+ | perform_analysis (a_stone) | ||
+ | end | ||
+ | end | ||
+ | end | ||
+ | </e> | ||
+ | |||
+ | <e>eiffel_project.quick_melt</e> starts the compilation. A successful compilation is required for code analysis; otherwise nothing is analyzed. After compilation has succeeded we check if code analysis is already running. If this is the case then a dialog is displayed. If on the other hand this last possible obstacle is not present we finally start analyzing by calling <e>perform_analysis</e>. | ||
+ | |||
+ | <e> | ||
+ | perform_analysis (a_stone: STONE) | ||
+ | -- Analyze `a_stone' only. | ||
+ | local | ||
+ | l_helper: ES_CODE_ANALYSIS_BENCH_HELPER | ||
+ | l_scope_label: EV_LABEL | ||
+ | do | ||
+ | -- For simplicity let us assume that `a_stone' does not | ||
+ | -- correspond to the system or is equivalent to it. | ||
+ | last_was_analyze_all := False | ||
+ | |||
+ | create l_helper | ||
+ | code_analyzer := l_helper.code_analyzer | ||
+ | code_analyzer.clear_classes_to_analyze | ||
+ | code_analyzer.rule_violations.wipe_out | ||
+ | |||
+ | l_scope_label := ca_tool.panel.scope_label | ||
+ | |||
+ | if attached {CLASSC_STONE} a_stone as s then | ||
+ | code_analyzer.add_class (s.class_i.config_class) | ||
+ | l_scope_label.set_text (s.class_name) | ||
+ | l_scope_label.set_foreground_color (create {EV_COLOR}.make_with_8_bit_rgb (140, 140, 255)) | ||
+ | l_scope_label.set_pebble (s) | ||
+ | l_scope_label.set_pick_and_drop_mode | ||
+ | l_scope_label.set_tooltip (ca_messages.class_scope_tooltip) | ||
+ | elseif [...] | ||
+ | [...] | ||
+ | end | ||
+ | |||
+ | disable_tool_button | ||
+ | window_manager.display_message (ca_messages.status_bar_running) | ||
+ | code_analyzer.add_completed_action (agent analysis_completed) | ||
+ | code_analyzer.analyze | ||
+ | end | ||
+ | </e> | ||
+ | |||
+ | (The code that deals with stones other than classes is omitted.) | ||
+ | |||
+ | At the start of the routine the code analyzer instance is retrieved from the helper class. All classes that may have been addedbefore, are removed. All previous rule violations are removed as well. The if clause creates a stone for the ''Last Scope'' label in the graphical panel. Then, the button in the tool is disabled so that starting another analysis is prevented until the current one has completed. Finally, the analysis is started. As soon as the analysis has completed <e>{ES_CODE_ANALYSIS_COMMAND}.analysis_completed</e> is called. In this procedure the rule violations (and possibly the exceptions) are retrieved from the code analyzer and displayed in the list in the tool panel. | ||
+ | |||
+ | == Command-Line Interface == | ||
+ | |||
+ | The whole command-line functionality of the code analyzer is located in the class <e>{EWB_CODE_ANALYSIS}</e>. It is located in the ''tty'' cluster of EiffelStudio. <e>{EWB_CODE_ANALYSIS}</e> is invoked by <e>{ES}</e>, the root class for the ''batch'' (command-line) version of EiffelStudio. In <e>{ES}</e>, the invocation looks as follows: | ||
<e> | <e> |
Latest revision as of 14:21, 3 June 2014
Contents
Graphical User Interface
The classes of the graphical user interface of Inspector Eiffel are all located in the interface cluster of EiffelStudio, in the subfolder graphical > tools > code_analysis. Here is a short overview of what the single classes do:
-
{ES_CODE_ANALYSIS_TOOL}
- Represents the code analysis GUI tool. Contains the tool title and icon, otherwise not much interesting stuff.
-
{ES_CODE_ANALYSIS_TOOL_PANEL}
- The graphical panel for the code analysis tool. It contains buttons, labels, the rule violations table view, and other user interface elements.
-
{ES_CODE_ANALYSIS_COMMAND}
- The command to launch the code analyzer. It can be added to toolbars and menus. It can be executed using stones. This class also handles the caching.
-
{ES_CODE_ANALYSIS_BENCH_HELPER}
- A helper class for the integration of the code analysis tool. It contains shared instances of
{CA_CODE_ANALYZER}
and{ES_CODE_ANALYSIS_COMMAND}
, which are used by the GUI. -
{ES_CA_SHOW_PREFERENCES_COMMAND}
- The command is used by the Preferences button in the panel.
-
{ES_CA_FIX_EXECUTOR}
- This class fixes a rule violation that has been found by the code analysis tool.
These are roughly the class relations for the Inspector Eiffel GUI:
Caching
It is a common case that GUI users run the code analyzer again after having made some changes to the code. We do not need to analyze the same unchanged code again and again. Therefore code analysis caches the results in memory. This only applies to the GUI mode.
Code analysis uses cached results exactly in one case: when the whole system is analyzed and the previous analysis was on the whole system, too.
The caching functionality is implemented in {ES_CODE_ANALYSIS_COMMAND}
. When the command for analyzing the system is executed, the timestamps of the last modification of the classes are stored in analysis_timestamp : HASH_TABLE [INTEGER, CLASS_I]
before the analysis. Note that the cached results (the rule violations) themselves are managed by {CA_CODE_ANALYZER}
. The only difference to a non-cached analysis is that the rule violations are not deleted by {ES_CODE_ANALYSIS_COMMAND}
before the next analysis. Then, in case the next command is also for analyzing the whole system, the current timestamps are compared to the stored timestamps. Any class that has been changed in the meantime will be analyzed again; for any unchanged class the rule violations are taken from the cache.
Example Command: Analyze One Class
We will now roughly go through the code that is executed on the GUI part when the user wants to analyze a single class. As mentioned in Chapter Running the Analyzer, this can be done using the class context menu or by dropping the class stone on the button Analyze Item.
In any case {ES_CODE_ANALYSIS_COMMAND}.execute_with_stone
is called, which delegates to execute_with_stone_content
:
execute_with_stone (a_stone: STONE) -- Execute with `a_stone'. do execute_with_stone_content (a_stone, Void) end execute_with_stone_content (a_stone: STONE; a_content: SD_CONTENT) -- Execute with `a_stone'. local l_save_confirm: ES_DISCARDABLE_COMPILE_SAVE_FILES_PROMPT l_classes: DS_ARRAYED_LIST [CLASS_I] do -- Show the tool right from the start. show_ca_tool if not eiffel_project.is_compiling then if window_manager.has_modified_windows then create l_classes.make_default window_manager.all_modified_classes.do_all (agent l_classes.force_last) create l_save_confirm.make (l_classes) l_save_confirm.set_button_action (l_save_confirm.dialog_buttons.yes_button, agent save_compile_and_analyze (a_stone)) l_save_confirm.set_button_action (l_save_confirm.dialog_buttons.no_button, agent compile_and_analyze (a_stone)) l_save_confirm.show_on_active_window else compile_and_analyze (a_stone) end end end
If there are modified, unsaved windows a save confirmation dialog is displayed. Eventually program flow passes on to compile_and_analyze
:
compile_and_analyze (a_stone: STONE) -- Compile project and perform analysis of stone `a_stone'. local l_helper: ES_CODE_ANALYSIS_BENCH_HELPER l_dialog: ES_INFORMATION_PROMPT do -- Compile the project and only analyze if the compilation was successful. eiffel_project.quick_melt (True, True, True) if eiffel_project.successful then create l_helper if l_helper.code_analyzer.is_running then create l_dialog.make_standard (ca_messages.already_running_long) l_dialog.show_on_active_window else perform_analysis (a_stone) end end end
eiffel_project.quick_melt
starts the compilation. A successful compilation is required for code analysis; otherwise nothing is analyzed. After compilation has succeeded we check if code analysis is already running. If this is the case then a dialog is displayed. If on the other hand this last possible obstacle is not present we finally start analyzing by calling perform_analysis
.
perform_analysis (a_stone: STONE) -- Analyze `a_stone' only. local l_helper: ES_CODE_ANALYSIS_BENCH_HELPER l_scope_label: EV_LABEL do -- For simplicity let us assume that `a_stone' does not -- correspond to the system or is equivalent to it. last_was_analyze_all := False create l_helper code_analyzer := l_helper.code_analyzer code_analyzer.clear_classes_to_analyze code_analyzer.rule_violations.wipe_out l_scope_label := ca_tool.panel.scope_label if attached {CLASSC_STONE} a_stone as s then code_analyzer.add_class (s.class_i.config_class) l_scope_label.set_text (s.class_name) l_scope_label.set_foreground_color (create {EV_COLOR}.make_with_8_bit_rgb (140, 140, 255)) l_scope_label.set_pebble (s) l_scope_label.set_pick_and_drop_mode l_scope_label.set_tooltip (ca_messages.class_scope_tooltip) elseif [...] [...] end disable_tool_button window_manager.display_message (ca_messages.status_bar_running) code_analyzer.add_completed_action (agent analysis_completed) code_analyzer.analyze end
(The code that deals with stones other than classes is omitted.)
At the start of the routine the code analyzer instance is retrieved from the helper class. All classes that may have been addedbefore, are removed. All previous rule violations are removed as well. The if clause creates a stone for the Last Scope label in the graphical panel. Then, the button in the tool is disabled so that starting another analysis is prevented until the current one has completed. Finally, the analysis is started. As soon as the analysis has completed {ES_CODE_ANALYSIS_COMMAND}.analysis_completed
is called. In this procedure the rule violations (and possibly the exceptions) are retrieved from the code analyzer and displayed in the list in the tool panel.
Command-Line Interface
The whole command-line functionality of the code analyzer is located in the class {EWB_CODE_ANALYSIS}
. It is located in the tty cluster of EiffelStudio. {EWB_CODE_ANALYSIS}
is invoked by {ES}
, the root class for the batch (command-line) version of EiffelStudio. In {ES}
, the invocation looks as follows:
elseif option.is_equal ("-code-analysis") then l_at_args := arguments_in_range (current_option + 1, argument_count) current_option := argument_count + 1 create {EWB_CODE_ANALYSIS} command.make_with_arguments (l_at_args)
Any command-line arguments after -code-analysis are passed on to {EWB_CODE_ANALYSIS}
. This class, in its creation procedure, processes the arguments as described in Command Line Usage. Classes that were passed as command-line arguments are added to the analyzer. Then the actual execution happens in the procedure execute
. EWB_CODE_ANALYSIS
of course uses the code_analysis library and the previously described interface of CA_CODE_ANALYZER
. After analysis a list of rule violations is output to the command-line. In the code it looks like this:
across l_code_analyzer.rule_violations as l_vlist loop if not l_vlist.item.is_empty then l_has_violations := True -- Always sort the rule violations by the class they are referring to. output_window.add (ca_messages.cmd_class + l_vlist.key.name + "':%N") -- See `{CA_RULE_VIOLATION}.is_less' for information on the sorting. across l_vlist.item as ic loop l_rule_name := ic.item.rule.title l_rule_id := ic.item.rule.id if attached ic.item.location as l_loc then l_line := ic.item.location.line.out l_col := ic.item.location.column.out output_window.add (" (" + l_line + ":" + l_col + "): " + l_rule_name + " (" + l_rule_id + "): ") else -- No location attached. Print without location. output_window.add (" " + l_rule_name + " (" + l_rule_id + "): ") end ic.item.format_violation_description (output_window) output_window.add ("%N") end end end if not l_has_violations then output_window.add (ca_messages.no_issues + "%N") end