Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@
<Compile Include="Services\ConnectionSettingsService.cs" />
<Compile Include="Services\CouchbaseService.cs" />
<Compile Include="Services\CredentialManagerService.cs" />
<Compile Include="Services\DocumentEditorService.cs" />
<Compile Include="ViewModels\BindingProxy.cs" />
<Compile Include="ViewModels\BucketNode.cs" />
<Compile Include="ViewModels\BucketsFolderNode.cs" />
Expand All @@ -99,12 +100,21 @@
<Compile Include="Dialogs\ConnectionDialog.xaml.cs">
<DependentUpon>ConnectionDialog.xaml</DependentUpon>
</Compile>
<Compile Include="Editors\DocumentEditorControl.xaml.cs">
<DependentUpon>DocumentEditorControl.xaml</DependentUpon>
</Compile>
<Compile Include="Editors\DocumentEditorFactory.cs" />
<Compile Include="Editors\DocumentEditorPane.cs" />
</ItemGroup>
<ItemGroup>
<Page Include="Dialogs\ConnectionDialog.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Editors\DocumentEditorControl.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
</ItemGroup>
<ItemGroup>
<Resource Include="Resources\watermark.png" />
Expand Down
18 changes: 18 additions & 0 deletions src/CodingWithCalvin.CouchbaseExplorer/CouchbaseExplorerPackage.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using System.Runtime.InteropServices;
using System.Threading;
using CodingWithCalvin.CouchbaseExplorer.Editors;
using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.Shell.Interop;

Expand All @@ -16,18 +17,35 @@ namespace CodingWithCalvin.CouchbaseExplorer
Window = ToolWindowGuids.ServerExplorer)]
[ProvideAutoLoad(UIContextGuids80.NoSolution, PackageAutoLoadFlags.BackgroundLoad)]
[ProvideAutoLoad(UIContextGuids80.SolutionExists, PackageAutoLoadFlags.BackgroundLoad)]
[ProvideEditorFactory(typeof(DocumentEditorFactory), 110, TrustLevel = __VSEDITORTRUSTLEVEL.ETL_AlwaysTrusted)]
[ProvideEditorExtension(typeof(DocumentEditorFactory), ".cbjson", 50)]
public sealed class CouchbaseExplorerPackage : AsyncPackage
{
public const string PackageGuidString = "ef261503-b2ae-4b90-8c86-0becd83348cc";

private DocumentEditorFactory _editorFactory;

protected override async System.Threading.Tasks.Task InitializeAsync(
CancellationToken cancellationToken,
IProgress<ServiceProgressData> progress
)
{
await JoinableTaskFactory.SwitchToMainThreadAsync();

// Register the editor factory
_editorFactory = new DocumentEditorFactory();
RegisterEditorFactory(_editorFactory);

CouchbaseExplorerWindowCommand.Initialize(this);
}

protected override void Dispose(bool disposing)
{
if (disposing)
{
_editorFactory?.Dispose();
}
base.Dispose(disposing);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ public CouchbaseExplorerWindowControl()

ExplorerTreeView.SelectedItemChanged += OnSelectedItemChanged;
ExplorerTreeView.PreviewMouseRightButtonDown += OnPreviewMouseRightButtonDown;
ExplorerTreeView.MouseDoubleClick += OnMouseDoubleClick;
}

private void OnSelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
Expand All @@ -40,6 +41,15 @@ private void OnPreviewMouseRightButtonDown(object sender, MouseButtonEventArgs e
}
}

private void OnMouseDoubleClick(object sender, MouseButtonEventArgs e)
{
if (ViewModel.SelectedNode is DocumentNode)
{
ViewModel.OpenDocumentCommand.Execute(null);
e.Handled = true;
}
}

