| 
4 | 4 | require 'irb/completion'  | 
5 | 5 | require 'tmpdir'  | 
6 | 6 | require 'fileutils'  | 
 | 7 | +require_relative 'variable'  | 
 | 8 | +require_relative 'variable_inspector'  | 
7 | 9 | 
 
  | 
8 | 10 | module DEBUGGER__  | 
9 | 11 |   module UI_DAP  | 
@@ -765,18 +767,11 @@ def register_vars vars, tid  | 
765 | 767 |     end  | 
766 | 768 |   end  | 
767 | 769 | 
 
  | 
768 |  | -  class NaiveString  | 
769 |  | -    attr_reader :str  | 
770 |  | -    def initialize str  | 
771 |  | -      @str = str  | 
772 |  | -    end  | 
773 |  | -  end  | 
774 |  | - | 
775 | 770 |   class ThreadClient  | 
776 | 771 |     MAX_LENGTH = 180  | 
777 | 772 | 
 
  | 
778 | 773 |     def value_inspect obj, short: true  | 
779 |  | -      # TODO: max length should be configuarable?  | 
 | 774 | +      # TODO: max length should be configurable?  | 
780 | 775 |       str = DEBUGGER__.safe_inspect obj, short: short, max_length: MAX_LENGTH  | 
781 | 776 | 
 
  | 
782 | 777 |       if str.encoding == Encoding::UTF_8  | 
@@ -867,56 +862,28 @@ def process_dap args  | 
867 | 862 |         fid = args.shift  | 
868 | 863 |         frame = get_frame(fid)  | 
869 | 864 |         vars = collect_locals(frame).map do |var, val|  | 
870 |  | -          variable(var, val)  | 
 | 865 | +          render_variable Variable.new(name: var, value: val)  | 
871 | 866 |         end  | 
872 | 867 | 
 
  | 
873 | 868 |         event! :protocol_result, :scope, req, variables: vars, tid: self.id  | 
874 | 869 |       when :variable  | 
875 | 870 |         vid = args.shift  | 
876 |  | -        obj = @var_map[vid]  | 
877 |  | -        if obj  | 
878 |  | -          case req.dig('arguments', 'filter')  | 
879 |  | -          when 'indexed'  | 
880 |  | -            start = req.dig('arguments', 'start') || 0  | 
881 |  | -            count = req.dig('arguments', 'count') || obj.size  | 
882 |  | -            vars = (start ... (start + count)).map{|i|  | 
883 |  | -              variable(i.to_s, obj[i])  | 
884 |  | -            }  | 
885 |  | -          else  | 
886 |  | -            vars = []  | 
887 | 871 | 
 
  | 
888 |  | -            case obj  | 
889 |  | -            when Hash  | 
890 |  | -              vars = obj.map{|k, v|  | 
891 |  | -                variable(value_inspect(k), v,)  | 
892 |  | -              }  | 
893 |  | -            when Struct  | 
894 |  | -              vars = obj.members.map{|m|  | 
895 |  | -                variable(m, obj[m])  | 
896 |  | -              }  | 
897 |  | -            when String  | 
898 |  | -              vars = [  | 
899 |  | -                variable('#length', obj.length),  | 
900 |  | -                variable('#encoding', obj.encoding),  | 
901 |  | -              ]  | 
902 |  | -              printed_str = value_inspect(obj)  | 
903 |  | -              vars << variable('#dump', NaiveString.new(obj)) if printed_str.end_with?('...')  | 
904 |  | -            when Class, Module  | 
905 |  | -              vars << variable('%ancestors', obj.ancestors[1..])  | 
906 |  | -            when Range  | 
907 |  | -              vars = [  | 
908 |  | -                variable('#begin', obj.begin),  | 
909 |  | -                variable('#end', obj.end),  | 
910 |  | -              ]  | 
911 |  | -            end  | 
 | 872 | +        if @var_map.has_key?(vid)  | 
 | 873 | +          obj = @var_map[vid]  | 
