Skip to content

Commit

Permalink
! Merged root and namespace buttons in Navi Bar
Browse files Browse the repository at this point in the history
  • Loading branch information
wmjordan committed Dec 20, 2018
1 parent 9c68d90 commit 80053ac
Show file tree
Hide file tree
Showing 5 changed files with 139 additions and 46 deletions.
2 changes: 1 addition & 1 deletion Codist/Commands/CodistPackage.vsct
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@
</Button>
<Button guid="guidCodistPackageCmdSet4" id="NaviBarSearchDeclarationId" priority="0x0100" type="Button">
<Parent guid="guidCodistPackageCmdSet4" id="IDG_VS_EDIT_GOTO" />
<Icon guid="ImageCatalogGuid" id="SearchMember" />
<Icon guid="ImageCatalogGuid" id="SearchContract" />
<CommandFlag>DefaultDisabled</CommandFlag>
<CommandFlag>IconIsMoniker</CommandFlag>
<Strings>
Expand Down
4 changes: 3 additions & 1 deletion Codist/Controls/MemberFilterBox.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ public SearchScopeBox() {
CornerRadius = new CornerRadius(3),
Child = new StackPanel {
Children = {
_DocumentFilter, _ProjectFilter
_DocumentFilter, _ProjectFilter,
},
Orientation = Orientation.Horizontal
}
Expand All @@ -42,6 +42,8 @@ public SearchScopeBox() {

public ScopeType Filter { get; private set; }

public UIElementCollection Contents => ((StackPanel)((Border)Content).Child).Children;

ThemedToggleButton CreateButton(int imageId, string toolTip) {
var b = new ThemedToggleButton(imageId, toolTip) { BorderThickness = WpfHelper.NoMargin };
b.Checked += UpdateFilterValue;
Expand Down
13 changes: 13 additions & 0 deletions Codist/Helpers/CodeAnalysisHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,19 @@ public static bool IsDeclaration(this SyntaxNode node) {
return false;
}

public static bool IsTypeOrNamespaceDeclaration(this SyntaxNode node) {
switch (node.Kind()) {
case SyntaxKind.ClassDeclaration:
case SyntaxKind.DelegateDeclaration:
case SyntaxKind.EnumDeclaration:
case SyntaxKind.EventDeclaration:
case SyntaxKind.InterfaceDeclaration:
case SyntaxKind.StructDeclaration:
case SyntaxKind.NamespaceDeclaration:
return true;
}
return false;
}
public static bool IsTypeDeclaration(this SyntaxNode node) {
switch (node.Kind()) {
case SyntaxKind.ClassDeclaration:
Expand Down
25 changes: 21 additions & 4 deletions Codist/Helpers/SemanticContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,15 @@ namespace Codist
{
sealed class SemanticContext
{
readonly IWpfTextView _TextView;
VersionStamp _Version;
SyntaxNode _Node, _NodeIncludeTrivia;

public SemanticContext(IWpfTextView textView) {
_TextView = textView;
View = textView;
}

public IWpfTextView View { get; }
public Workspace Workspace { get; private set; }
public Document Document { get; private set; }
public SemanticModel SemanticModel { get; private set; }
public CompilationUnitSyntax Compilation { get; private set; }
Expand Down Expand Up @@ -224,8 +225,16 @@ public async Task<ISymbol> GetSymbolAsync(CancellationToken cancellationToken) {

public async Task<bool> UpdateAsync(CancellationToken cancellationToken) {
try {
var doc = _TextView.TextSnapshot.GetOpenDocumentInCurrentContextWithChanges();
var text = View.TextSnapshot.AsText();
Document doc = null;
if (Workspace.TryGetWorkspace(text.Container, out var workspace)) {
var id = workspace.GetDocumentIdInCurrentContext(text.Container);
if (id != null && workspace.CurrentSolution.ContainsDocument(id)) {
doc = workspace.CurrentSolution.WithDocumentText(id, text, PreservationMode.PreserveIdentity).GetDocument(id);
}
}
if (doc != Document) {
Workspace = workspace;
Document = doc;
SemanticModel = await Document.GetSemanticModelAsync(cancellationToken);
Compilation = SemanticModel.SyntaxTree.GetCompilationUnitRoot(cancellationToken);
Expand All @@ -241,7 +250,15 @@ public async Task<bool> UpdateAsync(CancellationToken cancellationToken) {
public async Task<bool> UpdateAsync(int position, CancellationToken cancellationToken) {
bool versionChanged = false;
try {
Document = _TextView.TextSnapshot.GetOpenDocumentInCurrentContextWithChanges();
var text = View.TextSnapshot.AsText();
Document = null;
if (Workspace.TryGetWorkspace(text.Container, out var workspace)) {
Workspace = workspace;
var id = workspace.GetDocumentIdInCurrentContext(text.Container);
if (id != null && workspace.CurrentSolution.ContainsDocument(id)) {
Document = workspace.CurrentSolution.WithDocumentText(id, text, PreservationMode.PreserveIdentity).GetDocument(id);
}
}
var ver = await Document.GetTextVersionAsync(cancellationToken);
if (versionChanged = ver != _Version) {
_Version = ver;
Expand Down
141 changes: 101 additions & 40 deletions Codist/NaviBar/CSharpBar.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ public sealed class CSharpBar : Menu
readonly IAdornmentLayer _Adornment;
readonly SemanticContext _SemanticContext;
CancellationTokenSource _cancellationSource = new CancellationTokenSource();
RootItem _RootItem;
NaviItem _MouseHoverItem;

public CSharpBar(IWpfTextView textView) {
Expand All @@ -37,11 +38,16 @@ public CSharpBar(IWpfTextView textView) {
Resources = SharedDictionaryManager.Menu;
SetResourceReference(BackgroundProperty, VsBrushes.CommandBarMenuBackgroundGradientKey);
SetResourceReference(ForegroundProperty, VsBrushes.CommandBarTextInactiveKey);
Items.Add(new RootItem(this));
Items.Add(_RootItem = new RootItem(this));
_View.Selection.SelectionChanged += Update;
_View.Closed += ViewClosed;
Update(this, EventArgs.Empty);
// todo update icons if theme changed
foreach(var m in _SemanticContext.Compilation.Members) {
if (m.IsKind(SyntaxKind.NamespaceDeclaration)) {
_RootItem.SetText(m.GetDeclarationSignature());
break;
}
}
}

protected override void OnMouseMove(MouseEventArgs e) {
Expand Down Expand Up @@ -131,13 +137,18 @@ async Task Update(CancellationToken token) {
int i;
for (i = 0; i < c; i++) {
var n = nodes[i];
if ((Items[i + 1] as NaviItem).Node != n) {
// keep the NaviItem if node is not updated
break;
}
if (token.IsCancellationRequested) {
return;
}
if ((Items[i + 1] as NaviItem).Node == n) {
// keep the NaviItem if node is not updated
continue;
}
break;
}
if ((i == 0 || nodes[i - 1].IsTypeOrNamespaceDeclaration()) && _RootItem.FilterText.Length == 0) {
// clear type and namespace menu items if a type is changed
_RootItem.ClearItems();
}
c = Items.Count;
while (--c > i) {
Expand All @@ -148,8 +159,14 @@ async Task Update(CancellationToken token) {
if (token.IsCancellationRequested) {
return;
}
bool highlight = nodes[i].IsMemberDeclaration();
var newItem = new NaviItem(this, nodes[i], highlight, false);
var node = nodes[i];
if (node.IsKind(SyntaxKind.NamespaceDeclaration)) {
_RootItem.SetText(node.GetDeclarationSignature());
++i;
continue;
}
bool highlight = node.IsMemberDeclaration();
var newItem = new NaviItem(this, node, highlight, false);
if (highlight) {
newItem.IsChecked = true;
}
Expand Down Expand Up @@ -208,36 +225,106 @@ sealed class RootItem : ThemedMenuItem
readonly SearchScopeBox _ScopeBox;
public RootItem(CSharpBar bar) {
_Bar = bar;
Icon = ThemeHelper.GetImage(KnownImageIds.CSProjectNode);
Icon = ThemeHelper.GetImage(KnownImageIds.Namespace);
this.ReferenceCrispImageBackground(EnvironmentColors.MainWindowActiveCaptionColorKey);
SetResourceReference(ForegroundProperty, VsBrushes.CommandBarTextActiveKey);
Header = new ThemedToolBarText("//");
Header = new ThemedToolBarText();
SubMenuHeader = new StackPanel {
Margin = WpfHelper.MenuItemMargin,
Children = {
new Separator { Tag = new ThemedMenuText("Search Declaration") },
new StackPanel {
Orientation = Orientation.Horizontal,
Children = {
ThemeHelper.GetImage(KnownImageIds.SearchMember).WrapMargin(WpfHelper.GlyphMargin),
ThemeHelper.GetImage(KnownImageIds.SearchContract).WrapMargin(WpfHelper.GlyphMargin),
(_FinderBox = new MemberFinderBox(Items) { MinWidth = 150 }),
(_ScopeBox = new SearchScopeBox()),
(_ScopeBox = new SearchScopeBox {
Contents = {
new ThemedButton(KnownImageIds.Cancel, "Clear filter", ClearFilter) { Margin = WpfHelper.NoMargin, BorderThickness = WpfHelper.NoMargin }
}
}),
}
},
}
};
SubmenuOpened += RootItem_SubmenuOpened;
_FinderBox.TextChanged += SearchCriteriaChanged;
_ScopeBox.FilterChanged += SearchCriteriaChanged;
_ScopeBox.FilterChanged += (s, args) => {
_FinderBox.Focus();
};
}

public string FilterText => _FinderBox.Text;

internal void SetText(string text) {
((TextBlock)Header).Text = text;
}

void RootItem_SubmenuOpened(object sender, RoutedEventArgs e) {
if (_FinderBox.Text.Length == 0) {
if (HasExplicitItems == false) {
AddNamespaceAndTypes();
}
else {
MarkEnclosingItem();
}
}
}

void MarkEnclosingItem() {
int pos = _Bar._View.GetCaretPosition();
bool marked = false;
for (int i = Items.Count - 1; i >= 0; i--) {
var n = Items[i] as NaviItem;
if (n == null) {
continue;
}
if (marked == false) {
n.MarkEnclosingItem(pos);
if (n.IsChecked) {
marked = true;
}
}
else {
n.IsChecked = false;
}
}
}

void AddNamespaceAndTypes() {
foreach (var node in _Bar._SemanticContext.Compilation.ChildNodes()) {
if (node.IsTypeOrNamespaceDeclaration()) {
Items.Add(new NaviItem(_Bar, node) { ClickHandler = i => i.GoToLocation() });
AddTypeDeclarations(node);
}
}
MarkEnclosingItem();
}

void AddTypeDeclarations(SyntaxNode node) {
int pos = _Bar._View.GetCaretPosition();
foreach (var child in node.ChildNodes()) {
if (child.IsTypeOrNamespaceDeclaration()) {
Items.Add(new NaviItem(_Bar, child) { ClickHandler = i => i.GoToLocation() });
AddTypeDeclarations(child);
}
}
}

void ClearFilter() {
if (_FinderBox.Text.Length > 0) {
_FinderBox.Text = String.Empty;
}
_FinderBox.Focus();
}

void SearchCriteriaChanged(object sender, EventArgs e) {
CancellationHelper.CancelAndDispose(ref _Bar._cancellationSource, true);
ClearItems();
var s = _FinderBox.Text;
if (s.Length == 0) {
AddNamespaceAndTypes();
return;
}
try {
Expand Down Expand Up @@ -324,7 +411,7 @@ internal set {
}
internal SyntaxNode Node { get; }

private Action<NaviItem> ClickHandler { get; set; }
internal Action<NaviItem> ClickHandler { get; set; }

async void NaviItem_Click(object sender, RoutedEventArgs e) {
CancellationHelper.CancelAndDispose(ref _Bar._cancellationSource, true);
Expand Down Expand Up @@ -449,7 +536,7 @@ NaviItem ShowNodeValue() {
return this;
}

NaviItem MarkEnclosingItem(int position) {
internal NaviItem MarkEnclosingItem(int position) {
if (NodeIsExternal) {
return this;
}
Expand All @@ -466,16 +553,6 @@ NaviItem MarkEnclosingItem(int position) {

async Task AddItemsAsync(ItemCollection items, SyntaxNode node, CancellationToken cancellationToken) {
switch (node.Kind()) {
case SyntaxKind.NamespaceDeclaration:
SubMenuMaxHeight = _Bar._View.ViewportHeight / 2;
SubMenuHeader = new StackPanel {
Children = {
new NaviItem(_Bar, node) { ClickHandler = i => i.GoToLocation() },
new Separator()
}
};
await AddTypeDeclarationsAsync(node, cancellationToken);
break;
case SyntaxKind.ClassDeclaration:
case SyntaxKind.StructDeclaration:
case SyntaxKind.InterfaceDeclaration:
Expand Down Expand Up @@ -569,22 +646,6 @@ async Task AddPartialTypeDeclarationsAsync(BaseTypeDeclarationSyntax node, Cance
AddMemberDeclarations(partial, true);
}
}
async Task AddTypeDeclarationsAsync(SyntaxNode node, CancellationToken cancellationToken) {
int pos = _Bar._View.GetCaretPosition();
foreach(var child in node.ChildNodes()) {
if (cancellationToken.IsCancellationRequested) {
break;
}
if (child.IsTypeDeclaration() == false) {
if (child.IsKind(SyntaxKind.NamespaceDeclaration)) {
Items.Add(new NaviItem(_Bar, child) { ClickHandler = i => i.GoToLocation() }.MarkEnclosingItem(pos));
}
continue;
}
Items.Add(new NaviItem(_Bar, child) { ClickHandler = i => i.GoToLocation() }.MarkEnclosingItem(pos));
await AddTypeDeclarationsAsync(child, cancellationToken);
}
}
void AddMemberDeclarations(SyntaxNode node, bool isExternal) {
const byte UNDEFINED = 0xFF, TRUE = 1, FALSE = 0;
var directives = Config.Instance.NaviBarOptions.MatchFlags(NaviBarOptions.Region)
Expand Down

0 comments on commit 80053ac

Please sign in to comment.