1 % (c) 2009-2026 Lehrstuhl fuer Softwaretechnik und Programmiersprachen,
2 % Heinrich Heine Universitaet Duesseldorf
3 % This software is licenced under EPL 1.0 (http://www.eclipse.org/org/documents/epl-v10.html)
4
5 :- module(tools_commands,
6 [ edit_file/1,edit_file/2, diff_files_with_editor/2,
7 show_errors_with_bb_results/0, show_errors_with_bb_results/1, show_source_locations_with_bb_results/1,
8 gen_dot_output/4, gen_plantuml_output/3,
9 valid_dot_output_format/1,
10 get_dot_engine_options/2,
11 get_dot_default_engine_option/1,
12 valid_plantuml_output_format/1,
13 show_pdf_file/1,
14 show_dot_file/1,
15 open_file/1]).
16
17 :- use_module(module_information).
18 :- module_info(group,infrastructure).
19 :- module_info(description,'A few utilities to call external programs/commands.').
20 :- use_module(preferences).
21 :- use_module(debug).
22 :- use_module(tools).
23 :- use_module(error_manager).
24 :- use_module(probsrc(system_call),[system_call_with_diagnostics/5, system_call_with_diagnostics/6]).
25
26 edit_file(F) :- edit_file(F,none).
27 edit_file(FILE,LINE) :-
28 get_preference(path_to_text_editor_launch,EDITOR),
29 edit_file_with_editor(EDITOR,FILE,LINE).
30
31 edit_file_with_editor('',FILE,_LINE) :- !,
32 add_error(edit_file_with_editor,'No text editor set (be sure to set the ProB EDITOR preference), cannot edit: ',FILE).
33 edit_file_with_editor(EDITOR,FILE,LINE) :- number(LINE),
34 tools:get_tail_filename(EDITOR,EdName),
35 editor_understands_plus_line(EdName),!, % bbedit/vim can handle +LINE arg
36 format('Opening file "~w" at LINE ~w using EDITOR "~w"~n',[FILE,LINE,EDITOR]),
37 string_concatenate('+',LINE,LinePosArg),
38 system_call_with_diagnostics(editor_launch,EDITOR,[LinePosArg,FILE],ErrorTextAsCodeList,ExitCode),
39 debug_println(9,exit(ExitCode,ErrorTextAsCodeList)).
40 edit_file_with_editor(EDITOR,FILE,LINE) :- number(LINE),
41 get_tail_filename(EDITOR,atom),!, % atom can handle FILE:LINE
42 format('Opening file "~w" at LINE ~w using EDITOR "~w"~n',[FILE,LINE,EDITOR]),
43 ajoin([FILE,':',LINE],FILE_LINE),
44 system_call_with_diagnostics(editor_launch,EDITOR,[FILE_LINE],ErrorTextAsCodeList,ExitCode),
45 debug_println(9,exit(ExitCode,ErrorTextAsCodeList)).
46 edit_file_with_editor(EDITOR,FILE,_) :-
47 format('Opening file "~w" using EDITOR "~w"~n',[FILE,EDITOR]),
48 system_call_with_diagnostics(editor_launch,EDITOR,[FILE],ErrorTextAsCodeList,ExitCode),
49 debug_println(9,exit(ExitCode,ErrorTextAsCodeList)).
50
51
52 editor_understands_plus_line(bbedit).
53 editor_understands_plus_line(edit). % Text Wrangler
54 editor_understands_plus_line(vim).
55
56
57
58 diff_files_with_editor(FILE1,FILE2) :-
59 DIFFER = '/usr/local/bin/bbdiff', % TO DO: provide preference
60 system_call_with_diagnostics(bbdiff,DIFFER,[FILE1,FILE2],ErrorTextAsCodeList,ExitCode),
61 debug_println(9,exit(ExitCode,ErrorTextAsCodeList)).
62
63
64 :- use_module(library(process)).
65 :- use_module(system_call,[system_call_keep_open/7]).
66 show_errors_with_bb_results :- show_errors_with_bb_results([]).
67 show_errors_with_bb_results(Options) :-
68 stored_error(_,_,_,Span,Options),
69 extract_file_number_and_name(Span,_,_Filename),
70 extract_line_col(Span,_,_,_,_),
71 !,
72 Command = '/usr/local/bin/bbresults', % TO DO: put in preference
73 BBArgs = ['-p', 'flake8'], Env = [],
74 catch(
75 ( system_call_keep_open(Command,BBArgs,Process,STDIn,STDOut,STDErr,Env),
76 write_errors_for_bbresults(STDIn,Options),
77 close(STDIn), close(STDOut), close(STDErr),
78 process_release(Process)),
79 error(existence_error(_,_),E),
80 add_error(show_errors_with_bb_results,'Cannot start error viewer: ',Command:E)).
81 show_errors_with_bb_results(_). % no errors to show
82
83 stored_error(Source,S,TContext,Span,Options) :-
84 (member(current,Options) -> logged_error(Source,S,TContext,Span)
85 ; backed_up_error(Source,S,_TContext,Span)).
86
87 % ---------------------
88 % write errors in bbresults style (flake8)
89 % filename:line:column: W/E1 message
90
91 write_errors_for_bbresults(Stream,Options) :-
92 stored_error(Source,S,_TContext,Span,Options),
93 extract_file_number_and_name(Span,_,Filename),
94 extract_line_col(Span,SL,SC,_EL,_EC),
95 StartCol is SC+1,
96 (Source = warning(_) -> Type='W1' ; Type='E1'),
97 format(Stream,'~w:~w:~w: ~w ~w~n',[Filename,SL,StartCol,Type,S]),
98 %format(user_output,'~w:~w:~w: ~w ~w~n',[Filename,SL,SC,Type,S]),
99 fail.
100 write_errors_for_bbresults(_,_).
101
102 :- use_module(library(lists)).
103 % show a list of source locations
104 show_source_locations_with_bb_results(List) :-
105 Command = '/usr/local/bin/bbresults', % TO DO: put in preference
106 BBArgs = ['-p', 'flake8'], Env = [],
107 catch(
108 ( system_call_keep_open(Command,BBArgs,Process,STDIn,STDOut,STDErr,Env),
109 maplist(write_src_loc_msg_for_bbresults(STDIn),List),
110 close(STDIn), close(STDOut), close(STDErr),
111 process_release(Process)),
112 error(existence_error(_,_),E),
113 add_error(show_errors_with_bb_results,'Cannot start error viewer: ',Command:E)).
114
115 write_src_loc_msg_for_bbresults(Stream,src_loc_msg(S,Filename,SL,StartCol,_EndLine,_EndCol)) :-
116 format(Stream,'~w:~w:~w: ~w ~w~n',[Filename,SL,StartCol,'W1',S]).
117
118 % ------------------------------
119
120 % show pdf/ps file using Preview/open/gsview/okular/evince/...
121 show_pdf_file(FILE) :-
122 get_preference(path_to_ps_viewer,PSVIEWER),
123 system_call_with_diagnostics(psviewer,PSVIEWER,[FILE],ErrorTextAsCodeList,ExitCode),
124 (ErrorTextAsCodeList=[] -> true
125 ; format('PS/PDF Viewer (~w) Error Result: ~s~nExit code:~w.~n',[PSVIEWER,ErrorTextAsCodeList,ExitCode]),
126 fail
127 ).
128
129 % show dot file using dotty
130 show_dot_file(FILE) :-
131 get_preference(path_to_dotty,DOTTY),
132 system_call_with_diagnostics(dotty,DOTTY,[FILE],ErrorTextAsCodeList,ExitCode),
133 (ErrorTextAsCodeList=[] -> true
134 ; format('Dot Viewer(~w) Error Result: ~s~nExit code:~w.~n',[DOTTY,ErrorTextAsCodeList,ExitCode]),
135 fail
136 ).
137
138 % show file using OS specific application
139 open_file(FILE) :-
140 try_get_program_path(open,OPENCMD),
141 system_call_with_diagnostics(open,OPENCMD,[FILE],ErrorTextAsCodeList,ExitCode), % cf open -a
142 (ErrorTextAsCodeList=[] -> true
143 ; format('Error Result for open: ~s~nExit code:~w.~n',[ErrorTextAsCodeList,ExitCode]),
144 fail
145 ).
146
147 % ------------------------------
148
149 % call dot or sfdp to generate a PDF/PNG/SVG file:
150 :- use_module(probsrc(system_call),[system_call_with_diagnostics/6]).
151 gen_dot_output(DOTFILE,DotType,OutputType,OutputFILE) :-
152 get_preference(path_to_dot,DOTCMD),
153 valid_dot_output_parameter(OutputType,Para),!,
154 absolute_file_name(OutputFILE,AOutFile),
155 format(user_output,'Calling dot: ~w to generate ~w from ~w~n',[DOTCMD,AOutFile,DOTFILE]),
156 get_dot_engine_options(DotType,DotOptions),
157 append([Para|DotOptions],['-o', AOutFile, DOTFILE],DotArgs),
158 system_call_with_diagnostics(dot,DOTCMD, DotArgs,_TextOut,ErrorTextAsCodeList,_JExit),
159 (ErrorTextAsCodeList=[] -> true
160 ; format('Dot Error Result: ~s~n.',[ErrorTextAsCodeList]),
161 fail
162 ).
163
164 % useful for Tcl/Tk:
165 get_dot_default_engine_option(Option) :-
166 get_dot_engine_options(default,R),
167 (R=[Opt] -> Option=Opt ; Option = '-Kdot').
168
169 get_dot_engine_options(circo,R) :- !, R=['-Kcirco'].
170 get_dot_engine_options(dot,R) :- !, R=['-Kdot'].
171 get_dot_engine_options(fdp,R) :- !, R=['-Kfdp'].
172 get_dot_engine_options(neato,R) :- !, R=['-Kneato'].
173 get_dot_engine_options(nop,R) :- !, R=['-Knop'].
174 get_dot_engine_options(nop2,R) :- !, R=['-Knop2'].
175 get_dot_engine_options(osage,R) :- !, R=['-Kosage'].
176 get_dot_engine_options(patchwork,R) :- !, R=['-Kpatchwork'].
177 get_dot_engine_options(sfdp,R) :- !, R=['-Ksfdp'].
178 get_dot_engine_options(twopi,R) :- !, R=['-Ktwopi'].
179 get_dot_engine_options(default,R) :- !, get_preference(dot_default_engine,Eng),
180 (Eng=default -> R=[] ; get_dot_engine_options(Eng,R)).
181 get_dot_engine_options(OTHER,[]) :- add_error(get_dot_engine_options,'Unknown DOT Engine:',OTHER).
182
183 valid_dot_output_format2(pdf,'-Tpdf').
184 valid_dot_output_format2(png,'-Tpng').
185 valid_dot_output_format2(svg,'-Tsvg').
186 valid_dot_output_format2(dot,'-Tdot'). % dot with layout
187 valid_dot_output_format2(canon,'-Tcanon'). % dot without layout
188 valid_dot_output_format2(xdot,'-Txdot'). % dot with layout
189 % many more a supported: eps, xdot, canon, ...
190
191 valid_dot_output_parameter(Ext,Res) :- valid_dot_output_format2(Ext,Para),!,Res=Para.
192 valid_dot_output_parameter(T,_) :- add_internal_error('Illegal dot output type: ',T),fail.
193
194 valid_dot_output_format(Ext) :- valid_dot_output_format2(Ext,_).
195
196 % call plantuml to generate a PNG/SVG file:
197 :- use_module(library(file_systems),[file_exists/1]).
198 gen_plantuml_output(UmlFiles,OutputType,_OutputFiles) :-
199 safe_absolute_file_name(prob_lib('plantuml.jar'),PUMLTool),
200 (file_exists(PUMLTool)
201 -> true
202 ; add_error_and_fail(gen_plantuml_output,'Could not find plantuml.jar file in ProB lib. Run \'probcli -install plantuml\'.')),
203 parsercall:get_java_command_path(JavaCmd),
204 (valid_plantuml_output_format2(OutputType,OutputArg)
205 -> true
206 ; add_error_fail(gen_plantuml_output,'Output type not supported for PlantUML: ',OutputType)),
207 Args = ['-jar',PUMLTool,OutputArg],
208 append(Args,UmlFiles,AllArgs),
209 formatsilent('Run PlantUML... (output_type: ~w, files: ~w)~n',[OutputArg,UmlFiles]),
210 statistics(walltime,[W1,_]),
211 system_call_with_diagnostics(java,JavaCmd,AllArgs,Text,JExit),
212 statistics(walltime,[W2,_]),
213 WTime is W2-W1,
214 formatsilent('PlantUML finished: ~w, walltime: ~w ms~n',[JExit,WTime]),
215 (JExit=exit(0)
216 -> true
217 ; add_error(gen_plantuml_output,'Error while creating PlantUML output: ',UmlFiles),
218 atom_codes(T,Text),
219 add_error_fail(gen_plantuml_output,'Std error: ',T)
220 ).
221
222 valid_plantuml_output_format2(png,'-tpng').
223 valid_plantuml_output_format2(svg,'-tsvg').
224 % pdf is not available by default, see https://plantuml.com/pdf
225
226 valid_plantuml_output_format(Ext) :- valid_plantuml_output_format2(Ext,_).