# # patch "agraph.ml" # from [3720c75f76f3cefce06caf6dbc4ec724dc3507bc] # to [a6e6dbc0c5569090a3941d0b323a55e7423edd26] # # patch "agraph.mli" # from [f6eccc6f52c059e695a6b07aa56275714c923b6a] # to [88bee9cb508728a7e7510152a4443bf78c98f970] # # patch "database.ml" # from [90b37e10fe620841c457fe01d5232557bc4c61a1] # to [d175953b39d064060c10e7e83020819b29e10ad7] # # patch "database.mli" # from [c73fe20033fb3e87ce40f360750a23b79666f190] # to [8839002cdbfcdd75ee27a6acbbcd54fcd3ff3554] # # patch "query.ml" # from [9052a40399e3393ce59027a5b4003090a5f90c3f] # to [85e05714c302fcd4df68c95f994532a5b9c8dfb6] # # patch "view.ml" # from [e4b963561802c88a3939e9ed4cdb7f1e24776340] # to [03f0acbc46c59d2b6bd24c7c74d9df5a4c1c3561] # # patch "view.mli" # from [132253c13a9469d919d301d6c0e845b78f34d9d1] # to [e5c355a71b30159cdce2eb5f6073b73e66e05ba6] # ======================================================================== --- agraph.ml 3720c75f76f3cefce06caf6dbc4ec724dc3507bc +++ agraph.ml a6e6dbc0c5569090a3941d0b323a55e7423edd26 @@ -281,6 +281,9 @@ let get_query { query = q } = q +let get_ids { agraph = g } = + NodeMap.fold (fun id _ acc -> id :: acc) g.nodes [] + let mem { agraph = g } id = NodeMap.mem id g.nodes ======================================================================== --- agraph.mli f6eccc6f52c059e695a6b07aa56275714c923b6a +++ agraph.mli 88bee9cb508728a7e7510152a4443bf78c98f970 @@ -18,6 +18,7 @@ val abort_layout : t -> unit val get_query : t -> query +val get_ids : t -> string list val mem : t -> string -> bool val get_ancestors : t -> string -> string list ======================================================================== --- database.ml 90b37e10fe620841c457fe01d5232557bc4c61a1 +++ database.ml d175953b39d064060c10e7e83020819b29e10ad7 @@ -209,22 +209,6 @@ let fetch_agraph query db = fetch_with_view query db fetch_agraph_with_view -let revision_contains pattern = function - | (_, [ edge ]) -> - List.exists - (function - | Revision_types.PATCH (f, _, _) -> f = pattern - | Revision_types.ADD_FILE f - | Revision_types.DELETE_FILE f - | Revision_types.DELETE_DIR f -> f = pattern - | Revision_types.RENAME_FILE (f1, f2) - | Revision_types.RENAME_DIR (f1, f2) -> - f1 = pattern || f2 = pattern) - edge.Revision_types.change_set - | _ -> - (* return false for merges *) - false - let decode_and_parse_revision s = Revision_parser.revision_set Revision_lexer.lex @@ -274,11 +258,8 @@ FROM revision_certs WHERE id = '%s' AND name = '%s'" id (sql_escape name) -let spawn_monotone_diff db_fname monotone_exe (old_id, new_id) status cb = - let cmd = [ monotone_exe ; - "--db=" ^ db_fname ; - "--revision=" ^ old_id ; - "--revision=" ^ new_id ; "diff" ] in +let spawn_monotone monotone_exe db_fname cmd status cb = + let cmd = monotone_exe :: "--db" :: db_fname :: cmd in if Viz_misc.debug "exec" then Printf.eprintf "### exec: Running '%s'\n%!" (String.concat " " cmd) ; try @@ -289,7 +270,7 @@ (fun ~exceptions ~stdout ~stderr status -> if status = 0 then - cb (`DIFF stdout) + cb (`OUTPUT stdout) else let error fmt = Printf.kprintf (fun s -> cb (`SUB_PROC_ERROR s)) fmt in @@ -304,18 +285,6 @@ -let with_bool_funs db funs f = - List.iter - (fun (name, p) -> - Sqlite3.create_fun_1 db name - (fun v -> if p (Sqlite3.value_text v) then `INT 1 else `INT 0)) - funs ; - let unregister () = - List.iter - (fun (name, _) -> Sqlite3.delete_function db name) - funs in - try let r = f db in unregister () ; r - with exn -> unregister () ; raise exn @@ -418,54 +387,14 @@ get_matching_cert db "date" (string_is_prefix d_pref) -let fetch_list db sql = - Sqlite3.fetch db sql - (fun acc -> function [| v |] -> v :: acc | _ -> acc) - [] +let run_monotone_diff db monotone_exe status cb (old_id, new_id) = + ignore (spawn_monotone + monotone_exe db.filename + [ "--revision" ; old_id ; + "--revision" ; new_id ; "diff" ] + status cb) +let run_monotone_select db monotone_exe status cb selectors = + spawn_monotone + monotone_exe db.filename [ "automate" ; "select" ; selectors ] + status cb -let do_query_certs db mode = - let select_cert name = - Printf.sprintf - "SELECT C.id FROM revision_certs AS C \ - WHERE C.id IN %s AND C.name = '%s' AND cert_match(C.value)" - view_name name in - let select_revision sub = - Printf.sprintf - "SELECT R.id FROM revisions AS R \ - WHERE R.id IN %s AND revision_match(R.data)" sub in - match mode with - | `CERTS name -> - fetch_list db (select_cert name) - | `REVISIONS -> - fetch_list db (select_revision view_name) - | `BOTH name -> - fetch_list db (select_revision ("(" ^ select_cert name ^ ")")) - -let query_certs db query name pattern revision_content = - let mode = - match revision_content, name, pattern with - | "", "", _ - | "", _, "" -> `NOTHING - | "", _, _ -> `CERTS name - | _, "", "" -> `REVISIONS - | _, _, _ -> `BOTH name in - match mode with - | `NOTHING -> [] - | `CERTS _ | `REVISIONS | `BOTH _ as mode -> - fetch_with_view query db.db - (fun db _ -> - let p = string_contains pattern in - with_bool_funs db - [ "cert_match", - (fun v -> p (monot_decode v)) ; - "revision_match", - (fun v -> - revision_contains - revision_content - (decode_and_parse_revision v)) ] - (fun db -> do_query_certs db mode)) - - -let run_monotone_diff db monotone_exe edge status cb = - ignore - (spawn_monotone_diff db.filename monotone_exe edge status cb) ======================================================================== --- database.mli c73fe20033fb3e87ce40f360750a23b79666f190 +++ database.mli 8839002cdbfcdd75ee27a6acbbcd54fcd3ff3554 @@ -20,11 +20,17 @@ val get_matching_tags : t -> (string -> bool) -> (string * string) list val get_matching_dates : t -> string -> (string * string) list -val query_certs : t -> query -> string -> string -> string -> string list val get_key_rowid : t -> string -> int val run_monotone_diff : - t -> string -> string * string -> + t -> string -> Status.reporter -> + ([>`SUB_PROC_ERROR of string | `OUTPUT of string] -> unit) -> + string * string -> unit + +val run_monotone_select : + t -> string -> + Status.reporter -> + ([>`SUB_PROC_ERROR of string | `OUTPUT of string] -> unit) -> + string -> Subprocess.t - ([>`SUB_PROC_ERROR of string | `DIFF of string] -> unit) -> unit ======================================================================== --- query.ml 9052a40399e3393ce59027a5b4003090a5f90c3f +++ query.ml 85e05714c302fcd4df68c95f994532a5b9c8dfb6 @@ -9,34 +9,127 @@ let init_stock = Lazy.lazy_from_fun make_factory +open Viz_types -let do_query ~cert_name ~cert_value ~revision_content v = - match v.View.db, v.View.agraph with - | Some db, Some g when revision_content <> "" || (cert_value <> "" && cert_value <> "") -> - View.nice_fetch - (fun db -> - let ids = - Database.query_certs - db (Agraph.get_query g) - cert_name cert_value revision_content in +module Selector = struct - let fetch_first_cert id c = - match Database.fetch_cert_value db id c with - | h :: _ -> h - | [] -> "" in + let make_selector g sel = + match Agraph.get_query g with + | ALL -> + sel + | BRANCH b -> + Printf.sprintf "b:%s/%s" b sel + | COLLECTION b -> + Printf.sprintf "b:%s*/%s" b sel - List.map - (fun id -> - let date = fetch_first_cert id "date" in - let author = fetch_first_cert id "author" in - id, date, author) - ids) + let running_select = ref None + + let abort () = + match !running_select with + | Some id -> + Subprocess.abort id ; + running_select := None + | _ -> + () + + let select v status db g sel cont = + let id = + Database.run_monotone_select db + v.View.prefs.Viz_style.monotone_path + status + (fun r -> + running_select := None ; + match r with + | `OUTPUT l -> + cont (`IDS (Viz_misc.string_split '\n' l)) + | `SUB_PROC_ERROR _ as err -> + cont err) + (make_selector g sel) in + running_select := Some id +end + +let revision_contains pattern = function + | [ _, changes ] -> + List.exists + (function + | Revision_types.PATCH (f, _, _) -> f = pattern + | Revision_types.ADD_FILE f + | Revision_types.DELETE_FILE f + | Revision_types.DELETE_DIR f -> f = pattern + | Revision_types.RENAME_FILE (f1, f2) + | Revision_types.RENAME_DIR (f1, f2) -> + f1 = pattern || f2 = pattern) + changes | _ -> - [] + (* return false for merges *) + false +let filter_by_revision_content status db revision_content ids = + Status.with_status + status + "Searching the monotone database ..." + (fun () -> + List.fold_left + (fun acc id -> + let r = Database.fetch_revision db id in + if revision_contains revision_content r.revision_set + then id :: acc + else acc) + [] ids) + +let select_by_revision_content status db revision_content g = + filter_by_revision_content + status db revision_content + (Agraph.get_ids g) + + +let expand_results db ids = + let fetch_first_cert id c = + match Database.fetch_cert_value db id c with + | h :: _ -> h + | [] -> "" in + + List.map + (fun id -> + let date = fetch_first_cert id "date" in + let author = fetch_first_cert id "author" in + id, date, author) + ids + + +let do_query status ~selector ~revision_content v results_cb = + let no_results () = + results_cb (`IDS []) in + let results_ids db ids = + results_cb (`IDS (expand_results db ids)) in + + match v.View.db, v.View.agraph with + | Some db, Some g when selector <> "" -> + Selector.select + v status db g selector + (function + | `IDS ids when revision_content <> "" -> + results_ids db + (filter_by_revision_content + status db revision_content ids) + | `IDS ids -> + results_ids db ids + | `SUB_PROC_ERROR _ as err -> + results_cb err) + | Some db, Some g when revision_content <> "" -> + results_ids db + (select_by_revision_content + status db revision_content g) + | _ -> + no_results () + + + + + let category title ?expand (vbox : #GPack.box) = let base_label = Printf.sprintf "%s" (Glib.Markup.escape_text title) in @@ -58,20 +151,15 @@ let (packing, _) = category "Query" vbox in let packing = (GPack.vbox ~packing ())#pack in let hbox = GPack.hbox ~packing () in - let _ = GMisc.label ~text:"Cert name: " ~packing:hbox#pack () in - let (e_cert_name, _) = - GEdit.combo_box_entry_text - ~strings:["author" ; "changelog" ; "comment" ; "date" ; "tag" ] - ~packing:hbox#pack () in - let _ = GMisc.label ~text:" contains " ~packing:hbox#pack () in - let e_cert_value = + let _ = GMisc.label ~text:"Monotone selector: " ~packing:hbox#pack () in + let e_selector = GEdit.entry ~packing:(hbox#pack ~expand:true) () in let hbox = GPack.hbox ~packing () in let _ = GMisc.label ~text:"Revision concerns file: " ~packing:hbox#pack () in let e_revision = GEdit.entry ~packing:(hbox#pack ~expand:true) () in - (e_cert_name#entry, e_cert_value, e_revision) + (e_selector, e_revision) type model = { @@ -134,7 +222,7 @@ ~border_width:8 ~type_hint:`NORMAL () in - let (e1, e2, e3) = setup_query_builder w#vbox in + let (e1, e2) = setup_query_builder w#vbox in let (m, rv, set_label) = setup_results_view w#vbox in let status = Status.new_reporter "query" in @@ -145,10 +233,10 @@ ignore (w#connect#close w#misc#hide) ; ignore (w#event#connect#delete (fun _ -> w#misc#hide () ; true)) ; + ignore (e1#connect#activate (fun () -> + w#response `QUERY)) ; ignore (e2#connect#activate (fun () -> w#response `QUERY)) ; - ignore (e3#connect#activate (fun () -> - w#response `QUERY)) ; ignore (w#connect#response (function | `CLOSE | `DELETE_EVENT -> @@ -157,18 +245,24 @@ clear_model m ; set_label 0 | `QUERY -> - View.with_busy_cursor w (fun () -> - Status.with_status - status - "Searching the monotone database ..." - (fun () -> - let results = - do_query - ~cert_name:e1#text ~cert_value:e2#text - ~revision_content:e3#text - v in - update_results m results ; - set_label (List.length results))))) ; + w#set_response_sensitive `QUERY false ; + View.set_busy_cursor w true ; + do_query + status + ~selector:e1#text + ~revision_content:e2#text + v + (fun r -> + begin + match r with + | `IDS results -> + update_results m results ; + set_label (List.length results) + | `SUB_PROC_ERROR msg -> + View.error_notice ~parent:w msg + end ; + View.set_busy_cursor w false ; + w#set_response_sensitive `QUERY true))) ; ignore (rv#connect#row_activated (fun path view_col -> let id = @@ -179,11 +273,11 @@ View.connect_event v (function | `CLEAR -> + Selector.abort () ; clear_model m ; set_label 0 ; e1#set_text "" ; e2#set_text "" ; - e3#set_text "" ; w#set_response_sensitive `QUERY false | `UPDATE_BEGIN -> w#set_response_sensitive `QUERY true ======================================================================== --- view.ml e4b963561802c88a3939e9ed4cdb7f1e24776340 +++ view.ml 03f0acbc46c59d2b6bd24c7c74d9df5a4c1c3561 @@ -41,10 +41,6 @@ Gdk.Window.set_cursor w#misc#window (Lazy.force (if busy then busy_cursor else normal_cursor)) -let with_busy_cursor w f = - set_busy_cursor w true ; - try let r = f () in set_busy_cursor w false ; r - with exn -> set_busy_cursor w false ; raise exn type info_display = { revision_label : GMisc.label ; @@ -1218,14 +1214,15 @@ let parent = v.canvas.w in try Database.run_monotone_diff - (some v.db) v.prefs.Viz_style.monotone_path (old_id, new_id) + (some v.db) v.prefs.Viz_style.monotone_path (Lazy.force v.status_reporter) (fun res -> match res with - | `DIFF d -> + | `OUTPUT d -> Unidiff.view ~parent d | `SUB_PROC_ERROR msg -> error_notice ~parent msg) + (old_id, new_id) with Viz_types.Error msg -> error_notice ~parent msg ======================================================================== --- view.mli 132253c13a9469d919d301d6c0e845b78f34d9d1 +++ view.mli e5c355a71b30159cdce2eb5f6073b73e66e05ba6 @@ -1,10 +1,10 @@ val error_notice : parent:#GObj.widget -> string -> unit val wrap_in_scroll_window : (GObj.widget -> unit) -> GObj.widget -> unit val nice_fetch : (Database.t -> 'a) -> Database.t -> 'a -val with_busy_cursor : #GObj.widget -> (unit -> 'a) -> 'a +val set_busy_cursor : #GObj.widget -> bool -> unit type info_display type branch_selector