Skip to content

Commit

Permalink
Added navigation bars
Browse files Browse the repository at this point in the history
[release]
  • Loading branch information
madskristensen committed Jan 10, 2022
1 parent 98e360d commit a4bacef
Show file tree
Hide file tree
Showing 6 changed files with 216 additions and 6 deletions.
61 changes: 61 additions & 0 deletions src/RestClientVS/Commands/Commenting.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
using System.Collections.ObjectModel;
using System.Linq;
using Microsoft.VisualStudio;
using Microsoft.VisualStudio.Text;
using Microsoft.VisualStudio.Text.Formatting;
using RestClient;

namespace RestClientVS
{
public class Commenting
{
public static async Task InitializeAsync()
{
// We need to manually intercept the commenting command, because language services swallow these commands.
await VS.Commands.InterceptAsync(VSConstants.VSStd2KCmdID.COMMENT_BLOCK, () => Execute(Comment));
await VS.Commands.InterceptAsync(VSConstants.VSStd2KCmdID.UNCOMMENT_BLOCK, () => Execute(Uncomment));
}

private static CommandProgression Execute(Action<DocumentView> action)
{
return ThreadHelper.JoinableTaskFactory.Run(async () =>
{
DocumentView doc = await VS.Documents.GetActiveDocumentViewAsync();

if (doc?.TextBuffer != null && doc.TextBuffer.ContentType.IsOfType(LanguageFactory.LanguageName))
{
action(doc);
return CommandProgression.Stop;
}

return CommandProgression.Continue;
});
}

private static void Comment(DocumentView doc)
{
SnapshotSpan spans = doc.TextView.Selection.SelectedSpans.First();
Collection<ITextViewLine> lines = doc.TextView.TextViewLines.GetTextViewLinesIntersectingSpan(spans);

foreach (ITextViewLine line in lines.Reverse())
{
doc.TextBuffer.Insert(line.Start.Position, Constants.CommentChar.ToString());
}
}

private static void Uncomment(DocumentView doc)
{
SnapshotSpan spans = doc.TextView.Selection.SelectedSpans.First();
Collection<ITextViewLine> lines = doc.TextView.TextViewLines.GetTextViewLinesIntersectingSpan(spans);

foreach (ITextViewLine line in lines.Reverse())
{
var span = Span.FromBounds(line.Start, line.End);
var originalText = doc.TextBuffer.CurrentSnapshot.GetText(span).TrimStart(Constants.CommentChar);
Span commentCharSpan = new(span.Start, span.Length - originalText.Length);

doc.TextBuffer.Delete(commentCharSpan);
}
}
}
}
5 changes: 3 additions & 2 deletions src/RestClientVS/Commands/GoToDefinitionCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,13 @@ public bool ExecuteCommand(GoToDefinitionCommandArgs args, CommandExecutionConte
{
var position = args.TextView.Caret.Position.BufferPosition.Position;

RestClient.Document document = args.TextView.TextBuffer.GetRestDocument();
Document document = args.TextView.TextBuffer.GetRestDocument();
ParseItem token = document.FindItemFromPosition(position);

if (token?.Type == ItemType.Reference)
{
Variable definition = document.Variables.FirstOrDefault(v => v.Name.Text.Substring(1).Equals(token.Text, StringComparison.OrdinalIgnoreCase));
var varName = token.Text.Trim('{', '}');
Variable definition = document.Variables.FirstOrDefault(v => v.Name.Text.Substring(1).Equals(varName, StringComparison.OrdinalIgnoreCase));

if (definition != null)
{
Expand Down
118 changes: 118 additions & 0 deletions src/RestClientVS/Editor/DropdownBars.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
using System.Collections;
using System.Linq;
using Microsoft.VisualStudio.Editor;
using Microsoft.VisualStudio.Package;
using Microsoft.VisualStudio.Text.Editor;
using Microsoft.VisualStudio.TextManager.Interop;
using RestClient;

namespace RestClientVS
{
internal class DropdownBars : TypeAndMemberDropdownBars, IDisposable
{
private readonly LanguageService _languageService;
private readonly IWpfTextView _textView;
private readonly Document _document;
private bool _disposed;
private bool _bufferHasChanged;

public DropdownBars(IVsTextView textView, LanguageService languageService)
: base(languageService)
{
_languageService = languageService;

IVsEditorAdaptersFactoryService adapter = VS.GetMefService<IVsEditorAdaptersFactoryService>();

_textView = adapter.GetWpfTextView(textView);
_textView.Caret.PositionChanged += CaretPositionChanged;

_document = _textView.TextBuffer.GetRestDocument();
_document.Parsed += OnDocumentParsed;

SynchronizeDropdowns();
}

private void OnDocumentParsed(object sender, EventArgs e)
{
_bufferHasChanged = true;
SynchronizeDropdowns();
}

private void CaretPositionChanged(object sender, CaretPositionChangedEventArgs e) => SynchronizeDropdowns();

private void SynchronizeDropdowns()
{
if (_document.IsParsing)
{
return;
}

_ = ThreadHelper.JoinableTaskFactory.StartOnIdle(() =>
{
if (!_document.IsParsing)
{
_languageService.SynchronizeDropdowns();
}
}, VsTaskRunContext.UIThreadIdlePriority);
}

public override bool OnSynchronizeDropdowns(LanguageService languageService, IVsTextView textView, int line, int col, ArrayList dropDownTypes, ArrayList dropDownMembers, ref int selectedType, ref int selectedMember)
{
if (_bufferHasChanged || dropDownMembers.Count == 0)
{
dropDownMembers.Clear();

_document.Items.OfType<Request>()
.Select(entry => CreateDropDownMember(entry, textView))
.ToList()
.ForEach(ddm => dropDownMembers.Add(ddm));
}

if (dropDownTypes.Count == 0)
{
var thisExt = $"{Vsix.Name} ({Vsix.Version})";
dropDownTypes.Add(new DropDownMember(thisExt, new TextSpan(), 126, DROPDOWNFONTATTR.FONTATTR_GRAY));
}

DropDownMember currentDropDown = dropDownMembers
.OfType<DropDownMember>()
.Where(d => d.Span.iStartLine <= line)
.LastOrDefault();

selectedMember = dropDownMembers.IndexOf(currentDropDown);
selectedType = 0;
_bufferHasChanged = false;

return true;
}

private static DropDownMember CreateDropDownMember(Request request, IVsTextView textView)
{
TextSpan textSpan = GetTextSpan(request, textView);
var text = request.Method.Text + " " + request.Url.Text;
return new DropDownMember(text, textSpan, 126, DROPDOWNFONTATTR.FONTATTR_PLAIN);
}

private static TextSpan GetTextSpan(Request item, IVsTextView textView)
{
TextSpan textSpan = new();

textView.GetLineAndColumn(item.Method.Start, out textSpan.iStartLine, out textSpan.iStartIndex);
textView.GetLineAndColumn(item.Url.End + 1, out textSpan.iEndLine, out textSpan.iEndIndex);

return textSpan;
}

public void Dispose()
{
if (_disposed)
{
return;
}

_disposed = true;
_textView.Caret.PositionChanged -= CaretPositionChanged;
_document.Parsed -= OnDocumentParsed;
}
}
}
25 changes: 24 additions & 1 deletion src/RestClientVS/Editor/LanguageFactory.cs
Original file line number Diff line number Diff line change
@@ -1,25 +1,33 @@
using System.ComponentModel.Design;
using System.Runtime.InteropServices;
using Microsoft.VisualStudio.Package;
using Microsoft.VisualStudio.TextManager.Interop;

namespace RestClientVS
{
[ComVisible(true)]
[Guid(PackageGuids.RestEditorFactoryString)]
public class LanguageFactory : LanguageBase
{
public const string LanguageName = "Rest";
public const string FileExtension = ".http";
private DropdownBars _dropdownBars;

public LanguageFactory(object site) : base(site)
{ }

public void RegisterLanguageService(Package package)
{
((IServiceContainer)package).AddService(GetType(), this, true);
}

public override string Name => LanguageName;

public override string[] FileExtensions => new[] { FileExtension };

public override void SetDefaultPreferences(LanguagePreferences preferences)
{
preferences.EnableCodeSense = true;
preferences.EnableCodeSense = false;
preferences.EnableMatchBraces = true;
preferences.EnableMatchBracesAtCaret = true;
preferences.EnableShowMatchingBrace = true;
Expand All @@ -41,5 +49,20 @@ public override void SetDefaultPreferences(LanguagePreferences preferences)
preferences.EnableQuickInfo = true;
preferences.ParameterInformation = true;
}

public override TypeAndMemberDropdownBars CreateDropDownHelper(IVsTextView textView)
{
_dropdownBars?.Dispose();
_dropdownBars = new DropdownBars(textView, this);

return _dropdownBars;
}

public override void Dispose()
{
_dropdownBars?.Dispose();
_dropdownBars = null;
base.Dispose();
}
}
}
4 changes: 3 additions & 1 deletion src/RestClientVS/RestClientVS.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Compile Include="Commands\Commenting.cs" />
<Compile Include="Editor\DropdownBars.cs" />
<Compile Include="Editor\EditorFeatures.cs" />
<Compile Include="Commands\GoToDefinitionCommand.cs" />
<Compile Include="Commands\UncommentCommand.cs" />
Expand Down Expand Up @@ -110,7 +112,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Community.VisualStudio.VSCT" Version="16.0.29.6" PrivateAssets="all" />
<PackageReference Include="Community.VisualStudio.Toolkit.17" Version="17.0.378" ExcludeAssets="Runtime">
<PackageReference Include="Community.VisualStudio.Toolkit.17" Version="17.0.384" ExcludeAssets="Runtime">
<IncludeAssets>compile; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.VisualStudio.Threading">
Expand Down
9 changes: 7 additions & 2 deletions src/RestClientVS/RestClientVSPackage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ namespace RestClientVS
[ProvideLanguageExtension(typeof(LanguageFactory), LanguageFactory.FileExtension)]
[ProvideLanguageEditorOptionPage(typeof(OptionsProvider.GeneralOptions), LanguageFactory.LanguageName, null, "Advanced", null, new[] { "http", "rest", "timeout" })]

[ProvideEditorFactory(typeof(LanguageFactory), 351, false, CommonPhysicalViewAttributes = (int)__VSPHYSICALVIEWATTRIBUTES.PVA_SupportsPreview, TrustLevel = __VSEDITORTRUSTLEVEL.ETL_AlwaysTrusted)]
[ProvideEditorFactory(typeof(LanguageFactory), 351, CommonPhysicalViewAttributes = (int)__VSPHYSICALVIEWATTRIBUTES.PVA_SupportsPreview, TrustLevel = __VSEDITORTRUSTLEVEL.ETL_AlwaysTrusted)]
[ProvideEditorExtension(typeof(LanguageFactory), LanguageFactory.FileExtension, 65535, NameResourceID = 351)]
[ProvideEditorLogicalView(typeof(LanguageFactory), VSConstants.LOGVIEWID.TextView_string, IsTrusted = true)]

Expand All @@ -27,9 +27,14 @@ public sealed class RestClientVSPackage : ToolkitPackage
{
protected override async Task InitializeAsync(CancellationToken cancellationToken, IProgress<ServiceProgressData> progress)
{
RegisterEditorFactory(new LanguageFactory(this));
await JoinableTaskFactory.SwitchToMainThreadAsync();

var language = new LanguageFactory(this);
RegisterEditorFactory(language);
language.RegisterLanguageService(this);

await this.RegisterCommandsAsync();
await Commenting.InitializeAsync();
}
}
}

0 comments on commit a4bacef

Please sign in to comment.