912 | 874 | 
 
  | 
913 |  | -            unless NaiveString === obj  | 
914 |  | -              vars += M_INSTANCE_VARIABLES.bind_call(obj).sort.map{|iv|  | 
915 |  | -                variable(iv, M_INSTANCE_VARIABLE_GET.bind_call(obj, iv))  | 
916 |  | -              }  | 
917 |  | -              vars.unshift variable('#class', M_CLASS.bind_call(obj))  | 
918 |  | -            end  | 
 | 875 | +          members = case req.dig('arguments', 'filter')  | 
 | 876 | +          when 'indexed'  | 
 | 877 | +            VariableInspector.new.indexed_members_of(  | 
 | 878 | +              obj,  | 
 | 879 | +              start: req.dig('arguments', 'start') || 0,  | 
 | 880 | +              count: req.dig('arguments', 'count') || obj.size,  | 
 | 881 | +            )  | 
 | 882 | +          else  | 
 | 883 | +            VariableInspector.new.named_members_of(obj)  | 
919 | 884 |           end  | 
 | 885 | + | 
 | 886 | +          vars = members.map { |member| render_variable member }  | 
920 | 887 |         end  | 
921 | 888 |         event! :protocol_result, :variable, req, variables: (vars || []), tid: self.id  | 
922 | 889 | 
 
  | 
@@ -973,7 +940,13 @@ def process_dap args  | 
973 | 940 |           result = 'Error: Can not evaluate on this frame'  | 
974 | 941 |         end  | 
975 | 942 | 
 
  | 
976 |  | -        event! :protocol_result, :evaluate, req, message: message, tid: self.id, **evaluate_result(result)  | 
 | 943 | +        result_variable = Variable.new(name: nil, value: result)  | 
 | 944 | + | 
 | 945 | +        event! :protocol_result, :evaluate, req,  | 
 | 946 | +               message: message,  | 
 | 947 | +               tid: self.id,  | 
 | 948 | +               result: result_variable.inspect_value,  | 
 | 949 | +               **render_variable(result_variable)  | 
977 | 950 | 
 
  | 
978 | 951 |       when :completions  | 
979 | 952 |         fid, text = args  | 
@@ -1035,72 +1008,48 @@ def search_const b, expr  | 
1035 | 1008 |       false  | 
1036 | 1009 |     end  | 
1037 | 1010 | 
 
  | 
1038 |  | -    def evaluate_result r  | 
1039 |  | -      variable nil, r  | 
1040 |  | -    end  | 
1041 |  | - | 
1042 |  | -    def type_name obj  | 
1043 |  | -      klass = M_CLASS.bind_call(obj)  | 
1044 |  | - | 
1045 |  | -      begin  | 
1046 |  | -        M_NAME.bind_call(klass) || klass.to_s  | 
1047 |  | -      rescue Exception => e  | 
1048 |  | -        "<Error: #{e.message} (#{e.backtrace.first}>"  | 
 | 1011 | +    # Renders the given Member into a DAP Variable  | 
 | 1012 | +    # https://microsoft.github.io/debug-adapter-protocol/specification#variable  | 
 | 1013 | +    def render_variable member  | 
 | 1014 | +      indexedVariables, namedVariables = if Array === member.value  | 
 | 1015 | +        [member.value.size, 0]  | 
 | 1016 | +      else  | 
 | 1017 | +        [0, VariableInspector.new.named_members_of(member.value).count]  | 
1049 | 1018 |       end  | 
1050 |  | -    end  | 
1051 | 1019 | 
 
  | 
1052 |  | -    def variable_ name, obj, indexedVariables: 0, namedVariables: 0  | 
 | 1020 | +      #  > If `variablesReference` is > 0, the variable is structured and its children  | 
 | 1021 | +      #  > can be retrieved by passing `variablesReference` to the `variables` request  | 
 | 1022 | +      #  > as long as execution remains suspended.  | 
