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