35
36:- module(swish_highlight,
37 [ current_highlight_state/2, 38 man_predicate_summary/2 39 ]). 40:- use_module(library(debug)). 41:- use_module(library(settings)). 42:- use_module(library(http/http_dispatch)). 43:- use_module(library(http/html_write)). 44:- use_module(library(http/http_json)). 45:- use_module(library(http/http_path), []). 46:- use_module(library(http/http_parameters)). 47:- use_module(library(pairs)). 48:- use_module(library(apply)). 49:- use_module(library(error)). 50:- use_module(library(prolog_xref)). 51:- use_module(library(memfile)). 52:- use_module(library(prolog_colour)). 53:- use_module(library(lazy_lists)). 54:- if(exists_source(library(pldoc/man_index))). 55:- use_module(library(pldoc/man_index)). 56:- endif. 57
58http:location(codemirror, swish(cm), []).
59
60:- http_handler(codemirror(.), http_404([]), [id(cm_highlight)]). 61:- http_handler(codemirror(change), codemirror_change, []). 62:- http_handler(codemirror(tokens), codemirror_tokens, []). 63:- http_handler(codemirror(leave), codemirror_leave, []). 64:- http_handler(codemirror(info), token_info, []). 65
66:- setting(swish:editor_max_idle_time, nonneg, 3600,
67 "Maximum time we keep a mirror editor around"). 68
78
79 82
100
101codemirror_change(Request) :-
102 call_cleanup(codemirror_change_(Request),
103 check_unlocked).
104
105codemirror_change_(Request) :-
106 http_read_json_dict(Request, Change, []),
107 debug(cm(change), 'Change ~p', [Change]),
108 atom_string(UUID, Change.uuid),
109 catch(shadow_editor(Change, TB),
110 cm(Reason), true),
111 ( var(Reason)
112 -> ( catch(apply_change(TB, Changed, Change.change),
113 cm(outofsync), fail)
114 -> mark_changed(TB, Changed),
115 release_editor(UUID),
116 reply_json_dict(true)
117 ; destroy_editor(UUID),
118 change_failed(UUID, outofsync)
119 )
120 ; change_failed(UUID, Reason)
121 ).
122
123change_failed(UUID, Reason) :-
124 reply_json_dict(json{ type:Reason,
125 object:UUID
126 },
127 [status(409)]).
128
129
138
139apply_change(_, _Changed, []) :- !.
140apply_change(TB, Changed, Change) :-
141 _{from:From} :< Change,
142 Line is From.line+1,
143 memory_file_line_position(TB, Line, From.ch, ChPos),
144 remove(Change.removed, TB, ChPos, Changed),
145 insert(Change.text, TB, ChPos, _End, Changed),
146 ( Next = Change.get(next)
147 -> apply_change(TB, Changed, Next)
148 ; true
149 ).
150
151remove([], _, _, _) :- !.
152remove([H|T], TB, ChPos, Changed) :-
153 string_length(H, Len),
154 ( T == []
155 -> DLen is Len
156 ; DLen is Len+1
157 ),
158 ( DLen == 0
159 -> true
160 ; Changed = true,
161 memory_file_substring(TB, ChPos, Len, _, Text),
162 ( Text == H
163 -> true
164 ; throw(cm(outofsync))
165 ),
166 delete_memory_file(TB, ChPos, DLen)
167 ),
168 remove(T, TB, ChPos, Changed).
169
170insert([], _, ChPos, ChPos, _) :- !.
171insert([H|T], TB, ChPos0, ChPos, Changed) :-
172 ( H == ""
173 -> Len = 0
174 ; Changed = true,
175 string_length(H, Len),
176 debug(cm(change_text), 'Insert ~q at ~d', [H, ChPos0]),
177 insert_memory_file(TB, ChPos0, H)
178 ),
179 ChPos1 is ChPos0+Len,
180 ( T == []
181 -> ChPos2 = ChPos1
182 ; debug(cm(change_text), 'Adding newline at ~d', [ChPos1]),
183 Changed = true,
184 insert_memory_file(TB, ChPos1, '\n'),
185 ChPos2 is ChPos1+1
186 ),
187 insert(T, TB, ChPos2, ChPos, Changed).
188
189:- dynamic
190 current_editor/5, 191 editor_last_access/2, 192 xref_upto_data/1. 193
199
200create_editor(UUID, Editor, Change) :-
201 must_be(atom, UUID),
202 uuid_like(UUID),
203 new_memory_file(Editor),
204 ( RoleString = Change.get(role)
205 -> atom_string(Role, RoleString)
206 ; Role = source
207 ),
208 get_time(Now),
209 mutex_create(Lock),
210 with_mutex(swish_create_editor,
211 register_editor(UUID, Editor, Role, Lock, Now)), !.
212create_editor(UUID, Editor, _Change) :-
213 fetch_editor(UUID, Editor).
214
216register_editor(UUID, Editor, Role, Lock, Now) :-
217 \+ current_editor(UUID, _, _, _, _),
218 mutex_lock(Lock),
219 asserta(current_editor(UUID, Editor, Role, Lock, Now)).
220
224
225current_highlight_state(UUID,
226 highlight{data:Editor,
227 role:Role,
228 created:Created,
229 lock:Lock,
230 access:Access
231 }) :-
232 current_editor(UUID, Editor, Role, Lock, Created),
233 ( editor_last_access(Editor, Access)
234 -> true
235 ; Access = Created
236 ).
237
238
244
245uuid_like(UUID) :-
246 split_string(UUID, "-", "", Parts),
247 maplist(string_length, Parts, [8,4,4,4,12]),
248 \+ current_editor(UUID, _, _, _, _).
249
256
257destroy_editor(UUID) :-
258 must_be(atom, UUID),
259 current_editor(UUID, Editor, _, Lock, _), !,
260 mutex_unlock(Lock),
261 retractall(xref_upto_data(UUID)),
262 retractall(editor_last_access(UUID, _)),
263 ( xref_source_id(UUID, SourceID)
264 -> xref_clean(SourceID),
265 destroy_state_module(UUID)
266 ; true
267 ),
268 269 retractall(current_editor(UUID, Editor, _, _, _)),
270 free_memory_file(Editor).
271destroy_editor(_).
272
285
286:- dynamic
287 gced_editors/1. 288
289editor_max_idle_time(Time) :-
290 setting(swish:editor_max_idle_time, Time).
291
292gc_editors :-
293 get_time(Now),
294 ( gced_editors(Then),
295 editor_max_idle_time(MaxIdle),
296 Now - Then < MaxIdle/3
297 -> true
298 ; retractall(gced_editors(_)),
299 asserta(gced_editors(Now)),
300 fail
301 ).
302gc_editors :-
303 editor_max_idle_time(MaxIdle),
304 forall(garbage_editor(UUID, MaxIdle),
305 destroy_garbage_editor(UUID)).
306
307garbage_editor(UUID, TimeOut) :-
308 get_time(Now),
309 current_editor(UUID, _TB, _Role, _Lock, Created),
310 Now - Created > TimeOut,
311 ( editor_last_access(UUID, Access)
312 -> Now - Access > TimeOut
313 ; true
314 ).
315
316destroy_garbage_editor(UUID) :-
317 fetch_editor(UUID, _TB), !,
318 destroy_editor(UUID).
319destroy_garbage_editor(_).
320
326
327fetch_editor(UUID, TB) :-
328 current_editor(UUID, TB, Role, Lock, _),
329 catch(mutex_lock(Lock), error(existence_error(mutex,_),_), fail),
330 debug(cm(lock), 'Locked ~p', [UUID]),
331 ( current_editor(UUID, TB, Role, Lock, _)
332 -> update_access(UUID)
333 ; mutex_unlock(Lock)
334 ).
335
336release_editor(UUID) :-
337 current_editor(UUID, _TB, _Role, Lock, _),
338 debug(cm(lock), 'Unlocked ~p', [UUID]),
339 mutex_unlock(Lock).
340
341check_unlocked :-
342 check_unlocked(unknown).
343
348
349check_unlocked(Reason) :-
350 thread_self(Me),
351 current_editor(_UUID, _TB, _Role, Lock, _),
352 mutex_property(Lock, status(locked(Me, _Count))), !,
353 unlock(Me, Lock),
354 print_message(error, locked(Reason, Me)),
355 assertion(fail).
356check_unlocked(_).
357
358unlock(Me, Lock) :-
359 mutex_property(Lock, status(locked(Me, _Count))), !,
360 mutex_unlock(Lock),
361 unlock(Me, Lock).
362unlock(_, _).
363
368
369update_access(UUID) :-
370 get_time(Now),
371 ( editor_last_access(UUID, Last),
372 Now-Last < 60
373 -> true
374 ; retractall(editor_last_access(UUID, _)),
375 asserta(editor_last_access(UUID, Now))
376 ).
377
378:- multifile
379 prolog:xref_source_identifier/2,
380 prolog:xref_open_source/2,
381 prolog:xref_close_source/2. 382
383prolog:xref_source_identifier(UUID, UUID) :-
384 current_editor(UUID, _, _, _, _).
385
392
393:- if(current_predicate(prolog_source:close_source/3)). 394prolog:xref_open_source(UUID, Stream) :-
395 fetch_editor(UUID, TB),
396 open_memory_file(TB, read, Stream).
397
398prolog:xref_close_source(UUID, Stream) :-
399 release_editor(UUID),
400 close(Stream).
401:- else. 402prolog:xref_open_source(UUID, Stream) :-
403 fetch_editor(UUID, TB),
404 open_memory_file(TB, read, Stream),
405 release_editor(UUID).
406:- endif. 407
413
414codemirror_leave(Request) :-
415 call_cleanup(codemirror_leave_(Request),
416 check_unlocked).
417
418codemirror_leave_(Request) :-
419 http_read_json_dict(Request, Data, []),
420 ( atom_string(UUID, Data.get(uuid))
421 -> debug(cm(leave), 'Leaving editor ~p', [UUID]),
422 ( fetch_editor(UUID, _TB)
423 -> destroy_editor(UUID)
424 ; debug(cm(leave), 'No editor for ~p', [UUID])
425 )
426 ; debug(cm(leave), 'No editor?? (data=~p)', [Data])
427 ),
428 reply_json_dict(true).
429
433
434mark_changed(MemFile, Changed) :-
435 ( Changed == true,
436 current_editor(UUID, MemFile, _Role, _, _)
437 -> retractall(xref_upto_data(UUID))
438 ; true
439 ).
440
442
443xref(UUID) :-
444 xref_upto_data(UUID), !.
445xref(UUID) :-
446 setup_call_cleanup(
447 fetch_editor(UUID, _TB),
448 ( xref_source_id(UUID, SourceId),
449 xref_state_module(UUID, Module),
450 xref_source(SourceId,
451 [ silent(true),
452 module(Module)
453 ]),
454 asserta(xref_upto_data(UUID))
455 ),
456 release_editor(UUID)).
457
462
463xref_source_id(UUID, UUID).
464
469
470xref_state_module(UUID, UUID) :-
471 ( module_property(UUID, class(temporary))
472 -> true
473 ; set_module(UUID:class(temporary)),
474 add_import_module(UUID, swish, start),
475 maplist(copy_flag(UUID, swish), [var_prefix])
476 ).
477
478copy_flag(Module, Application, Flag) :-
479 current_prolog_flag(Application:Flag, Value), !,
480 set_prolog_flag(Module:Flag, Value).
481copy_flag(_, _, _).
482
483destroy_state_module(UUID) :-
484 module_property(UUID, class(temporary)), !,
485 '$destroy_module'(UUID).
486destroy_state_module(_).
487
488
489 492
497
498codemirror_tokens(Request) :-
499 setup_call_catcher_cleanup(
500 true,
501 codemirror_tokens_(Request),
502 Reason,
503 check_unlocked(Reason)).
504
505codemirror_tokens_(Request) :-
506 http_read_json_dict(Request, Data, []),
507 atom_string(UUID, Data.get(uuid)),
508 debug(cm(tokens), 'Asking for tokens: ~p', [Data]),
509 ( catch(shadow_editor(Data, TB), cm(Reason), true)
510 -> ( var(Reason)
511 -> call_cleanup(enriched_tokens(TB, Data, Tokens),
512 release_editor(UUID)),
513 reply_json_dict(json{tokens:Tokens}, [width(0)])
514 ; check_unlocked(Reason),
515 change_failed(UUID, Reason)
516 )
517 ; reply_json_dict(json{tokens:[[]]})
518 ),
519 gc_editors.
520
521
522enriched_tokens(TB, _Data, Tokens) :- 523 current_editor(UUID, TB, source, _Lock, _), !,
524 xref(UUID),
525 server_tokens(TB, Tokens).
526enriched_tokens(TB, Data, Tokens) :- 527 json_source_id(Data.get(sourceID), SourceID), !,
528 memory_file_to_string(TB, Query),
529 with_mutex(swish_highlight_query,
530 prolog_colourise_query(Query, SourceID, colour_item(TB))),
531 collect_tokens(TB, Tokens).
532enriched_tokens(TB, _Data, Tokens) :-
533 memory_file_to_string(TB, Query),
534 prolog_colourise_query(Query, module(swish), colour_item(TB)),
535 collect_tokens(TB, Tokens).
536
542
543:- if(current_predicate(prolog_colour:to_list/2)). 544json_source_id(StringList, SourceIDList) :-
545 is_list(StringList),
546 StringList \== [], !,
547 maplist(string_source_id, StringList, SourceIDList).
548:- else. 549json_source_id([String|_], SourceID) :-
550 maplist(string_source_id, String, SourceID).
551:- endif. 552json_source_id(String, SourceID) :-
553 string(String),
554 string_source_id(String, SourceID).
555
556string_source_id(String, SourceID) :-
557 atom_string(SourceID, String),
558 ( fetch_editor(SourceID, _TB)
559 -> release_editor(SourceID)
560 ; true
561 ).
562
563
580
581shadow_editor(Data, TB) :-
582 atom_string(UUID, Data.get(uuid)),
583 setup_call_catcher_cleanup(
584 fetch_editor(UUID, TB),
585 once(update_editor(Data, UUID, TB)),
586 Catcher,
587 cleanup_update(Catcher, UUID)), !.
588shadow_editor(Data, TB) :-
589 Text = Data.get(text), !,
590 atom_string(UUID, Data.uuid),
591 create_editor(UUID, TB, Data),
592 debug(cm(change), 'Create editor for ~p', [UUID]),
593 debug(cm(change_text), 'Initialising editor to ~q', [Text]),
594 insert_memory_file(TB, 0, Text).
595shadow_editor(Data, TB) :-
596 _{role:_} :< Data, !,
597 atom_string(UUID, Data.uuid),
598 create_editor(UUID, TB, Data).
599shadow_editor(_Data, _TB) :-
600 throw(cm(existence_error)).
601
602update_editor(Data, _UUID, TB) :-
603 Text = Data.get(text), !,
604 size_memory_file(TB, Size),
605 delete_memory_file(TB, 0, Size),
606 insert_memory_file(TB, 0, Text),
607 mark_changed(TB, true).
608update_editor(Data, UUID, TB) :-
609 Changes = Data.get(changes), !,
610 ( debug(cm(change), 'Patch editor for ~p', [UUID]),
611 maplist(apply_change(TB, Changed), Changes)
612 -> true
613 ; throw(cm(out_of_sync))
614 ),
615 mark_changed(TB, Changed).
616
617cleanup_update(exit, _) :- !.
618cleanup_update(_, UUID) :-
619 release_editor(UUID).
620
621:- thread_local
622 token/3. 623
633
634:- public
635 show_mirror/1,
636 server_tokens/1. 637
638show_mirror(Role) :-
639 current_editor(_UUID, TB, Role, _Lock, _), !,
640 memory_file_to_string(TB, String),
641 write(user_error, String).
642
643server_tokens(Role) :-
644 current_editor(_UUID, TB, Role, _Lock, _), !,
645 enriched_tokens(TB, _{}, Tokens),
646 print_term(Tokens, [output(user_error)]).
647
652
653server_tokens(TB, GroupedTokens) :-
654 current_editor(UUID, TB, _Role, _Lock, _),
655 Ignore = error(syntax_error(swi_backslash_newline),_),
656 setup_call_cleanup(
657 asserta(user:thread_message_hook(Ignore, _, _), Ref),
658 setup_call_cleanup(
659 open_memory_file(TB, read, Stream),
660 ( set_stream_file(TB, Stream),
661 prolog_colourise_stream(Stream, UUID, colour_item(TB))
662 ),
663 close(Stream)),
664 erase(Ref)),
665 collect_tokens(TB, GroupedTokens).
666
667collect_tokens(TB, GroupedTokens) :-
668 findall(Start-Token, json_token(TB, Start, Token), Pairs),
669 keysort(Pairs, Sorted),
670 pairs_values(Sorted, Tokens),
671 group_by_term(Tokens, GroupedTokens).
672
673set_stream_file(_,_). 674
681
682group_by_term([], []) :- !.
683group_by_term(Flat, [Term|Grouped]) :-
684 take_term(Flat, Term, Rest),
685 group_by_term(Rest, Grouped).
686
687take_term([], [], []).
688take_term([H|T0], [H|T], R) :-
689 ( ends_term(H.get(type))
690 -> T = [],
691 R = T0
692 ; take_term(T0, T, R)
693 ).
694
695ends_term(fullstop).
696ends_term(syntax_error).
697
706
707json_token(TB, Start, Token) :-
708 retract(token(Style, Start0, Len)),
709 debug(color, 'Trapped ~q.', [token(Style, Start0, Len)]),
710 ( atomic_special(Style, Start0, Len, TB, Type, Attrs)
711 -> Start = Start0
712 ; style(Style, Type0, Attrs0)
713 -> ( Type0 = StartType-EndType
714 -> ( Start = Start0,
715 Type = StartType
716 ; Start is Start0+Len-1,
717 Type = EndType
718 )
719 ; Type = Type0,
720 Start = Start0
721 ),
722 json_attributes(Attrs0, Attrs, TB, Start0, Len)
723 ),
724 dict_create(Token, json, [type(Type)|Attrs]).
725
726atomic_special(atom, Start, Len, TB, Type, Attrs) :-
727 memory_file_substring(TB, Start, 1, _, FirstChar),
728 ( FirstChar == "'"
729 -> Type = qatom,
730 Attrs = []
731 ; char_type(FirstChar, upper)
732 -> Type = uatom, 733 Attrs = []
734 ; Type = atom,
735 ( Len =< 5 736 -> memory_file_substring(TB, Start, Len, _, Text),
737 Attrs = [text(Text)]
738 ; Attrs = []
739 )
740 ).
741
742json_attributes([], [], _, _, _).
743json_attributes([H0|T0], Attrs, TB, Start, Len) :-
744 json_attribute(H0, Attrs, T, TB, Start, Len), !,
745 json_attributes(T0, T, TB, Start, Len).
746json_attributes([_|T0], T, TB, Start, Len) :-
747 json_attributes(T0, T, TB, Start, Len).
748
749json_attribute(text, [text(Text)|T], T, TB, Start, Len) :- !,
750 memory_file_substring(TB, Start, Len, _, Text).
751json_attribute(line(File:Line), [line(Line),file(File)|T], T, _, _, _) :- !.
752json_attribute(Term, [Term|T], T, _, _, _).
753
754colour_item(_TB, Style, Start, Len) :-
755 ( style(Style)
756 -> assertz(token(Style, Start, Len))
757 ; debug(color, 'Ignored ~q.', [token(Style, Start, Len)])
758 ).
759
786
787:- multifile
788 style/3. 789
790style(Style) :-
791 style(Style, _, _).
792
793style(neck(Neck), neck, [ text(Text) ]) :-
794 neck_text(Neck, Text).
795style(head(Class, Head), Type, [ text, arity(Arity) ]) :-
796 goal_arity(Head, Arity),
797 head_type(Class, Type).
798style(goal_term(_Class, Goal), var, []) :-
799 var(Goal), !.
800style(goal_term(Class, {_}), brace_term_open-brace_term_close,
801 [ name({}), arity(1) | More ]) :-
802 goal_type(Class, _Type, More).
803style(goal(Class, Goal), Type, [ text, arity(Arity) | More ]) :-
804 Goal \= {_},
805 goal_arity(Goal, Arity),
806 goal_type(Class, Type, More).
807style(file_no_depend(Path), file_no_depends, [text, path(Path)]).
808style(file(Path), file, [text, path(Path)]).
809style(nofile, nofile, [text]).
810style(option_name, option_name, [text]).
811style(no_option_name, no_option_name, [text]).
812style(flag_name(_Flag), flag_name, [text]).
813style(no_flag_name(_Flag), no_flag_name, [text]).
814style(fullstop, fullstop, []).
815style(var, var, [text]).
816style(singleton, singleton, [text]).
817style(string, string, []).
818style(codes, codes, []).
819style(chars, chars, []).
820style(atom, atom, []).
821style(rational(_Value), rational, [text]).
822style(format_string, format_string, []).
823style(meta(_Spec), meta, []).
824style(op_type(_Type), op_type, [text]).
825style(decl_option(_Name),decl_option, [text]).
826style(functor, functor, [text]).
827style(control, control, [text]).
828style(delimiter, delimiter, [text]).
829style(identifier, identifier, [text]).
830style(module(_Module), module, [text]).
831style(error, error, [text]).
832style(constraint(Set), constraint, [text, set(Set)]).
833style(type_error(Expect), error, [text,expected(Expect)]).
834style(syntax_error(_Msg,_Pos), syntax_error, []).
835style(instantiation_error, instantiation_error, [text]).
836style(predicate_indicator, atom, [text]).
837style(predicate_indicator, atom, [text]).
838style(arity, int, []).
839style(int, int, []).
840style(float, float, []).
841style(keyword(_), keyword, [text]).
842style(qq(open), qq_open, []).
843style(qq(sep), qq_sep, []).
844style(qq(close), qq_close, []).
845style(qq_type, qq_type, [text]).
846style(dict_tag, tag, [text]).
847style(dict_key, key, [text]).
848style(dict_sep, sep, []).
849style(func_dot, atom, [text(.)]).
850style(dict_return_op, atom, [text(:=)]).
851style(dict_function(F), dict_function, [text(F)]).
852style(empty_list, list_open-list_close, []).
853style(list, list_open-list_close, []).
854style(dcg(terminal), list_open-list_close, []).
855style(dcg(string), string_terminal, []).
856style(dcg(plain), brace_term_open-brace_term_close, []).
857style(brace_term, brace_term_open-brace_term_close, []).
858style(dict_content, dict_open-dict_close, []).
859style(expanded, expanded, [text]).
860style(comment_string, comment_string, []). 861style(comment(string), comment_string, []). 862style(ext_quant, ext_quant, []).
863style(unused_import, unused_import, [text]).
864style(undefined_import, undefined_import, [text]).
865 866style(html(_Element), html, []).
867style(entity(_Element), entity, []).
868style(html_attribute(_), html_attribute, []).
869style(sgml_attr_function,sgml_attr_function, []).
870style(html_call, html_call, [text]). 871style(html_raw, html_raw, [text]). 872style(http_location_for_id(_), http_location_for_id, []).
873style(http_no_location_for_id(_), http_no_location_for_id, []).
874 875style(method(send), xpce_method, [text]).
876style(method(get), xpce_method, [text]).
877style(class(built_in,_Name), xpce_class_built_in, [text]).
878style(class(library(File),_Name), xpce_class_lib, [text, file(File)]).
879style(class(user(File),_Name), xpce_class_user, [text, file(File)]).
880style(class(user,_Name), xpce_class_user, [text]).
881style(class(undefined,_Name), xpce_class_undef, [text]).
882
883style(table_mode(_Mode), table_mode, [text]).
884style(table_option(_Mode), table_option, [text]).
885
886
887neck_text(clause, (:-)) :- !.
888neck_text(grammar_rule, (-->)) :- !.
889neck_text(method(send), (:->)) :- !.
890neck_text(method(get), (:<-)) :- !.
891neck_text(directive, (:-)) :- !.
892neck_text(Text, Text). 893
894head_type(exported, head_exported).
895head_type(public(_), head_public).
896head_type(extern(_), head_extern).
897head_type(extern(_,_), head_extern).
898head_type(dynamic, head_dynamic).
899head_type(multifile, head_multifile).
900head_type(unreferenced, head_unreferenced).
901head_type(hook, head_hook).
902head_type(meta, head_meta).
903head_type(constraint(_), head_constraint).
904head_type(imported, head_imported).
905head_type(built_in, head_built_in).
906head_type(iso, head_iso).
907head_type(def_iso, head_def_iso).
908head_type(def_swi, head_def_swi).
909head_type(_, head).
910
911goal_type(built_in, goal_built_in, []).
912goal_type(imported(File), goal_imported, [file(File)]).
913goal_type(autoload(File), goal_autoload, [file(File)]).
914goal_type(global, goal_global, []).
915goal_type(undefined, goal_undefined, []).
916goal_type(thread_local(Line), goal_thread_local, [line(Line)]).
917goal_type(dynamic(Line), goal_dynamic, [line(Line)]).
918goal_type(multifile(Line), goal_multifile, [line(Line)]).
919goal_type(expanded, goal_expanded, []).
920goal_type(extern(_), goal_extern, []).
921goal_type(extern(_,_), goal_extern, []).
922goal_type(recursion, goal_recursion, []).
923goal_type(meta, goal_meta, []).
924goal_type(foreign(_), goal_foreign, []).
925goal_type(local(Line), goal_local, [line(Line)]).
926goal_type(constraint(Line), goal_constraint, [line(Line)]).
927goal_type(not_callable, goal_not_callable, []).
928goal_type(global(Type,_Loc), Class, []) :-
929 global_class(Type, Class).
930
931global_class(dynamic, goal_dynamic) :- !.
932global_class(multifile, goal_multifile) :- !.
933global_class(_, goal_global).
934
938
939goal_arity(Goal, Arity) :-
940 ( compound(Goal)
941 -> compound_name_arity(Goal, _, Arity)
942 ; Arity = 0
943 ).
944
945 948
949:- multifile
950 swish_config:config/2,
951 css/3. 952
961
962swish_config:config(cm_style, Styles) :-
963 findall(Name-Style, highlight_style(Name, Style), Pairs),
964 keysort(Pairs, Sorted),
965 remove_duplicate_styles(Sorted, Unique),
966 dict_pairs(Styles, json, Unique).
967swish_config:config(cm_hover_style, Styles) :-
968 findall(Sel-Attrs, css_dict(hover, Sel, Attrs), Pairs),
969 dict_pairs(Styles, json, Pairs).
970
971remove_duplicate_styles([], []).
972remove_duplicate_styles([H|T0], [H|T]) :-
973 H = K-_,
974 remove_same(K, T0, T1),
975 remove_duplicate_styles(T1, T).
976
977remove_same(K, [K-_|T0], T) :- !,
978 remove_same(K, T0, T).
979remove_same(_, Rest, Rest).
980
981highlight_style(StyleName, Style) :-
982 style(Term, StyleName, _),
983 atom(StyleName),
984 ( prolog_colour:style(Term, Attrs0)
985 -> maplist(css_style, Attrs0, Attrs),
986 dict_create(Style, json, Attrs)
987 ).
988
989css_style(bold(true), 'font-weight'(bold)) :- !.
990css_style(underline(true), 'text-decoration'(underline)) :- !.
991css_style(colour(Name), color(RGB)) :-
992 x11_color(Name, R, G, B),
993 format(atom(RGB), '#~|~`0t~16r~2+~`0t~16r~2+~`0t~16r~2+', [R,G,B]).
994css_style(Style, Style).
995
999
1000x11_color(Name, R, G, B) :-
1001 ( x11_colors_done
1002 -> true
1003 ; with_mutex(swish_highlight, load_x11_colours)
1004 ),
1005 x11_color_cache(Name, R, G, B).
1006
1007:- dynamic
1008 x11_color_cache/4,
1009 x11_colors_done/0. 1010
1011load_x11_colours :-
1012 x11_colors_done, !.
1013load_x11_colours :-
1014 source_file(load_x11_colours, File),
1015 file_directory_name(File, Dir),
1016 directory_file_path(Dir, 'rgb.txt', RgbFile),
1017 setup_call_cleanup(
1018 open(RgbFile, read, In),
1019 ( lazy_list(lazy_read_lines(In, [as(string)]), List),
1020 maplist(assert_colour, List)
1021 ),
1022 close(In)),
1023 asserta(x11_colors_done).
1024
1025assert_colour(String) :-
1026 split_string(String, "\s\t\r", "\s\t\r", [RS,GS,BS|NameParts]),
1027 number_string(R, RS),
1028 number_string(G, GS),
1029 number_string(B, BS),
1030 atomic_list_concat(NameParts, '_', Name0),
1031 downcase_atom(Name0, Name),
1032 assertz(x11_color_cache(Name, R, G, B)).
1033
1034:- catch(initialization(load_x11_colours, prepare_state), _, true). 1035
1046
1047css_dict(Context, Selector, Style) :-
1048 css(Context, Selector, Attrs0),
1049 maplist(css_style, Attrs0, Attrs),
1050 dict_create(Style, json, Attrs).
1051
1052
1053 1056
1057:- multifile
1058 prolog:predicate_summary/2. 1059
1063
1064token_info(Request) :-
1065 http_parameters(Request, [], [form_data(Form)]),
1066 maplist(type_convert, Form, Values),
1067 dict_create(Token, token, Values),
1068 reply_html_page(plain,
1069 title('token info'),
1070 \token_info_or_none(Token)).
1071
1072type_convert(Name=Atom, Name=Number) :-
1073 atom_number(Atom, Number), !.
1074type_convert(NameValue, NameValue).
1075
1076
1077token_info_or_none(Token) -->
1078 token_info(Token), !.
1079token_info_or_none(_) -->
1080 html(span(class('token-noinfo'), 'No info available')).
1081
1088
1089:- multifile token_info//1. 1090
1091token_info(Token) -->
1092 { _{type:Type, text:Name, arity:Arity} :< Token,
1093 goal_type(_, Type, _), !,
1094 ignore(token_predicate_module(Token, Module)),
1095 text_arity_pi(Name, Arity, PI),
1096 predicate_info(Module:PI, Info)
1097 },
1098 pred_info(Info).
1099
1100pred_info([]) -->
1101 html(span(class('pred-nosummary'), 'No help available')).
1102pred_info([Info|_]) --> 1103 (pred_tags(Info) -> [];[]),
1104 (pred_summary(Info) -> [];[]).
1105
1106pred_tags(Info) -->
1107 { Info.get(iso) == true },
1108 html(span(class('pred-tag'), 'ISO')).
1109
1110pred_summary(Info) -->
1111 html(span(class('pred-summary'), Info.get(summary))).
1112
1116
1117token_predicate_module(Token, Module) :-
1118 source_file_property(Token.get(file), module(Module)), !.
1119
1120text_arity_pi('[', 2, consult/1) :- !.
1121text_arity_pi(']', 2, consult/1) :- !.
1122text_arity_pi(Name, Arity, Name/Arity).
1123
1124
1140
1141predicate_info(PI, Info) :-
1142 PI = Module:Name/Arity,
1143 findall(Dict,
1144 ( setof(Key-Value,
1145 predicate_info(PI, Key, Value),
1146 Pairs),
1147 dict_pairs(Dict, json,
1148 [ module - Module,
1149 name - Name,
1150 arity - Arity
1151 | Pairs
1152 ])
1153 ),
1154 Info).
1155
1166
1167 1168predicate_info(Module:Name/Arity, Key, Value) :-
1169 functor(Head, Name, Arity),
1170 predicate_property(system:Head, iso), !,
1171 ignore(Module = system),
1172 ( man_predicate_summary(Name/Arity, Summary),
1173 Key = summary,
1174 Value = Summary
1175 ; Key = iso,
1176 Value = true
1177 ).
1178predicate_info(PI, summary, Summary) :-
1179 PI = Module:Name/Arity,
1180
1181 ( man_predicate_summary(Name/Arity, Summary)
1182 -> true
1183 ; Arity >= 2,
1184 DCGArity is Arity - 2,
1185 man_predicate_summary(Name//DCGArity, Summary)
1186 -> true
1187 ; prolog:predicate_summary(PI, Summary)
1188 -> true
1189 ; Arity >= 2,
1190 DCGArity is Arity - 2,
1191 prolog:predicate_summary(Module:Name/DCGArity, Summary)
1192 ).
1193
1194:- if(current_predicate(man_object_property/2)). 1195man_predicate_summary(PI, Summary) :-
1196 man_object_property(PI, summary(Summary)).
1197:- else. 1198man_predicate_summary(_, _) :-
1199 fail.
1200:- endif.