1053 | 1023 |       if indexedVariables > 0 || namedVariables > 0  | 
 | 1024 | +        # This object has children that we might need to query, so we need to remember it by its vid  | 
1054 | 1025 |         vid = @var_map.size + 1  | 
1055 |  | -        @var_map[vid] = obj  | 
 | 1026 | +        @var_map[vid] = member.value  | 
1056 | 1027 |       else  | 
 | 1028 | +        # This object has no children, so we don't need to remember it in the `@var_map`  | 
1057 | 1029 |         vid = 0  | 
1058 | 1030 |       end  | 
1059 | 1031 | 
 
  | 
1060 |  | -      namedVariables += M_INSTANCE_VARIABLES.bind_call(obj).size  | 
1061 |  | - | 
1062 |  | -      if NaiveString === obj  | 
1063 |  | -        str = obj.str.dump  | 
1064 |  | -        vid = indexedVariables = namedVariables = 0  | 
1065 |  | -      else  | 
1066 |  | -        str = value_inspect(obj)  | 
1067 |  | -      end  | 
1068 |  | - | 
1069 |  | -      if name  | 
1070 |  | -        { name: name,  | 
1071 |  | -          value: str,  | 
1072 |  | -          type: type_name(obj),  | 
 | 1032 | +      variable = if member.name  | 
 | 1033 | +        # These two hashes are repeated so the "name" can come always come first, when available,  | 
 | 1034 | +        # which improves the readability of protocol responses.  | 
 | 1035 | +        {  | 
 | 1036 | +          name: member.name,  | 
 | 1037 | +          value: member.inspect_value,  | 
 | 1038 | +          type: member.value_type_name,  | 
1073 | 1039 |           variablesReference: vid,  | 
1074 |  | -          indexedVariables: indexedVariables,  | 
1075 |  | -          namedVariables: namedVariables,  | 
1076 | 1040 |         }  | 
1077 | 1041 |       else  | 
1078 |  | -        { result: str,  | 
1079 |  | -          type: type_name(obj),  | 
 | 1042 | +        {  | 
 | 1043 | +          value: member.inspect_value,  | 
 | 1044 | +          type: member.value_type_name,  | 
1080 | 1045 |           variablesReference: vid,  | 
1081 |  | -          indexedVariables: indexedVariables,  | 
1082 |  | -          namedVariables: namedVariables,  | 
1083 | 1046 |         }  | 
1084 | 1047 |       end  | 
1085 |  | -    end  | 
1086 | 1048 | 
 
  | 
1087 |  | -    def variable name, obj  | 
1088 |  | -      case obj  | 
1089 |  | -      when Array  | 
1090 |  | -        variable_ name, obj, indexedVariables: obj.size  | 
1091 |  | -      when Hash  | 
1092 |  | -        variable_ name, obj, namedVariables: obj.size  | 
1093 |  | -      when String  | 
1094 |  | -        variable_ name, obj, namedVariables: 3 # #length, #encoding, #to_str  | 
1095 |  | -      when Struct  | 
1096 |  | -        variable_ name, obj, namedVariables: obj.size  | 
1097 |  | -      when Class, Module  | 
1098 |  | -        variable_ name, obj, namedVariables: 1 # %ancestors (#ancestors without self)  | 
1099 |  | -      when Range  | 
1100 |  | -        variable_ name, obj, namedVariables: 2 # #begin, #end  | 
1101 |  | -      else  | 
1102 |  | -        variable_ name, obj, namedVariables: 1 # #class  | 
1103 |  | -      end  | 
 | 1049 | +      variable[:indexedVariables] = indexedVariables unless indexedVariables == 0  | 
 | 1050 | +      variable[:namedVariables] = namedVariables unless namedVariables == 0  | 
 | 1051 | + | 
 | 1052 | +      variable  | 
1104 | 1053 |     end  | 
1105 | 1054 |   end  | 
1106 | 1055 | end  | 
0 commit comments