Skip to content

Commit 37ebd39

Browse files
authored
Merge pull request #1229 from microsoft/main
Merge 'main' into 'release/mdd'
2 parents 20323a7 + b2b3932 commit 37ebd39

File tree

13 files changed

+315
-27
lines changed

13 files changed

+315
-27
lines changed

build/version.settings.targets

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
22
<PropertyGroup>
33
<!--SxS: These three properties should be changed at the start of a new version, VersionZeroYear should be the year
4-
before the start of the project. When updating the version, also update MIEngine\metadata.json.-->
4+
before the start of the project.-->
55
<MajorVersion>17</MajorVersion>
6-
<MinorVersion>0</MinorVersion>
6+
<MinorVersion>1</MinorVersion>
77
<VersionZeroYear>2020</VersionZeroYear>
88
<!-- Note: for compatibility, we leave the default assembly version of the repo at 14.0.
99
If we ever decide to change this, make sure that you notify partner teams such as C++ IOT -->

src/DebugEngineHost.Stub/Shared/Microsoft.VisualStudio.Debugger.Interop.DAP.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,15 @@ public interface IDebugProgramDAP
5151
/// <param name="pResult">Number of bits in a pointer.</param>
5252
/// [PreserveSig]
5353
int GetPointerSize([Out] out int pResult);
54+
55+
/// <summary>
56+
/// Tries to complete a given command string.
57+
/// </summary>
58+
/// <param name="command">Partial command to complete</param>
59+
/// <param name="stackFrame">Optional stack frame as context</param>
60+
/// <param name="result">Completion List or null</param>
61+
/// <returns></returns>
62+
int AutoComplete([In] string command, [In] IDebugStackFrame2 stackFrame, [Out] out string[] result);
5463
}
5564

5665
/// <summary>

src/MICore/CommandFactories/MICommandFactory.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -591,6 +591,15 @@ public virtual void DecodeExceptionReceivedProperties(Results miExceptionResult,
591591

592592
#endregion
593593

594+
#region Miscellaneous
595+
596+
public virtual Task<string[]> AutoComplete(string command, int threadId, uint frameLevel)
597+
{
598+
throw new NotImplementedException();
599+
}
600+
601+
#endregion
602+
594603
#region Helpers
595604

596605
public abstract string GetTargetArchitectureCommand();

src/MICore/CommandFactories/gdb.cs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -278,5 +278,22 @@ public override async Task Catch(string name, bool onlyOnce = false, ResultClass
278278
string command = onlyOnce ? "tcatch " : "catch ";
279279
await _debugger.ConsoleCmdAsync(command + name, allowWhileRunning: false);
280280
}
281+
282+
public override async Task<string[]> AutoComplete(string command, int threadId, uint frameLevel)
283+
{
284+
command = "-complete \"" + command + "\"";
285+
Results res;
286+
if (threadId == -1)
287+
res = await _debugger.CmdAsync(command, ResultClass.done);
288+
else
289+
res = await ThreadFrameCmdAsync(command, ResultClass.done, threadId, frameLevel);
290+
291+
var matchlist = res.Find<ValueListValue>("matches");
292+
293+
if (int.Parse(res.FindString("max_completions_reached"), CultureInfo.InvariantCulture) != 0)
294+
_debugger.Logger.WriteLine("We reached max-completions!");
295+
296+
return matchlist?.AsStrings;
297+
}
281298
}
282299
}

