Skip to content

Commit 653971b

Browse files
committed
VS插件
1 parent c8401f9 commit 653971b

17 files changed

+951
-0
lines changed

.gitignore

+3
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,6 @@ node_modules
55
src/*.js
66
.DS_Store
77
codepulse-idea/.idea/
8+
.vs
9+
bin
10+
obj
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
2+
Microsoft Visual Studio Solution File, Format Version 12.00
3+
# Visual Studio Version 17
4+
VisualStudioVersion = 17.9.34728.123
5+
MinimumVisualStudioVersion = 10.0.40219.1
6+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "codepulse-visualstudio", "codepulse-visualstudio\codepulse-visualstudio.csproj", "{BCCE28AF-C6CA-4071-B071-0A5B55568B97}"
7+
EndProject
8+
Global
9+
GlobalSection(SolutionConfigurationPlatforms) = preSolution
10+
Debug|Any CPU = Debug|Any CPU
11+
Debug|arm64 = Debug|arm64
12+
Debug|x86 = Debug|x86
13+
Release|Any CPU = Release|Any CPU
14+
Release|arm64 = Release|arm64
15+
Release|x86 = Release|x86
16+
EndGlobalSection
17+
GlobalSection(ProjectConfigurationPlatforms) = postSolution
18+
{BCCE28AF-C6CA-4071-B071-0A5B55568B97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
19+
{BCCE28AF-C6CA-4071-B071-0A5B55568B97}.Debug|Any CPU.Build.0 = Debug|Any CPU
20+
{BCCE28AF-C6CA-4071-B071-0A5B55568B97}.Debug|arm64.ActiveCfg = Debug|arm64
21+
{BCCE28AF-C6CA-4071-B071-0A5B55568B97}.Debug|arm64.Build.0 = Debug|arm64
22+
{BCCE28AF-C6CA-4071-B071-0A5B55568B97}.Debug|x86.ActiveCfg = Debug|x86
23+
{BCCE28AF-C6CA-4071-B071-0A5B55568B97}.Debug|x86.Build.0 = Debug|x86
24+
{BCCE28AF-C6CA-4071-B071-0A5B55568B97}.Release|Any CPU.ActiveCfg = Release|Any CPU
25+
{BCCE28AF-C6CA-4071-B071-0A5B55568B97}.Release|Any CPU.Build.0 = Release|Any CPU
26+
{BCCE28AF-C6CA-4071-B071-0A5B55568B97}.Release|arm64.ActiveCfg = Release|arm64
27+
{BCCE28AF-C6CA-4071-B071-0A5B55568B97}.Release|arm64.Build.0 = Release|arm64
28+
{BCCE28AF-C6CA-4071-B071-0A5B55568B97}.Release|x86.ActiveCfg = Release|x86
29+
{BCCE28AF-C6CA-4071-B071-0A5B55568B97}.Release|x86.Build.0 = Release|x86
30+
EndGlobalSection
31+
GlobalSection(SolutionProperties) = preSolution
32+
HideSolutionNode = FALSE
33+
EndGlobalSection
34+
GlobalSection(ExtensibilityGlobals) = postSolution
35+
SolutionGuid = {C540FBAF-5FEB-48CB-83DE-DAD0A10CB3AA}
36+
EndGlobalSection
37+
EndGlobal
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
using Microsoft.VisualStudio.Shell;
2+
using System;
3+
using System.Runtime.InteropServices;
4+
using System.Threading;
5+
using Task = System.Threading.Tasks.Task;
6+
7+
namespace CodePulse
8+
{
9+
/// <summary>
10+
/// This is the class that implements the package exposed by this assembly.
11+
/// </summary>
12+
/// <remarks>
13+
/// <para>
14+
/// The minimum requirement for a class to be considered a valid package for Visual Studio
15+
/// is to implement the IVsPackage interface and register itself with the shell.
16+
/// This package uses the helper classes defined inside the Managed Package Framework (MPF)
17+
/// to do it: it derives from the Package class that provides the implementation of the
18+
/// IVsPackage interface and uses the registration attributes defined in the framework to
19+
/// register itself and its components with the shell. These attributes tell the pkgdef creation
20+
/// utility what data to put into .pkgdef file.
21+
/// </para>
22+
/// <para>
23+
/// To get loaded into VS, the package must be referred by &lt;Asset Type="Microsoft.VisualStudio.VsPackage" ...&gt; in .vsixmanifest file.
24+
/// </para>
25+
/// </remarks>
26+
[PackageRegistration(UseManagedResourcesOnly = true, AllowsBackgroundLoading = true)]
27+
[Guid(CodePulse.PackageGuidString)]
28+
[ProvideMenuResource("Menus.ctmenu", 1)]
29+
public sealed class CodePulse : AsyncPackage
30+
{
31+
/// <summary>
32+
/// codepulse_visualstudioPackage GUID string.
33+
/// </summary>
34+
public const string PackageGuidString = "c74266cb-ac06-4f69-915e-503598095b0e";
35+
36+
#region Package Members
37+
38+
/// <summary>
39+
/// Initialization of the package; this method is called right after the package is sited, so this is the place
40+
/// where you can put all the initialization code that rely on services provided by VisualStudio.
41+
/// </summary>
42+
/// <param name="cancellationToken">A cancellation token to monitor for initialization cancellation, which can occur when VS is shutting down.</param>
43+
/// <param name="progress">A provider for progress updates.</param>
44+
/// <returns>A task representing the async work of package initialization, or an already completed task if there is none. Do not return null from this method.</returns>
45+
protected override async Task InitializeAsync(CancellationToken cancellationToken, IProgress<ServiceProgressData> progress)
46+
{
47+
// When initialized asynchronously, the current thread may be a background thread at this point.
48+
// Do any initialization that requires the UI thread after switching to the UI thread.
49+
await this.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken);
50+
await SettingCommand.InitializeAsync(this);
51+
}
52+
53+
#endregion
54+
}
55+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<CommandTable xmlns="http://schemas.microsoft.com/VisualStudio/2005-10-18/CommandTable" xmlns:xs="http://www.w3.org/2001/XMLSchema">
3+
4+
<!-- This is the file that defines the actual layout and type of the commands.
5+
It is divided in different sections (e.g. command definition, command
6+
placement, ...), with each defining a specific set of properties.
7+
See the comment before each section for more details about how to
8+
use it. -->
9+
10+
<!-- The VSCT compiler (the tool that translates this file into the binary
11+
format that VisualStudio will consume) has the ability to run a preprocessor
12+
on the vsct file; this preprocessor is (usually) the C++ preprocessor, so
13+
it is possible to define includes and macros with the same syntax used
14+
in C++ files. Using this ability of the compiler here, we include some files
15+
defining some of the constants that we will use inside the file. -->
16+
17+
<!--This is the file that defines the IDs for all the commands exposed by VisualStudio. -->
18+
<Extern href="stdidcmd.h"/>
19+
20+
<!--This header contains the command ids for the menus provided by the shell. -->
21+
<Extern href="vsshlids.h"/>
22+
23+
<!--The Commands section is where commands, menus, and menu groups are defined.
24+
This section uses a Guid to identify the package that provides the command defined inside it. -->
25+
<Commands package="guidCodePulse">
26+
<!-- Inside this section we have different sub-sections: one for the menus, another
27+
for the menu groups, one for the buttons (the actual commands), one for the combos
28+
and the last one for the bitmaps used. Each element is identified by a command id that
29+
is a unique pair of guid and numeric identifier; the guid part of the identifier is usually
30+
called "command set" and is used to group different command inside a logically related
31+
group; your package should define its own command set in order to avoid collisions
32+
with command ids defined by other packages. -->
33+
34+
<!-- In this section you can define new menu groups. A menu group is a container for
35+
other menus or buttons (commands); from a visual point of view you can see the
36+
group as the part of a menu contained between two lines. The parent of a group
37+
must be a menu. -->
38+
<Groups>
39+
<Group guid="guidCodePulseCmdSet" id="MyMenuGroup" priority="0x0600">
40+
<Parent guid="guidSHLMainMenu" id="IDM_VS_MENU_TOOLS"/>
41+
</Group>
42+
</Groups>
43+
44+
<!--Buttons section. -->
45+
<!--This section defines the elements the user can interact with, like a menu command or a button
46+
or combo box in a toolbar. -->
47+
<Buttons>
48+
<!--To define a menu group you have to specify its ID, the parent menu and its display priority.
49+
The command is visible and enabled by default. If you need to change the visibility, status, etc, you can use
50+
the CommandFlag node.
51+
You can add more than one CommandFlag node e.g.:
52+
<CommandFlag>DefaultInvisible</CommandFlag>
53+
<CommandFlag>DynamicVisibility</CommandFlag>
54+
If you do not want an image next to your command, remove the Icon node /> -->
55+
<Button guid="guidCodePulseCmdSet" id="SettingCommandId" priority="0x0100" type="Button">
56+
<Parent guid="guidCodePulseCmdSet" id="MyMenuGroup" />
57+
<Icon guid="guidImages" id="bmpPic1" />
58+
<Strings>
59+
<ButtonText>码脉设置</ButtonText>
60+
</Strings>
61+
</Button>
62+
</Buttons>
63+
64+
<!--The bitmaps section is used to define the bitmaps that are used for the commands.-->
65+
<Bitmaps>
66+
<!-- The bitmap id is defined in a way that is a little bit different from the others:
67+
the declaration starts with a guid for the bitmap strip, then there is the resource id of the
68+
bitmap strip containing the bitmaps and then there are the numeric ids of the elements used
69+
inside a button definition. An important aspect of this declaration is that the element id
70+
must be the actual index (1-based) of the bitmap inside the bitmap strip. -->
71+
<Bitmap guid="guidImages" href="Resources\SettingCommand.png" usedList="bmpPic1, bmpPic2, bmpPicSearch, bmpPicX, bmpPicArrows, bmpPicStrikethrough"/>
72+
</Bitmaps>
73+
</Commands>
74+
75+
<Symbols>
76+
<!-- This is the package guid. -->
77+
<GuidSymbol name="guidCodePulse" value="{c74266cb-ac06-4f69-915e-503598095b0e}" />
78+
79+
<!-- This is the guid used to group the menu commands together -->
80+
<GuidSymbol name="guidCodePulseCmdSet" value="{2c981448-c814-4c66-a9ff-c4d42bf444c3}">
81+
<IDSymbol name="MyMenuGroup" value="0x1020" />
82+
<IDSymbol name="SettingCommandId" value="0x0100" />
83+
</GuidSymbol>
84+
85+
<GuidSymbol name="guidImages" value="{9a9a80d6-814a-458a-b7ff-794d95e5b8c7}" >
86+
<IDSymbol name="bmpPic1" value="1" />
87+
<IDSymbol name="bmpPic2" value="2" />
88+
<IDSymbol name="bmpPicSearch" value="3" />
89+
<IDSymbol name="bmpPicX" value="4" />
90+
<IDSymbol name="bmpPicArrows" value="5" />
91+
<IDSymbol name="bmpPicStrikethrough" value="6" />
92+
</GuidSymbol>
93+
</Symbols>
94+
</CommandTable>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Text;
5+
using System.Threading.Tasks;
6+
7+
namespace CodePulse
8+
{
9+
public class ConfigFile
10+
{
11+
public readonly string ConfigFilepath;
12+
13+
public ConfigFile(string configFilepath) => this.ConfigFilepath = configFilepath;
14+
15+
public string GetSetting(string key, string section = "settings")
16+
{
17+
StringBuilder retVal = new StringBuilder((int)byte.MaxValue);
18+
return NativeMethods.GetPrivateProfileString(section, key, "", retVal, (int)byte.MaxValue, this.ConfigFilepath) <= 0U ? string.Empty : retVal.ToString();
19+
}
20+
21+
public bool GetSettingAsBoolean(string key, bool @default = false, string section = "settings")
22+
{
23+
StringBuilder retVal = new StringBuilder((int)byte.MaxValue);
24+
bool result;
25+
return NativeMethods.GetPrivateProfileString(section, key, @default.ToString(), retVal, (int)byte.MaxValue, this.ConfigFilepath) > 0U && bool.TryParse(retVal.ToString(), out result) ? result : @default;
26+
}
27+
28+
public void SaveSetting(string section, string key, string value)
29+
{
30+
if (bool.TryParse(value.Trim(), out bool _))
31+
NativeMethods.WritePrivateProfileString(section, key, value.Trim().ToLower(), this.ConfigFilepath);
32+
else
33+
NativeMethods.WritePrivateProfileString(section, key, value.Trim(), this.ConfigFilepath);
34+
}
35+
}
36+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Text;
5+
using System.Threading.Tasks;
6+
7+
namespace CodePulse
8+
{
9+
public interface ILogger
10+
{
11+
void Debug(string message);
12+
13+
void Error(string message, Exception ex = null);
14+
15+
void Warning(string message);
16+
17+
void Info(string message);
18+
}
19+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Text;
5+
using System.Threading.Tasks;
6+
7+
namespace CodePulse
8+
{
9+
public enum LogLevel
10+
{
11+
Debug = 1,
12+
Info = 2,
13+
Warning = 3,
14+
HandledException = 4,
15+
}
16+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
using Microsoft.VisualStudio.Shell.Interop;
2+
using System;
3+
using System.Collections.Generic;
4+
using System.Globalization;
5+
using System.Linq;
6+
using System.Text;
7+
using System.Threading.Tasks;
8+
using Microsoft.VisualStudio.Shell;
9+
10+
namespace CodePulse
11+
{
12+
public class Logger : ILogger
13+
{
14+
private IVsOutputWindowPane _wakatimeOutputWindowPane;
15+
private IVsOutputWindowPane WakatimeOutputWindowPane =>
16+
_wakatimeOutputWindowPane ?? (_wakatimeOutputWindowPane = GetWakatimeOutputWindowPane());
17+
private readonly bool _isDebugEnabled;
18+
19+
public Logger(string configFilepath)
20+
{
21+
var configFile = new ConfigFile(configFilepath);
22+
23+
_isDebugEnabled = configFile.GetSettingAsBoolean("debug");
24+
}
25+
26+
private static IVsOutputWindowPane GetWakatimeOutputWindowPane()
27+
{
28+
if (!(Package.GetGlobalService(typeof(SVsOutputWindow)) is IVsOutputWindow outputWindow)) return null;
29+
30+
var outputPaneGuid = new Guid(GuidList.GuidWakatimeOutputPane.ToByteArray());
31+
32+
outputWindow.CreatePane(ref outputPaneGuid, "WakaTime", 1, 1);
33+
outputWindow.GetPane(ref outputPaneGuid, out var windowPane);
34+
35+
return windowPane;
36+
}
37+
38+
public void Debug(string message)
39+
{
40+
if (!_isDebugEnabled)
41+
return;
42+
43+
Log(LogLevel.Debug, message);
44+
}
45+
46+
public void Error(string message, Exception ex = null)
47+
{
48+
var exceptionMessage = $"{message}: {ex}";
49+
50+
Log(LogLevel.HandledException, exceptionMessage);
51+
}
52+
53+
public void Warning(string message)
54+
{
55+
Log(LogLevel.Warning, message);
56+
}
57+
58+
public void Info(string message)
59+
{
60+
Log(LogLevel.Info, message);
61+
}
62+
63+
private void Log(LogLevel level, string message)
64+
{
65+
var outputWindowPane = WakatimeOutputWindowPane;
66+
if (outputWindowPane == null) return;
67+
68+
var outputMessage =
69+
$"[CodePulse {Enum.GetName(level.GetType(), level)} {DateTime.Now.ToString("hh:mm:ss tt", CultureInfo.InvariantCulture)}] {message}{Environment.NewLine}";
70+
71+
outputWindowPane.OutputString(outputMessage);
72+
}
73+
}
74+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Runtime.InteropServices;
5+
using System.Text;
6+
using System.Threading.Tasks;
7+
8+
namespace CodePulse
9+
{
10+
public static class GuidList
11+
{
12+
public const string GuidWakaTimePkgString = "52d9c3ff-c893-408e-95e4-d7484ec7fa47";
13+
public const string GuidWakaTimeUIString = "ADFC4E64-0397-11D1-9F4E-00A0C911004F";
14+
public const string GuidWakaTimeCmdSetString = "054caf12-7fba-40d1-8dc8-bd69f838b910";
15+
public static readonly Guid GuidWakatimeOutputPane = new Guid("a635ec18-1b8f-468d-832e-8e5eda489815");
16+
}
17+
18+
internal static class NativeMethods
19+
{
20+
[DllImport("kernel32", CharSet = CharSet.Auto, SetLastError = true)]
21+
internal static extern bool WritePrivateProfileString(
22+
[MarshalAs(UnmanagedType.LPWStr)] string section,
23+
[MarshalAs(UnmanagedType.LPWStr)] string key,
24+
[MarshalAs(UnmanagedType.LPWStr)] string val,
25+
[MarshalAs(UnmanagedType.LPWStr)] string filePath);
26+
27+
[DllImport("kernel32", CharSet = CharSet.Auto, SetLastError = true)]
28+
internal static extern uint GetPrivateProfileString(
29+
[MarshalAs(UnmanagedType.LPWStr)] string section,
30+
[MarshalAs(UnmanagedType.LPWStr)] string key,
31+
[MarshalAs(UnmanagedType.LPWStr)] string def,
32+
[MarshalAs(UnmanagedType.LPWStr)] StringBuilder retVal,
33+
int size,
34+
[MarshalAs(UnmanagedType.LPWStr)] string filePath);
35+
36+
[DllImport("kernel32.dll", SetLastError = true)]
37+
[return: MarshalAs(UnmanagedType.Bool)]
38+
internal static extern bool IsWow64Process([In] IntPtr hProcess, out bool wow64Process);
39+
}
40+
}

0 commit comments

Comments
 (0)