
Introduction
The less I have to navigate a directory tree, the happier I become. The arrival of Google Desktop hails the "flattening" of the file structure, as finally there is something that can just find my file for me, right when I want it. I have been a little slow to adopt the Google Desktop Search, because it still wants to open a file in the default way. If I tell it to open a web.config file, a new instance of TextPad will open (fine). I start getting annoyed when I go to open a C# source file, and suddenly I'm waiting for a new copy of VS.NET to open. That's bad.
So I have begun the task of integrating Google Desktop into text editors, with this ReSharper plug-in.
With this plug-in, I hit Ctrl-Alt-Z, type in a file name, and two seconds later, I'm editing it. Yah!
If you use Google Desktop and ReSharper, you might be interested in this plug-in. If you haven't gone to ReSharper yet, maybe you'll be interested in turning this into a VS.NET plug-in?
Background
I chose to use ReSharper because it comes with some fancy PopupList user interface stuff. The next iteration will probably be more like the Common File dialog, and if possible, I will simply integrate with the Windows Common File dialog. Anyone knows how to do this?
I fooled around with the Google Desktop Native API for a long time before deciding that C# isn't the right language for doing that. Eventually, I found this .NET wrapper for the web interface to Google Desktop Search, which I included in the project.
Using the code
First, register the Action, with this bit from AssemblyInfo.cs:
[assembly : PluginTitle("OpenByGoogleDesktopSearch")]
[assembly : PluginVendor("Tyler Gannon")]
[assembly : PluginDescription("Find a file using
Google Desktop search and open it.")]
[assembly : ActionsXml("Resharper.GoogleSearchPlugin.Actions.xml")]
Then you need a bit in Actions.xml:
The GDS.SearchFiles method returns a sorted array of SearchResult objects, filtered to only include results that correspond to files on the local file system.
Getting the search results looks like this:
IGoogleSearch search = GoogleSearchFactory.CreateGoogleDesktopSearch();
search.ResultsPerPage = 20;
search.ResultType = ResultTypes.DataSet;
IGoogleSearchResult searchResult = search.Search(query);
DataSet currentResult;
Now, iterate through them, and get rid of any junk.
foreach (DataRow row in table.Rows)
{
if ("file".CompareTo((string) row["category"]) == 0)
{
object title = row["title"];
object url = row["url"];
if (DBNull.Value.Equals(title) || DBNull.Value.Equals(url))
continue;
object snippet = title;
try
{
snippet = row["snippet"];
if (DBNull.Value.Equals(snippet))
snippet = title;
}
catch (Exception)
{
continue;
}
string urlString = (string) url;
if (urlString.StartsWith("archive://"))
continue;
SearchResult result = new SearchResult((string) title,
urlString, (string) snippet);
resultsToReturn.Add(result);
currentResultCount++;
}
if (currentResultCount >= maxResults)
{
break;
}
}
Now, make it so that file names most similar to the actual search query will show up first.
Array.Sort(resultsArray, new SearchResultComparer(query));
Making use of the ReSharper popup requires implementing a few interfaces:
IPositionProvider, which tells the system where it can present the popup.
IPopupListItemDescriptor, a list of which are given to the PopupList by the PopupListProvider. In this project, SearchResult implements that interface.
IPopupListProvider, which is the liaison between the GDS search and the PopUpList.
IPopupWindowContext, which provides general functionality around PopupWindows. I inherited my PopupWindowContext from PopupWindowContextBase, and then I only had to implement CreateDefaultPositionProvider(), which is defined as abstract on the base class.
The SearchPopup form code ties it all together:
public SearchPopup()
{
InitializeComponent();
textBox1.Enter += new EventHandler(TextBox1_OnEnter);
}
internal void UpdateList()
{
if (textBox1.Text.Trim().Equals(string.Empty))
{
TrimWindowSize();
return;
}
list = new EscapingPopupList(new SearchResultListProvider(
GDS.SearchFiles(textBox1.Text, MAX_RECORDS_TO_SHOW), this));
list.Escape += new PopupListEscapeEvent(TrimWindowSize);
list.Show(positionProvider);
}
private void TrimWindowSize()
{
this.ResizeRedraw = true;
this.Size = textBox1.Size;
}
protected void TextBox1_OnEnter(Object Sender, EventArgs e)
{
if (popupWindowContext==null)
popupWindowContext = new PopupWindowContext(this);
if (positionProvider==null)
positionProvider =
popupWindowContext.CreateDefaultPositionProvider();
base.OnEnter(e);
}
protected override bool ProcessDialogKey(Keys keyData)
{
if (keyData == (Keys.Control| Keys.W) || keyData==(Keys.Escape))
Close();
if (keyData == Keys.Tab || keyData == Keys.Enter)
UpdateList();
return base.ProcessDialogKey (keyData);
}
Installation
Once it's built and ready, you need to register the plug-in with ReSharper, and finally you must set up a Command Key. Copy the DLL into its own subdirectory of the PlugIns directory underneath where ReSharper is installed. The directory should be named the same as the plug-in. In this case, the assembly is named Resharper.GoogleSearchPlugin, so that's what I named the directory. Then, start VS.NET, and do the following settings in the ReSharper Plug-Ins dialog, followed by the Keyboard tab on the VS.NET general options:


And you're ready to go! Have fun.
History