swish/commit
Copied files
author | Jan Wielemaker |
---|---|
Fri Mar 4 17:30:54 2016 +0100 | |
committer | Jan Wielemaker |
Fri Mar 4 17:30:54 2016 +0100 | |
commit | d0e7313128d750ac32f781d54da304a2cae8c833 |
tree | 4278ab625c9e9e8874f64a02ed95e4a01af35225 |
parent | 89da5b1e8d57456217ca09b655fa6881b945de0b |
Diff style: patch stat
diff --git a/lib/swish/config.pl b/lib/swish/config.pl index 59a1632..9ca6978 100644 --- a/lib/swish/config.pl +++ b/lib/swish/config.pl @@ -28,9 +28,9 @@ */ :- module(swish_config, - [ swish_reply_config/1, % +Request + [ swish_reply_config/2, % +Request, +Options swish_config/2, % ?Type, ?Config - swish_config_hash/1 % -HASH + swish_config_hash/2 % -HASH, +Options ]). :- use_module(library(http/http_dispatch)). :- use_module(library(http/http_json)). @@ -38,6 +38,7 @@ :- multifile config/2, % ?Key, ?Value + config/3, % ?Key, ?Value, +Options source_alias/2, % ?Alias, ?Options authenticate/2. % +Request, -User @@ -48,31 +49,31 @@ * CONFIG * *******************************/ -%% swish_reply_config(+Request) is semidet. +%% swish_reply_config(+Request, +Options) is semidet. % % Emit a configuration object to the client if the client requests % for '.../swish_config.json', regardless of the path prefix. -swish_reply_config(Request) :- +swish_reply_config(Request, Options) :- option(path(Path), Request), file_base_name(Path, 'swish_config.json'), - json_config(JSON), + json_config(JSON, Options), reply_json(JSON). -%% swish_config_hash(-Hash) is det. +%% swish_config_hash(-Hash, +Options) is det. % % True if Hash is the SHA1 of the SWISH config. -swish_config_hash(Hash) :- - json_config(Config), +swish_config_hash(Hash, Options) :- + json_config(Config, Options), variant_sha1(Config, Hash). json_config(json{ http: json{ locations:JSON }, swish: SWISHConfig - }) :- + }, Options) :- http_locations(JSON), - swish_config(SWISHConfig). + swish_config_dict(SWISHConfig, Options). http_locations(JSON) :- findall(ID-Path, @@ -101,12 +102,12 @@ same_ids([Id-Path|T0], Id, T, [Path|TP]) :- !, same_ids(T, _, T, []). -%% swish_config(-Config:dict) is det. +%% swish_config_dict(-Config:dict, +Options) is det. % % Obtain name-value pairs from swish_config:config/2 -swish_config(Config) :- - findall(Key-Value, config(Key, Value), Pairs), +swish_config_dict(Config, Options) :- + findall(Key-Value, swish_config(Key, Value, Options), Pairs), dict_pairs(Config, json, Pairs). %% config(-Key, -Value) is nondet. @@ -116,6 +117,11 @@ swish_config(Config) :- % object (see =web/js/config.js=) swish_config(Key, Value) :- + swish_config(Key, Value, []). + +swish_config(Key, Value, Options) :- + config(Key, Value, Options). +swish_config(Key, Value, _) :- config(Key, Value). %% source_alias(?Alias, ?Options) is nondet. diff --git a/lib/swish/highlight.pl b/lib/swish/highlight.pl index 57412c2..0775725 100644 --- a/lib/swish/highlight.pl +++ b/lib/swish/highlight.pl @@ -690,6 +690,7 @@ style(meta(_Spec), meta, []). style(op_type(_Type), op_type, [text]). style(functor, functor, [text]). style(control, control, [text]). +style(delimiter, delimiter, [text]). style(identifier, identifier, [text]). style(module(_Module), module, [text]). style(error, error, [text]). @@ -740,21 +741,21 @@ neck_text(method(send), (:->)). neck_text(method(get), (:<-)). neck_text(directive, (:-)). -head_type(exported, head_exported). -head_type(public(_), head_public). -head_type(extern(_), head_extern). -head_type(dynamic, head_dynamic). -head_type(multifile, head_multifile). -head_type(unreferenced, head_unreferenced). -head_type(hook, head_hook). -head_type(meta, head_meta). -head_type(constraint, head_constraint). -head_type(imported, head_imported). -head_type(built_in, head_built_in). -head_type(iso, head_iso). -head_type(def_iso, head_def_iso). -head_type(def_swi, head_def_swi). -head_type(_, head). +head_type(exported, head_exported). +head_type(public(_), head_public). +head_type(extern(_), head_extern). +head_type(dynamic, head_dynamic). +head_type(multifile, head_multifile). +head_type(unreferenced, head_unreferenced). +head_type(hook, head_hook). +head_type(meta, head_meta). +head_type(constraint(_), head_constraint). +head_type(imported, head_imported). +head_type(built_in, head_built_in). +head_type(iso, head_iso). +head_type(def_iso, head_def_iso). +head_type(def_swi, head_def_swi). +head_type(_, head). goal_type(built_in, goal_built_in, []). goal_type(imported(File), goal_imported, [file(File)]). diff --git a/lib/swish/include.pl b/lib/swish/include.pl index c6a0d8c..39eb41a 100644 --- a/lib/swish/include.pl +++ b/lib/swish/include.pl @@ -60,11 +60,13 @@ swish:term_expansion(:- include(FileIn), Expansion) :- 'swish included'(File), (:- include(stream(Id, Stream, [close(true)]))) ], + '$push_input_context'(swish_include), setting(web_storage:directory, Store), add_extension(File, FileExt), catch(gitty_data(Store, FileExt, Data, _Meta), _, fail), atom_concat('swish://', FileExt, Id), - open_string(Data, Stream) + open_string(Data, Stream), + '$pop_input_context' ). add_extension(File, FileExt) :- diff --git a/lib/swish/logging.pl b/lib/swish/logging.pl index 51e2183..55339aa 100644 --- a/lib/swish/logging.pl +++ b/lib/swish/logging.pl @@ -64,13 +64,32 @@ swish_log(send(Pengine, Event)) :- [HDate, Now, send(Pengine, Event)]). :- dynamic - text_hash/2. + text_hash/3, + gc_text_hash_time/1. hash_option(src_text(Text), src_text(Result)) :- !, - ( text_hash(Text, Hash) + ( text_hash(Text, _, Hash) -> Result = Hash ; variant_sha1(Text, Hash), - assert(text_hash(Text, Hash)), + get_time(Now), + assert(text_hash(Text, Now, Hash)), + gc_text_hash, Result = Hash-Text ). hash_option(Option, Option). + +gc_text_hash :- + gc_text_hash_time(Last), + get_time(Now), + Now - Last < 900, !. +gc_text_hash :- + get_time(Now), + retractall(gc_text_hash_time(_)), + asserta(gc_text_hash_time(Now)), + Before is Now - 3600, + ( text_hash(Text, Time, Hash), + Time < Before, + retractall(text_hash(Text, Time, Hash)), + fail + ; true + ). diff --git a/lib/swish/page.pl b/lib/swish/page.pl index e800d93..0778187 100644 --- a/lib/swish/page.pl +++ b/lib/swish/page.pl @@ -96,18 +96,21 @@ http:location(pldoc, swish(pldoc), [priority(100)]). % - q(Query) % Use Query as the initial query. -swish_reply(_Options, Request) :- - swish_config:authenticate(Request, _User), % must throw to deny access - fail. swish_reply(Options, Request) :- + swish_config:authenticate(Request, User), !, % must throw to deny access + swish_reply2([user(User)|Options], Request). +swish_reply(Options, Request) :- + swish_reply2(Options, Request). + +swish_reply2(Options, Request) :- option(method(Method), Request), Method \== get, !, swish_rest_reply(Method, Request, Options). -swish_reply(_, Request) :- +swish_reply2(_, Request) :- serve_resource(Request), !. -swish_reply(_, Request) :- - swish_reply_config(Request), !. -swish_reply(SwishOptions, Request) :- +swish_reply2(Options, Request) :- + swish_reply_config(Request, Options), !. +swish_reply2(SwishOptions, Request) :- Params = [ code(_, [optional(true)]), background(_, [optional(true)]), examples(_, [optional(true)]), @@ -119,19 +122,19 @@ swish_reply(SwishOptions, Request) :- merge_options(Options0, SwishOptions, Options1), source_option(Request, Options1, Options2), option(format(Format), Options2), - swish_reply2(Format, Options2). + swish_reply3(Format, Options2). -swish_reply2(raw, Options) :- +swish_reply3(raw, Options) :- option(code(Code), Options), !, format('Content-type: text/x-prolog~n~n'), format('~s', [Code]). -swish_reply2(json, Options) :- +swish_reply3(json, Options) :- option(code(Code), Options), !, option(meta(Meta), Options, _{}), reply_json_dict(json{data:Code, meta:Meta}). -swish_reply2(_, Options) :- +swish_reply3(_, Options) :- swish_config:reply_page(Options), !. -swish_reply2(_, Options) :- +swish_reply3(_, Options) :- reply_html_page( swish(main), [ title('SWISH -- SWI-Prolog for SHaring'), @@ -352,7 +355,7 @@ swish_content(Options) --> { document_type(Type, Options) }, swish_resources, - swish_config_hash, + swish_config_hash(Options), html(div([id(content), class([container, swish])], [ div([class([tile, horizontal]), 'data-split'('50%')], [ div([ class([editors, tabbed]) @@ -370,14 +373,14 @@ swish_content(Options) --> ])). -%% swish_config_hash// +%% swish_config_hash(+Options)// % % Set `window.swish.config_hash` to a hash that represents the % current configuration. This is used by config.js to cache the % configuration in the browser's local store. -swish_config_hash --> - { swish_config_hash(Hash) }, +swish_config_hash(Options) --> + { swish_config_hash(Hash, Options) }, js_script({|javascript(Hash)|| window.swish = window.swish||{}; window.swish.config_hash = Hash; diff --git a/lib/swish/procps.pl b/lib/swish/procps.pl index e31e824..61aff16 100644 --- a/lib/swish/procps.pl +++ b/lib/swish/procps.pl @@ -93,6 +93,8 @@ stat_field_value(_, String, Number) :- number_string(Number, String). :- if(current_predicate(sysconf/1)). +% the weird way to call sysconf confuses ClioPatria's cpack code +% analysis enough to accept this ... term_expansion(clockticks(sysconf), Expansion) :- ( member(Sysconf, [sysconf(clk_tck(TicksPerSec))]), call(Sysconf) diff --git a/lib/swish/render/c3.pl b/lib/swish/render/c3.pl index 069a340..b90fd57 100644 --- a/lib/swish/render/c3.pl +++ b/lib/swish/render/c3.pl @@ -54,6 +54,10 @@ Render data as a chart. % Renders Term as a C3.js chart. This renderer recognises C3, as a % dict with tag `c3`. +% This renderer hooks into SWISH using two event-handlers and associated +% classes. The `reactive-size` is called after the window or pane is +% resized and the `export-dom` is called from the _download_ button. + term_rendering(C30, _Vars, _Options) --> { is_dict(C30, Tag), Tag == c3, @@ -62,7 +66,7 @@ term_rendering(C30, _Vars, _Options) --> atom_concat(#, Id, RefId), put_dict(bindto, C3, RefId, C3b) }, - html(div([ class(['render-C3', 'reactive-size']), + html(div([ class(['render-C3', 'reactive-size', 'export-dom']), 'data-render'('As C3 chart') ], [ div(id(Id), []), @@ -75,6 +79,17 @@ term_rendering(C30, _Vars, _Options) --> var sizing = {}; var tmo; + div.on("export-dom", function(ev, r) { + var svg = div.find("svg"); + svg.attr("xmlns", "http://www.w3.org/2000/svg"); + svg.css("font", "10px sans-serif"); + svg.find(".c3-path,.c3-line,.tick,.domain").css("fill", "none"); + svg.find(".tick,.domain").css("stroke", "#000"); + r.element = svg[0]; + r.extension = "svg"; + r.contentType = "image/svg+xml"; + }); + div.on("reactive-resize", function() { if ( chart ) { if ( tmo ) clearTimeout(tmo); diff --git a/lib/swish/render/table.pl b/lib/swish/render/table.pl index d11962e..400c852 100644 --- a/lib/swish/render/table.pl +++ b/lib/swish/render/table.pl @@ -32,6 +32,8 @@ ]). :- use_module(library(apply)). :- use_module(library(lists)). +:- use_module(library(pairs)). +:- use_module(library(dicts)). :- use_module(library(option)). :- use_module(library(http/html_write)). :- use_module(library(http/term_html)). @@ -54,6 +56,17 @@ Render table-like data. % % @tbd: recognise more formats +term_rendering(Term, _Vars, Options) --> + { is_list_of_dicts(Term, _Rows, ColNames) + }, !, + html(div([ style('display:inline-block'), + 'data-render'('List of terms as a table') + ], + [ table(class('render-table'), + [ \header_row(ColNames), + \rows(Term, Options) + ]) + ])). term_rendering(Term, _Vars, Options) --> { is_list_of_terms(Term, _Rows, _Cols), header(Term, Header, Options) @@ -63,7 +76,7 @@ term_rendering(Term, _Vars, Options) --> ], [ table(class('render-table'), [ \header_row(Header), - \rows(Term) + \rows(Term, Options) ]) ])). term_rendering(Term, _Vars, Options) --> @@ -75,24 +88,31 @@ term_rendering(Term, _Vars, Options) --> ], [ table(class('render-table'), [ \header_row(Header), - \rows(Term) + \rows(Term, Options) ]) ])). -rows([]) --> []. -rows([H|T]) --> +rows([], _) --> []. +rows([H|T], Options) --> { cells(H, Cells) }, - html(tr(\row(Cells))), - rows(T). - -row([]) --> []. -row([H|T]) --> + html(tr(\row(Cells, Options))), + rows(T, Options). + +row([], _) --> []. +row([H|T], Options) --> + html(td(\term(H, Options))), + row(T, Options). +row([H|T], Options) --> html(td(\term(H, []))), - row(T). + row(T, Options). cells(Row, Cells) :- is_list(Row), !, Cells = Row. +cells(Row, Cells) :- + is_dict(Row), !, + dict_pairs(Row, _Tag, Pairs), + pairs_values(Pairs, Cells). cells(Row, Cells) :- compound(Row), compound_name_arguments(Row, _, Cells). @@ -156,6 +176,20 @@ is_term_row(Name, Arity, Term) :- compound(Term), compound_name_arity(Term, Name, Arity). +%% is_list_of_dicts(@Term, -Rows, -ColNames) is semidet. +% +% True when Term is a list of Rows dicts, each holding ColNames as +% keys. + +is_list_of_dicts(Term, Rows, ColNames) :- + is_list(Term), Term \== [], + length(Term, Rows), + maplist(is_dict_row(ColNames), Term). + +is_dict_row(ColNames, Dict) :- + is_dict(Dict), + dict_keys(Dict, ColNames). + %% is_list_of_lists(@Term, -Rows, -Cols) is semidet. % % Recognise a list of lists of equal length. diff --git a/lib/swish/storage.pl b/lib/swish/storage.pl index da7dcbc..5a9f6a1 100644 --- a/lib/swish/storage.pl +++ b/lib/swish/storage.pl @@ -84,6 +84,10 @@ web_storage(Request) :- storage(Method, Request). storage(get, Request) :- + ( swish_config:authenticate(Request, User) + -> Options = [user(User)] + ; Options = [] + ), http_parameters(Request, [ format(Fmt, [ oneof([swish,raw,json,history,diff]), default(swish), @@ -106,7 +110,8 @@ storage(get, Request) :- -> Format = diff(RelTo) ; Format = Fmt ), - storage_get(Request, Format). + storage_get(Request, Format, Options). + storage(post, Request) :- http_read_json_dict(Request, Dict), option(data(Data), Dict, ""), @@ -220,7 +225,7 @@ meta_allowed(tags, list(string)). meta_allowed(description, string). meta_allowed(commit_message, string). -%% storage_get(+Request, +Format) is det. +%% storage_get(+Request, +Format, +Options) is det. % % HTTP handler that returns information a given gitty file. % @@ -238,9 +243,9 @@ meta_allowed(commit_message, string). % Reply with diff relative to RelTo. Default is the % previous commit. -storage_get(Request, swish) :- - swish_reply_config(Request), !. -storage_get(Request, Format) :- +storage_get(Request, swish, Options) :- + swish_reply_config(Request, Options), !. +storage_get(Request, Format, _) :- setting(directory, Dir), request_file_or_hash(Request, Dir, FileOrHash, Type), storage_get(Format, Dir, Type, FileOrHash, Request). diff --git a/lib/swish/swish_debug.pl b/lib/swish/swish_debug.pl index d4039c4..1a2aaea 100644 --- a/lib/swish/swish_debug.pl +++ b/lib/swish/swish_debug.pl @@ -28,7 +28,8 @@ */ :- module(swish_debug, - [ pengine_stale_module/1, % -Module + [ pengine_stale_module/1, % -Module, -State + pengine_stale_module/2, % -Module, -State swish_statistics/1, % -Statistics start_swish_stat_collector/0, swish_stats/2 % ?Period, ?Dicts @@ -38,13 +39,18 @@ :- use_module(library(lists)). :- use_module(library(apply)). :- use_module(library(debug)). +:- use_module(library(aggregate)). :- use_module(procps). :- use_module(highlight). +:- if(exists_source(library(mallocinfo))). +:- use_module(library(mallocinfo)). +:- endif. -%% pengine_stale_module(-M) is nondet. +%% pengine_stale_module(-M, -State) is nondet. % % True if M seems to be a pengine module with no associated -% pengine. +% pengine. State is a dict that describes what we know about the +% module. pengine_stale_module(M) :- current_module(M), @@ -52,11 +58,38 @@ pengine_stale_module(M) :- \+ live_module(M), \+ current_highlight_state(M, _). +pengine_stale_module(M, State) :- + pengine_stale_module(M), + stale_module_state(M, State). + live_module(M) :- pengine_property(Pengine, module(M)), pengine_property(Pengine, thread(Thread)), catch(thread_property(Thread, status(running)), _, fail). +stale_module_state(M, State) :- + findall(N-V, stale_module_property(M, N, V), Properties), + dict_create(State, stale, Properties). + +stale_module_property(M, pengine, Pengine) :- + pengine_property(Pengine, module(M)). +stale_module_property(M, pengine_queue, Queue) :- + pengine_property(Pengine, module(M)), + pengines:pengine_queue(Pengine, Queue, _TimeOut, _Time). +stale_module_property(M, pengine_pending_queue, Queue) :- + pengine_property(Pengine, module(M)), + pengines:output_queue(Pengine, Queue, _Time). +stale_module_property(M, thread, Thread) :- + pengine_property(Pengine, module(M)), + pengine_property(Pengine, thread(Thread)). +stale_module_property(M, thread_status, Status) :- + pengine_property(Pengine, module(M)), + pengine_property(Pengine, thread(Thread)), + catch(thread_property(Thread, status(Status)), _, fail). +stale_module_property(M, program_space, Space) :- + module_property(M, program_space(Space)). + + %% swish_statistics(?State) % % True if State is a statistics about SWISH @@ -64,7 +97,9 @@ live_module(M) :- swish_statistics(highlight_states(Count)) :- aggregate_all(count, current_highlight_state(_,_), Count). swish_statistics(pengines(Count)) :- - aggregate_all(count, pengine_property(_,self(_)), Count). + aggregate_all(count, pengine_property(_,thread(_)), Count). +swish_statistics(remote_pengines(Count)) :- + aggregate_all(count, pengine_property(_,remote(_)), Count). swish_statistics(pengines_created(Count)) :- ( flag(pengines_created, Old, Old) -> Count = Old @@ -100,8 +135,13 @@ uuid_code(_, X) :- char_type(X, xdigit(_)). * STATISTICS * *******************************/ +:- if(current_predicate(http_unix_daemon:http_daemon/0)). +:- use_module(library(broadcast)). +:- listen(http(post_server_start), start_swish_stat_collector). +:- else. :- initialization start_swish_stat_collector. +:- endif. %% start_swish_stat_collector % @@ -142,7 +182,7 @@ swish_stat_collector(Thread, Dims, Interval) :- % Number of running pengines % * pengines_created % Total number of pengines created -% * d_pengines +% * d_pengines_created % Pengines created per second % * rss % Total resident memory @@ -210,20 +250,32 @@ reply_stats_request(Client-get_stats(Period), SlidingStat) :- swish_stats(stats{ cpu:CPU, rss:RSS, + fordblks:Fordblks, stack:Stack, pengines:Pengines, - pengines_created:PenginesCreated + pengines_created:PenginesCreated, + time:Time }) :- + get_time(Now), + Time is floor(Now), statistics(process_cputime, PCPU), statistics(cputime, MyCPU), CPU is PCPU-MyCPU, statistics(stack, Stack), + fordblks(Fordblks), catch(procps_stat(Stat), _, Stat = stat{rss:0}), RSS = Stat.rss, swish_statistics(pengines(Pengines)), swish_statistics(pengines_created(PenginesCreated)). +:- if(current_predicate(mallinfo/1)). +fordblks(Fordblks) :- + mallinfo(MallInfo), + Fordblks = MallInfo.fordblks. +:- endif. + + /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Maintain sliding statistics. The statistics are maintained in a ring. If the ring wraps around, the average is pushed to the next ring. @@ -305,7 +357,6 @@ avg_key(Dicts, Len, Key, Key-Avg) :- sandbox:safe_primitive/1. sandbox:safe_primitive(swish_debug:pengine_stale_module(_)). +sandbox:safe_primitive(swish_debug:pengine_stale_module(_,_)). sandbox:safe_primitive(swish_debug:swish_statistics(_)). sandbox:safe_primitive(swish_debug:swish_stats(_, _)). - - diff --git a/lib/swish/trace.pl b/lib/swish/trace.pl index 3772053..f009aad 100644 --- a/lib/swish/trace.pl +++ b/lib/swish/trace.pl @@ -28,7 +28,7 @@ */ :- module(swish_trace, - [ '$swish wrapper'/1 % +Goal + [ '$swish wrapper'/2 % +Goal, -Residuals ]). :- use_module(library(debug)). :- use_module(library(settings)). @@ -54,7 +54,7 @@ :- set_prolog_flag(generate_debug_info, false). :- meta_predicate - '$swish wrapper'(0). + '$swish wrapper'(0, -). /** <module> @@ -177,7 +177,7 @@ strip_stack(error(Error, context(prolog_stack(S), Msg)), nonvar(S). strip_stack(Error, Error). -%% '$swish wrapper'(:Goal) +%% '$swish wrapper'(:Goal, -Residuals) % % Wrap a SWISH goal in '$swish wrapper'. This has two advantages: % we can detect that the tracer is operating on a SWISH goal by @@ -186,9 +186,11 @@ strip_stack(Error, Error). :- meta_predicate swish_call(0). -'$swish wrapper'(Goal) :- +'$swish wrapper'(Goal, '$residuals'(Residuals)) :- catch(swish_call(Goal), E, throw(E)), deterministic(Det), + Goal = M:_, + residuals(M, Residuals), ( tracing, Det == false -> ( notrace, @@ -210,6 +212,30 @@ no_lco. :- '$hide'(no_lco/0). +%% residuals(+PengineModule, -Goals:list(callable)) is det. +% +% Find residual goals that are not bound to the projection +% variables. We must do so while we are in the Pengine as the +% goals typically live in global variables that are not visible +% when formulating the answer from the projection variables as +% done in library(pengines_io). +% +% This relies on the SWI-Prolog 7.3.14 residual goal extension. + +:- if(current_predicate(prolog:residual_goals//0)). +residuals(TypeIn, Goals) :- + phrase(prolog:residual_goals, Goals0), + maplist(unqualify_residual(TypeIn), Goals0, Goals). + +unqualify_residual(M, M:G, G) :- !. +unqualify_residual(T, M:G, G) :- + predicate_property(T:G, imported_from(M)), !. +unqualify_residual(_, G, G). +:- else. +residuals(_, []). +:- endif. + + /******************************* * SOURCE LOCATION * *******************************/ @@ -558,6 +584,7 @@ prolog_clause:open_source(File, Stream) :- exception_hook(Ex, Ex, _Frame, Catcher) :- Catcher \== none, + Catcher \== 'C', prolog_frame_attribute(Catcher, predicate_indicator, PI), debug(trace(exception), 'Ex: ~p, catcher: ~p', [Ex, PI]), PI == '$swish wrapper'/1, @@ -596,6 +623,7 @@ sandbox:safe_primitive(system:notrace). sandbox:safe_primitive(system:tracing). sandbox:safe_primitive(edinburgh:debug). sandbox:safe_primitive(system:deterministic(_)). +sandbox:safe_primitive(swish_trace:residuals(_,_)). /******************************* diff --git a/web/help/help.html b/web/help/help.html index ecee09e..a47ac56 100644 --- a/web/help/help.html +++ b/web/help/help.html @@ -5,6 +5,13 @@ <title>SWISH: SWI-Prolog for SHaring</title> </head> <body> +<style> +p.note { margin-left: 5%; position: relative } +p.note span.glyphicon-hand-right { +float: left; font-size: 150%; color: orange; padding-right: 0.2em; +} +</style> + <h4>Table of Contents</h4> <ul> <li><a href="#help-basics">Basic operation</a></li> @@ -26,9 +33,10 @@ against the <i>built-in</i> predicates of Prolog. For example: ?- format("Hello world!~n").</pre> <p> -A query can be executed by hitting <code>RETURN</code> if the query -is complete and the caret is behind the '.' that terminates the query -or by using the <b>Run!</b> button. At this moment, the following happens: +A query can be executed by hitting <code>RETURN</code> if the query is +<em>complete</em> (i.e., ends in a full-stop) or by using the <a +class="btn btn-xs btn-primary">Run!</a> button. At this moment, the +following happens: <ol> <li>The interface creates a <em>runner</em> in the top-right window @@ -48,11 +56,16 @@ or by using the <b>Run!</b> button. At this moment, the following happens: </ol> <p> -Note that <b>you do not have to save your program to execute it</b>. If -your are not satisfied with the answer to a query, you can simply -edit the program and use the <b>Run!</b> again. The new query is -executed in a completely new environment. In particular, data that -you asserted in a previous query is not available in the next. +Note that <b>you do not have to save your program to execute it</b>. If +your are not satisfied with the answer to a query, you can simply edit +the program and use <a class="btn btn-xs btn-primary">Run!</a> again. +The new query is executed in a completely new environment. In +particular, data that you asserted in a previous query is not available +in the next. + +<p class="note"> +<span class="glyphicon glyphicon-hand-right"></span> Use +<strong>Ctrl-Enter</strong> to insert a newline in a complete query <h2 id="help-examples">Embedding examples in the program text</h2> <p> @@ -119,13 +132,17 @@ that provides shared editing, chat and voice communication. <p> After running a query, the <strong>complete</strong> result set for the query can be downloaded as a CSV (Comma Separated Values) document by -clicking the double down arrow at the <em>runner</em> window. This -causes a dialogue to appear that allows for specifying the columns, -optionally the detailed result format (if the server provides multiple -result formats), whether only <em>distinct</em> results should be -returned and the maximum number of results to return. The latter is by -default set to 10 000 to avoid sending huge documents by accident. -The field can be cleared to return all results. +clicking the <span class="glyphicon glyphicon-download"></span> button +at the top-right of a <em>runner</em> window or using the +<strong>Download answers as CSV</strong> option from the <span +class="glyphicon glyphicon-menu-hamburger"></span> button on +<em>notebook query cells</em>. This causes a dialogue to appear that +allows for specifying the columns, optionally the detailed result format +(if the server provides multiple result formats), whether only +<em>distinct</em> results should be returned and the maximum number of +results to return. The latter is by default set to 10 000 to avoid +sending huge documents by accident. The field can be cleared to return +all results. <h3>Download query results through an API</h3> <p> @@ -198,6 +215,7 @@ providing the parameters below. The URL accepts both `GET` and `POST` requests. <pre> http://swish.swi-prolog.org/?code=https://github.com/SWI-Prolog/swipl-devel/raw/master/demo/likes.pl&q=likes(sam,Food).</pre> +<a target="_blank" href="http://swish.swi-prolog.org/?code=https://github.com/SWI-Prolog/swipl-devel/raw/master/demo/likes.pl&q=likes(sam,Food).">Try it!</a> (launches a new tab) </body> </html> diff --git a/web/help/notebook.html b/web/help/notebook.html index 1c70cb5..ef786d8 100644 --- a/web/help/notebook.html +++ b/web/help/notebook.html @@ -2,10 +2,15 @@ <html> <head> + <title>About Prolog notebooks</title> </head> <body> -<h2>About Prolog notebooks</h2> - +<style> +p.note { margin-left: 5%; margin-top: 1em; position: relative } +p.note span.glyphicon-hand-right { +float: left; font-size: 120%; color: orange; padding-right: 0.2em; +} +</style> <p> A notebook is a list of <i>cells</i>. Notebooks are different from a program with examples because they can <em>tell a story</em>, mixing @@ -40,14 +45,24 @@ and moving cells. <li> To <b>link to another notebook</b>, create a markdown cell and use the markdown notation <code>[label](reference)</code>, e.g., - -<pre style="font-size:80%"> -[All about lists](lists.swinb) -</pre> +<code>[All about lists](lists.swinb)</code> <li> To <b>link to file in the store</b>, create a markdown cell and use the markdown notation <code>[label](myfile.pl)</code>, e.g., +<code>[Basic predicates](basics.pl)</code> </ul> +<h3>Notebook queries and programs</h3> + +A notebook <em>query</em> cell is executed against the program cell +<strong>above it</strong> and all program cells marked as +<em>background</em> using the <a class="btn btn-success btn-xs"><span +style="color:#bef" class="glyphicon glyphicon-cloud"></span></a> button. + +<p class="note"><span class="glyphicon glyphicon-hand-right"></span> +Longer fragments of code that are required throughout a notebook and +possibly on multiple notebooks are defined in a <em>program</em> tab, +saved and included using, e.g., <code>:- include(mybasics).</code> in a +background program kept at the bottom of the notebook. </body> </html> diff --git a/web/help/runner.html b/web/help/runner.html index 3afe1dd..752053c 100644 --- a/web/help/runner.html +++ b/web/help/runner.html @@ -85,9 +85,10 @@ queries. <h2>Get results as CSV or through an API</h2> <p> -Variable bindings can be downloaded as CSV using the double down arrow -at the top-right of a runner or using an appropriate client library to -access the web API. See the <b>Help/help ...</b> for details. +Variable bindings can be downloaded as CSV using the <span +class="glyphicon glyphicon-download"></span> button at the top-right of +a runner or using an appropriate client library to access the web API. +See the <b>Help/help ...</b> for details. </body> </html> diff --git a/web/icons/wip.png b/web/icons/wip.png new file mode 100644 index 0000000..62dbd0e Binary files /dev/null and b/web/icons/wip.png differ