src/MIDebugEngine/AD7.Impl/AD7Engine.cs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1153,6 +1153,30 @@ int IDebugProgramDAP.GetPointerSize(out int pResult)
11531153
}
11541154
return hr;
11551155
}
1156+
1157+
int IDebugProgramDAP.AutoComplete(string command, IDebugStackFrame2 stackFrame, out string[] result)
1158+
{
1159+
var frame = stackFrame as AD7StackFrame;
1160+
int threadId = frame?.Thread.Id ?? -1;
1161+
uint frameLevel = frame?.ThreadContext.Level ?? 0;
1162+
1163+
string[] matches = null;
1164+
if (EngineUtils.IsConsoleExecCmd(command, out string prefix, out string consoleCommand))
1165+
{
1166+
_debuggedProcess.WorkerThread.RunOperation(async () =>
1167+
{
1168+
matches = await _debuggedProcess.MICommandFactory.AutoComplete(consoleCommand, threadId, frameLevel);
1169+
});
1170+
1171+
for (int i = 0; i < matches.Length; i++)
1172+
{
1173+
matches[i] = prefix + matches[i];
1174+
}
1175+
}
1176+
1177+
result = matches;
1178+
return matches != null ? Constants.S_OK : Constants.E_FAIL;
1179+
}
11561180
#endregion
11571181

11581182
#region Deprecated interface methods

src/MIDebugEngine/Engine.Impl/EngineUtils.cs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,33 @@ internal static bool ProcIdEquals(AD_PROCESS_ID pid1, AD_PROCESS_ID pid2)
109109
}
110110
}
111111

112+
113+
/// <summary>
114+
/// This allows console commands to be sent through the eval channel via a '-exec ' or '`' preface
115+
/// </summary>
116+
/// <param name="command">raw command</param>
117+
/// <param name="strippedCommand">command stripped of the preface ('-exec ' or '`')</param>
118+
/// <returns>true if it is a console command</returns>
119+
internal static bool IsConsoleExecCmd(string command, out string prefix, out string strippedCommand)
120+
{
121+
prefix = string.Empty;
122+
strippedCommand = string.Empty;
123+
string execCommandString = "-exec ";
124+
if (command.StartsWith(execCommandString, StringComparison.Ordinal))
125+
{
126+
prefix = execCommandString;
127+
strippedCommand = command.Substring(execCommandString.Length);
128+
return true;
129+
}
130+
else if (command.Length > 0 && command[0] == '`')
131+
{
132+
prefix = "`";
133+
strippedCommand = command.Substring(1);
134+
return true;
135+
}
136+
return false;
137+
}
138+
112139
//
113140
// The RegisterNameMap maps register names to logical group names. The architecture of
114141
// the platform is described with all its varients. Any particular target may only contains a subset

src/MIDebugEngine/Engine.Impl/Variables.cs

Lines changed: 1 addition & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -493,29 +493,6 @@ public string EvalDependentExpression(string expr)
493493
return val;
494494
}
495495

