d3a25d37创建于 2024年6月4日历史提交
open! Core
open! Async

type t =
  | Use_fzf_to_select_one of [ `File | `Func | `File_or_func ]
  | User_selected of string

let of_command_string s =
  match s with
  | "?" -> Use_fzf_to_select_one `File_or_func
  | "line:?" -> Use_fzf_to_select_one `File
  | "symbol:?" -> Use_fzf_to_select_one `Func
  | "." -> User_selected Magic_trace.Private.stop_symbol
  | s -> User_selected s
;;

let select_owee_symbol ~elf ~header select =
  let open Deferred.Or_error.Let_syntax in
  let all_symbols = Elf.all_symbols ~select elf in
  let all_symbol_names = List.map all_symbols ~f:Tuple2.get1 in
  let fzf_pick_from : _ Fzf.Pick_from.t =
    if not Env_vars.fzf_demangle_symbols
    then Inputs all_symbol_names
    else
      List.map all_symbol_names ~f:(fun mangled_symbol ->
        match Demangle_ocaml_symbols.demangle mangled_symbol with
        | None -> mangled_symbol, mangled_symbol
        | Some demangled_symbol -> demangled_symbol, mangled_symbol)
      |> Assoc
  in
  match%bind Fzf.pick_one ~header fzf_pick_from with
  | None -> Deferred.Or_error.error_string "No symbol selected"
  | Some chosen_name ->
    let chosen_symbol =
      List.find_exn all_symbols ~f:(fun (name, _symbol) -> String.(name = chosen_name))
      |> Tuple2.get2
    in
    return (chosen_name, chosen_symbol)
;;

let select_within_file ~elf ~header symbol =
  let open Deferred.Or_error.Let_syntax in
  let all_file_selections = Elf.all_file_selections elf symbol in
  let all_file_selection_names = List.map all_file_selections ~f:Tuple2.get1 in
  let%bind () =
    if List.is_empty all_file_selections
    then
      Deferred.Or_error.error_string
        "No lines found, possibly because of missing debug info"
    else return ()
  in
  match%bind Fzf.pick_one ~header (Inputs all_file_selection_names) with
  | None -> Deferred.Or_error.error_string "No location selected"
  | Some chosen_name -> return chosen_name
;;

let evaluate ~supports_fzf ~elf ~header symbol_selection =
  let open Deferred.Or_error.Let_syntax in
  let%bind elf =
    match elf with
    | None -> Deferred.Or_error.error_string "No ELF found"
    | Some elf -> return elf
  in
  match symbol_selection with
  | Use_fzf_to_select_one select ->
    let%bind () =
      if force supports_fzf
      then return ()
      else
        Deferred.Or_error.error_string
          "magic-trace could show you a fuzzy-finding selector here if \"fzf\" were in \
           your PATH, but it is not."
    in
    let%bind chosen_name, chosen_symbol = select_owee_symbol ~elf ~header select in
    (match Owee_elf.Symbol_table.Symbol.type_attribute chosen_symbol with
     | File ->
       let%bind chosen_name =
         select_within_file ~elf ~header:(header ^ " line") chosen_symbol
       in
       return chosen_name
     | _ -> return chosen_name)
  | User_selected user_selection -> return user_selection
;;