private static T FindAncestor<T>(DependencyObject current) where T : DependencyObject
{
while (current != null)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
<UserControl x:Class="CodingWithCalvin.CouchbaseExplorer.Editors.DocumentEditorControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vsshell="clr-namespace:Microsoft.VisualStudio.Shell;assembly=Microsoft.VisualStudio.Shell.15.0"
Background="{DynamicResource {x:Static vsshell:VsBrushes.ToolWindowBackgroundKey}}"
Foreground="{DynamicResource {x:Static vsshell:VsBrushes.ToolWindowTextKey}}">

<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>

<!-- Header with document info and actions -->
<Border Grid.Row="0"
Background="{DynamicResource {x:Static vsshell:VsBrushes.CommandBarGradientBeginKey}}"
BorderBrush="{DynamicResource {x:Static vsshell:VsBrushes.ToolWindowBorderKey}}"
BorderThickness="0,0,0,1"
Padding="10,8">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>

<!-- Document Info -->
<StackPanel Grid.Column="0">
<TextBlock x:Name="DocumentIdText" FontWeight="Bold" FontSize="14"
Foreground="{DynamicResource {x:Static vsshell:VsBrushes.ToolWindowTextKey}}" />
<StackPanel Orientation="Horizontal" Margin="0,4,0,0">
<TextBlock Text="Collection: " FontSize="11" Opacity="0.7"
Foreground="{DynamicResource {x:Static vsshell:VsBrushes.ToolWindowTextKey}}" />
<TextBlock x:Name="CollectionPathText" FontSize="11" Opacity="0.7"
Foreground="{DynamicResource {x:Static vsshell:VsBrushes.ToolWindowTextKey}}" />
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="0,2,0,0">
<TextBlock Text="CAS: " FontSize="11" Opacity="0.7"
Foreground="{DynamicResource {x:Static vsshell:VsBrushes.ToolWindowTextKey}}" />
<TextBlock x:Name="CasText" FontSize="11" Opacity="0.7"
Foreground="{DynamicResource {x:Static vsshell:VsBrushes.ToolWindowTextKey}}" />
</StackPanel>
</StackPanel>

<!-- Action Buttons -->
<StackPanel Grid.Column="1" Orientation="Horizontal" VerticalAlignment="Center">
<Button x:Name="RefreshButton" Content="Refresh" Padding="12,4" Margin="0,0,8,0"
Click="RefreshButton_Click"
Background="{DynamicResource {x:Static vsshell:VsBrushes.CommandBarGradientBeginKey}}"
Foreground="{DynamicResource {x:Static vsshell:VsBrushes.CommandBarTextActiveKey}}" />
<Button x:Name="CopyButton" Content="Copy JSON" Padding="12,4"
Click="CopyButton_Click"
Background="{DynamicResource {x:Static vsshell:VsBrushes.CommandBarGradientBeginKey}}"
Foreground="{DynamicResource {x:Static vsshell:VsBrushes.CommandBarTextActiveKey}}" />
</StackPanel>
</Grid>
</Border>

<!-- JSON Content -->
<TextBox Grid.Row="1"
x:Name="JsonContentBox"
IsReadOnly="True"
FontFamily="Consolas"
FontSize="13"
AcceptsReturn="True"
TextWrapping="NoWrap"
VerticalScrollBarVisibility="Auto"
HorizontalScrollBarVisibility="Auto"
Background="{DynamicResource {x:Static vsshell:VsBrushes.ToolWindowBackgroundKey}}"
Foreground="{DynamicResource {x:Static vsshell:VsBrushes.ToolWindowTextKey}}"
BorderThickness="0"
Padding="10" />
</Grid>
</UserControl>
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
using System;
using System.Windows;
using System.Windows.Controls;
using CodingWithCalvin.CouchbaseExplorer.Services;
using Newtonsoft.Json;

namespace CodingWithCalvin.CouchbaseExplorer.Editors
{
public partial class DocumentEditorControl : UserControl
{
public string ConnectionId { get; private set; }
public string BucketName { get; private set; }
public string ScopeName { get; private set; }
public string CollectionName { get; private set; }
public string DocumentId { get; private set; }
public ulong Cas { get; private set; }

public DocumentEditorControl()
{
InitializeComponent();
}

public void SetDocument(string connectionId, string documentId, string bucketName, string scopeName, string collectionName, object content, ulong cas)
{
ConnectionId = connectionId;
DocumentId = documentId;
BucketName = bucketName;
ScopeName = scopeName;
CollectionName = collectionName;
Cas = cas;

DocumentIdText.Text = documentId;
CollectionPathText.Text = $"{bucketName}.{scopeName}.{collectionName}";
CasText.Text = cas.ToString();

if (content != null)
{
JsonContentBox.Text = JsonConvert.SerializeObject(content, Formatting.Indented);
}
else
{
JsonContentBox.Text = "null";
}
}

private async void RefreshButton_Click(object sender, RoutedEventArgs e)
{
try
{
RefreshButton.IsEnabled = false;
RefreshButton.Content = "Refreshing...";

var content = await CouchbaseService.GetDocumentAsync(
ConnectionId,
BucketName,
ScopeName,
CollectionName,
DocumentId);

Cas = content.Cas;
CasText.Text = content.Cas.ToString();

if (content.Content != null)
{
JsonContentBox.Text = JsonConvert.SerializeObject(content.Content, Formatting.Indented);
}
else
{
JsonContentBox.Text = "null";
}
}
catch (Exception ex)
{
MessageBox.Show(
$"Failed to refresh document: {ex.Message}",
"Error",
MessageBoxButton.OK,
MessageBoxImage.Error);
}
finally
{
RefreshButton.IsEnabled = true;
RefreshButton.Content = "Refresh";
}
}

private void CopyButton_Click(object sender, RoutedEventArgs e)
{
Clipboard.SetText(JsonContentBox.Text);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
using System;
using System.Runtime.InteropServices;
using Microsoft.VisualStudio;
using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.Shell.Interop;
using IOleServiceProvider = Microsoft.VisualStudio.OLE.Interop.IServiceProvider;

namespace CodingWithCalvin.CouchbaseExplorer.Editors
{
[Guid("B7D4E2F1-8A3C-4E5D-9F6A-2B1C3D4E5F6A")]
public class DocumentEditorFactory : IVsEditorFactory, IDisposable
{
private ServiceProvider _serviceProvider;

public int SetSite(IOleServiceProvider psp)
{
_serviceProvider = new ServiceProvider(psp);
return VSConstants.S_OK;
}

public int MapLogicalView(ref Guid rguidLogicalView, out string pbstrPhysicalView)
{
pbstrPhysicalView = null;

if (rguidLogicalView == VSConstants.LOGVIEWID_Primary ||
rguidLogicalView == VSConstants.LOGVIEWID_TextView ||
rguidLogicalView == Guid.Empty)
{
return VSConstants.S_OK;
}

return VSConstants.E_NOTIMPL;
}

public int CreateEditorInstance(
uint grfCreateDoc,
string pszMkDocument,
string pszPhysicalView,
IVsHierarchy pvHier,
uint itemid,
IntPtr punkDocDataExisting,
out IntPtr ppunkDocView,
out IntPtr ppunkDocData,
out string pbstrEditorCaption,
out Guid pguidCmdUI,
out int pgrfCDW)
{
ppunkDocView = IntPtr.Zero;
ppunkDocData = IntPtr.Zero;
pbstrEditorCaption = string.Empty;
pguidCmdUI = Guid.Empty;
pgrfCDW = 0;

// Create a new editor pane
var editorPane = new DocumentEditorPane();

ppunkDocView = Marshal.GetIUnknownForObject(editorPane);
ppunkDocData = Marshal.GetIUnknownForObject(editorPane);
pbstrEditorCaption = "";
pguidCmdUI = typeof(DocumentEditorPane).GUID;

return VSConstants.S_OK;
}

public int Close()
{
return VSConstants.S_OK;
}

public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}

protected virtual void Dispose(bool disposing)
{
if (disposing)
{
_serviceProvider?.Dispose();
_serviceProvider = null;
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
using System;
using System.Runtime.InteropServices;
using Microsoft.VisualStudio.Shell;

namespace CodingWithCalvin.CouchbaseExplorer.Editors
{
[Guid("E8A3C5D1-9B2F-4A6C-8D7E-1F2A3B4C5D6E")]
public class DocumentEditorPane : WindowPane
{
private readonly DocumentEditorControl _control;

public DocumentEditorControl EditorControl => _control;

public DocumentEditorPane() : base(null)
{
_control = new DocumentEditorControl();
Content = _control;
}

public void SetDocument(string connectionId, string documentId, string bucketName, string scopeName, string collectionName, object content, ulong cas)
{
_control.SetDocument(connectionId, documentId, bucketName, scopeName, collectionName, content, cas);
}
}
}
Loading