496-
/// <summary>
497-
/// This allows console commands to be sent through the eval channel via a '-exec ' or '`' preface
498-
/// </summary>
499-
/// <param name="command">raw command</param>
500-
/// <param name="strippedCommand">command stripped of the preface ('-exec ' or '`')</param>
501-
/// <returns>true if it is a console command</returns>
502-
private bool IsConsoleExecCmd(string command, out string strippedCommand)
503-
{
504-
strippedCommand = string.Empty;
505-
string execCommandString = "-exec ";
506-
if (command.StartsWith(execCommandString, StringComparison.Ordinal))
507-
{
508-
strippedCommand = command.Substring(execCommandString.Length);
509-
return true;
510-
}
511-
else if (command[0] == '`')
512-
{
513-
strippedCommand = command.Substring(1).TrimStart(); // remove spaces if any
514-
return true;
515-
}
516-
return false;
517-
}
518-
519496
internal async Task Eval(enum_EVALFLAGS dwFlags = 0, DAPEvalFlags dwDAPFlags = 0)
520497
{
521498
this.VerifyNotDisposed();
@@ -524,8 +501,7 @@ internal async Task Eval(enum_EVALFLAGS dwFlags = 0, DAPEvalFlags dwDAPFlags = 0
524501

525502
try
526503
{
527-
string consoleCommand;
528-
if (IsConsoleExecCmd(_strippedName, out consoleCommand))
504+
if (EngineUtils.IsConsoleExecCmd(_strippedName, out string _, out string consoleCommand))
529505
{
530506
// special case for executing raw mi commands.
531507
string consoleResults = null;

src/MIDebugEngine/Microsoft.MIDebugEngine.pkgdef

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,8 @@
8181
[$RootKey$\AD7Metrics\Engine\{5D630903-189D-4837-9785-699B05BEC2A9}\PortSupplier]
8282
; SSH Port Supplier
8383
"0"="{3FDDF14E-E758-4695-BE0C-7509920432C9}"
84+
; WSL Port supplier
85+
"1"="{267B1341-AC92-44DC-94DF-2EE4205DD17E}"
8486

8587
[$RootKey$\AD7Metrics\Engine\{5D630903-189D-4837-9785-699B05BEC2A9}\IncompatibleList]
8688
"MI Debug Engine - gdb"="{91744D97-430F-42C1-9779-A5813EBD6AB2}"

src/OpenDebugAD7/AD7DebugSession.cs

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -874,6 +874,7 @@ protected override void HandleInitializeRequestAsync(IRequestResponder<Initializ
874874
InitializeResponse initializeResponse = new InitializeResponse()
875875
{
876876
SupportsConfigurationDoneRequest = true,
877+
SupportsCompletionsRequest = m_engine is IDebugProgramDAP,
877878
SupportsEvaluateForHovers = true,
878879
SupportsSetVariable = true,
879880
SupportsFunctionBreakpoints = m_engineConfiguration.FunctionBP,
@@ -2438,6 +2439,53 @@ protected override void HandleSetFunctionBreakpointsRequestAsync(IRequestRespond
24382439
responder.SetResponse(response);
24392440
}
24402441

2442+
protected override void HandleCompletionsRequestAsync(IRequestResponder<CompletionsArguments, CompletionsResponse> responder)
2443+
{
2444+
if (!m_isStopped)
2445+
{
2446+
responder.SetError(new ProtocolException("Failed to handle CompletionsRequest", new Message(1105, AD7Resources.Error_TargetNotStopped)));
2447+
return;
2448+
}
2449+
2450+
IDebugStackFrame2 frame = null;
2451+
int? frameId = responder.Arguments.FrameId;
2452+
if (frameId != null)
2453+
_ = m_frameHandles.TryGet(frameId.Value, out frame);
2454+
2455+
try
2456+
{
2457+
string command = responder.Arguments.Text;
2458+
var matchlist = new List<CompletionItem>();
2459+
2460+
var debugProgram = m_engine as IDebugProgramDAP;
2461+
2462+
if (debugProgram.AutoComplete(command, frame, out string[] results) == HRConstants.S_OK)
2463+
{
2464+
foreach (string result in results)
2465+
{
2466+
matchlist.Add(new CompletionItem()
2467+
{
2468+
Label = result,
2469+
Start = 0,
2470+
Type = CompletionItemType.Text,
2471+
Length = result.Length
2472+
});
2473+
}
2474+
}
2475+
2476+
responder.SetResponse(new CompletionsResponse(matchlist));
2477+
}
2478+
catch (NotImplementedException)
2479+
{
2480+
// If MIDebugEngine does not implemented AutoCompleted, just return an empty response.
2481+
responder.SetResponse(new CompletionsResponse());
2482+
}
2483+
catch (Exception e)
2484+
{
2485+
responder.SetError(new ProtocolException("Auto-completion failed!", e));
2486+
}
2487+
}
2488+
24412489
protected override void HandleEvaluateRequestAsync(IRequestResponder<EvaluateArguments, EvaluateResponse> responder)
24422490
{
24432491
EvaluateArguments.ContextValue context = responder.Arguments.Context.GetValueOrDefault(EvaluateArguments.ContextValue.Unknown);
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
// // Copyright (c) Microsoft. All rights reserved.
2+
// // Licensed under the MIT license. See LICENSE file in the project root for full license information.
3+
4+
using System;
5+
using System.Collections.Generic;
6+
using System.IO;
7+
using System.Linq;
8+
using DebuggerTesting;
9+
using DebuggerTesting.Compilation;
10+
using DebuggerTesting.OpenDebug;
11+
using DebuggerTesting.OpenDebug.Commands;
12+
using DebuggerTesting.OpenDebug.CrossPlatCpp;
13+
using DebuggerTesting.OpenDebug.Events;
14+
using DebuggerTesting.OpenDebug.Extensions;
15+
using DebuggerTesting.Ordering;
16+
using DebuggerTesting.Settings;
17+
using DebuggerTesting.Utilities;
18+
using Xunit;
19+
using Xunit.Abstractions;
20+
21+
namespace CppTests.Tests
22+
{
23+
[TestCaseOrderer(DependencyTestOrderer.TypeName, DependencyTestOrderer.AssemblyName)]
24+
public class AutoCompleteTests : TestBase
25+
{
26+
#region Constructor
27+
28+
public AutoCompleteTests(ITestOutputHelper outputHelper) : base(outputHelper)
29+
{
30+
}
31+
32+
#endregion
33+
34+
#region Methods
35+
36+
private const string HelloName = "hello";
37+
private const string HelloSourceName = "hello.cpp";
38+
39+
[Theory]
40+
[RequiresTestSettings]
41+
public void CompileHelloDebuggee(ITestSettings settings)
42+
{
43+
this.TestPurpose("Create and compile the 'hello' debuggee");
44+
this.WriteSettings(settings);
45+
46+
IDebuggee debuggee = Debuggee.Create(this, settings.CompilerSettings, HelloName, DebuggeeMonikers.HelloWorld.Sample);
47+
debuggee.AddSourceFiles(HelloSourceName);
48+
debuggee.Compile();
49+
}
50+
51+
[Theory]
52+
[DependsOnTest(nameof(CompileHelloDebuggee))]
53+
[RequiresTestSettings]
54+
[UnsupportedDebugger(SupportedDebugger.Lldb | SupportedDebugger.VsDbg, SupportedArchitecture.x64 | SupportedArchitecture.x86)]
55+
public void TestAutoComplete(ITestSettings settings)
56+
{
57+
this.TestPurpose("This test checks a bunch of commands and events.");
58+
this.WriteSettings(settings);
59+
60+
IDebuggee debuggee = Debuggee.Open(this, settings.CompilerSettings, HelloName, DebuggeeMonikers.HelloWorld.Sample);
61+
62+
using (IDebuggerRunner runner = CreateDebugAdapterRunner(settings))
63+
{
64+
this.Comment("Launch the debuggee");
65+
runner.Launch(settings.DebuggerSettings, debuggee);
66+
67+
SourceBreakpoints callingBreakpoints = new SourceBreakpoints(debuggee, HelloSourceName);
68+
callingBreakpoints.Add(9);
69+
runner.SetBreakpoints(callingBreakpoints);
70+
71+
runner.Expects.HitBreakpointEvent(HelloSourceName, 9)
72+
.AfterConfigurationDone();
73+
74+
// Test completion with -exec
75+
string[] completions = runner.CompletionsRequest("-exec break");
76+
Assert.Collection(completions,
77+
elem1 => Assert.Equal("-exec break", elem1),
78+
elem2 => Assert.Equal("-exec break-range", elem2)
79+
);
80+
81+
// Test completion with `
82+
completions = runner.CompletionsRequest("`pw");
83+
Assert.Collection(completions,
84+
elem1 => Assert.Equal("`pwd", elem1)
85+
);
86+
87+
// Test completions without -exec or `
88+
completions = runner.CompletionsRequest("pw");
89+
Assert.Empty(completions);
90+
91+
runner.Expects.ExitedEvent(0).TerminatedEvent().AfterContinue();
92+
runner.DisconnectAndVerify();
93+
}
94+
}
95+
96+
#endregion
97+
}
98+
}

0 commit comments

Comments
 (0)