Skip to content
Merged
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
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
174 changes: 174 additions & 0 deletions Extensions/dnSpy.StringSearcher/Commands.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.Linq;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Input;
using dnlib.DotNet;
using dnSpy.Contracts.Controls;
using dnSpy.Contracts.Documents.Tabs;
using dnSpy.Contracts.Documents.TreeView;
using dnSpy.Contracts.Extension;
using dnSpy.Contracts.Images;
using dnSpy.Contracts.Menus;
using dnSpy.Contracts.ToolWindows.App;
using dnSpy.Contracts.TreeView;

namespace dnSpy.StringSearcher {
[ExportAutoLoaded]
sealed class StringSearcherCommandLoader : IAutoLoaded {
[ImportingConstructor]
StringSearcherCommandLoader(IWpfCommandService wpfCommandService, Lazy<IStringReferencesService> analyzerService) {
var cmds = wpfCommandService.GetCommands(new Guid(StringSearcherConstants.GUID_STRINGS_LISTBOX));
cmds.Add(ApplicationCommands.Copy,
(s, e) => CopyStringLiteralCommand.ExecuteInternal(analyzerService.Value.CurrentReference),
(s, e) => e.CanExecute = analyzerService.Value.CurrentReference is not null
);
}
}

abstract class FindStringReferencesInModuleCommand(IDsToolWindowService toolWindowService, IStringReferencesService stringReferencesService) : MenuItemBase {

public override bool IsVisible(IMenuItemContext context) => GetModules(context).Any();

public override void Execute(IMenuItemContext context) {
var modules = GetModules(context).ToArray();
if (modules.Length == 0)
return;

toolWindowService.Show(StringReferencesToolWindowContent.THE_GUID);
stringReferencesService.Analyze(modules);
}

protected abstract IEnumerable<ModuleDef> GetModules(IMenuItemContext context);

[ExportMenuItem(Header = "res:FindStringReferencesInModuleCommand", Icon = DsImagesAttribute.Search, Group = MenuConstants.GROUP_CTX_DOCUMENTS_OTHER, Order = 0)]
sealed class DocumentsCommand : FindStringReferencesInModuleCommand {

[ImportingConstructor]
public DocumentsCommand(IDsToolWindowService toolWindowService, IStringReferencesService stringReferencesService)
: base(toolWindowService, stringReferencesService) {
}

protected override IEnumerable<ModuleDef> GetModules(IMenuItemContext context) {
var nodes = context.Find<TreeNodeData[]>();
if (nodes is null)
return [];

return nodes.OfType<DocumentTreeNodeData>()
.SelectMany(n => n.GetModule()?.Assembly.Modules ?? [])
.Distinct();
}
}

[ExportMenuItem(OwnerGuid = MenuConstants.APP_MENU_EDIT_GUID, Header = "res:FindStringReferencesInModuleCommand", Icon = DsImagesAttribute.Search, Group = MenuConstants.GROUP_APP_MENU_EDIT_FIND, Order = 20)]
sealed class MenuBarCommand : FindStringReferencesInModuleCommand {
private readonly IDocumentTabService documentTabService;

[ImportingConstructor]
public MenuBarCommand(IDsToolWindowService toolWindowService, IStringReferencesService stringReferencesService, IDocumentTabService documentTabService)
: base(toolWindowService, stringReferencesService) {
this.documentTabService = documentTabService;
}

protected override IEnumerable<ModuleDef> GetModules(IMenuItemContext context) {
return documentTabService.DocumentTreeView.TreeView.SelectedItems
.OfType<DocumentTreeNodeData>()
.SelectMany(n => n.GetModule()?.Assembly.Modules ?? [])
.Distinct();
}
}
}

abstract class ReferenceCommandBase(Lazy<IStringReferencesService> service) : MenuItemBase {
public Lazy<IStringReferencesService> Service { get; } = service;

public override void Execute(IMenuItemContext context) {
var reference = GetReference(context);
if (reference is null)
return;
Execute(context, reference);
}

protected abstract void Execute(IMenuItemContext context, StringReference reference);

public override bool IsVisible(IMenuItemContext context) => GetReference(context) is not null;

private static StringReference? GetReference(IMenuItemContext context) {
if (context.CreatorObject.Guid != new Guid(StringSearcherConstants.GUID_STRINGS_LISTBOX))
return null;

return context.Find<StringReference>();
}
}

[ExportMenuItem(Header = "res:CopyStringLiteralCommand", Group = StringSearcherConstants.GUID_CTX_GROUP_COPY, Order = 0, Icon = DsImagesAttribute.Copy, InputGestureText = "res:ShortCutKeyCtrlC")]
sealed class CopyStringLiteralCommand : ReferenceCommandBase {
[ImportingConstructor]
CopyStringLiteralCommand(Lazy<IStringReferencesService> service)
: base(service) {
}

protected override void Execute(IMenuItemContext context, StringReference reference) => ExecuteInternal(reference);

internal static void ExecuteInternal(StringReference? reference) {
if (reference is null) {
return;
}

try {
Clipboard.SetText(reference.FormattedLiteral);
}
catch (ExternalException) { }
}
}

[ExportMenuItem(Header = "res:CopyRawStringLiteralCommand", Group = StringSearcherConstants.GUID_CTX_GROUP_COPY, Order = 1)]
sealed class CopyRawStringLiteralCommand : ReferenceCommandBase {
[ImportingConstructor]
CopyRawStringLiteralCommand(Lazy<IStringReferencesService> service)
: base(service) {
}

protected override void Execute(IMenuItemContext context, StringReference reference) => ExecuteInternal(reference);

internal static void ExecuteInternal(StringReference? reference) {
if (reference is null) {
return;
}

try {
Clipboard.SetText(reference.Literal, TextDataFormat.UnicodeText);
}
catch (ExternalException) { }
}
}

abstract class OpenReferenceCommandBase : ReferenceCommandBase {
readonly bool newTab;

protected OpenReferenceCommandBase(Lazy<IStringReferencesService> service, bool newTab)
: base(service) {
this.newTab = newTab;
}

protected override void Execute(IMenuItemContext context, StringReference reference) => Service.Value.FollowReference(reference, newTab);
}

[ExportMenuItem(Header = "res:GoToReferenceInCodeCommand", InputGestureText = "res:DoubleClick", Group = StringSearcherConstants.GUID_CTX_GROUP_FOLLOW, Order = 0)]
sealed class OpenReferenceCommand : OpenReferenceCommandBase {
[ImportingConstructor]
OpenReferenceCommand(Lazy<IStringReferencesService> service)
: base(service, false) {
}
}

[ExportMenuItem(Header = "res:GoToReferenceInCodeNewTabCommand", Group = StringSearcherConstants.GUID_CTX_GROUP_FOLLOW, Order = 1)]
sealed class OpenReferenceNewTabCommand : OpenReferenceCommandBase {
[ImportingConstructor]
OpenReferenceNewTabCommand(Lazy<IStringReferencesService> service)
: base(service, true) {
}
}
}
33 changes: 33 additions & 0 deletions Extensions/dnSpy.StringSearcher/ConstantStringReference.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
using System;
using dnlib.DotNet;
using dnSpy.Contracts.Text.Classification;

