implement function breakpoints
This commit is contained in:
Родитель
d6e8b1c2fe
Коммит
6f5bd46f8a
|
@ -308,6 +308,7 @@ namespace Microsoft.Bot.Builder.Dialogs.Debugging
|
|||
supportsConfigurationDoneRequest = true,
|
||||
supportsSetVariable = true,
|
||||
supportsEvaluateForHovers = true,
|
||||
supportsFunctionBreakpoints = true,
|
||||
supportTerminateDebuggee = this.terminate != null,
|
||||
supportsTerminateRequest = this.terminate != null,
|
||||
};
|
||||
|
@ -341,6 +342,22 @@ namespace Microsoft.Bot.Builder.Dialogs.Debugging
|
|||
|
||||
return Protocol.Response.From(NextSeq, setBreakpoints, new { breakpoints });
|
||||
}
|
||||
else if (message is Protocol.Request<Protocol.SetFunctionBreakpoints> setFunctionBreakpoints)
|
||||
{
|
||||
var arguments = setFunctionBreakpoints.arguments;
|
||||
await OutputAsync($"Set function breakpoints.", null, cancellationToken).ConfigureAwait(false);
|
||||
var breakpoints = this.breakpoints.SetBreakpoints(arguments.breakpoints);
|
||||
foreach (var breakpoint in breakpoints)
|
||||
{
|
||||
if (breakpoint.verified)
|
||||
{
|
||||
var item = this.breakpoints.ItemFor(breakpoint);
|
||||
await OutputAsync($"Set breakpoint at {codeModel.NameFor(item)}", item, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
return Protocol.Response.From(NextSeq, setFunctionBreakpoints, new { breakpoints });
|
||||
}
|
||||
else if (message is Protocol.Request<Protocol.Threads> threads)
|
||||
{
|
||||
var body = new
|
||||
|
|
|
@ -22,7 +22,8 @@ namespace Microsoft.Bot.Builder.Dialogs.Debugging
|
|||
/// <returns></returns>
|
||||
public static BotAdapter UseDebugger(this BotAdapter botAdapter, int port, Source.IRegistry registry = null, IBreakpoints breakpoints = null, Action terminate = null, ICodeModel codeModel = null, IDataModel dataModel = null, ILogger logger = null, ICoercion coercion = null)
|
||||
{
|
||||
var sourceMap = new SourceMap();
|
||||
codeModel = codeModel ?? new CodeModel();
|
||||
var sourceMap = new SourceMap(codeModel);
|
||||
DebugSupport.SourceRegistry = registry ?? sourceMap;
|
||||
return botAdapter.Use(new DebugAdapter(port: port,
|
||||
registry: registry ?? sourceMap,
|
||||
|
|
|
@ -122,6 +122,17 @@ namespace Microsoft.Bot.Builder.Dialogs.Debugging
|
|||
|
||||
IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable<KeyValuePair<ulong, T>>)this).GetEnumerator();
|
||||
|
||||
public IEnumerable<T> Items
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (gate)
|
||||
{
|
||||
return this.itemByCode.Values.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public T this[ulong code]
|
||||
{
|
||||
get
|
||||
|
|
|
@ -241,6 +241,8 @@ namespace Microsoft.Bot.Builder.Dialogs.Debugging
|
|||
case "attach": return token.ToObject<Request<Attach>>();
|
||||
case "initialize": return token.ToObject<Request<Initialize>>();
|
||||
case "setBreakpoints": return token.ToObject<Request<SetBreakpoints>>();
|
||||
case "setFunctionBreakpoints": return token.ToObject<Request<SetFunctionBreakpoints>>();
|
||||
case "configurationDone": return token.ToObject<Request<ConfigurationDone>>();
|
||||
case "threads": return token.ToObject<Request<Threads>>();
|
||||
case "stackTrace": return token.ToObject<Request<StackTrace>>();
|
||||
case "scopes": return token.ToObject<Request<Scopes>>();
|
||||
|
@ -252,10 +254,8 @@ namespace Microsoft.Bot.Builder.Dialogs.Debugging
|
|||
case "next": return token.ToObject<Request<Next>>();
|
||||
case "stepIn": return token.ToObject<Request<Next>>();
|
||||
case "stepOut": return token.ToObject<Request<Next>>();
|
||||
case "configurationDone": return token.ToObject<Request<ConfigurationDone>>();
|
||||
case "terminate": return token.ToObject<Request<Terminate>>();
|
||||
case "disconnect": return token.ToObject<Request<Disconnect>>();
|
||||
case "setFunctionBreakpoints":
|
||||
case "setExceptionBreakpoints":
|
||||
default: return token.ToObject<Request>();
|
||||
}
|
||||
|
|
|
@ -11,15 +11,22 @@ namespace Microsoft.Bot.Builder.Dialogs.Debugging
|
|||
bool IsBreakPoint(object item);
|
||||
object ItemFor(Protocol.Breakpoint breakpoint);
|
||||
IReadOnlyList<Protocol.Breakpoint> SetBreakpoints(Protocol.Source source, IReadOnlyList<Protocol.SourceBreakpoint> sourceBreakpoints);
|
||||
IReadOnlyList<Protocol.Breakpoint> SetBreakpoints(IReadOnlyList<Protocol.FunctionBreakpoint> functionBreakpoints);
|
||||
IReadOnlyList<Protocol.Breakpoint> ApplyUpdates();
|
||||
}
|
||||
|
||||
public sealed class SourceMap : Source.IRegistry, IBreakpoints
|
||||
{
|
||||
private readonly ICodeModel codeModel;
|
||||
private readonly object gate = new object();
|
||||
private readonly Dictionary<object, Source.Range> sourceByItem = new Dictionary<object, Source.Range>(ReferenceEquality<object>.Instance);
|
||||
private bool dirty = true;
|
||||
|
||||
public SourceMap(ICodeModel codeModel)
|
||||
{
|
||||
this.codeModel = codeModel ?? throw new ArgumentNullException(nameof(codeModel));
|
||||
}
|
||||
|
||||
void Source.IRegistry.Add(object item, Source.Range range)
|
||||
{
|
||||
if (!Path.IsPathRooted(range.Path))
|
||||
|
@ -52,15 +59,19 @@ namespace Microsoft.Bot.Builder.Dialogs.Debugging
|
|||
|
||||
public sealed class Row
|
||||
{
|
||||
public Row(Protocol.Source source, Protocol.SourceBreakpoint sourceBreakpoint, Protocol.Breakpoint breakpoint)
|
||||
public Row(Protocol.Source source, Protocol.SourceBreakpoint sourceBreakpoint)
|
||||
{
|
||||
Source = source;
|
||||
SourceBreakpoint = sourceBreakpoint;
|
||||
Breakpoint = breakpoint;
|
||||
}
|
||||
public Row(Protocol.FunctionBreakpoint functionBreakpoint)
|
||||
{
|
||||
FunctionBreakpoint = functionBreakpoint;
|
||||
}
|
||||
public Protocol.Source Source { get; }
|
||||
public Protocol.SourceBreakpoint SourceBreakpoint { get; }
|
||||
public Protocol.Breakpoint Breakpoint { get; }
|
||||
public Protocol.FunctionBreakpoint FunctionBreakpoint { get; }
|
||||
public Protocol.Breakpoint Breakpoint { get; } = new Protocol.Breakpoint();
|
||||
public object item { get; set; }
|
||||
}
|
||||
|
||||
|
@ -72,54 +83,120 @@ namespace Microsoft.Bot.Builder.Dialogs.Debugging
|
|||
private static bool PathEquals(string one, string two) =>
|
||||
string.Equals(one, two, StringComparison.CurrentCultureIgnoreCase);
|
||||
|
||||
private IReadOnlyList<Protocol.Breakpoint> Update()
|
||||
public static bool Equals(Protocol.Breakpoint breakpoint, Source.Range range) =>
|
||||
(breakpoint.source == null && range == null)
|
||||
|| (breakpoint.source.path == range.Path
|
||||
&& breakpoint.line == range.Start.LineIndex
|
||||
&& breakpoint.endLine == range.After.LineIndex
|
||||
&& breakpoint.column == range.Start.CharIndex
|
||||
&& breakpoint.endColumn == range.After.CharIndex);
|
||||
|
||||
public static void Assign(Protocol.Breakpoint breakpoint, Source.Range range)
|
||||
{
|
||||
if (range != null)
|
||||
{
|
||||
breakpoint.verified = true;
|
||||
breakpoint.source = new Protocol.Source(range.Path);
|
||||
breakpoint.line = range.Start.LineIndex;
|
||||
breakpoint.endLine = range.After.LineIndex;
|
||||
breakpoint.column = range.Start.CharIndex;
|
||||
breakpoint.endColumn = range.After.CharIndex;
|
||||
}
|
||||
else
|
||||
{
|
||||
breakpoint.verified = false;
|
||||
breakpoint.source = null;
|
||||
breakpoint.line = null;
|
||||
breakpoint.endLine = null;
|
||||
breakpoint.column = null;
|
||||
breakpoint.endColumn = null;
|
||||
}
|
||||
}
|
||||
|
||||
private bool TryUpdate(Row row, KeyValuePair<object, Source.Range> sourceItem)
|
||||
{
|
||||
var item = sourceItem.Key;
|
||||
var source = sourceItem.Value;
|
||||
if (object.Equals(row.item, item) && Equals(row.Breakpoint, source))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
row.item = item;
|
||||
Assign(row.Breakpoint, source);
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool TryUpdate(Row row)
|
||||
{
|
||||
var breakpoint = row.Breakpoint;
|
||||
if (breakpoint.id == 0)
|
||||
{
|
||||
breakpoint.id = this.rows.Add(row);
|
||||
}
|
||||
|
||||
IEnumerable<KeyValuePair<object, Source.Range>> options;
|
||||
|
||||
var functionBreakpoint = row.FunctionBreakpoint;
|
||||
if (functionBreakpoint != null)
|
||||
{
|
||||
options = from sourceItem in sourceByItem
|
||||
let item = sourceItem.Key
|
||||
let name = codeModel.NameFor(item)
|
||||
where name.IndexOf(functionBreakpoint.name, StringComparison.CurrentCultureIgnoreCase) >= 0
|
||||
orderby name.Length
|
||||
select sourceItem;
|
||||
}
|
||||
else
|
||||
{
|
||||
options = from sourceItem in sourceByItem
|
||||
let source = sourceItem.Value
|
||||
where PathEquals(source.Path, row.Source.path)
|
||||
where source.Start.LineIndex >= row.SourceBreakpoint.line
|
||||
let distance = Math.Abs(source.Start.LineIndex - row.SourceBreakpoint.line)
|
||||
orderby distance
|
||||
select sourceItem;
|
||||
}
|
||||
|
||||
options = options.ToArray();
|
||||
var best = options.FirstOrDefault();
|
||||
|
||||
return TryUpdate(row, best);
|
||||
}
|
||||
|
||||
private void RebuildItems()
|
||||
{
|
||||
lock (gate)
|
||||
{
|
||||
items.Clear();
|
||||
|
||||
var changes = new List<Protocol.Breakpoint>();
|
||||
foreach (var kv in rows)
|
||||
foreach (var row in rows.Items)
|
||||
{
|
||||
var row = kv.Value;
|
||||
var item = row.item;
|
||||
if (item != null)
|
||||
{
|
||||
items.Add(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var options = from sourceItem in sourceByItem
|
||||
let source = sourceItem.Value
|
||||
where PathEquals(source.Path, row.Source.path)
|
||||
where source.Start.LineIndex >= row.SourceBreakpoint.line
|
||||
let distance = Math.Abs(source.Start.LineIndex - row.SourceBreakpoint.line)
|
||||
orderby distance
|
||||
select sourceItem;
|
||||
private IReadOnlyList<Protocol.Breakpoint> Update()
|
||||
{
|
||||
lock (gate)
|
||||
{
|
||||
var changes = new List<Protocol.Breakpoint>();
|
||||
|
||||
options = options.ToArray();
|
||||
|
||||
var best = options.FirstOrDefault();
|
||||
var itemNew = best.Key;
|
||||
var verifiedNew = itemNew != null;
|
||||
var lineNew = verifiedNew
|
||||
? best.Value.Start.LineIndex
|
||||
: row.SourceBreakpoint.line;
|
||||
|
||||
var itemOld = row.item;
|
||||
var verifiedOld = row.Breakpoint.verified;
|
||||
var lineOld = row.Breakpoint.line;
|
||||
|
||||
var changed = itemNew != itemOld
|
||||
|| verifiedNew != verifiedOld
|
||||
|| lineNew != lineOld;
|
||||
|
||||
if (changed)
|
||||
foreach (var row in rows.Items)
|
||||
{
|
||||
if (TryUpdate(row))
|
||||
{
|
||||
changes.Add(row.Breakpoint);
|
||||
row.item = itemNew;
|
||||
row.Breakpoint.verified = verifiedNew;
|
||||
row.Breakpoint.line = lineNew;
|
||||
}
|
||||
}
|
||||
|
||||
if (itemNew != null)
|
||||
{
|
||||
items.Add(itemNew);
|
||||
}
|
||||
if (changes.Count > 0)
|
||||
{
|
||||
RebuildItems();
|
||||
}
|
||||
|
||||
return changes;
|
||||
|
@ -131,23 +208,53 @@ namespace Microsoft.Bot.Builder.Dialogs.Debugging
|
|||
lock (gate)
|
||||
{
|
||||
var path = source.path;
|
||||
foreach (var kv in rows)
|
||||
foreach (var row in rows.Items)
|
||||
{
|
||||
var row = kv.Value;
|
||||
if (PathEquals(row.Source.path, path))
|
||||
if (row.FunctionBreakpoint == null && PathEquals(row.Source.path, path))
|
||||
{
|
||||
rows.Remove(row);
|
||||
}
|
||||
}
|
||||
|
||||
var breakpoints = new List<Protocol.Breakpoint>(sourceBreakpoints.Count);
|
||||
|
||||
foreach (var sourceBreakpoint in sourceBreakpoints)
|
||||
{
|
||||
var breakpoint = new Protocol.Breakpoint() { source = source };
|
||||
var row = new Row(source, sourceBreakpoint, breakpoint);
|
||||
breakpoint.id = this.rows.Add(row);
|
||||
var row = new Row(source, sourceBreakpoint);
|
||||
TryUpdate(row);
|
||||
breakpoints.Add(row.Breakpoint);
|
||||
}
|
||||
|
||||
return Update();
|
||||
RebuildItems();
|
||||
|
||||
return breakpoints;
|
||||
}
|
||||
}
|
||||
|
||||
IReadOnlyList<Protocol.Breakpoint> IBreakpoints.SetBreakpoints(IReadOnlyList<Protocol.FunctionBreakpoint> functionBreakpoints)
|
||||
{
|
||||
lock (gate)
|
||||
{
|
||||
foreach (var row in rows.Items)
|
||||
{
|
||||
if (row.FunctionBreakpoint != null)
|
||||
{
|
||||
rows.Remove(row);
|
||||
}
|
||||
}
|
||||
|
||||
var breakpoints = new List<Protocol.Breakpoint>(functionBreakpoints.Count);
|
||||
|
||||
foreach (var functionBreakpoint in functionBreakpoints)
|
||||
{
|
||||
var row = new Row(functionBreakpoint);
|
||||
TryUpdate(row);
|
||||
breakpoints.Add(row.Breakpoint);
|
||||
}
|
||||
|
||||
RebuildItems();
|
||||
|
||||
return breakpoints;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче