|
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 |
@@ -869,57 +864,26 @@ def process_dap args |
869 | 864 | fid = args.shift |
870 | 865 | frame = get_frame(fid) |
871 | 866 | vars = collect_locals(frame).map do |var, val| |
872 | | - variable(var, val) |
| 867 | + render_variable Variable.new(name: var, value: val) |
873 | 868 | end |
874 | 869 |
|
875 | 870 | event! :protocol_result, :scope, req, variables: vars, tid: self.id |
876 | 871 | when :variable |
877 | 872 | vid = args.shift |
878 | 873 | obj = @var_map[vid] |
879 | 874 | if obj |
880 | | - case req.dig('arguments', 'filter') |
| 875 | + members = case req.dig('arguments', 'filter') |
881 | 876 | when 'indexed' |
882 | | - start = req.dig('arguments', 'start') || 0 |
883 | | - # FIXME: `req.dig('arguments', 'count')` needs to be capped to be `<= obj.size`. |
884 | | - count = req.dig('arguments', 'count') || obj.size |
885 | | - vars = (start ... (start + count)).map{|i| |
886 | | - variable(i.to_s, obj[i]) |
887 | | - } |
| 877 | + VariableInspector.new.indexed_members_of( |
| 878 | + obj, |
| 879 | + start: req.dig('arguments', 'start') || 0, |
| 880 | + count: req.dig('arguments', 'count') || obj.size, |
| 881 | + ) |
888 | 882 | else |
889 | | - vars = [] |
890 | | - |
891 | | - case obj |
892 | | - when Hash |
893 | | - vars = obj.map{|k, v| |
894 | | - variable(value_inspect(k), v,) |
895 | | - } |
896 | | - when Struct |
897 | | - vars = obj.members.map{|m| |
898 | | - variable(m, obj[m]) |
899 | | - } |
900 | | - when String |
901 | | - vars = [ |
902 | | - variable('#length', obj.length), |
903 | | - variable('#encoding', obj.encoding), |
904 | | - ] |
905 | | - printed_str = value_inspect(obj) |
906 | | - vars << variable('#dump', NaiveString.new(obj)) if printed_str.end_with?('...') |
907 | | - when Class, Module |
908 | | - vars << variable('%ancestors', obj.ancestors[1..]) |
909 | | - when Range |
910 | | - vars = [ |
911 | | - variable('#begin', obj.begin), |
912 | | - variable('#end', obj.end), |
913 | | - ] |
914 | | - end |
915 | | - |
916 | | - unless NaiveString === obj |
917 | | - vars += M_INSTANCE_VARIABLES.bind_call(obj).sort.map{|iv| |
918 | | - variable(iv, M_INSTANCE_VARIABLE_GET.bind_call(obj, iv)) |
919 | | - } |
920 | | - vars.unshift variable('#class', M_CLASS.bind_call(obj)) |
921 | | - end |
| 883 | + VariableInspector.new.named_members_of(obj) |
922 | 884 | end |
| 885 | + |
| 886 | + vars = members.map { |member| render_variable member } |
923 | 887 | end |
924 | 888 | event! :protocol_result, :variable, req, variables: (vars || []), tid: self.id |
925 | 889 |
|
@@ -976,7 +940,13 @@ def process_dap args |
976 | 940 | result = 'Error: Can not evaluate on this frame' |
977 | 941 | end |
978 | 942 |
|
979 | | - 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) |
980 | 950 |
|
981 | 951 | when :completions |
982 | 952 | fid, text = args |
@@ -1052,59 +1022,48 @@ def type_name obj |
1052 | 1022 | end |
1053 | 1023 | end |
1054 | 1024 |
|
1055 | | - def variable_ name, obj, indexedVariables: 0, namedVariables: 0 |
1056 | | - if indexedVariables > 0 || namedVariables > 0 |
1057 | | - vid = @var_map.size + 1 |
1058 | | - @var_map[vid] = obj |
1059 | | - File.write("/tmp/server_dap_debug_log1.txt", "Assigned id #{vid} to name=#{name.inspect} value=#{obj.inspect}\n", mode: 'a+') |
| 1025 | + # Renders the given Member into a DAP Variable |
| 1026 | + # https://microsoft.github.io/debug-adapter-protocol/specification#variable |
| 1027 | + def render_variable member |
| 1028 | + indexedVariables, namedVariables = if Array === member.value |
| 1029 | + [member.value.size, 0] |
1060 | 1030 | else |
1061 | | - vid = 0 |
| 1031 | + [0, VariableInspector.new.named_members_of(member.value).count] |
1062 | 1032 | end |
1063 | 1033 |
|
1064 | | - namedVariables += M_INSTANCE_VARIABLES.bind_call(obj).size |
1065 | | - |
1066 | | - if NaiveString === obj |
1067 | | - str = obj.str.dump |
1068 | | - vid = indexedVariables = namedVariables = 0 |
| 1034 | + # > If `variablesReference` is > 0, the variable is structured and its children |
| 1035 | + # > can be retrieved by passing `variablesReference` to the `variables` request |
| 1036 | + # > as long as execution remains suspended. |
| 1037 | + if indexedVariables > 0 || namedVariables > 0 |
| 1038 | + # This object has children that we might need to query, so we need to remember it by its vid |
| 1039 | + vid = @var_map.size + 1 |
| 1040 | + @var_map[vid] = member.value |
1069 | 1041 | else |
1070 | | - str = value_inspect(obj) |
| 1042 | + # This object has no children, so we don't need to remember it in the `@var_map` |
| 1043 | + vid = 0 |
1071 | 1044 | end |
1072 | 1045 |
|
1073 | | - if name |
1074 | | - { name: name, |
1075 | | - value: str, |
1076 | | - type: type_name(obj), |
| 1046 | + variable = if member.name |
| 1047 | + # These two hashes are repeated so the "name" can come always come first, when available, |
| 1048 | + # which improves the readability of protocol responses. |
| 1049 | + { |
| 1050 | + name: member.name, |
| 1051 | + value: member.inspect_value, |
| 1052 | + type: member.value_type_name, |
1077 | 1053 | variablesReference: vid, |
1078 | | - indexedVariables: indexedVariables, |
1079 | | - namedVariables: namedVariables, |
1080 | 1054 | } |
1081 | 1055 | else |
1082 | | - { result: str, |
1083 | | - type: type_name(obj), |
| 1056 | + { |
| 1057 | + value: member.inspect_value, |
| 1058 | + type: member.value_type_name, |
1084 | 1059 | variablesReference: vid, |
1085 | | - indexedVariables: indexedVariables, |
1086 | | - namedVariables: namedVariables, |
1087 | 1060 | } |
1088 | 1061 | end |
1089 | | - end |
1090 | 1062 |
|
1091 | | - def variable name, obj |
1092 | | - case obj |
1093 | | - when Array |
1094 | | - variable_ name, obj, indexedVariables: obj.size |
1095 | | - when Hash |
1096 | | - variable_ name, obj, namedVariables: obj.size |
1097 | | - when String |
1098 | | - variable_ name, obj, namedVariables: 3 # #length, #encoding, #to_str |
1099 | | - when Struct |
1100 | | - variable_ name, obj, namedVariables: obj.size |
1101 | | - when Class, Module |
1102 | | - variable_ name, obj, namedVariables: 1 # %ancestors (#ancestors without self) |
1103 | | - when Range |
1104 | | - variable_ name, obj, namedVariables: 2 # #begin, #end |
1105 | | - else |
1106 | | - variable_ name, obj, namedVariables: 1 # #class |
1107 | | - end |
| 1063 | + variable[:indexedVariables] = indexedVariables unless indexedVariables == 0 |
| 1064 | + variable[:namedVariables] = namedVariables unless namedVariables == 0 |
| 1065 | + |
| 1066 | + variable |
1108 | 1067 | end |
1109 | 1068 | end |
1110 | 1069 | end |
0 commit comments