namespace dnSpy.StringSearcher {
public sealed class ConstantStringReference(StringReferenceContext context, string literal, IHasConstant referrer)
: StringReference(context, literal, referrer) {

public new IHasConstant Referrer => (IHasConstant)base.Referrer;

public IMemberDef Container => Referrer switch {
FieldDef or PropertyDef => (IMemberDef)Referrer,
ParamDef param => param.DeclaringMethod,
_ => throw new ArgumentOutOfRangeException(nameof(Referrer)),
};

public override StringReferenceKind Kind => StringReferenceKind.Constant;

public override ModuleDef Module => Container.Module;

public override MDToken Token => Referrer.MDToken;

public override object ContainerObject => Container;

protected override void WriteReferrerUI(TextClassifierTextColorWriter writer) {
Context.Decompiler.Write(writer, Container, DefaultFormatterOptions);

if (Referrer is ParamDef param) {
WriteParameterReference(writer, param);
}
}
}
}
65 changes: 65 additions & 0 deletions Extensions/dnSpy.StringSearcher/CustomAttributeStringReference.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
using dnlib.DotNet;
using dnSpy.Contracts.Documents.TreeView;
using dnSpy.Contracts.Text;
using dnSpy.Contracts.Text.Classification;
using dnSpy.StringSearcher.Properties;

namespace dnSpy.StringSearcher {
public sealed class CustomAttributeStringReference(
StringReferenceContext context,
string literal,
IHasCustomAttribute referrer,
IMDTokenProvider container,
ModuleDef module,
CustomAttribute attribute,
string argumentName)
: StringReference(context, literal, referrer) {

public CustomAttribute CustomAttribute { get; } = attribute;

public override StringReferenceKind Kind => StringReferenceKind.Attribute;

public override ModuleDef Module => module;

public override MDToken Token => ((IMDTokenProvider)ContainerObject).MDToken;

public override object ContainerObject => container;

protected override void WriteReferrerUI(TextClassifierTextColorWriter writer) {
switch (ContainerObject) {
case ModuleDef module:
writer.WriteModule(module.Name);
break;
case AssemblyDef assembly:
new NodeFormatter().Write(writer, Context.Decompiler, assembly, false, true, true);
break;
case IMemberRef memberRef:
Context.Decompiler.Write(writer, memberRef, DefaultFormatterOptions);
break;
default:
writer.Write(ContainerObject.ToString()!);
break;
}

switch (Referrer) {
case ParamDef param:
WriteParameterReference(writer, param);
break;
case GenericParam param:
writer.Write(" ");
writer.Write(TextColor.DarkGray, dnSpy_StringSearcher_Resources.ReferrerGenericParameter);
writer.Write(" ");
Context.Decompiler.Write(writer, param, DefaultFormatterOptions);
break;
}

writer.Write(" ");
writer.Write(TextColor.DarkGray, dnSpy_StringSearcher_Resources.ReferrerAttribute);
writer.Write(" ");
Context.Decompiler.Write(writer, CustomAttribute.AttributeType, DefaultFormatterOptions);
writer.Write(TextColor.Punctuation, " (");
writer.Write(TextColor.InstanceProperty, argumentName);
writer.Write(TextColor.Punctuation, ")");
}
}
}
27 changes: 27 additions & 0 deletions Extensions/dnSpy.StringSearcher/ILStringReference.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
using dnlib.DotNet;
using dnSpy.Contracts.Text;
using dnSpy.Contracts.Text.Classification;

namespace dnSpy.StringSearcher {
public sealed class ILStringReference(StringReferenceContext context, string literal, MethodDef referrer, uint offset)
: StringReference(context, literal, referrer) {

public new MethodDef Referrer => (MethodDef)base.Referrer;

public override StringReferenceKind Kind => StringReferenceKind.IL;

public override ModuleDef Module => Referrer.Module;

public override MDToken Token => Referrer.MDToken;

public uint Offset { get; } = offset;

public override object ContainerObject => Referrer;

protected override void WriteReferrerUI(TextClassifierTextColorWriter writer) {
Context.Decompiler.Write(writer, Referrer, DefaultFormatterOptions);
writer.Write(TextColor.Punctuation, "+");
writer.Write(TextColor.Label, $"IL_{Offset:X4}");
}
}
}
Loading
Loading